Compare commits

...

8 Commits

Author SHA1 Message Date
yu.kuai fe60e2924d fix: 发布人员 icon 调整大小 2025-11-05 10:55:26 +08:00
yu.kuai edb7e01085 feat: 历史版本展示 markdown 文本 2025-11-05 10:40:41 +08:00
yu.kuai a4679f0ada feat: 前台支持展示更新人/创建人 2025-11-05 10:40:41 +08:00
yu.kuai 5e93e9da73 feat: 文档显示人员操作记录 2025-11-05 10:40:41 +08:00
xiaomakuaiz d630567a3c
Merge pull request #1463 from guanweiwang/pref/landing
pref: 优化主题
2025-11-04 20:03:09 +08:00
Gavan 20a0a4ded4 style: 样式问题 2025-11-04 19:56:44 +08:00
Gavan 38383b983d pref: 美化样式 2025-11-04 19:46:36 +08:00
Gavan 030a8ac25d pref: 优化主题 2025-11-04 19:13:31 +08:00
15 changed files with 254 additions and 56 deletions

File diff suppressed because one or more lines are too long

View File

@ -385,14 +385,14 @@ const ThemeWrapper = ({ children }: { children: React.ReactNode }) => {
const { appPreviewData } = useAppSelector(state => state.config);
const theme = useMemo(() => {
const themeName =
appPreviewData?.settings?.web_app_landing_theme?.name || 'blue';
return createTheme(
// @ts-expect-error themeOptions is not typed
{
...themeOptions[0],
palette:
THEME_TO_PALETTE[
appPreviewData?.settings?.web_app_landing_theme?.name || 'blue'
].palette,
THEME_TO_PALETTE[themeName]?.palette || THEME_TO_PALETTE.blue.palette,
},
...themeOptions.slice(1),
);

View File

@ -269,7 +269,9 @@ const ComponentBar = ({
<Stack sx={{ pr: '20px', marginTop: '15px' }}>
<Select
value={
appPreviewData.settings?.web_app_landing_theme?.name || 'blue'
THEME_TO_PALETTE[
appPreviewData.settings?.web_app_landing_theme?.name || 'blue'
]?.value || 'blue'
}
renderValue={value => {
return THEME_TO_PALETTE[value]?.label;

View File

@ -1,7 +1,7 @@
import { DomainNodeReleaseListItem } from '@/request/pro';
import { Modal } from '@ctzhian/ui';
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
import { Box, Stack } from '@mui/material';
import { Modal } from '@ctzhian/ui';
interface VersionRollbackProps {
open: boolean;
@ -29,14 +29,32 @@ const VersionRollback = ({
onOk={onOk}
onCancel={onClose}
>
<Stack direction='row' spacing={2} sx={{ mb: 1, lineHeight: '24px' }}>
<Box sx={{ fontSize: 14, width: 100 }}></Box>
<Stack direction='row' spacing={2}>
<Box sx={{ fontSize: 14, width: 100, flexShrink: 0 }}></Box>
<Box sx={{ fontWeight: 700 }}>{data.release_name}</Box>
</Stack>
<Stack direction='row' spacing={2} sx={{ lineHeight: '24px' }}>
<Stack direction='row' spacing={2} sx={{ mt: 2 }}>
<Box sx={{ fontSize: 14, width: 100, flexShrink: 0 }}></Box>
<Box sx={{ fontSize: 14, mt: 1 }}>{data.release_message}</Box>
<Box sx={{ fontSize: 14 }}>{data.release_message}</Box>
</Stack>
{data.creator_account && (
<Stack direction='row' spacing={2} sx={{ mt: 2 }}>
<Box sx={{ fontSize: 14, width: 100, flexShrink: 0 }}></Box>
<Box sx={{ fontSize: 14 }}>{data.creator_account}</Box>
</Stack>
)}
{data.editor_account && (
<Stack direction='row' spacing={2} sx={{ mt: 2 }}>
<Box sx={{ fontSize: 14, width: 100, flexShrink: 0 }}></Box>
<Box sx={{ fontSize: 14 }}>{data.editor_account}</Box>
</Stack>
)}
{data.publisher_account && (
<Stack direction='row' spacing={2} sx={{ mt: 2 }}>
<Box sx={{ fontSize: 14, width: 100, flexShrink: 0 }}></Box>
<Box sx={{ fontSize: 14 }}>{data.publisher_account}</Box>
</Stack>
)}
</Modal>
);
};

View File

@ -329,6 +329,37 @@ const Wrap = ({ detail: defaultDetail }: WrapProps) => {
/>
</Stack>
<Stack direction={'row'} alignItems={'center'} gap={2} sx={{ mb: 4 }}>
{nodeDetail?.editor_account && (
<Tooltip
arrow
title={
nodeDetail?.creator_account || nodeDetail?.publisher_account ? (
<Stack>
{nodeDetail?.creator_account && (
<Box>{nodeDetail?.creator_account}</Box>
)}
{nodeDetail?.publisher_account && (
<Box>{nodeDetail?.publisher_account}</Box>
)}
</Stack>
) : null
}
>
<Stack
direction={'row'}
alignItems={'center'}
gap={0.5}
sx={{
cursor: 'pointer',
fontSize: 12,
color: 'text.tertiary',
}}
>
<Icon type='icon-tianjiawendang' sx={{ fontSize: 9 }} />
{nodeDetail?.editor_account}
</Stack>
</Tooltip>
)}
<Tooltip arrow title={isEnterprise ? '查看历史版本' : ''}>
<Stack
direction={'row'}

View File

@ -8,13 +8,14 @@ import {
} from '@/request/pro';
import { useAppSelector } from '@/store';
import { Editor, useTiptap } from '@ctzhian/tiptap';
import { Icon } from '@ctzhian/ui';
import { Ellipsis, Icon } from '@ctzhian/ui';
import {
alpha,
Box,
Divider,
IconButton,
Stack,
Tooltip,
useTheme,
} from '@mui/material';
import { useEffect, useState } from 'react';
@ -39,6 +40,8 @@ const History = () => {
);
const [characterCount, setCharacterCount] = useState(0);
const [isMarkdown, setIsMarkdown] = useState(false);
const editorRef = useTiptap({
content: '',
editable: false,
@ -48,10 +51,26 @@ const History = () => {
},
});
const editorMdRef = useTiptap({
content: '',
contentType: 'markdown',
editable: false,
immediatelyRender: true,
onUpdate: ({ editor }) => {
setCharacterCount((editor.storage as any).characterCount.characters());
},
});
const getDetail = (v: DomainNodeReleaseListItem) => {
getApiProV1NodeReleaseDetail({ id: v.id! }).then(res => {
getApiProV1NodeReleaseDetail({ id: v.id!, kb_id: kb_id! }).then(res => {
setCurNode(res);
editorRef.setContent(res.content || '');
if (res.meta?.content_type === 'md') {
setIsMarkdown(true);
editorMdRef.setContent(res.content || '');
} else {
setIsMarkdown(false);
editorRef.setContent(res.content || '');
}
window.scrollTo({ top: 0, behavior: 'smooth' });
});
};
@ -166,9 +185,51 @@ const History = () => {
<Stack
direction={'row'}
alignItems={'center'}
flexWrap={'wrap'}
gap={2}
sx={{ mb: 4, fontSize: 12, color: 'text.tertiary' }}
>
{curNode.editor_account && (
<Tooltip
arrow
title={
curNode.creator_account || curNode.publisher_account ? (
<Stack>
{curNode.creator_account && (
<Stack
direction={'row'}
alignItems={'center'}
gap={0.5}
>
<Icon type='icon-chuangjian' />
{curNode.creator_account}
</Stack>
)}
{curNode.publisher_account && (
<Stack
direction={'row'}
alignItems={'center'}
gap={0.5}
>
<Icon type='icon-fabu' />
{curNode.publisher_account}
</Stack>
)}
</Stack>
) : null
}
>
<Stack
direction={'row'}
alignItems={'center'}
gap={0.5}
sx={{ cursor: 'pointer' }}
>
<Icon type='icon-tianjiawendang' sx={{ fontSize: 9 }} />
{curNode.editor_account}
</Stack>
</Tooltip>
)}
<Stack direction={'row'} alignItems={'center'} gap={0.5}>
<Icon type='icon-a-shijian2' />
{curVersion?.release_message}
@ -206,13 +267,6 @@ const History = () => {
</Box>
</Box>
)}
{/* <EditorThemeProvider
colors={{ light }}
mode='light'
theme={{
components: componentStyleOverrides,
}}
> */}
<Box
sx={{
'.tiptap': {
@ -224,9 +278,12 @@ const History = () => {
},
}}
>
<Editor editor={editorRef.editor} />
{isMarkdown ? (
<Editor editor={editorMdRef.editor} />
) : (
<Editor editor={editorRef.editor} />
)}
</Box>
{/* </EditorThemeProvider> */}
</Box>
)}
</Box>
@ -268,19 +325,39 @@ const History = () => {
setCurVersion(item);
}}
>
<Ellipsis sx={{ color: 'text.primary' }}>
{item.release_name}
</Ellipsis>
<Box sx={{ fontSize: 13, color: 'text.tertiary' }}>
{item.release_message}
</Box>
<Stack
direction={'row'}
alignItems={'center'}
justifyContent={'space-between'}
sx={{ mb: 1 }}
sx={{ mt: 1, height: 21 }}
>
<Box
sx={{
color: 'text.primary',
}}
>
{item.release_name}
</Box>
{item.publisher_account ? (
<Stack
direction={'row'}
alignItems={'center'}
gap={0.5}
sx={{
bgcolor: 'primary.main',
display: 'inline-flex',
color: 'white',
borderRadius: '4px',
p: 0.5,
fontSize: 12,
lineHeight: 1,
}}
>
<Icon type='icon-fabu' />
{item.publisher_account}
</Stack>
) : (
<Box></Box>
)}
{curVersion?.id === item.id && (
<Box
sx={{
@ -301,9 +378,6 @@ const History = () => {
</Box>
)}
</Stack>
<Box sx={{ fontSize: 13, color: 'text.tertiary' }}>
{item.release_message}
</Box>
</Box>
{idx !== list.length - 1 && <Divider sx={{ my: 0.5 }} />}
</>

View File

@ -106,8 +106,15 @@ export interface DomainDocumentFeedbackListItem {
export interface DomainGetNodeReleaseDetailResp {
content?: string;
creator_account?: string;
creator_id?: string;
editor_account?: string;
editor_id?: string;
meta?: DomainNodeMeta;
name?: string;
node_id?: string;
publisher_account?: string;
publisher_id?: string;
}
export interface DomainIPAddress {
@ -131,11 +138,16 @@ export interface DomainNodeMeta {
}
export interface DomainNodeReleaseListItem {
creator_account?: string;
creator_id?: string;
editor_account?: string;
editor_id?: string;
id?: string;
meta?: DomainNodeMeta;
name?: string;
node_id?: string;
/** release */
publisher_account?: string;
publisher_id?: string;
release_id?: string;
release_message?: string;
release_name?: string;
@ -632,6 +644,7 @@ export interface GetApiProV1DocumentListParams {
export interface GetApiProV1NodeReleaseDetailParams {
id: string;
kb_id: string;
}
export interface GetApiProV1NodeReleaseListParams {

View File

@ -1339,6 +1339,8 @@ export interface DomainWidgetBotSettings {
btn_logo?: string;
btn_text?: string;
is_open?: boolean;
recommend_node_ids?: string[];
recommend_questions?: string[];
theme_mode?: string;
}
@ -1579,12 +1581,18 @@ export interface V1LoginResp {
export interface V1NodeDetailResp {
content?: string;
created_at?: string;
creator_account?: string;
creator_id?: string;
editor_account?: string;
editor_id?: string;
id?: string;
kb_id?: string;
meta?: DomainNodeMeta;
name?: string;
parent_id?: string;
permissions?: DomainNodePermissions;
publisher_account?: string;
publisher_id?: string;
status?: DomainNodeStatus;
type?: DomainNodeType;
updated_at?: string;

View File

@ -19,7 +19,9 @@ const HomePage = () => {
cssVariables: {
cssVarPrefix: 'welcome',
},
palette: THEME_TO_PALETTE[themeMode].palette,
palette:
THEME_TO_PALETTE[themeMode]?.palette ||
THEME_TO_PALETTE['blue'].palette,
typography: {
fontFamily: 'var(--font-gilory), PingFang SC, sans-serif',
},

View File

@ -136,6 +136,8 @@ export interface NodeDetail {
created_at: string;
updated_at: string;
type: 1 | 2;
creator_account: string;
editor_account: string;
meta: {
doc_width: string;
summary: string;

View File

@ -1,18 +1,17 @@
'use client';
import { NodeDetail } from '@/assets/type';
import { IconFile, IconFolder } from '@/components/icons';
import CommentInput, {
ImageItem,
CommentInputRef,
ImageItem,
} from '@/components/commentInput';
import { IconFile, IconFolder } from '@/components/icons';
import { DocWidth } from '@/constant';
import { useStore } from '@/provider';
import {
getShareV1CommentList,
postShareV1Comment,
} from '@/request/ShareComment';
import { PhotoProvider, PhotoView } from 'react-photo-view';
import { Editor, UseTiptapReturn } from '@ctzhian/tiptap';
import { message } from '@ctzhian/ui';
import { Box, Button, Divider, Stack, TextField, alpha } from '@mui/material';
@ -22,6 +21,7 @@ import relativeTime from 'dayjs/plugin/relativeTime';
import { useParams } from 'next/navigation';
import React, { useEffect, useRef, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { PhotoProvider, PhotoView } from 'react-photo-view';
dayjs.extend(relativeTime);
dayjs.locale('zh-cn');
@ -207,11 +207,23 @@ const DocContent = ({
color: 'text.tertiary',
}}
>
{info?.created_at && <Box>{dayjs(info?.created_at).fromNow()}</Box>}
{info?.created_at && (
<Box>
{info?.creator_account && info?.creator_account === 'admin'
? '管理员'
: info?.creator_account}{' '}
{dayjs(info?.created_at).fromNow()}
</Box>
)}
{info?.updated_at && info.updated_at.slice(0, 1) !== '0' && (
<>
<Box>·</Box>
<Box>{dayjs(info.updated_at).fromNow()}</Box>
<Box>
{info?.creator_account && info?.creator_account === 'admin'
? '管理员'
: info?.creator_account}{' '}
{dayjs(info.updated_at).fromNow()}
</Box>
</>
)}
{!!characterCount && characterCount > 0 && (

View File

@ -46,11 +46,11 @@ export const THEME_LIST = [
value: 'darkDeepForest',
palette: darkDeepForestPalette,
},
{
label: '深邃黑',
value: 'white',
palette: whitePalette,
},
// {
// label: '深邃黑',
// value: 'white',
// palette: whitePalette,
// },
{
label: '电光蓝',
value: 'electricBlue',

View File

@ -18,9 +18,10 @@ interface BlockGridProps {
}
const StyledBlockGridItem = styled(Stack)(({ theme }) => ({
aspectRatio: '1 / 1',
position: 'relative',
border: `1px solid ${alpha(theme.palette.text.primary, 0.15)}`,
borderRadius: '10px',
padding: theme.spacing(2),
padding: theme.spacing(1),
boxShadow: `0px 5px 20px 0px ${alpha(theme.palette.text.primary, 0.06)}`,
transition: 'all 0.2s ease',
'&:hover': {
@ -44,14 +45,22 @@ export const StyledBlockGridItemImg = styled('img')(({ theme }) => ({
}));
const StyledBlockGridItemTitle = styled('div')(({ theme }) => ({
position: 'absolute',
bottom: '24px',
left: '50%',
maxWidth: 'calc(100% - 24px)',
transform: 'translateX(-50%)',
padding: theme.spacing(0.5, 1),
overflow: 'hidden',
textOverflow: 'ellipsis',
flexShrink: 0,
whiteSpace: 'nowrap',
fontSize: 16,
fontSize: 14,
textAlign: 'center',
fontWeight: 700,
color: theme.palette.text.primary,
color: theme.palette.background.default,
backgroundColor: alpha(theme.palette.text.primary, 0.5),
borderRadius: '6px',
}));
// 单个卡片组件,带动画效果

View File

@ -68,7 +68,7 @@ const StyledSwiperSlideDesc = styled('div')(({ theme }) => ({
fontSize: 14,
fontWeight: 400,
color: theme.palette.background.default,
borderRadius: '12px',
borderRadius: '6px',
overflow: 'hidden',
whiteSpace: 'nowrap',
zIndex: 0,
@ -113,7 +113,7 @@ const StyledTabs = styled(Tabs)(({ theme }) => ({
const StyledTab = styled(Tab)(({ theme }) => ({
minHeight: 'auto',
padding: theme.spacing(1, 2),
borderRadius: '10px',
borderRadius: '6px',
backgroundColor: theme.palette.background.default,
color: theme.palette.text.secondary,
fontSize: 14,

View File

@ -18,8 +18,8 @@ interface ImgTextProps {
const StyledImgTextItem = styled(Stack)(({ theme }) => ({}));
export const StyledImgTextItemImg = styled('img')(({ theme }) => ({
maxWidth: 350,
maxHeight: 350,
maxWidth: '100%',
maxHeight: '100%',
width: '100%',
height: '100%',
objectFit: 'cover',
@ -28,7 +28,7 @@ export const StyledImgTextItemImg = styled('img')(({ theme }) => ({
}));
const StyledImgTextItemTitle = styled('h3')(({ theme }) => ({
fontSize: 20,
fontSize: 24,
fontWeight: 700,
color: theme.palette.text.primary,
}));
@ -73,7 +73,7 @@ const ImgText: React.FC<ImgTextProps> = React.memo(
<StyledTopicBox>
<StyledTopicTitle ref={titleRef}>{title}</StyledTopicTitle>
<StyledImgTextItem
gap={mobile ? 4 : { xs: 4, sm: 6, md: 38 }}
gap={mobile ? 4 : { xs: 4, sm: 6, md: 16 }}
direction={
mobile
? 'column-reverse'
@ -96,9 +96,36 @@ const ImgText: React.FC<ImgTextProps> = React.memo(
gap={1}
sx={{ width: '100%' }}
ref={cardRightRef as React.Ref<HTMLDivElement>}
alignItems={
mobile
? 'flex-start'
: direction === 'row'
? 'flex-start'
: 'flex-end'
}
>
<StyledImgTextItemTitle>{item.name}</StyledImgTextItemTitle>
<StyledImgTextItemSummary>{item.desc}</StyledImgTextItemSummary>
<StyledImgTextItemTitle
sx={{
textAlign: mobile
? 'left'
: direction === 'row'
? 'left'
: 'right',
}}
>
{item.name}
</StyledImgTextItemTitle>
<StyledImgTextItemSummary
sx={{
textAlign: mobile
? 'left'
: direction === 'row'
? 'left'
: 'right',
}}
>
{item.desc}
</StyledImgTextItemSummary>
</Stack>
</StyledImgTextItem>
</StyledTopicBox>