Compare commits

...

7 Commits

Author SHA1 Message Date
Coltea 59ca885518
Merge pull request #1539 from KuaiYu95/fe/doc
文档前台查看功能
2025-11-19 18:48:14 +08:00
Coltea 0e64ff946f
Merge pull request #1540 from coltea/feat-node-list-publisher
feat 文档相关页面支持直接跳转至前台
2025-11-19 18:47:43 +08:00
coltea 19e6a66809 feat node list publish id 2025-11-19 18:43:10 +08:00
yu.kuai c15272aeb2 feat: 分享文档 2025-11-19 18:39:18 +08:00
yu.kuai 32ed999b48 fix: 挂件 esc 快捷关闭 2025-11-19 17:24:15 +08:00
Coltea 98e4a917e0
Merge pull request #1538 from guanweiwang/feature/icon
pref: 统一图标地址, 去除无用文件
2025-11-19 17:13:59 +08:00
Gavan 6e5f780771 pref: 统一图标地址, 去除无用文件 2025-11-19 17:08:28 +08:00
129 changed files with 877 additions and 4179 deletions

View File

@ -6582,6 +6582,9 @@ const docTemplate = `{
"position": {
"type": "number"
},
"publisher_id": {
"type": "string"
},
"rag_info": {
"$ref": "#/definitions/domain.RagInfo"
},

View File

@ -6575,6 +6575,9 @@
"position": {
"type": "number"
},
"publisher_id": {
"type": "string"
},
"rag_info": {
"$ref": "#/definitions/domain.RagInfo"
},

View File

@ -1763,6 +1763,8 @@ definitions:
$ref: '#/definitions/domain.NodePermissions'
position:
type: number
publisher_id:
type: string
rag_info:
$ref: '#/definitions/domain.RagInfo'
status:

View File

@ -174,6 +174,7 @@ type NodeListItemResp struct {
EditorId string `json:"editor_id"`
Creator string `json:"creator"`
Editor string `json:"editor"`
PublisherId string `json:"publisher_id" gorm:"-"`
Permissions NodePermissions `json:"permissions" gorm:"type:jsonb"`
}

View File

@ -157,6 +157,32 @@ func (r *NodeRepository) GetLatestNodeReleaseByNodeIDs(ctx context.Context, kbID
return nodeReleases, nil
}
func (r *NodeRepository) GetNodeReleasePublisherMap(ctx context.Context, kbID string) (map[string]string, error) {
type Result struct {
NodeID string `gorm:"column:node_id"`
PublisherID string `gorm:"column:publisher_id"`
}
var results []Result
if err := r.db.WithContext(ctx).
Model(&domain.NodeRelease{}).
Select("node_id, publisher_id").
Where("kb_id = ?", kbID).
Where("node_releases.doc_id != '' ").
Find(&results).Error; err != nil {
return nil, err
}
publisherMap := make(map[string]string)
for _, result := range results {
if result.PublisherID != "" {
publisherMap[result.NodeID] = result.PublisherID
}
}
return publisherMap, nil
}
func (r *NodeRepository) UpdateNodeContent(ctx context.Context, req *domain.UpdateNodeReq, userId string) error {
// Use transaction to ensure data consistency
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {

View File

@ -86,6 +86,21 @@ func (u *NodeUsecase) GetList(ctx context.Context, req *domain.GetNodeListReq) (
if err != nil {
return nil, err
}
if len(nodes) == 0 {
return nodes, nil
}
publisherMap, err := u.nodeRepo.GetNodeReleasePublisherMap(ctx, req.KBID)
if err != nil {
return nil, err
}
for _, node := range nodes {
if publisherID, exists := publisherMap[node.ID]; exists {
node.PublisherId = publisherID
}
}
return nodes, nil
}

View File

@ -8,7 +8,6 @@
"build:dev": "vite build --m development",
"build": "tsc -b && vite build",
"build:analyze": "tsc -b && vite build -- --analyze",
"icon": "node ./scripts/downLoadIcon.cjs",
"api": "cx-swagger-api"
},
"dependencies": {

View File

@ -1,23 +0,0 @@
const fs = require("fs");
const path = require("path");
async function downloadFile(url) {
const iconPath = path.resolve(__dirname, "../src/assets/fonts/iconfont.js");
const iconDir = path.dirname(iconPath);
// 检查目录是否存在,不存在则创建
if (!fs.existsSync(iconDir)) {
fs.mkdirSync(iconDir, { recursive: true });
console.log(`目录 ${iconDir} 已创建`);
}
const response = await fetch(`https:${url}`, {
method: "GET",
// responseType: "stream", // fetch 不支持此参数
}).then((res) => res.text());
fs.writeFileSync(iconPath, response);
console.log("Download Icon Success");
}
let argument = process.argv.splice(2);
downloadFile(argument[0]);

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,6 +1,6 @@
import Logo from '@/assets/images/logo.png';
import { Avatar as MuiAvatar, SxProps } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { IconDandulogo } from '@panda-wiki/icons';
import { ReactNode } from 'react';
interface AvatarProps {
@ -15,9 +15,8 @@ const Avatar = (props: AvatarProps) => {
const src = props.src;
const LogoIcon = (
<Icon
<IconDandulogo
sx={{ width: '100%', height: '100%', color: 'text.primary' }}
type='icon-dandulogo'
/>
);

View File

@ -1,5 +1,5 @@
import { FooterSetting } from '@/api/type';
import { Icon } from '@ctzhian/ui';
import { IconShanchu2, IconDrag, IconTianjia } from '@panda-wiki/icons';
import {
closestCenter,
DndContext,
@ -100,7 +100,7 @@ const LinkItem = forwardRef<HTMLDivElement, LinkItemProps>(
}}
{...dragHandleProps}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
<Box
sx={{
@ -123,7 +123,7 @@ const LinkItem = forwardRef<HTMLDivElement, LinkItemProps>(
ml: 'auto',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
</Stack>
<Controller
@ -373,7 +373,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
}}
{...dragHandleProps}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
<Box
sx={{
@ -396,7 +396,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
ml: 'auto',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
</Stack>
<Controller
@ -513,8 +513,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
}}
onClick={handleAddLink}
>
<Icon
type='icon-tianjia'
<IconTianjia
sx={{ fontSize: '10px !important', color: '#5F58FE' }}
/>
<Box

View File

@ -1,7 +1,7 @@
import { CardWebHeaderBtn } from '@/api';
import UploadFile from '@/components/UploadFile';
import { useAppDispatch, useAppSelector } from '@/store';
import { setAppPreviewData } from '@/store/slices/config';
import {
Box,
Checkbox,
@ -13,16 +13,15 @@ import {
Stack,
TextField,
} from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { IconShanchu2, IconDrag } from '@panda-wiki/icons';
import {
CSSProperties,
Dispatch,
forwardRef,
HTMLAttributes,
SetStateAction,
useEffect,
} from 'react';
import { Control, Controller, useForm } from 'react-hook-form';
import { Control, Controller } from 'react-hook-form';
export type ItemProps = Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> & {
item: CardWebHeaderBtn;
@ -300,7 +299,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
height: '28px',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
size='small'
@ -311,7 +310,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
}}
{...dragHandleProps}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -1,6 +1,6 @@
import UploadFile from '@/components/UploadFile';
import { DomainSocialMediaAccount } from '@/request/types';
import { Icon } from '@ctzhian/ui';
import { IconShanchu2, IconDrag } from '@panda-wiki/icons';
import {
Box,
IconButton,
@ -92,7 +92,7 @@ const Item = forwardRef<HTMLDivElement, SocialInfoProps>(
}}
{...dragHandleProps}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
<Box
sx={{
@ -120,7 +120,7 @@ const Item = forwardRef<HTMLDivElement, SocialInfoProps>(
ml: 'auto',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
</Stack>
<Stack direction={'row'} gap={1}>
@ -142,16 +142,10 @@ const Item = forwardRef<HTMLDivElement, SocialInfoProps>(
}}
renderValue={selected => {
const option = options.find(i => i.key === selected);
const AppIcon = option?.config_type || option?.type;
return (
<Stack justifyContent={'center'} sx={{ mt: '2px' }}>
<Icon
type={
option
? option?.config_type || option?.type || ''
: ''
}
sx={{ fontSize: '14px' }}
/>
{AppIcon && <AppIcon sx={{ fontSize: '14px' }} />}
</Stack>
);
}}
@ -185,7 +179,9 @@ const Item = forwardRef<HTMLDivElement, SocialInfoProps>(
borderRadius: 1,
}}
>
{options.map(item => (
{options.map(item => {
const AppIcon = item?.config_type || item?.type;
return (
<ToggleButton
key={item.key}
value={item.key}
@ -201,14 +197,13 @@ const Item = forwardRef<HTMLDivElement, SocialInfoProps>(
gap={1}
alignItems='center'
>
<Icon
type={item?.config_type || item?.type}
sx={{ fontSize: '16px' }}
/>
{/* <Box>{item.value || item.key}</Box> */}
{AppIcon && (
<AppIcon sx={{ fontSize: '16px' }} />
)}
</Stack>
</ToggleButton>
))}
);
})}
</ToggleButtonGroup>
</MenuItem>
</Select>

View File

@ -1,99 +0,0 @@
import { Box, Button, IconButton, Stack } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { AppSetting } from '@/api';
import { useState } from 'react';
import { getButtonThemeStyle } from './buttonThemeUtils';
const NavBtns = ({ detail }: { detail?: Partial<AppSetting> }) => {
const [open, setOpen] = useState(false);
return (
<>
<IconButton
size='small'
onClick={() => setOpen(!open)}
sx={{
color: 'text.primary',
width: 40,
height: 40,
}}
>
<Icon type='icon-a-caidan' />
</IconButton>
<Box
sx={{
position: 'absolute',
width: '100%',
height: '110vh',
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: 999,
transition: 'all 0.3s ease-in-out',
transform: 'translateX(200%) translateY(-200%)',
...(open && {
bgcolor: 'background.default',
transform: 'translateX(0) translateY(0)',
}),
}}
>
<Stack
direction='row'
alignItems='center'
gap={1.5}
sx={{ py: '14px', cursor: 'pointer', ml: 1.5, color: 'text.primary' }}
>
{detail?.icon && <img src={detail?.icon} alt='logo' width={32} />}
<Box sx={{ fontSize: 18 }}>{detail?.title}</Box>
</Stack>
<Stack gap={4} sx={{ px: 3, mt: 4, bgcolor: 'background.default' }}>
{detail?.btns?.map(item => (
<Button
key={item.id}
fullWidth
variant={item.variant}
startIcon={
item.showIcon && item.icon ? (
<img src={item.icon} alt='logo' width={36} height={36} />
) : null
}
sx={{
textTransform: 'none',
justifyContent: 'flex-start',
height: '60px',
px: 4,
gap: 3,
fontSize: 18,
'& .MuiButton-startIcon': {
ml: 0,
mr: 0,
},
...getButtonThemeStyle(detail?.theme_mode, item.variant),
}}
>
{item.text}
</Button>
))}
</Stack>
<IconButton
size='small'
onClick={() => setOpen(!open)}
sx={{
position: 'absolute',
top: 10,
right: 10,
color: 'text.primary',
width: 40,
height: 40,
zIndex: 1,
}}
>
<Icon type='icon-chahao' />
</IconButton>
</Box>
</>
);
};
export default NavBtns;

View File

@ -1,49 +0,0 @@
import React, { Dispatch, ReactNode, SetStateAction } from 'react';
import { Box, IconButton } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
interface OverlayProps {
open: boolean;
onClose: Dispatch<SetStateAction<boolean>>;
children: ReactNode;
}
const Overlay: React.FC<OverlayProps> = ({ open, onClose, children }) => {
return (
<>
{open && (
<Box
sx={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.7)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 1300,
}}
onClick={() => onClose(false)}
>
<IconButton
onClick={() => onClose(false)}
sx={{
position: 'absolute',
top: 16,
right: 16,
color: 'white',
zIndex: 1310,
}}
>
<CloseIcon />
</IconButton>
<Box onClick={e => e.stopPropagation()}>{children}</Box>
</Box>
)}
</>
);
};
export default Overlay;

View File

@ -1,55 +0,0 @@
/**
*
* @param themeMode ('light' | 'dark')
* @param variant ('contained' | 'outlined' | 'text')
* @returns
*/
export const getButtonThemeStyle = (
themeMode: 'light' | 'dark' | undefined,
variant: string,
) => {
// 只在dark主题下应用特殊样式
if (themeMode === 'dark') {
switch (variant) {
case 'contained':
return {
bgcolor: 'primary.main',
color: 'primary.contrastText',
borderColor: 'primary.main',
'&:hover': {
bgcolor: 'primary.dark',
borderColor: 'primary.dark',
},
};
case 'outlined':
return {
borderColor: 'primary.main',
color: 'primary.main',
'&:hover': {
bgcolor: 'action.hover',
borderColor: 'primary.main',
},
};
case 'text':
return {
color: 'primary.main',
'&:hover': {
bgcolor: 'action.hover',
},
};
default:
return {};
}
}
if (themeMode === 'light') {
switch (variant) {
case 'text':
return {
color: 'text.primary',
};
}
}
return {};
};
export default getButtonThemeStyle;

View File

@ -1,69 +0,0 @@
import { SwitchProps, Box } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import MySwitch from './Switch';
const ThemeSwitch = (props: SwitchProps) => {
return (
<MySwitch
{...props}
icon={
<Box
sx={{
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Box
sx={{
width: 22,
height: 22,
bgcolor: '#fff',
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Icon
type='icon-mingliangmoshi'
sx={{ color: '#000', fontSize: 16 }}
/>
</Box>
</Box>
}
checkedIcon={
<Box
sx={{
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Box
sx={{
width: 22,
height: 22,
bgcolor: '#fff',
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Icon
type='icon-shensemoshi'
sx={{ color: '#000', fontSize: 16 }}
/>
</Box>
</Box>
}
/>
);
};
export default ThemeSwitch;

View File

@ -10,7 +10,7 @@ import {
alpha,
} from '@mui/material';
import { v4 as uuidv4 } from 'uuid';
import { Icon } from '@ctzhian/ui';
import { IconWangyeguajian } from '@panda-wiki/icons';
import { Dispatch, SetStateAction, useMemo, useState } from 'react';
import type { CSSProperties } from 'react';
import { Component } from '../../index';
@ -185,8 +185,7 @@ const ComponentBar = ({
}}
{...(!item.fixed ? { ...attributes, ...listeners } : {})}
>
<Icon
type='icon-wangyeguajian'
<IconWangyeguajian
sx={{
color:
item.id === curComponent.id
@ -196,7 +195,7 @@ const ComponentBar = ({
: 'text.secondary',
fontSize: '14px',
}}
></Icon>
></IconWangyeguajian>
<Typography
sx={{
marginLeft: '8px',

View File

@ -1,5 +1,5 @@
import { Box, IconButton, Stack, TextField } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { IconShanchu2, IconDrag } from '@panda-wiki/icons';
import {
CSSProperties,
Dispatch,
@ -118,7 +118,7 @@ const HotSearchItem = forwardRef<HTMLDivElement, HotSearchItemProps>(
height: '28px',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
size='small'
@ -129,7 +129,7 @@ const HotSearchItem = forwardRef<HTMLDivElement, HotSearchItemProps>(
}}
{...(dragHandleProps as any)}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -8,7 +8,7 @@ import {
Select,
MenuItem,
} from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { IconShanchu2, IconDrag } from '@panda-wiki/icons';
import {
CSSProperties,
Dispatch,
@ -159,7 +159,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
height: '28px',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
size='small'
@ -170,7 +170,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
}}
{...(dragHandleProps as any)}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -2,8 +2,14 @@ import { postApiV1NodeSummary } from '@/request/Node';
import { DomainRecommendNodeListResp } from '@/request/types';
import { useAppSelector } from '@/store';
import { Box, IconButton, Stack } from '@mui/material';
import { Ellipsis, Icon, message } from '@ctzhian/ui';
import { Ellipsis, message } from '@ctzhian/ui';
import { CSSProperties, forwardRef, HTMLAttributes, useState } from 'react';
import {
IconShanchu2,
IconDrag,
IconWenjianjia,
IconWenjian,
} from '@panda-wiki/icons';
export type ItemProps = HTMLAttributes<HTMLDivElement> & {
item: DomainRecommendNodeListResp;
@ -80,9 +86,12 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
<Box sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}>
{item.emoji}
</Box>
) : item.type === 1 ? (
<IconWenjianjia
sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}
/>
) : (
<Icon
type={item.type === 1 ? 'icon-wenjianjia' : 'icon-wenjian'}
<IconWenjian
sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}
/>
)}
@ -121,12 +130,16 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
.slice(0, 4)
.map(it => (
<Stack direction={'row'} alignItems={'center'} gap={1}>
<Icon
type={
it.type === 1 ? 'icon-wenjianjia' : 'icon-wenjian'
}
{it.type === 1 ? (
<IconWenjianjia
sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}
/>
) : (
<IconWenjian
sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}
/>
)}
<Ellipsis sx={{ flex: 1, width: 0 }}>{it.name}</Ellipsis>
</Stack>
))}
@ -147,7 +160,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
height: '28px',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
@ -159,7 +172,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
}}
{...dragHandleProps}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -1,5 +1,5 @@
import { Box, IconButton, Stack, TextField } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { IconShanchu2, IconDrag } from '@panda-wiki/icons';
import UploadFile from '@/components/UploadFile';
import {
CSSProperties,
@ -132,7 +132,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
height: '28px',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
size='small'
@ -143,7 +143,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
}}
{...(dragHandleProps as any)}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -1,5 +1,5 @@
import { Box, IconButton, Stack, TextField } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { IconShanchu2, IconDrag } from '@panda-wiki/icons';
import UploadFile from '@/components/UploadFile';
import {
CSSProperties,
@ -152,7 +152,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
height: '28px',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
size='small'
@ -163,7 +163,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
}}
{...(dragHandleProps as any)}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -1,5 +1,5 @@
import { Box, IconButton, Stack, TextField } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { IconShanchu2, IconDrag } from '@panda-wiki/icons';
import {
CSSProperties,
Dispatch,
@ -143,7 +143,7 @@ const ItemType = forwardRef<HTMLDivElement, ItemTypeProps>(
height: '28px',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
size='small'
@ -154,7 +154,7 @@ const ItemType = forwardRef<HTMLDivElement, ItemTypeProps>(
}}
{...(dragHandleProps as any)}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -1,5 +1,5 @@
import { Box, IconButton, Stack, TextField } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { IconShanchu2, IconDrag } from '@panda-wiki/icons';
import {
CSSProperties,
Dispatch,
@ -185,7 +185,7 @@ const ItemType = forwardRef<HTMLDivElement, ItemTypeProps>(
height: '28px',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
size='small'
@ -196,7 +196,7 @@ const ItemType = forwardRef<HTMLDivElement, ItemTypeProps>(
}}
{...(dragHandleProps as any)}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -2,7 +2,13 @@ import { postApiV1NodeSummary } from '@/request/Node';
import { DomainRecommendNodeListResp } from '@/request/types';
import { useAppSelector } from '@/store';
import { Box, IconButton, Stack } from '@mui/material';
import { Ellipsis, Icon, message } from '@ctzhian/ui';
import { Ellipsis, message } from '@ctzhian/ui';
import {
IconShanchu2,
IconDrag,
IconWenjianjia,
IconWenjian,
} from '@panda-wiki/icons';
import { CSSProperties, forwardRef, HTMLAttributes, useState } from 'react';
export type ItemProps = HTMLAttributes<HTMLDivElement> & {
@ -82,9 +88,12 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
<Box sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}>
{item.emoji}
</Box>
) : item.type === 1 ? (
<IconWenjianjia
sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}
/>
) : (
<Icon
type={item.type === 1 ? 'icon-wenjianjia' : 'icon-wenjian'}
<IconWenjian
sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}
/>
)}
@ -128,11 +137,12 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
>
{it.emoji}
</Box>
) : it.type === 1 ? (
<IconWenjianjia
sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}
/>
) : (
<Icon
type={
it.type === 1 ? 'icon-wenjianjia' : 'icon-wenjian'
}
<IconWenjian
sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}
/>
)}
@ -157,7 +167,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
height: '28px',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
@ -169,7 +179,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
}}
{...dragHandleProps}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -1,5 +1,5 @@
import { Box, IconButton, Stack, TextField } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { IconShanchu2, IconDrag } from '@panda-wiki/icons';
import {
CSSProperties,
Dispatch,
@ -143,7 +143,7 @@ const FaqItem = forwardRef<HTMLDivElement, FaqItemProps>(
height: '28px',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
size='small'
@ -154,7 +154,7 @@ const FaqItem = forwardRef<HTMLDivElement, FaqItemProps>(
}}
{...(dragHandleProps as any)}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -1,5 +1,5 @@
import { Box, IconButton, Stack, TextField } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { IconShanchu2, IconDrag } from '@panda-wiki/icons';
import {
CSSProperties,
Dispatch,
@ -143,7 +143,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
height: '28px',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
size='small'
@ -154,7 +154,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
}}
{...(dragHandleProps as any)}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -1,9 +1,8 @@
import { AppDetail, HeaderSetting } from '@/api';
import UploadFile from '@/components/UploadFile';
import { Stack, Box, TextField } from '@mui/material';
import { Stack, Box, TextField, SvgIconProps } from '@mui/material';
import DragBrand from '../basicComponents/DragBrand';
import { Icon } from '@ctzhian/ui';
import { Dispatch, SetStateAction, useEffect, useMemo } from 'react';
import { Dispatch, SetStateAction, useEffect } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { useAppDispatch, useAppSelector } from '@/store';
import { setAppPreviewData } from '@/store/slices/config';
@ -12,6 +11,13 @@ import Switch from '../basicComponents/Switch';
import DragSocialInfo from '../basicComponents/DragSocialInfo';
import VersionMask from '@/components/VersionMask';
import { PROFESSION_VERSION_PERMISSION } from '@/constant/version';
import { IconTianjia } from '@panda-wiki/icons';
import {
IconWeixingongzhonghao,
IconDianhua,
IconWeixingongzhonghaoDaiyanse,
IconDianhua1,
} from '@panda-wiki/icons';
interface FooterConfigProps {
data?: AppDetail | null;
@ -21,8 +27,8 @@ interface FooterConfigProps {
export interface Option {
key: string;
value: string;
type: string;
config_type?: string;
type: React.ComponentType<SvgIconProps>;
config_type?: React.ComponentType<SvgIconProps>;
text_placeholder?: string;
text_label?: string;
}
@ -30,16 +36,16 @@ export const options: Option[] = [
{
key: 'wechat_oa',
value: '微信公众号',
type: 'icon-weixingongzhonghao',
config_type: 'icon-weixingongzhonghao-daiyanse',
type: IconWeixingongzhonghao,
config_type: IconWeixingongzhonghaoDaiyanse,
text_placeholder: '请输入公众号名称',
text_label: '公众号名称',
},
{
key: 'phone',
value: '电话',
type: 'icon-dianhua',
config_type: 'icon-dianhua1',
type: IconDianhua,
config_type: IconDianhua1,
text_placeholder: '请输入文字',
text_label: '文字',
},
@ -331,8 +337,7 @@ const FooterConfig = ({ data, setIsEdit, isEdit }: FooterConfigProps) => {
setIsEdit(true);
}}
>
<Icon
type='icon-tianjia'
<IconTianjia
sx={{ fontSize: '10px !important', color: '#5F58FE' }}
/>
<Box
@ -396,8 +401,7 @@ const FooterConfig = ({ data, setIsEdit, isEdit }: FooterConfigProps) => {
setIsEdit(true);
}}
>
<Icon
type='icon-tianjia'
<IconTianjia
sx={{ fontSize: '10px !important', color: '#5F58FE' }}
/>
<Box

View File

@ -2,12 +2,11 @@ import { AppDetail, HeaderSetting } from '@/api';
import DragBtn from '../basicComponents/DragBtn';
import UploadFile from '@/components/UploadFile';
import { Stack, Box, TextField } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { Dispatch, SetStateAction, useEffect } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { useAppDispatch, useAppSelector } from '@/store';
import { setAppPreviewData } from '@/store/slices/config';
import { useAppSelector } from '@/store';
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
import { IconTianjia } from '@panda-wiki/icons';
interface CardWebHeaderProps {
data?: AppDetail | null;
@ -17,7 +16,6 @@ interface CardWebHeaderProps {
const HeaderConfig = ({ data, setIsEdit, isEdit }: CardWebHeaderProps) => {
const { appPreviewData } = useAppSelector(state => state.config);
const dispatch = useAppDispatch();
const debouncedDispatch = useDebounceAppPreviewData();
const {
control,
@ -257,8 +255,7 @@ const HeaderConfig = ({ data, setIsEdit, isEdit }: CardWebHeaderProps) => {
}}
onClick={handleAddButton}
>
<Icon
type='icon-tianjia'
<IconTianjia
sx={{ fontSize: '10px !important', color: '#5F58FE' }}
/>
<Box sx={{ fontSize: 14, lineHeight: '22px', marginLeft: 0.5 }}>

View File

@ -1,5 +1,5 @@
import { Box, IconButton, Stack, TextField } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { IconShanchu2, IconDrag } from '@panda-wiki/icons';
import {
CSSProperties,
Dispatch,
@ -143,7 +143,7 @@ const ItemType = forwardRef<HTMLDivElement, ItemTypeProps>(
height: '28px',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
size='small'
@ -154,7 +154,7 @@ const ItemType = forwardRef<HTMLDivElement, ItemTypeProps>(
}}
{...(dragHandleProps as any)}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -1,5 +1,5 @@
import { Box, IconButton, Stack, TextField } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { IconShanchu2, IconDrag } from '@panda-wiki/icons';
import {
CSSProperties,
Dispatch,
@ -115,7 +115,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
height: '28px',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
size='small'
@ -126,7 +126,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
}}
{...(dragHandleProps as any)}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -1,9 +1,13 @@
import { postApiV1NodeSummary } from '@/request/Node';
import { DomainRecommendNodeListResp } from '@/request/types';
import { useAppSelector } from '@/store';
import { Box, IconButton, Stack } from '@mui/material';
import { Ellipsis, Icon, message } from '@ctzhian/ui';
import { CSSProperties, forwardRef, HTMLAttributes, useState } from 'react';
import {
IconShanchu2,
IconDrag,
IconWenjianjia,
IconWenjian,
} from '@panda-wiki/icons';
import { Ellipsis } from '@ctzhian/ui';
import { CSSProperties, forwardRef, HTMLAttributes } from 'react';
export type ItemProps = HTMLAttributes<HTMLDivElement> & {
item: DomainRecommendNodeListResp;
@ -66,9 +70,12 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
<Box sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}>
{item.emoji}
</Box>
) : item.type === 1 ? (
<IconWenjianjia
sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}
/>
) : (
<Icon
type={item.type === 1 ? 'icon-wenjianjia' : 'icon-wenjian'}
<IconWenjian
sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}
/>
)}
@ -91,7 +98,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
height: '28px',
}}
>
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
@ -103,7 +110,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
}}
{...dragHandleProps}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -1,6 +1,6 @@
import { useEffect, useState, useRef } from 'react';
import { Button, Stack, Typography } from '@mui/material';
import { CusTabs, Icon, message, Modal } from '@ctzhian/ui';
import { CusTabs, message, Modal } from '@ctzhian/ui';
import {
getApiV1NodeRecommendNodes,
getApiV1KnowledgeBaseDetail,
@ -9,6 +9,7 @@ import {
DomainAppDetailResp,
DomainWebAppLandingConfigResp,
} from '@/request/types';
import { IconPCduan, IconYidongduan } from '@panda-wiki/icons';
import { getApiV1AppDetail, putApiV1App } from '@/request/App';
import { useAppSelector, useAppDispatch } from '@/store';
@ -338,18 +339,11 @@ const CustomModal = ({
<CusTabs
list={[
{
label: (
<Icon type='icon-PCduan' sx={{ height: '32px' }} />
),
label: <IconPCduan sx={{ fontSize: 14 }} />,
value: 'pc',
},
{
label: (
<Icon
type='icon-yidongduan'
sx={{ height: '32px' }}
/>
),
label: <IconYidongduan sx={{ fontSize: 14 }} />,
value: 'mobile',
},
]}

View File

@ -1,390 +0,0 @@
import { FooterSetting } from '@/api/type';
import {
closestCenter,
DndContext,
DragEndEvent,
DragOverlay,
DragStartEvent,
MouseSensor,
TouchSensor,
useSensor,
useSensors,
} from '@dnd-kit/core';
import {
rectSortingStrategy,
SortableContext,
useSortable,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { Box, Button, IconButton, Stack, TextField } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import {
CSSProperties,
forwardRef,
HTMLAttributes,
useCallback,
useState,
} from 'react';
import {
Control,
Controller,
FieldErrors,
useFieldArray,
} from 'react-hook-form';
export type ItemProps = HTMLAttributes<HTMLDivElement> & {
groupIndex: number;
withOpacity?: boolean;
isDragging?: boolean;
dragHandleProps?: any;
control: Control<FooterSetting>;
errors: FieldErrors<FooterSetting>;
setIsEdit: (value: boolean) => void;
handleRemove?: () => void;
};
interface LinkItemProps extends HTMLAttributes<HTMLDivElement> {
linkId: string;
linkIndex: number;
groupIndex: number;
control: Control<FooterSetting>;
errors: FieldErrors<FooterSetting>;
setIsEdit: (value: boolean) => void;
onRemove: () => void;
withOpacity?: boolean;
isDragging?: boolean;
dragHandleProps?: any;
}
const LinkItem = forwardRef<HTMLDivElement, LinkItemProps>(
(
{
linkIndex,
groupIndex,
control,
errors,
setIsEdit,
onRemove,
withOpacity,
isDragging,
dragHandleProps,
style,
...props
},
ref,
) => {
const inlineStyles: CSSProperties = {
opacity: withOpacity ? '0.5' : '1',
cursor: isDragging ? 'grabbing' : 'grab',
...style,
};
return (
<Box ref={ref} style={inlineStyles} {...props}>
<Stack gap={1} alignItems='center' direction='row'>
<IconButton
size='small'
sx={{
cursor: 'grab',
color: 'text.secondary',
'&:hover': { color: 'primary.main' },
flexShrink: 0,
}}
{...dragHandleProps}
>
<Icon type='icon-drag' />
</IconButton>
<Box
sx={{
color: 'text.secondary',
flexShrink: 0,
fontSize: 12,
width: 20,
}}
>
{linkIndex + 1}.
</Box>
<Controller
control={control}
name={`brand_groups.${groupIndex}.links.${linkIndex}.name`}
rules={{ required: '请输入链接文字' }}
render={({ field }) => (
<TextField
{...field}
sx={{ width: 300 }}
label='链接文字'
placeholder='链接文字'
onChange={e => {
field.onChange(e.target.value);
setIsEdit(true);
}}
error={
!!errors.brand_groups?.[groupIndex]?.links?.[linkIndex]?.name
}
helperText={
errors.brand_groups?.[groupIndex]?.links?.[linkIndex]?.name
?.message
}
/>
)}
/>
<Controller
control={control}
name={`brand_groups.${groupIndex}.links.${linkIndex}.url`}
rules={{ required: '请输入链接地址' }}
render={({ field }) => (
<TextField
{...field}
fullWidth
label='链接地址'
placeholder='链接地址'
onChange={e => {
field.onChange(e.target.value);
setIsEdit(true);
}}
error={
!!errors.brand_groups?.[groupIndex]?.links?.[linkIndex]?.url
}
helperText={
errors.brand_groups?.[groupIndex]?.links?.[linkIndex]?.url
?.message
}
/>
)}
/>
<IconButton size='small' sx={{ flexShrink: 0 }} onClick={onRemove}>
<Icon type='icon-icon_tool_close' />
</IconButton>
</Stack>
</Box>
);
},
);
const SortableLinkItem: React.FC<LinkItemProps> = ({ linkId, ...rest }) => {
const {
isDragging,
attributes,
listeners,
setNodeRef,
transform,
transition,
} = useSortable({ id: linkId });
const style = {
transform: CSS.Transform.toString(transform),
transition: transition || undefined,
};
return (
<LinkItem
ref={setNodeRef}
style={style}
withOpacity={isDragging}
dragHandleProps={{
...attributes,
...listeners,
}}
linkId={linkId}
{...rest}
/>
);
};
const Item = forwardRef<HTMLDivElement, ItemProps>(
(
{
groupIndex,
withOpacity,
isDragging,
style,
dragHandleProps,
handleRemove,
control,
errors,
setIsEdit,
...props
},
ref,
) => {
const [activeId, setActiveId] = useState<string | null>(null);
const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor));
const {
fields: linkFields,
append: appendLink,
remove: removeLink,
move: moveLink,
} = useFieldArray({
control,
name: `brand_groups.${groupIndex}.links`,
});
const inlineStyles: CSSProperties = {
opacity: withOpacity ? '0.5' : '1',
borderRadius: '10px',
cursor: isDragging ? 'grabbing' : 'grab',
backgroundColor: '#ffffff',
width: '100%',
...style,
};
const handleLinkDragStart = useCallback((event: DragStartEvent) => {
setActiveId(event.active.id as string);
}, []);
const handleLinkDragEnd = useCallback(
(event: DragEndEvent) => {
const { active, over } = event;
if (active.id !== over?.id) {
const oldIndex = linkFields.findIndex(
(_, index) => `link-${groupIndex}-${index}` === active.id,
);
const newIndex = linkFields.findIndex(
(_, index) => `link-${groupIndex}-${index}` === over!.id,
);
moveLink(oldIndex, newIndex);
setIsEdit(true);
}
setActiveId(null);
},
[linkFields, moveLink, setIsEdit, groupIndex],
);
const handleLinkDragCancel = useCallback(() => {
setActiveId(null);
}, []);
const handleAddLink = () => {
appendLink({ name: '', url: '' });
setIsEdit(true);
};
const handleRemoveLink = (linkIndex: number) => {
removeLink(linkIndex);
setIsEdit(true);
};
return (
<Box ref={ref} style={inlineStyles} {...props}>
<Box
sx={{
border: '1px solid',
borderColor: 'divider',
borderRadius: '10px',
p: 2,
mb: 1,
pb: 1,
}}
>
<Stack direction='row' alignItems='center' gap={1} sx={{ mb: 1 }}>
<IconButton
size='small'
sx={{
cursor: 'grab',
color: 'text.secondary',
'&:hover': { color: 'primary.main' },
flexShrink: 0,
}}
{...dragHandleProps}
>
<Icon type='icon-drag' />
</IconButton>
<Controller
control={control}
name={`brand_groups.${groupIndex}.name`}
rules={{ required: '请输入链接组名称' }}
render={({ field }) => (
<TextField
{...field}
fullWidth
placeholder='输入链接组名称'
label='链接组名称'
onChange={e => {
field.onChange(e.target.value);
setIsEdit(true);
}}
error={!!errors.brand_groups?.[groupIndex]?.name}
helperText={errors.brand_groups?.[groupIndex]?.name?.message}
/>
)}
/>
<IconButton size='small' onClick={handleRemove}>
<Icon type='icon-icon_tool_close' />
</IconButton>
</Stack>
{/* 链接拖拽区域 */}
{linkFields.length > 0 && (
<Box
sx={{
border: '1px dashed',
borderColor: 'divider',
borderRadius: '10px',
py: 1,
pl: 2,
}}
>
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragStart={handleLinkDragStart}
onDragEnd={handleLinkDragEnd}
onDragCancel={handleLinkDragCancel}
>
<SortableContext
items={linkFields.map(
(_, index) => `link-${groupIndex}-${index}`,
)}
strategy={rectSortingStrategy}
>
<Stack gap={1}>
{linkFields.map((link, linkIndex) => (
<SortableLinkItem
key={link.id}
linkId={`link-${groupIndex}-${linkIndex}`}
linkIndex={linkIndex}
groupIndex={groupIndex}
control={control}
errors={errors}
setIsEdit={setIsEdit}
onRemove={() => handleRemoveLink(linkIndex)}
/>
))}
</Stack>
</SortableContext>
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
{activeId ? (
<LinkItem
isDragging
linkId={activeId}
linkIndex={parseInt(activeId.split('-')[2])}
groupIndex={groupIndex}
control={control}
errors={errors}
setIsEdit={setIsEdit}
onRemove={() => {}}
/>
) : null}
</DragOverlay>
</DndContext>
</Box>
)}
<Button
size='small'
startIcon={
<Icon type='icon-add' sx={{ fontSize: '12px !important' }} />
}
onClick={handleAddLink}
sx={{ mt: 1 }}
>
</Button>
</Box>
</Box>
);
},
);
export default Item;

View File

@ -1,49 +0,0 @@
import { FooterSetting } from '@/api/type';
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { FC } from 'react';
import { Control, FieldErrors } from 'react-hook-form';
import Item, { ItemProps } from './Item';
type SortableItemProps = Omit<
ItemProps,
'withOpacity' | 'isDragging' | 'dragHandleProps'
> & {
id: string;
groupIndex: number;
control: Control<FooterSetting>;
errors: FieldErrors<FooterSetting>;
setIsEdit: (value: boolean) => void;
handleRemove: () => void;
};
const SortableItem: FC<SortableItemProps> = ({ id, ...rest }) => {
const {
isDragging,
attributes,
listeners,
setNodeRef,
transform,
transition,
} = useSortable({ id });
const style = {
transform: CSS.Transform.toString(transform),
transition: transition || undefined,
};
return (
<Item
ref={setNodeRef}
style={style}
withOpacity={isDragging}
dragHandleProps={{
...attributes,
...listeners,
}}
{...rest}
/>
);
};
export default SortableItem;

View File

@ -1,159 +0,0 @@
import { FooterSetting } from '@/api/type';
import {
closestCenter,
DndContext,
DragEndEvent,
DragOverlay,
DragStartEvent,
MouseSensor,
TouchSensor,
useSensor,
useSensors,
} from '@dnd-kit/core';
import { rectSortingStrategy, SortableContext } from '@dnd-kit/sortable';
import { Box, Button } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { FC, useCallback, useState } from 'react';
import { Control, FieldErrors, useFieldArray } from 'react-hook-form';
import Item from './Item';
import SortableItem from './SortableItem';
export interface BrandGroup {
id: string;
name: string;
links: {
id: string;
name: string;
url: string;
}[];
}
interface DragBrandProps {
control: Control<FooterSetting>;
errors: FieldErrors<FooterSetting>;
setIsEdit: (value: boolean) => void;
}
const DragBrand: FC<DragBrandProps> = ({ control, errors, setIsEdit }) => {
const [activeId, setActiveId] = useState<string | null>(null);
const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor));
const {
fields: brandGroupFields,
append: appendBrandGroup,
remove: removeBrandGroup,
move,
} = useFieldArray({
control,
name: 'brand_groups',
});
const handleDragStart = useCallback((event: DragStartEvent) => {
setActiveId(event.active.id as string);
}, []);
const handleDragEnd = useCallback(
(event: DragEndEvent) => {
const { active, over } = event;
if (active.id !== over?.id) {
const oldIndex = brandGroupFields.findIndex(
(_, index) => `group-${index}` === active.id,
);
const newIndex = brandGroupFields.findIndex(
(_, index) => `group-${index}` === over!.id,
);
move(oldIndex, newIndex);
setIsEdit(true);
}
setActiveId(null);
},
[brandGroupFields, move, setIsEdit],
);
const handleDragCancel = useCallback(() => {
setActiveId(null);
}, []);
const handleRemove = useCallback(
(index: number) => {
removeBrandGroup(index);
setIsEdit(true);
},
[removeBrandGroup, setIsEdit],
);
const handleAddBrandGroup = () => {
appendBrandGroup({
name: '',
links: [{ name: '', url: '' }],
});
setIsEdit(true);
};
if (brandGroupFields.length === 0) {
return (
<Button
size='small'
startIcon={
<Icon type='icon-add' sx={{ fontSize: '12px !important' }} />
}
onClick={handleAddBrandGroup}
>
</Button>
);
}
return (
<>
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
onDragCancel={handleDragCancel}
>
<SortableContext
items={brandGroupFields.map((_, index) => `group-${index}`)}
strategy={rectSortingStrategy}
>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
{brandGroupFields.map((group, groupIndex) => (
<SortableItem
key={group.id}
id={`group-${groupIndex}`}
groupIndex={groupIndex}
control={control}
errors={errors}
setIsEdit={setIsEdit}
handleRemove={() => handleRemove(groupIndex)}
/>
))}
</Box>
</SortableContext>
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
{activeId ? (
<Item
isDragging
groupIndex={parseInt(activeId.split('-')[1])}
control={control}
errors={errors}
setIsEdit={setIsEdit}
/>
) : null}
</DragOverlay>
</DndContext>
<Button
size='small'
startIcon={
<Icon type='icon-add' sx={{ fontSize: '12px !important' }} />
}
onClick={handleAddBrandGroup}
>
</Button>
</>
);
};
export default DragBrand;

View File

@ -1,95 +0,0 @@
import { CardWebHeaderBtn } from '@/api';
import Avatar from '@/components/Avatar';
import { Box, Button, IconButton, Stack } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { CSSProperties, forwardRef, HTMLAttributes } from 'react';
export type ItemProps = HTMLAttributes<HTMLDivElement> & {
item: CardWebHeaderBtn;
withOpacity?: boolean;
isDragging?: boolean;
dragHandleProps?: any;
selectedBtnId: string | null;
setSelectedBtnId: (id: string | null) => void;
handleRemove?: (id: string) => void;
};
const Item = forwardRef<HTMLDivElement, ItemProps>(
(
{
item,
withOpacity,
isDragging,
style,
dragHandleProps,
selectedBtnId,
setSelectedBtnId,
handleRemove,
...props
},
ref,
) => {
const inlineStyles: CSSProperties = {
opacity: withOpacity ? '0.5' : '1',
borderRadius: '10px',
cursor: isDragging ? 'grabbing' : 'grab',
backgroundColor: '#ffffff',
...style,
};
return (
<Box ref={ref} style={inlineStyles} {...props}>
<Stack
direction={'row'}
alignItems={'center'}
gap={0.5}
sx={{
p: selectedBtnId === item.id ? '3px' : 0.5,
border: selectedBtnId === item.id ? '2px solid' : '1px solid',
borderColor: selectedBtnId === item.id ? 'primary.main' : 'divider',
borderRadius: '10px',
}}
onClick={() => {
if (selectedBtnId === item.id) setSelectedBtnId(null);
else setSelectedBtnId(item.id);
}}
>
<IconButton
size='small'
sx={{
cursor: 'grab',
color: 'text.secondary',
'&:hover': { color: 'primary.main' },
}}
{...dragHandleProps}
>
<Icon type='icon-drag' />
</IconButton>
<Button
variant={item.variant}
sx={{ textTransform: 'none' }}
startIcon={
item.showIcon ? (
<Avatar src={item.icon} sx={{ width: 24, height: 24 }} />
) : undefined
}
>
{item.text}
</Button>
<IconButton
size='small'
onClick={e => {
e.stopPropagation();
handleRemove?.(item.id);
}}
sx={{ color: 'text.tertiary', ':hover': { color: 'error.main' } }}
>
<Icon type='icon-icon_tool_close' />
</IconButton>
</Stack>
</Box>
);
},
);
export default Item;

View File

@ -1,41 +0,0 @@
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { FC } from 'react';
import Item, { ItemProps } from './Item';
type SortableItemProps = ItemProps & {
selectedBtnId: string | null;
setSelectedBtnId: (id: string | null) => void;
};
const SortableItem: FC<SortableItemProps> = ({ item, ...rest }) => {
const {
isDragging,
attributes,
listeners,
setNodeRef,
transform,
transition,
} = useSortable({ id: item.id });
const style = {
transform: CSS.Transform.toString(transform),
transition: transition || undefined,
};
return (
<Item
ref={setNodeRef}
style={style}
withOpacity={isDragging}
dragHandleProps={{
...attributes,
...listeners,
}}
item={item}
{...rest}
/>
);
};
export default SortableItem;

View File

@ -1,108 +0,0 @@
import { CardWebHeaderBtn } from '@/api';
import {
closestCenter,
DndContext,
DragEndEvent,
DragOverlay,
DragStartEvent,
MouseSensor,
TouchSensor,
useSensor,
useSensors,
} from '@dnd-kit/core';
import {
arrayMove,
rectSortingStrategy,
SortableContext,
} from '@dnd-kit/sortable';
import { Stack } from '@mui/material';
import { FC, useCallback, useState } from 'react';
import Item from './Item';
import SortableItem from './SortableItem';
interface DragBtnProps {
data: CardWebHeaderBtn[];
columns?: number;
onChange: (data: CardWebHeaderBtn[]) => void;
selectedBtnId: string | null;
setSelectedBtnId: (id: string | null) => void;
}
const DragBtn: FC<DragBtnProps> = ({
data,
onChange,
selectedBtnId,
setSelectedBtnId,
}) => {
const [activeId, setActiveId] = useState<string | null>(null);
const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor));
const handleDragStart = useCallback((event: DragStartEvent) => {
setActiveId(event.active.id as string);
}, []);
const handleDragEnd = useCallback(
(event: DragEndEvent) => {
const { active, over } = event;
if (active.id !== over?.id) {
const oldIndex = data.findIndex(item => item.id === active.id);
const newIndex = data.findIndex(item => item.id === over!.id);
const newData = arrayMove(data, oldIndex, newIndex);
onChange(newData);
}
setActiveId(null);
},
[data, onChange],
);
const handleDragCancel = useCallback(() => {
setActiveId(null);
}, []);
const handleRemove = useCallback(
(id: string) => {
const newData = data.filter(item => item.id !== id);
onChange(newData);
},
[data, onChange],
);
if (data.length === 0) return null;
return (
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
onDragCancel={handleDragCancel}
>
<SortableContext
items={data.map(item => item.id)}
strategy={rectSortingStrategy}
>
<Stack direction={'row'} flexWrap={'wrap'} gap={0.5}>
{data.map((item, idx) => (
<SortableItem
key={idx}
id={item.id}
selectedBtnId={selectedBtnId}
setSelectedBtnId={setSelectedBtnId}
item={item}
handleRemove={handleRemove}
/>
))}
</Stack>
</SortableContext>
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
{activeId ? (
<Item
isDragging
item={data.find(item => item.id === activeId)!}
selectedBtnId={selectedBtnId}
setSelectedBtnId={setSelectedBtnId}
/>
) : null}
</DragOverlay>
</DndContext>
);
};
export default DragBtn;

View File

@ -1,7 +1,9 @@
import { DomainRecommendNodeListResp } from '@/request/types';
import { useAppSelector } from '@/store';
import { Ellipsis, Icon } from '@ctzhian/ui';
import { Ellipsis } from '@ctzhian/ui';
import { IconWenjianjia, IconWenjian, IconShanchu2 } from '@panda-wiki/icons';
import { Box, IconButton, Stack } from '@mui/material';
import { IconDrag } from '@panda-wiki/icons';
import { CSSProperties, forwardRef, HTMLAttributes } from 'react';
export type ItemProps = HTMLAttributes<HTMLDivElement> & {
@ -62,10 +64,15 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
}}
>
<Stack direction={'row'} alignItems={'center'} gap={1}>
<Icon
type={item.type === 1 ? 'icon-wenjianjia' : 'icon-wenjian'}
{item.type === 1 ? (
<IconWenjianjia
sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}
/>
) : (
<IconWenjian
sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}
/>
)}
<Ellipsis sx={{ flex: 1, width: 0, lineHeight: '32px' }}>
{item.name}
</Ellipsis>
@ -95,12 +102,15 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
.slice(0, 4)
.map(it => (
<Stack direction={'row'} alignItems={'center'} gap={1}>
<Icon
type={
it.type === 1 ? 'icon-wenjianjia' : 'icon-wenjian'
}
{it.type === 1 ? (
<IconWenjianjia
sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}
/>
) : (
<IconWenjian
sx={{ fontSize: 14, color: '#2f80f7', flexShrink: 0 }}
/>
)}
<Ellipsis sx={{ flex: 1, width: 0 }}>{it.name}</Ellipsis>
</Stack>
))}
@ -119,7 +129,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
':hover': { color: 'error.main' },
}}
>
<Icon type='icon-icon_tool_close' />
<IconShanchu2 sx={{ fontSize: '12px' }} />
</IconButton>
<IconButton
size='small'
@ -130,7 +140,7 @@ const Item = forwardRef<HTMLDivElement, ItemProps>(
}}
{...dragHandleProps}
>
<Icon type='icon-drag' />
<IconDrag sx={{ fontSize: '18px' }} />
</IconButton>
</Stack>
</Stack>

View File

@ -1,8 +1,8 @@
import { ITreeItem } from '@/api';
import Cascader from '@/components/Cascader';
import { addOpacityToColor } from '@/utils';
import { Icon } from '@ctzhian/ui';
import { Box, IconButton, Stack, useTheme } from '@mui/material';
import { IconXiala, IconGengduo } from '@panda-wiki/icons';
export type TreeMenuItem = {
key: string;
@ -96,10 +96,7 @@ const TreeMenu = ({
>
{value.label}
{value.children && (
<Icon
type='icon-xiala'
sx={{ fontSize: 20, transform: 'rotate(-90deg)' }}
/>
<IconXiala sx={{ fontSize: 20, transform: 'rotate(-90deg)' }} />
)}
</Stack>
{value.key === 'next-line' && (
@ -119,7 +116,7 @@ const TreeMenu = ({
context={
context || (
<IconButton size='small'>
<Icon type='icon-gengduo' />
<IconGengduo sx={{ fontSize: '14px' }} />
</IconButton>
)
}

View File

@ -1,8 +1,12 @@
import data from '@emoji-mart/data';
import Picker from '@emoji-mart/react';
import { Box, IconButton, Popover, SxProps } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import React, { useCallback } from 'react';
import {
IconWenjianjia,
IconWenjian,
IconWenjianjiaKai,
} from '@panda-wiki/icons';
import zh from '../../assets/emoji-data/zh.json';
interface EmojiPickerProps {
@ -66,17 +70,14 @@ const EmojiPicker: React.FC<EmojiPickerProps> = ({
<Box component='span' sx={{ fontSize: 14, ...iconSx }}>
{value}
</Box>
) : type === 1 ? (
collapsed ? (
<IconWenjianjia sx={{ fontSize: 16, ...iconSx }} />
) : (
<Icon
type={
type === 1
? collapsed
? 'icon-wenjianjia'
: 'icon-wenjianjia-kai'
: 'icon-wenjian'
}
sx={{ fontSize: 16, ...iconSx }}
/>
<IconWenjianjiaKai sx={{ fontSize: 16, ...iconSx }} />
)
) : (
<IconWenjian sx={{ fontSize: 16, ...iconSx }} />
)}
</IconButton>
<Popover

View File

@ -1,6 +1,6 @@
import { useAppSelector } from '@/store';
import { Box, Stack, useTheme } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { IconXiala } from '@panda-wiki/icons';
import { useEffect, useState } from 'react';
import { NavLink, useLocation } from 'react-router-dom';
import KBSelect from '../KB/KBSelect';
@ -76,10 +76,7 @@ const Bread = () => {
...(idx === breads.length - 1 && { fontWeight: 'bold' }),
}}
>
<Icon
type='icon-xiala'
sx={{ fontSize: 20, transform: 'rotate(-90deg)' }}
/>
<IconXiala sx={{ fontSize: 20, transform: 'rotate(-90deg)' }} />
{it.to === 'custom' ? (
<Box
sx={{ cursor: 'pointer', ':hover': { color: 'primary.main' } }}

View File

@ -3,11 +3,12 @@ import { useAppSelector, useAppDispatch } from '@/store';
import { setKbDetail } from '@/store/slices/config';
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
import { Button, IconButton, Stack, Tooltip } from '@mui/material';
import { Icon, message, Modal } from '@ctzhian/ui';
import { message, Modal } from '@ctzhian/ui';
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import System from '../System';
import Bread from './Bread';
import { IconDengchu } from '@panda-wiki/icons';
const Header = () => {
const navigate = useNavigate();
@ -94,7 +95,7 @@ const Header = () => {
setLogoutConfirmOpen(true);
}}
>
<Icon type='icon-dengchu' sx={{ fontSize: 18 }} />
<IconDengchu sx={{ fontSize: 18 }} />
</IconButton>
</Tooltip>
</Stack>

View File

@ -4,7 +4,13 @@ import { useFeatureValue } from '@/hooks';
import { ConstsUserRole } from '@/request/types';
import { useAppDispatch, useAppSelector } from '@/store';
import { setKbC, setKbId } from '@/store/slices/config';
import { Ellipsis, Icon, message } from '@ctzhian/ui';
import { Ellipsis, message } from '@ctzhian/ui';
import {
IconXiala,
IconZuzhi,
IconTianjiawendang,
IconShanchu,
} from '@panda-wiki/icons';
import {
Box,
Button,
@ -69,8 +75,7 @@ const KBSelect = () => {
}}
IconComponent={({ className, ...rest }) => {
return (
<Icon
type='icon-xiala'
<IconXiala
className={className + ' icon-xiala'}
sx={{
position: 'absolute',
@ -148,8 +153,7 @@ const KBSelect = () => {
gap={1.5}
sx={{ width: '100%' }}
>
<Icon
type='icon-zuzhi'
<IconZuzhi
sx={{ fontSize: 14, color: 'text.secondary', flexShrink: 0 }}
/>
<Ellipsis>{item.name}</Ellipsis>
@ -166,8 +170,7 @@ const KBSelect = () => {
setModifyOpen(true);
}}
>
<Icon
type='icon-tianjiawendang'
<IconTianjiawendang
sx={{
fontSize: 14,
color: 'text.tertiary',
@ -185,8 +188,7 @@ const KBSelect = () => {
setDeleteOpen(true);
}}
>
<Icon
type='icon-shanchu'
<IconShanchu
sx={{
fontSize: 14,
color: 'text.tertiary',

View File

@ -1,155 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { TrendData } from '@/api';
import { useThemeMode } from '@ctzhian/ui';
import * as echarts from 'echarts';
import { useEffect, useRef, useState } from 'react';
type ECharts = ReturnType<typeof echarts.init>;
export interface PropsData {
chartData: TrendData[];
height: number;
size?: 'small' | 'large';
}
const LineTrend = ({ chartData = [], height, size = 'large' }: PropsData) => {
const { mode } = useThemeMode();
const domWrapRef = useRef<HTMLDivElement>(null!);
const echartRef = useRef<ECharts>(null!);
const [loading, setLoading] = useState(true);
const [data, setData] = useState<TrendData[]>([]);
useEffect(() => {
if (domWrapRef.current) {
echartRef.current = echarts.init(domWrapRef.current, null, {
renderer: 'svg',
});
}
setData(chartData);
}, [chartData]);
useEffect(() => {
const option = {
grid: {
left: 0,
right: 0,
bottom: 10,
top: 10,
},
tooltip: {
trigger: 'axis',
position: function (
point: number[],
_params: any,
_dom: any,
_rect: any,
size: any,
) {
return [point[0] - size.contentSize[0], point[1] + 20];
},
axisPointer: {
type: 'shadow',
},
formatter: (
params: { name: string; seriesName: string; value: number }[],
) => {
if (params[0]) {
const { name, seriesName, value } = params[0];
return `<div style="font-family: G;">${name}<div>${seriesName} ${value}</div></div>`;
}
return '';
},
},
xAxis: {
type: 'category',
data: data.map(it => it.name),
splitLine: {
show: false,
},
axisLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
show: false,
},
},
yAxis: {
type: 'value',
splitNumber: 4,
axisLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
show: false,
},
splitLine:
size === 'small'
? false
: {
lineStyle: {
type: 'dashed',
color: '#F2F3F5',
},
},
},
series: {
name: '问答次数',
symbol: 'none',
type: 'line',
smooth: true,
data: data.map(it => it.count),
lineStyle: {
color: {
type: 'linear',
x: 0, // 起点 x 坐标0: 左侧)
y: 0, // 起点 y 坐标0: 顶部)
x2: 1, // 终点 x 坐标1: 右侧)
y2: 1, // 终点 y 坐标0: 保持顶部,形成水平渐变)
colorStops: [
{ offset: 0, color: '#9E68FC' }, // 起始颜色
{ offset: 1, color: '#3248F2' }, // 结束颜色
],
},
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(158,104,252,0.1)' },
{ offset: 1, color: 'rgba(50,72,242,0)' },
],
global: false,
},
},
},
};
if (domWrapRef.current && echartRef.current) {
echartRef.current.setOption(option);
setLoading(false);
}
const resize = () => {
if (echartRef.current) {
echartRef.current.resize();
}
};
window.addEventListener('resize', resize);
return () => window.removeEventListener('resize', resize);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data, mode]);
if (data.length === 0 && !loading)
return <div style={{ width: '100%', height }} />;
return <div ref={domWrapRef} style={{ width: '100%', height }} />;
};
export default LineTrend;

View File

@ -1,6 +1,5 @@
import { addOpacityToColor, copyText } from '@/utils';
import { Box, IconButton, useTheme } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import 'katex/dist/katex.min.css';
import React, { useState } from 'react';
import ReactMarkdown from 'react-markdown';
@ -12,6 +11,7 @@ import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
import remarkBreaks from 'remark-breaks';
import remarkGfm from 'remark-gfm';
import remarkMath from 'remark-math';
import { IconXiajiantou } from '@panda-wiki/icons';
interface MarkDownProps {
loading?: boolean;
@ -117,8 +117,7 @@ const MarkDown = ({ loading = false, content }: MarkDownProps) => {
},
}}
>
<Icon
type='icon-xiajiantou'
<IconXiajiantou
sx={{
fontSize: 18,
flexShrink: 0,

View File

@ -1,13 +1,14 @@
import { copyText } from '@/utils';
import { Box, Stack } from '@mui/material';
import { Ellipsis, Icon } from '@ctzhian/ui';
import { Ellipsis } from '@ctzhian/ui';
import { IconFuzhi } from '@panda-wiki/icons';
interface ShowTextProps {
text: string[];
copyable?: boolean;
showIcon?: boolean;
noEllipsis?: boolean;
icon?: string;
icon?: React.ReactNode;
onClick?: () => void;
}
@ -15,7 +16,9 @@ const ShowText = ({
text,
copyable = true,
showIcon = true,
icon = 'icon-fuzhi',
icon = (
<IconFuzhi sx={{ fontSize: 16, color: 'text.disabled', flexShrink: 0 }} />
),
onClick,
noEllipsis = false,
}: ShowTextProps) => {
@ -62,12 +65,7 @@ const ShowText = ({
),
)}
</Stack>
{showIcon && (
<Icon
type={icon}
sx={{ fontSize: 16, color: 'text.disabled', flexShrink: 0 }}
/>
)}
{showIcon && icon}
</Stack>
);
};

View File

@ -12,7 +12,8 @@ import { useVersionInfo } from '@/hooks';
import { useAppDispatch, useAppSelector } from '@/store';
import { setLicense } from '@/store/slices/config';
import { Box, Button, IconButton, Stack, TextField } from '@mui/material';
import { CusTabs, Icon, message, Modal } from '@ctzhian/ui';
import { CusTabs, message, Modal } from '@ctzhian/ui';
import { IconWenjian, IconIcon_tool_close } from '@panda-wiki/icons';
import dayjs from 'dayjs';
import { useState } from 'react';
import LottieIcon from '../LottieIcon';
@ -312,11 +313,11 @@ const AuthTypeModal = ({
}}
>
<Stack direction={'row'} alignItems={'center'} gap={1}>
<Icon type='icon-wenjian' />
<IconWenjian sx={{ fontSize: 16 }} />
{file.name}
</Stack>
<IconButton onClick={() => setFile(undefined)}>
<Icon type='icon-icon_tool_close' sx={{ fontSize: 16 }} />
<IconIcon_tool_close sx={{ fontSize: 16 }} />
</IconButton>
</Stack>
)}

View File

@ -3,19 +3,31 @@ import Qrcode from '@/assets/images/qrcode.png';
import { Box, Button, Stack, Typography, useTheme } from '@mui/material';
import { ConstsUserKBPermission } from '@/request/types';
import { Icon, Modal } from '@ctzhian/ui';
import { Modal } from '@ctzhian/ui';
import { useState, useMemo, useEffect } from 'react';
import { NavLink, useLocation, useNavigate } from 'react-router-dom';
import Avatar from '../Avatar';
import Version from './Version';
import { useAppSelector } from '@/store';
import {
IconBangzhuwendang1,
IconNeirongguanli,
IconTongjifenxi1,
IconJushou,
IconGongxian,
IconPaperFull,
IconDuihualishi1,
IconChilun,
IconGroup,
IconGithub,
} from '@panda-wiki/icons';
const MENUS = [
{
label: '文档',
value: '/',
pathname: 'document',
icon: 'icon-neirongguanli',
icon: IconNeirongguanli,
show: true,
perms: [
ConstsUserKBPermission.UserKBPermissionFullControl,
@ -26,7 +38,7 @@ const MENUS = [
label: '统计',
value: '/stat',
pathname: 'stat',
icon: 'icon-tongjifenxi1',
icon: IconTongjifenxi1,
show: true,
perms: [
ConstsUserKBPermission.UserKBPermissionFullControl,
@ -37,7 +49,7 @@ const MENUS = [
label: '贡献',
value: '/contribution',
pathname: 'contribution',
icon: 'icon-gongxian',
icon: IconGongxian,
show: true,
perms: [ConstsUserKBPermission.UserKBPermissionFullControl],
},
@ -45,7 +57,7 @@ const MENUS = [
label: '问答',
value: '/conversation',
pathname: 'conversation',
icon: 'icon-duihualishi1',
icon: IconDuihualishi1,
show: true,
perms: [
ConstsUserKBPermission.UserKBPermissionFullControl,
@ -56,7 +68,7 @@ const MENUS = [
label: '反馈',
value: '/feedback',
pathname: 'feedback',
icon: 'icon-jushou',
icon: IconJushou,
show: true,
perms: [
ConstsUserKBPermission.UserKBPermissionFullControl,
@ -67,7 +79,7 @@ const MENUS = [
label: '发布',
value: '/release',
pathname: 'release',
icon: 'icon-paper-full',
icon: IconPaperFull,
show: true,
perms: [
ConstsUserKBPermission.UserKBPermissionFullControl,
@ -78,7 +90,7 @@ const MENUS = [
label: '设置',
value: '/setting',
pathname: 'application-setting',
icon: 'icon-chilun',
icon: IconChilun,
show: true,
perms: [ConstsUserKBPermission.UserKBPermissionFullControl],
},
@ -153,6 +165,7 @@ const Sidebar = () => {
isActive = pathname.includes(it.value);
}
if (!it.show) return null;
const IconMenu = it.icon;
return (
<NavLink
key={it.pathname}
@ -181,8 +194,7 @@ const Sidebar = () => {
},
}}
>
<Icon
type={it.icon}
<IconMenu
sx={{
fontSize: 14,
mr: 1,
@ -216,7 +228,7 @@ const Sidebar = () => {
},
}}
startIcon={
<Icon type='icon-bangzhuwendang1' sx={{ width: 14, height: 14 }} />
<IconBangzhuwendang1 sx={{ fontSize: '14px !important' }} />
}
onClick={() =>
window.open('https://pandawiki.docs.baizhi.cloud/', '_blank')
@ -244,7 +256,7 @@ const Sidebar = () => {
color: 'primary.main',
},
}}
startIcon={<Icon type='icon-GitHub' sx={{ width: 14, height: 14 }} />}
startIcon={<IconGithub sx={{ fontSize: '14px !important' }} />}
onClick={() =>
window.open('https://github.com/chaitin/PandaWiki', '_blank')
}
@ -271,7 +283,7 @@ const Sidebar = () => {
},
}}
onClick={() => setShowQrcode(true)}
startIcon={<Icon type='icon-group' sx={{ width: 14, height: 14 }} />}
startIcon={<IconGroup sx={{ fontSize: '14px !important' }} />}
>
线
</Button>

View File

@ -1,8 +1,5 @@
import Card from '@/components/Card';
import { Icon, message } from '@ctzhian/ui';
import {
Box,
Button,
Stack,
TextField,
Select,
@ -10,6 +7,8 @@ import {
InputAdornment,
IconButton,
} from '@mui/material';
import InfoOutlineSharpIcon from '@mui/icons-material/InfoOutlineSharp';
import KeySharpIcon from '@mui/icons-material/KeySharp';
import Visibility from '@mui/icons-material/Visibility';
import VisibilityOff from '@mui/icons-material/VisibilityOff';
import { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
@ -107,8 +106,7 @@ const AutoModelConfig = forwardRef<AutoModelConfigRef, AutoModelConfigProps>(
border: '1px solid rgba(25, 118, 210, 0.2)',
}}
>
<Icon
type='icon-info-circle'
<InfoOutlineSharpIcon
sx={{
fontSize: 16,
color: 'primary.main',
@ -170,7 +168,7 @@ const AutoModelConfig = forwardRef<AutoModelConfigRef, AutoModelConfigProps>(
gap: 0.5,
}}
>
<Icon type='icon-key' sx={{ fontSize: 14 }} />
<KeySharpIcon sx={{ fontSize: 14, color: 'primary.main' }} />
API Key
</Box>
</Box>

View File

@ -4,9 +4,10 @@ import Card from '@/components/Card';
import { copyText, generatePassword } from '@/utils';
import { CheckCircle } from '@mui/icons-material';
import { Box, IconButton, Stack, TextField } from '@mui/material';
import { Icon, Modal } from '@ctzhian/ui';
import { Modal } from '@ctzhian/ui';
import { useEffect, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { IconShuaxin } from '@panda-wiki/icons';
type UpdateMemberProps = {
user: V1UserListItemResp;
@ -163,7 +164,7 @@ const MemberUpdate = ({ user, refresh, type }: UpdateMemberProps) => {
onClick={() => setValue('password', generatePassword())}
sx={{ flexShrink: 0 }}
>
<Icon type='icon-shuaxin' />
<IconShuaxin sx={{ fontSize: 16 }} />
</IconButton>
</Stack>
</Modal>

View File

@ -8,7 +8,7 @@ import {
} from '@/request/Model';
import { GithubComChaitinPandaWikiDomainModelListItem } from '@/request/types';
import { addOpacityToColor } from '@/utils';
import { Icon, message, Modal } from '@ctzhian/ui';
import { message, Modal } from '@ctzhian/ui';
import {
Box,
Button,
@ -188,7 +188,7 @@ const ModelConfig = forwardRef<ModelConfigRef, ModelConfigProps>(
const handleSave = async () => {
setIsSaving(true);
try {
let requestData: {
const requestData: {
mode: 'auto' | 'manual';
auto_mode_api_key?: string;
chat_model?: string;
@ -224,11 +224,37 @@ const ModelConfig = forwardRef<ModelConfigRef, ModelConfigProps>(
);
getModelList(); // 刷新模型列表
} catch (err) {
console.error(err);
} finally {
setIsSaving(false);
}
};
const IconModel = chatModelData
? ModelProvider[chatModelData.provider as keyof typeof ModelProvider].icon
: null;
const IconEmbeddingModel = embeddingModelData
? ModelProvider[embeddingModelData.provider as keyof typeof ModelProvider]
.icon
: null;
const IconRerankModel = rerankModelData
? ModelProvider[rerankModelData.provider as keyof typeof ModelProvider]
.icon
: null;
const IconAnalysisModel = analysisModelData
? ModelProvider[analysisModelData.provider as keyof typeof ModelProvider]
.icon
: null;
const IconAnalysisVLModel = analysisVLModelData
? ModelProvider[
analysisVLModelData.provider as keyof typeof ModelProvider
].icon
: null;
return (
<Stack gap={0}>
<Box
@ -331,14 +357,7 @@ const ModelConfig = forwardRef<ModelConfigRef, ModelConfigProps>(
>
{chatModelData ? (
<>
<Icon
type={
ModelProvider[
chatModelData.provider as keyof typeof ModelProvider
].icon
}
sx={{ fontSize: 18 }}
/>
{IconModel && <IconModel sx={{ fontSize: 18 }} />}
<Box
sx={{
fontSize: 14,
@ -540,14 +559,10 @@ const ModelConfig = forwardRef<ModelConfigRef, ModelConfigProps>(
>
{embeddingModelData ? (
<>
<Icon
type={
ModelProvider[
embeddingModelData.provider as keyof typeof ModelProvider
].icon
}
sx={{ fontSize: 18 }}
/>
{IconEmbeddingModel && (
<IconEmbeddingModel sx={{ fontSize: 18 }} />
)}
<Box
sx={{
fontSize: 14,
@ -754,14 +769,10 @@ const ModelConfig = forwardRef<ModelConfigRef, ModelConfigProps>(
>
{rerankModelData ? (
<>
<Icon
type={
ModelProvider[
rerankModelData.provider as keyof typeof ModelProvider
].icon
}
sx={{ fontSize: 18 }}
/>
{IconRerankModel && (
<IconRerankModel sx={{ fontSize: 18 }} />
)}
<Box
sx={{
fontSize: 14,
@ -963,14 +974,10 @@ const ModelConfig = forwardRef<ModelConfigRef, ModelConfigProps>(
>
{analysisModelData ? (
<>
<Icon
type={
ModelProvider[
analysisModelData.provider as keyof typeof ModelProvider
].icon
}
sx={{ fontSize: 18 }}
/>
{IconAnalysisModel && (
<IconAnalysisModel sx={{ fontSize: 18 }} />
)}
<Box
sx={{
fontSize: 14,
@ -1172,14 +1179,9 @@ const ModelConfig = forwardRef<ModelConfigRef, ModelConfigProps>(
>
{analysisVLModelData ? (
<>
<Icon
type={
ModelProvider[
analysisVLModelData.provider as keyof typeof ModelProvider
].icon
}
sx={{ fontSize: 18 }}
/>
{IconAnalysisVLModel && (
<IconAnalysisVLModel sx={{ fontSize: 18 }} />
)}
<Box
sx={{
fontSize: 14,

View File

@ -1,22 +1,12 @@
import ErrorJSON from '@/assets/json/error.json';
import Card from '@/components/Card';
import { ModelProvider } from '@/constant/enums';
import { getApiV1ModelList } from '@/request/Model';
import { GithubComChaitinPandaWikiDomainModelListItem } from '@/request/types';
import { useAppDispatch, useAppSelector } from '@/store';
import { setModelList, setModelStatus } from '@/store/slices/config';
import { Icon, message, Modal } from '@ctzhian/ui';
import {
Box,
Button,
Stack,
Tab,
Tabs,
Tooltip,
useTheme,
} from '@mui/material';
import { Modal } from '@ctzhian/ui';
import { IconAChilunshezhisheding } from '@panda-wiki/icons';
import { Box, Button, Tab, Tabs, useTheme } from '@mui/material';
import { useEffect, useState, useRef } from 'react';
import LottieIcon from '../LottieIcon';
import Member from './component/Member';
import ModelConfig, { ModelConfigRef } from './component/ModelConfig';
@ -91,7 +81,7 @@ const System = () => {
<Button
size='small'
variant='outlined'
startIcon={<Icon type='icon-a-chilunshezhisheding' />}
startIcon={<IconAChilunshezhisheding />}
onClick={() => setOpen(true)}
>

View File

@ -1,8 +1,8 @@
import { Icon } from '@ctzhian/ui';
import { Upload as UploadIcon } from '@mui/icons-material';
import { Box, Button, Stack, Typography, useTheme } from '@mui/material';
import { useCallback, useEffect, useRef, useState } from 'react';
import { FileRejection, useDropzone } from 'react-dropzone';
import { IconShangchuan } from '@panda-wiki/icons';
// 文件扩展名到 MIME 类型的映射
const FILE_EXTENSION_TO_MIME: Record<string, string> = {
@ -110,8 +110,7 @@ const Upload = ({
onClick={() => fileInputRef.current?.click()}
>
<input {...getInputProps()} />
<Icon
type='icon-shangchuan'
<IconShangchuan
sx={{ fontSize: 40, mb: 1, color: 'text.secondary' }}
/>
<Typography

View File

@ -1,8 +1,8 @@
import { CheckCircle } from '@mui/icons-material';
import { Box, Stack, Typography, useTheme, SxProps } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { IconShangchuan } from '@panda-wiki/icons';
interface FileTextProps {
file?: File;
@ -97,10 +97,7 @@ const FileText = ({
{dropFiles.length > 0 ? (
<CheckCircle sx={{ color: 'success.main', fontSize: 20 }} />
) : (
<Icon
type='icon-shangchuan'
sx={{ fontSize: 20, color: 'text.disabled' }}
/>
<IconShangchuan sx={{ fontSize: 20, color: 'text.disabled' }} />
)}
<Typography
variant='body1'

View File

@ -1,8 +1,9 @@
import { uploadFile } from '@/api';
import { Box, IconButton, LinearProgress, Stack } from '@mui/material';
import { Icon, message } from '@ctzhian/ui';
import { message } from '@ctzhian/ui';
import { useEffect, useRef, useState } from 'react';
import CustomImage from '../CustomImage';
import { IconShangchuan, IconIcon_tool_close } from '@panda-wiki/icons';
interface UploadFileProps {
type: 'url' | 'base64';
@ -241,7 +242,7 @@ const UploadFile = ({
onChange('');
}}
>
<Icon type='icon-icon_tool_close' sx={{ color: '#fff' }} />
<IconIcon_tool_close sx={{ fontSize: 16, color: '#fff' }} />
</IconButton>
</>
) : (
@ -253,8 +254,7 @@ const UploadFile = ({
fontSize: width ? (width < 40 ? 8 : 12) : 12,
}}
>
<Icon
type='icon-shangchuan'
<IconShangchuan
sx={{ fontSize: width ? (width < 40 ? 12 : 18) : 18 }}
/>
<Box>{label}</Box>

View File

@ -1,501 +0,0 @@
export const CountryEnums = {
Afghanistan: 'af',
'Aland Islands': 'ax',
Albania: 'al',
Algeria: 'dz',
'American Samoa': 'as',
Andorra: 'ad',
Angola: 'ao',
Anguilla: 'ai',
Antarctica: 'aq',
'Antigua and Barbuda': 'ag',
Argentina: 'ar',
Armenia: 'am',
Aruba: 'aw',
'Ascension Island': 'sh-ac',
'Association of Southeast Asian Nations': 'asean',
Australia: 'au',
Austria: 'at',
Azerbaijan: 'az',
Bahamas: 'bs',
Bahrain: 'bh',
Bangladesh: 'bd',
Barbados: 'bb',
'Basque Country': 'es-pv',
Belarus: 'by',
Belgium: 'be',
Belize: 'bz',
Benin: 'bj',
Bermuda: 'bm',
Bhutan: 'bt',
Bolivia: 'bo',
'Bonaire, Sint Eustatius and Saba': 'bq',
'Bosnia and Herzegovina': 'ba',
Botswana: 'bw',
'Bouvet Island': 'bv',
Brazil: 'br',
'British Indian Ocean Territory': 'io',
'Brunei Darussalam': 'bn',
Bulgaria: 'bg',
'Burkina Faso': 'bf',
Burundi: 'bi',
'Cabo Verde': 'cv',
Cambodia: 'kh',
Cameroon: 'cm',
Canada: 'ca',
'Canary Islands': 'ic',
Catalonia: 'es-ct',
'Cayman Islands': 'ky',
'Central African Republic': 'cf',
'Central European Free Trade Agreement': 'cefta',
Chad: 'td',
Chile: 'cl',
China: 'cn',
'Christmas Island': 'cx',
'Clipperton Island': 'cp',
'Cocos (Keeling) Islands': 'cc',
Colombia: 'co',
Comoros: 'km',
'Cook Islands': 'ck',
'Costa Rica': 'cr',
Croatia: 'hr',
Cuba: 'cu',
Curaçao: 'cw',
Cyprus: 'cy',
'Czech Republic': 'cz',
"Côte d'Ivoire": 'ci',
'Democratic Republic of the Congo': 'cd',
Denmark: 'dk',
'Diego Garcia': 'dg',
Djibouti: 'dj',
Dominica: 'dm',
'Dominican Republic': 'do',
'East African Community': 'eac',
Ecuador: 'ec',
Egypt: 'eg',
'El Salvador': 'sv',
England: 'gb-eng',
'Equatorial Guinea': 'gq',
Eritrea: 'er',
Estonia: 'ee',
Eswatini: 'sz',
Ethiopia: 'et',
Europe: 'eu',
'Falkland Islands': 'fk',
'Faroe Islands': 'fo',
'Federated States of Micronesia': 'fm',
Fiji: 'fj',
Finland: 'fi',
France: 'fr',
'French Guiana': 'gf',
'French Polynesia': 'pf',
'French Southern Territories': 'tf',
Gabon: 'ga',
Galicia: 'es-ga',
Gambia: 'gm',
Georgia: 'ge',
Germany: 'de',
Ghana: 'gh',
Gibraltar: 'gi',
Greece: 'gr',
Greenland: 'gl',
Grenada: 'gd',
Guadeloupe: 'gp',
Guam: 'gu',
Guatemala: 'gt',
Guernsey: 'gg',
Guinea: 'gn',
'Guinea-Bissau': 'gw',
Guyana: 'gy',
Haiti: 'ht',
'Heard Island and McDonald Islands': 'hm',
'Holy See': 'va',
Honduras: 'hn',
'Hong Kong': 'hk',
Hungary: 'hu',
Iceland: 'is',
India: 'in',
Indonesia: 'id',
Iran: 'ir',
Iraq: 'iq',
Ireland: 'ie',
'Isle of Man': 'im',
Israel: 'il',
Italy: 'it',
Jamaica: 'jm',
Japan: 'jp',
Jersey: 'je',
Jordan: 'jo',
Kazakhstan: 'kz',
Kenya: 'ke',
Kiribati: 'ki',
Kosovo: 'xk',
Kuwait: 'kw',
Kyrgyzstan: 'kg',
Laos: 'la',
Latvia: 'lv',
'League of Arab States': 'arab',
Lebanon: 'lb',
Lesotho: 'ls',
Liberia: 'lr',
Libya: 'ly',
Liechtenstein: 'li',
Lithuania: 'lt',
Luxembourg: 'lu',
Macau: 'mo',
Madagascar: 'mg',
Malawi: 'mw',
Malaysia: 'my',
Maldives: 'mv',
Mali: 'ml',
Malta: 'mt',
'Marshall Islands': 'mh',
Martinique: 'mq',
Mauritania: 'mr',
Mauritius: 'mu',
Mayotte: 'yt',
Mexico: 'mx',
Moldova: 'md',
Monaco: 'mc',
Mongolia: 'mn',
Montenegro: 'me',
Montserrat: 'ms',
Morocco: 'ma',
Mozambique: 'mz',
Myanmar: 'mm',
Namibia: 'na',
Nauru: 'nr',
Nepal: 'np',
'The Netherlands': 'nl',
'New Caledonia': 'nc',
'New Zealand': 'nz',
Nicaragua: 'ni',
Niger: 'ne',
Nigeria: 'ng',
Niue: 'nu',
'Norfolk Island': 'nf',
'North Korea': 'kp',
'North Macedonia': 'mk',
'Northern Ireland': 'gb-nir',
'Northern Mariana Islands': 'mp',
Norway: 'no',
Oman: 'om',
'Pacific Community': 'pc',
Pakistan: 'pk',
Palau: 'pw',
Panama: 'pa',
'Papua New Guinea': 'pg',
Paraguay: 'py',
Peru: 'pe',
Philippines: 'ph',
Pitcairn: 'pn',
Poland: 'pl',
Portugal: 'pt',
'Puerto Rico': 'pr',
Qatar: 'qa',
'Republic of the Congo': 'cg',
Romania: 'ro',
Russia: 'ru',
Rwanda: 'rw',
Réunion: 're',
'Saint Barthélemy': 'bl',
'Saint Helena': 'sh-hl',
'Saint Helena, Ascension and Tristan da Cunha': 'sh',
'Saint Kitts and Nevis': 'kn',
'Saint Lucia': 'lc',
'Saint Martin': 'mf',
'Saint Pierre and Miquelon': 'pm',
'Saint Vincent and the Grenadines': 'vc',
Samoa: 'ws',
'San Marino': 'sm',
'Sao Tome and Principe': 'st',
'Saudi Arabia': 'sa',
Scotland: 'gb-sct',
Senegal: 'sn',
Serbia: 'rs',
Seychelles: 'sc',
'Sierra Leone': 'sl',
Singapore: 'sg',
'Sint Maarten': 'sx',
Slovakia: 'sk',
Slovenia: 'si',
'Solomon Islands': 'sb',
Somalia: 'so',
'South Africa': 'za',
'South Georgia and the South Sandwich Islands': 'gs',
'South Korea': 'kr',
'South Sudan': 'ss',
Spain: 'es',
'Sri Lanka': 'lk',
'State of Palestine': 'ps',
Sudan: 'sd',
Suriname: 'sr',
'Svalbard and Jan Mayen': 'sj',
Sweden: 'se',
Switzerland: 'ch',
Syria: 'sy',
Taiwan: 'tw',
Tajikistan: 'tj',
Tanzania: 'tz',
Thailand: 'th',
'Timor-Leste': 'tl',
Togo: 'tg',
Tokelau: 'tk',
Tonga: 'to',
'Trinidad and Tobago': 'tt',
'Tristan da Cunha': 'sh-ta',
Tunisia: 'tn',
Turkmenistan: 'tm',
'Turks and Caicos Islands': 'tc',
Tuvalu: 'tv',
Türkiye: 'tr',
Uganda: 'ug',
Ukraine: 'ua',
'United Arab Emirates': 'ae',
'United Kingdom': 'gb',
'United Nations': 'un',
'United States': 'us',
Unknown: 'xx',
Uruguay: 'uy',
Uzbekistan: 'uz',
Vanuatu: 'vu',
Venezuela: 've',
Vietnam: 'vn',
'Viet Nam': 'vn',
'Virgin Islands (British)': 'vg',
'Virgin Islands (U.S.)': 'vi',
Wales: 'gb-wls',
'Wallis and Futuna': 'wf',
'Western Sahara': 'eh',
Yemen: 'ye',
Zambia: 'zm',
Zimbabwe: 'zw',
: 'xx',
: 'bt',
: 'tl',
: 'cn',
: 'cf',
: 'dk',
: 'ua',
: 'uz',
: 'ug',
: 'uy',
: 'td',
: 'ye',
: 'am',
: 'il',
: 'iq',
: 'ir',
: 'bz',
: 'cv',
: 'ru',
: 'bg',
: 'hr',
'关岛(美国)': 'gu',
: 'gm',
: 'is',
: 'gn',
: 'gw',
: 'li',
'刚果(金)': 'cd',
: 'cg',
: 'ly',
: 'lr',
: 'ca',
: 'gh',
: 'ga',
: 'hu',
: 'kp',
'北马里亚纳群岛(美国)': 'mp',
: 'ss',
: 'za',
: 'bw',
: 'qa',
: 'rw',
: 'lu',
: 'in',
西: 'id',
: 'gt',
: 'ec',
: 'er',
: 'sy',
: 'cu',
'台湾(中国)': 'tw',
: 'kg',
: 'dj',
: 'kz',
: 'co',
: 'cr',
: 'cm',
: '简称',
: 'tv',
: 'tm',
: 'tr',
西: 'lc',
: 'kn',
西: 'st',
: 'vc',
'圣皮埃尔和密克隆岛(法国)': 'pm',
: 'sh',
: 'sm',
: 'gy',
: 'tz',
: 'eg',
: 'et',
: 'ki',
: 'tj',
: 'sn',
: 'rs',
: 'sl',
: 'cy',
: 'sc',
西: 'mx',
: 'tg',
: 'dm',
: 'do',
: 'at',
: 've',
: 'bd',
: 'ao',
: 'ai',
: 'ag',
: 'ad',
西: 'fm',
: 'ni',
: 'ng',
: 'ne',
: 'np',
: 'ps',
: 'bs',
: 'pk',
: 'bb',
: 'pg',
: 'py',
: 'pa',
: 'bh',
西: 'br',
: 'bf',
: 'bi',
: 'gr',
: 'pw',
'库克群岛(新西兰)': 'ck',
'开曼群岛(英国)': 'ky',
: 'de',
: 'it',
: 'sb',
: 'tk',
: 'lv',
: 'no',
: 'cz',
: 'md',
: 'ma',
: 'mc',
: 'bn',
: 'fj',
: 'sz',
: 'sk',
: 'si',
: 'lk',
: 'sg',
'新喀里多尼亚(法国)': 'nc',
西: 'nz',
: 'jp',
: 'cl',
: 'kh',
: 'gd',
: 'gl',
: 'ge',
: 'be',
: 'mr',
: 'mu',
: 'to',
: 'sa',
: '',
: 'fr',
'法属圭亚那(法国)': 'gf',
西: 'pf',
'法罗群岛(丹麦)': 'fo',
: 'pl',
'波多黎各(美国)': 'pr',
: 'ba',
: 'th',
: 'zw',
: 'hn',
: 'ht',
: 'au',
'澳门(中国)': 'mo',
: 'ie',
: 'ee',
: 'jm',
'特克斯和凯科斯群岛(英国)': 'tc',
: 'tt',
: 'bo',
: 'nr',
: 'se',
: 'ch',
: 'gp',
'瓦利斯和富图纳群岛(法国)': 'wf',
: 'vu',
'留尼汪(法国)': 're',
: 'by',
: 'bm',
'直布罗陀(英国)': 'gi',
: 'kw',
: 'km',
: 'ci',
: 'pe',
: 'tn',
: 'lt',
: 'so',
: 'jo',
: 'na',
: 'nu',
: 'mm',
: 'ro',
: 'us',
'美属萨摩亚(美国)': 'as',
: 'la',
: 'ke',
: 'fi',
: 'sd',
: 'sr',
: 'gb',
'英属维京群岛(英国)': 'vg',
: 'nl',
: '',
: 'mz',
: 'ls',
: 'ph',
: 'sv',
: 'ws',
: 'pt',
: 'mn',
'蒙塞拉特岛(英国)': 'ms',
西: 'es',
: 'bj',
: 'zm',
: 'gq',
: 'vn',
: 'az',
: 'af',
: 'dz',
: 'al',
: 'ae',
: 'om',
: 'ar',
: 'aw',
: 'kr',
'香港(中国)': 'hk',
: 'mk',
: 'mv',
: 'mw',
'马提尼克(法国)': 'mq',
西: 'my',
: 'yt',
: 'mh',
: 'mt',
: 'mg',
: 'ml',
: 'lb',
: 'me',
};

View File

@ -1,3 +1,54 @@
import {
IconAWebyingyong,
IconWangyeguajian,
IconDingdingjiqiren,
IconWendajiqiren,
IconFeishujiqiren,
IconQiyeweixinjiqiren,
IconQiyeweixinkefu,
IconADiscordjiqiren,
IconWeixingongzhonghaoDaiyanse,
IconBaizhiyunlogo,
IconZhipuqingyan,
IconDeepseek,
IconTengxunhunyuan,
IconAliyunbailian,
IconHuoshanyinqing,
IconAzure,
IconGemini,
IconQiniuyun,
IconOllama,
IconAZiyuan2,
IconKim,
IconXinference,
IconGpustack,
IconLingyiwanwu,
IconChatgpt,
IconAAIshezhi,
IconHyperbolic,
IconPerplexity,
IconTianyiyun,
IconTengxunyun,
IconBaiduyun,
IconModaGPT,
IconInfini,
IconStep,
IconLanyun,
IconAlayanew,
IconPpio,
IconAihubmix,
IconOcoolai,
IconDMXAPI,
IconBurncloud,
IconYingweida,
IconTokenflux,
IconA302ai,
IconCephalon,
IconFireworks,
IconMistral,
IconOpenrouter,
} from '@panda-wiki/icons';
export const PageStatus = {
1: {
label: '正在处理',
@ -30,47 +81,47 @@ export const IconMap = {
export const AppType = {
1: {
label: 'Wiki 网站',
icon: 'icon-a-Webyingyong',
icon: IconAWebyingyong,
},
2: {
label: '网页挂件',
icon: 'icon-wangyeguajian',
icon: IconWangyeguajian,
},
3: {
label: '钉钉机器人',
icon: 'icon-dingdingjiqiren',
icon: IconDingdingjiqiren,
},
4: {
label: '飞书机器人',
icon: 'icon-feishujiqiren',
icon: IconFeishujiqiren,
},
5: {
label: '企业微信机器人',
icon: 'icon-qiyeweixinjiqiren',
icon: IconQiyeweixinjiqiren,
},
6: {
label: '企业微信客服',
icon: 'icon-qiyeweixinkefu',
icon: IconQiyeweixinkefu,
},
7: {
label: 'Discord 机器人',
icon: 'icon-a-discordjiqiren',
icon: IconADiscordjiqiren,
},
8: {
label: '微信公众号',
icon: 'icon-weixingongzhonghao-daiyanse',
icon: IconWeixingongzhonghaoDaiyanse,
},
9: {
label: '问答机器人 API',
icon: 'icon-Wendajiqiren',
icon: IconWendajiqiren,
},
10: {
label: '企业微信智能机器人',
icon: 'icon-qiyeweixinjiqiren',
icon: IconQiyeweixinjiqiren,
},
11: {
label: 'Lark 机器人',
icon: 'icon-feishujiqiren',
icon: IconFeishujiqiren,
},
};
@ -115,7 +166,7 @@ export const ModelProvider = {
BaiZhiCloud: {
label: 'BaiZhiCloud',
cn: '百智云',
icon: 'icon-baizhiyunlogo',
icon: IconBaizhiyunlogo,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -129,7 +180,7 @@ export const ModelProvider = {
ZhiPu: {
label: 'ZhiPu',
cn: '智谱',
icon: 'icon-zhipuqingyan', // 需要添加对应的图标
icon: IconZhipuqingyan, // 需要添加对应的图标
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -143,7 +194,7 @@ export const ModelProvider = {
DeepSeek: {
label: 'DeepSeek',
cn: 'DeepSeek',
icon: 'icon-deepseek',
icon: IconDeepseek,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -157,7 +208,7 @@ export const ModelProvider = {
Hunyuan: {
label: 'Hunyuan',
cn: '腾讯混元',
icon: 'icon-tengxunhunyuan',
icon: IconTengxunhunyuan,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -171,7 +222,7 @@ export const ModelProvider = {
BaiLian: {
label: 'BaiLian',
cn: '阿里云百炼',
icon: 'icon-aliyunbailian',
icon: IconAliyunbailian,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -186,7 +237,7 @@ export const ModelProvider = {
Volcengine: {
label: 'Volcengine',
cn: '火山引擎',
icon: 'icon-huoshanyinqing',
icon: IconHuoshanyinqing,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -200,7 +251,7 @@ export const ModelProvider = {
OpenAI: {
label: 'OpenAI',
cn: 'OpenAI',
icon: 'icon-chatgpt',
icon: IconChatgpt,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -214,7 +265,7 @@ export const ModelProvider = {
Ollama: {
label: 'Ollama',
cn: 'Ollama',
icon: 'icon-ollama',
icon: IconOllama,
urlWrite: true,
secretRequired: false,
customHeader: true,
@ -228,7 +279,7 @@ export const ModelProvider = {
SiliconFlow: {
label: 'SiliconFlow',
cn: '硅基流动',
icon: 'icon-a-ziyuan2',
icon: IconAZiyuan2,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -242,7 +293,7 @@ export const ModelProvider = {
Moonshot: {
label: 'Moonshot',
cn: '月之暗面',
icon: 'icon-Kim',
icon: IconKim,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -256,7 +307,7 @@ export const ModelProvider = {
AzureOpenAI: {
label: 'AzureOpenAI',
cn: 'Azure OpenAI',
icon: 'icon-azure',
icon: IconAzure,
urlWrite: true,
secretRequired: true,
customHeader: false,
@ -271,7 +322,7 @@ export const ModelProvider = {
Gemini: {
label: 'Gemini',
cn: 'Gemini',
icon: 'icon-gemini',
icon: IconGemini,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -285,7 +336,7 @@ export const ModelProvider = {
Qiniu: {
label: 'Qiniu',
cn: '七牛云',
icon: 'icon-qiniuyun',
icon: IconQiniuyun,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -339,7 +390,7 @@ export const ModelProvider = {
Xinference: {
label: 'Xinference',
cn: 'Xinference',
icon: 'icon-Xinference',
icon: IconXinference,
urlWrite: true,
secretRequired: false,
customHeader: false,
@ -355,7 +406,7 @@ export const ModelProvider = {
gpustack: {
label: 'gpustack',
cn: 'GPUStack',
icon: 'icon-gpustack',
icon: IconGpustack,
urlWrite: true,
secretRequired: true,
customHeader: false,
@ -370,7 +421,7 @@ export const ModelProvider = {
Yi: {
label: 'Yi',
cn: '零一万物',
icon: 'icon-lingyiwanwu',
icon: IconLingyiwanwu,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -445,7 +496,7 @@ export const ModelProvider = {
CTYun: {
label: 'CTYun',
cn: '天翼云息壤',
icon: 'icon-tianyiyun',
icon: IconTianyiyun,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -459,7 +510,7 @@ export const ModelProvider = {
TencentTI: {
label: 'TencentTI',
cn: '腾讯云TI',
icon: 'icon-tengxunyun',
icon: IconTengxunyun,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -473,7 +524,7 @@ export const ModelProvider = {
BaiDuQianFan: {
label: 'BaiDuQianFan',
cn: '百度云千帆',
icon: 'icon-baiduyun',
icon: IconBaiduyun,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -487,7 +538,7 @@ export const ModelProvider = {
ModelScope: {
label: 'ModelScope',
cn: '魔搭社区',
icon: 'icon-modaGPT',
icon: IconModaGPT,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -502,7 +553,7 @@ export const ModelProvider = {
Infini: {
label: 'Infini',
cn: '无问芯穹',
icon: 'icon-infini',
icon: IconInfini,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -517,7 +568,7 @@ export const ModelProvider = {
StepFun: {
label: 'StepFun',
cn: '阶跃星辰',
icon: 'icon-step',
icon: IconStep,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -531,7 +582,7 @@ export const ModelProvider = {
LanYun: {
label: 'LanYun',
cn: '蓝耘科技',
icon: 'icon-lanyun',
icon: IconLanyun,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -545,7 +596,7 @@ export const ModelProvider = {
AlayaNew: {
label: 'AlayaNew',
cn: '九章智算云',
icon: 'icon-alayanew',
icon: IconAlayanew,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -560,7 +611,7 @@ export const ModelProvider = {
PPIO: {
label: 'PPIO',
cn: '欧派云',
icon: 'icon-ppio',
icon: IconPpio,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -575,7 +626,7 @@ export const ModelProvider = {
AiHubMix: {
label: 'AiHubMix',
cn: 'AiHubMix',
icon: 'icon-aihubmix',
icon: IconAihubmix,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -589,7 +640,7 @@ export const ModelProvider = {
OcoolAI: {
label: 'OcoolAI',
cn: 'OcoolAI',
icon: 'icon-ocoolai',
icon: IconOcoolai,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -603,7 +654,7 @@ export const ModelProvider = {
DMXAPI: {
label: 'DMXAPI',
cn: 'DMXAPI',
icon: 'icon-DMXAPI',
icon: IconDMXAPI,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -617,7 +668,7 @@ export const ModelProvider = {
BurnCloud: {
label: 'BurnCloud',
cn: 'BurnCloud',
icon: 'icon-burncloud',
icon: IconBurncloud,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -641,7 +692,7 @@ export const ModelProvider = {
Nvidia: {
label: 'Nvidia',
cn: '英伟达',
icon: 'icon-yingweida',
icon: IconYingweida,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -655,7 +706,7 @@ export const ModelProvider = {
TokenFlux: {
label: 'TokenFlux',
cn: 'TokenFlux',
icon: 'icon-tokenflux',
icon: IconTokenflux,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -669,7 +720,7 @@ export const ModelProvider = {
AI302: {
label: 'AI302',
cn: '302.AI',
icon: 'icon-a-302ai',
icon: IconA302ai,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -683,7 +734,7 @@ export const ModelProvider = {
Cephalon: {
label: 'Cephalon',
cn: 'Cephalon',
icon: 'icon-cephalon',
icon: IconCephalon,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -697,7 +748,7 @@ export const ModelProvider = {
OpenRouter: {
label: 'OpenRouter',
cn: 'OpenRouter',
icon: 'icon-openrouter',
icon: IconOpenrouter,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -711,7 +762,7 @@ export const ModelProvider = {
Fireworks: {
label: 'Fireworks',
cn: 'Fireworks',
icon: 'icon-fireworks',
icon: IconFireworks,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -725,7 +776,7 @@ export const ModelProvider = {
Mistral: {
label: 'Mistral',
cn: 'Mistral',
icon: 'icon-Mistral',
icon: IconMistral,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -739,7 +790,7 @@ export const ModelProvider = {
Perplexity: {
label: 'Perplexity',
cn: 'Perplexity',
icon: 'icon-perplexity',
icon: IconPerplexity,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -753,7 +804,7 @@ export const ModelProvider = {
Hyperbolic: {
label: 'Hyperbolic',
cn: 'Hyperbolic',
icon: 'icon-hyperbolic',
icon: IconHyperbolic,
urlWrite: false,
secretRequired: true,
customHeader: false,
@ -767,7 +818,7 @@ export const ModelProvider = {
Other: {
label: 'Other',
cn: '其他',
icon: 'icon-a-AIshezhi',
icon: IconAAIshezhi,
urlWrite: true,
secretRequired: true,
customHeader: false,

View File

@ -11,8 +11,6 @@ import { BrowserRouter } from 'react-router-dom';
import App from './App';
import store from './store';
import '@/assets/fonts/iconfont';
dayjs.extend(duration);
dayjs.extend(relativeTime);
dayjs.locale('zh-cn');

View File

@ -4,9 +4,10 @@ import DragTree from '@/components/Drag/DragTree';
import { useAppSelector } from '@/store';
import { convertToTree } from '@/utils/drag';
import { Box, Checkbox, Stack } from '@mui/material';
import { Icon, Modal } from '@ctzhian/ui';
import { Modal } from '@ctzhian/ui';
import { useEffect, useState } from 'react';
import { getApiV1NodeList } from '@/request/Node';
import { IconWenjianjiaKai } from '@panda-wiki/icons';
interface DocDeleteProps {
open: boolean;
@ -48,7 +49,7 @@ const DocModal = ({ open, onClose, onOk }: DocDeleteProps) => {
setFolderIds(folderIds.includes('root') ? [] : ['root']);
}}
/>
<Icon type={'icon-wenjianjia-kai'} />
<IconWenjianjiaKai sx={{ fontSize: 16 }} />
<Box></Box>
</Stack>
<DragTree

View File

@ -19,6 +19,7 @@ import {
} from '@mui/material';
import { Ellipsis, Icon, Modal } from '@ctzhian/ui';
import { useEffect, useState } from 'react';
import { IconDitu_diqiu } from '@panda-wiki/icons';
const handleThinkingContent = (content: string) => {
const thinkRegex = /<think>([\s\S]*?)(?:<\/think>|$)/g;
@ -302,8 +303,7 @@ const Detail = ({
src={item.favicon}
sx={{ width: 18, height: 18 }}
errorIcon={
<Icon
type='icon-ditu_diqiu'
<IconDitu_diqiu
sx={{ fontSize: 18, color: 'text.tertiary' }}
/>
}

View File

@ -1,7 +1,7 @@
import { useURLSearchParams } from '@/hooks';
import { IconButton, InputAdornment, Stack, TextField } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { useState } from 'react';
import { IconIcon_tool_close } from '@panda-wiki/icons';
const Search = () => {
const [searchParams, setSearchParams] = useURLSearchParams();
@ -37,8 +37,7 @@ const Search = () => {
}}
size='small'
>
<Icon
type='icon-icon_tool_close'
<IconIcon_tool_close
sx={{ fontSize: 14, color: 'text.tertiary' }}
/>
</IconButton>
@ -70,8 +69,7 @@ const Search = () => {
}}
size='small'
>
<Icon
type='icon-icon_tool_close'
<IconIcon_tool_close
sx={{ fontSize: 14, color: 'text.tertiary' }}
/>
</IconButton>

View File

@ -34,15 +34,12 @@ const Conversation = () => {
title: '问题',
render: (text: string, record) => {
const isGroupChat = record.info?.user_info?.from === 1;
const AppIcon =
AppType[record.app_type as keyof typeof AppType]?.icon || '';
return (
<>
<Stack direction={'row'} alignItems={'center'} gap={1}>
<Icon
sx={{ fontSize: 12 }}
type={
AppType[record.app_type as keyof typeof AppType]?.icon || ''
}
/>
<AppIcon sx={{ fontSize: 12 }}></AppIcon>
<Ellipsis
className='primary-color'
sx={{ cursor: 'pointer', flex: 1, width: 0 }}

View File

@ -1,6 +1,6 @@
import { ConstsCrawlerSource, postApiV1CrawlerParse } from '@/request';
import { useAppSelector } from '@/store';
import { Ellipsis, Icon } from '@ctzhian/ui';
import { Ellipsis } from '@ctzhian/ui';
import {
alpha,
Box,
@ -20,6 +20,7 @@ import { ListDataItem } from '..';
import StatusBackground from '../components/StatusBackground';
import StatusBadge from '../components/StatusBadge';
import { flattenCrawlerParseResponse } from '../util';
import { IconWenjianjia, IconWenjian } from '@panda-wiki/icons';
interface ListRenderItemProps {
depth: number;
@ -182,15 +183,9 @@ const ListRenderItem = ({
)}
<Box sx={{ ml: depth * 3 }}>
{!data.file ? (
<Icon
type='icon-wenjianjia'
sx={{ fontSize: 14, width: 20, ml: '10px' }}
/>
<IconWenjianjia sx={{ fontSize: 14, width: 20, ml: '10px' }} />
) : (
<Icon
type='icon-wenjian'
sx={{ fontSize: 14, width: 20, ml: '10px' }}
/>
<IconWenjian sx={{ fontSize: 14, width: 20, ml: '10px' }} />
)}
</Box>
</ListItemIcon>

View File

@ -33,6 +33,7 @@ import { useEffect, useMemo, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { BUSINESS_VERSION_PERMISSION } from '@/constant/version';
import { VersionCanUse } from '@/components/VersionMask';
import { IconShuaxin } from '@panda-wiki/icons';
interface DocPropertiesModalProps {
open: boolean;
@ -472,13 +473,13 @@ const DocPropertiesModal = ({
onClick={onGenerateSummary}
disabled={loading}
startIcon={
<Icon
type='icon-shuaxin'
sx={
loading
<IconShuaxin
sx={{
fontSize: '16px !important',
...(loading
? { animation: 'loadingRotate 1s linear infinite' }
: {}
}
: {}),
}}
/>
}
>

View File

@ -1,6 +1,6 @@
import { useURLSearchParams } from '@/hooks';
import { IconButton, InputAdornment, Stack, TextField } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { IconIcon_tool_close } from '@panda-wiki/icons';
import { useState } from 'react';
const DocSearch = () => {
@ -32,8 +32,7 @@ const DocSearch = () => {
}}
size='small'
>
<Icon
type='icon-icon_tool_close'
<IconIcon_tool_close
sx={{ fontSize: 14, color: 'text.tertiary' }}
/>
</IconButton>

View File

@ -5,9 +5,10 @@ import { postApiV1NodeBatchMove } from '@/request/Node';
import { DomainNodeListItemResp } from '@/request/types';
import { useAppSelector } from '@/store';
import { convertToTree } from '@/utils/drag';
import { Icon, message, Modal } from '@ctzhian/ui';
import { message, Modal } from '@ctzhian/ui';
import { Box, Checkbox, Stack } from '@mui/material';
import { useEffect, useState } from 'react';
import { IconWenjianjiaKai } from '@panda-wiki/icons';
interface DocDeleteProps {
open: boolean;
@ -83,7 +84,7 @@ const MoveDocs = ({
setFolderIds(folderIds.includes('root') ? [] : ['root']);
}}
/>
<Icon type={'icon-wenjianjia-kai'} />
<IconWenjianjiaKai sx={{ fontSize: 14 }} />
<Box></Box>
</Stack>
<DragTree

View File

@ -1,8 +1,9 @@
import { postApiV1NodeSummary, putApiV1NodeDetail } from '@/request/Node';
import { DomainNodeListItemResp } from '@/request/types';
import { Button, Stack, TextField } from '@mui/material';
import { Icon, message, Modal } from '@ctzhian/ui';
import { message, Modal } from '@ctzhian/ui';
import { useEffect, useState } from 'react';
import { IconShuaxin } from '@panda-wiki/icons';
interface SummaryProps {
kb_id: string;
@ -63,13 +64,13 @@ const Summary = ({ open, data, kb_id, onClose, refresh }: SummaryProps) => {
onClick={createSummary}
disabled={loading}
startIcon={
<Icon
type='icon-shuaxin'
sx={
loading
<IconShuaxin
sx={{
fontSize: '16px !important',
...(loading
? { animation: 'loadingRotate 1s linear infinite' }
: {}
}
: {}),
}}
/>
}
>

View File

@ -1,8 +1,9 @@
import { useAppSelector } from '@/store';
import { Ellipsis, Icon } from '@ctzhian/ui';
import { Ellipsis } from '@ctzhian/ui';
import { Stack } from '@mui/material';
import { useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { IconZuzhi } from '@panda-wiki/icons';
const KBSwitch = () => {
// const dispatch = useAppDispatch();
@ -46,7 +47,7 @@ const KBSwitch = () => {
navigate('/');
}}
>
<Icon type='icon-zuzhi' sx={{ color: 'text.primary' }} />
<IconZuzhi type='icon-zuzhi' sx={{ color: 'text.primary' }} />
</Stack>
<Ellipsis sx={{ flex: 1, width: 0, overflow: 'hidden' }}>
{currentKb?.name}

View File

@ -9,12 +9,19 @@ import { DomainNodeListItemResp, V1NodeDetailResp } from '@/request/types';
import { useAppSelector } from '@/store';
import { addOpacityToColor } from '@/utils';
import { convertToTree } from '@/utils/drag';
import { Ellipsis, Icon } from '@ctzhian/ui';
import { Ellipsis } from '@ctzhian/ui';
import { alpha, Box, IconButton, Stack, useTheme } from '@mui/material';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useLocation, useNavigate, useParams } from 'react-router-dom';
import DocAddByCustomText from '../../component/DocAddByCustomText';
import KBSwitch from './KBSwitch';
import {
IconIcon_tool_close,
IconWenjianjia,
IconWenjian,
IconMulushouqi,
IconXiajiantou,
} from '@panda-wiki/icons';
interface CatalogProps {
curNode: V1NodeDetailResp;
@ -123,9 +130,8 @@ const Catalog = ({ curNode, setCatalogOpen }: CatalogProps) => {
}}
context={
<IconButton>
<Icon
<IconIcon_tool_close
className='catalog-folder-add-icon'
type='icon-icon_tool_close'
sx={{
fontSize: 16,
color: 'action.selected',
@ -235,8 +241,7 @@ const Catalog = ({ curNode, setCatalogOpen }: CatalogProps) => {
top: 13,
}}
>
<Icon
type='icon-xiajiantou'
<IconXiajiantou
sx={{
fontSize: 16,
color: 'text.disabled',
@ -251,10 +256,13 @@ const Catalog = ({ curNode, setCatalogOpen }: CatalogProps) => {
)}
{item.emoji ? (
<Box sx={{ fontSize: 14, flexShrink: 0 }}>{item.emoji}</Box>
) : item.type === 1 ? (
<IconWenjianjia
sx={{ color: '#2f80f7', flexShrink: 0, fontSize: 14 }}
/>
) : (
<Icon
type={item.type === 1 ? 'icon-wenjianjia' : 'icon-wenjian'}
sx={{ color: '#2f80f7', flexShrink: 0 }}
<IconWenjian
sx={{ color: '#2f80f7', flexShrink: 0, fontSize: 14 }}
/>
)}
<Ellipsis>{item.name}</Ellipsis>
@ -336,8 +344,7 @@ const Catalog = ({ curNode, setCatalogOpen }: CatalogProps) => {
},
}}
>
<Icon
type='icon-mulushouqi'
<IconMulushouqi
sx={{
fontSize: 24,
}}

View File

@ -1,11 +1,12 @@
import Cascader from '@/components/Cascader';
import { VersionCanUse } from '@/components/VersionMask';
import { BUSINESS_VERSION_PERMISSION } from '@/constant/version';
import VersionPublish from '@/pages/release/components/VersionPublish';
import { postApiV1Node } from '@/request';
import { V1NodeDetailResp } from '@/request/types';
import { useAppSelector } from '@/store';
import { addOpacityToColor, getShortcutKeyText } from '@/utils';
import { Ellipsis, Icon, message } from '@ctzhian/ui';
import InfoIcon from '@mui/icons-material/Info';
import { Ellipsis, message } from '@ctzhian/ui';
import {
Box,
Button,
@ -16,14 +17,18 @@ import {
Tooltip,
useTheme,
} from '@mui/material';
import {
IconBaocun,
IconDaochu,
IconGengduo,
IconMuluzhankai,
} from '@panda-wiki/icons';
import dayjs from 'dayjs';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useNavigate, useOutletContext } from 'react-router-dom';
import { WrapContext } from '..';
import DocAddByCustomText from '../../component/DocAddByCustomText';
import DocDelete from '../../component/DocDelete';
import { BUSINESS_VERSION_PERMISSION } from '@/constant/version';
import { VersionCanUse } from '@/components/VersionMask';
interface HeaderProps {
edit: boolean;
@ -43,8 +48,14 @@ const Header = ({
const theme = useTheme();
const navigate = useNavigate();
const firstLoad = useRef(true);
const [wikiUrl, setWikiUrl] = useState<string>('');
const { kb_id, license, kbList } = useAppSelector(state => state.config);
const currentKb = useMemo(() => {
return kbList?.find(item => item.id === kb_id);
}, [kbList, kb_id]);
const { kb_id, license } = useAppSelector(state => state.config);
const { catalogOpen, nodeDetail, setCatalogOpen } =
useOutletContext<WrapContext>();
@ -58,6 +69,25 @@ const Header = ({
return BUSINESS_VERSION_PERMISSION.includes(license.edition!);
}, [license]);
useEffect(() => {
if (currentKb?.access_settings?.base_url) {
setWikiUrl(currentKb.access_settings.base_url);
return;
}
const host = currentKb?.access_settings?.hosts?.[0] || '';
if (host === '') return;
const { ssl_ports = [], ports = [] } = currentKb?.access_settings || {};
if (ssl_ports) {
if (ssl_ports.includes(443)) setWikiUrl(`https://${host}`);
else if (ssl_ports.length > 0)
setWikiUrl(`https://${host}:${ssl_ports[0]}`);
} else if (ports) {
if (ports.includes(80)) setWikiUrl(`http://${host}`);
else if (ports.length > 0) setWikiUrl(`http://${host}:${ports[0]}`);
}
}, [currentKb]);
const handlePublish = useCallback(() => {
if (nodeDetail?.status === 2 && !edit) {
message.info('当前已是最新版本!');
@ -101,15 +131,13 @@ const Header = ({
},
}}
>
<Icon
type='icon-muluzhankai'
<IconMuluzhankai
sx={{
fontSize: 24,
}}
/>
</Stack>
)}
{/* {detail.meta?.content_type === 'md' && <Icon type='icon-a-icon_huaban1fuben22' sx={{ fontSize: 30 }} />} */}
{detail.meta?.content_type === 'md' && (
<Box
component={'span'}
@ -149,7 +177,7 @@ const Header = ({
gap={0.5}
sx={{ fontSize: 12, color: 'text.tertiary' }}
>
<Icon type='icon-baocun' />
<IconBaocun sx={{ fontSize: 12 }} />
{showSaveTip ? (
'已保存'
) : nodeDetail?.updated_at ? (
@ -159,156 +187,9 @@ const Header = ({
)}
</Stack>
</Stack>
{/* <Box sx={{ mr: 1 }}>
{isSyncing ? (
collaborativeUsers.length > 0 && (
<Stack
direction={'row'}
alignItems={'center'}
gap={1}
sx={{ color: 'text.disabled', fontSize: 14 }}
>
<Box
sx={{
width: 6,
height: 6,
borderRadius: '50%',
bgcolor: 'success.main',
}}
/>
{collaborativeUsers.length} 线
</Stack>
)
) : (
<Stack
direction={'row'}
alignItems={'center'}
gap={1}
sx={{ color: 'text.disabled', fontSize: 14 }}
>
<Box
sx={{
width: 6,
height: 6,
borderRadius: '50%',
bgcolor: 'divider',
}}
/>
线
</Stack>
)}
</Box> */}
<Stack direction={'row'} gap={1}>
<Cascader
list={[
// {
// key: 'page_width',
// label: (
// <StyledMenuSelect sx={{ width: 120 }}>
// <Stack
// direction={'row'}
// alignItems={'center'}
// justifyContent={'space-between'}
// sx={{ width: '100%' }}
// >
// 页面宽度
// <Icon
// type='icon-xiala-copy'
// sx={{ color: 'text.disabled', fontSize: 18, mr: -1 }}
// />
// </Stack>
// </StyledMenuSelect>
// ),
// children: [
// {
// key: 'full',
// label: (
// <StyledMenuSelect>
// <Stack
// direction={'row'}
// alignItems={'center'}
// justifyContent={'space-between'}
// sx={{ width: '100%' }}
// >
// 全屏
// {docWidth === 'full' && (
// <Icon
// type='icon-duihao1'
// sx={{
// color: 'primary.main',
// fontSize: 14,
// mr: -1,
// mt: -0.5,
// }}
// />
// )}
// </Stack>
// </StyledMenuSelect>
// ),
// onClick: () => {
// updateDocWidth('full');
// },
// },
// {
// key: 'wide',
// label: (
// <StyledMenuSelect>
// <Stack
// direction={'row'}
// alignItems={'center'}
// justifyContent={'space-between'}
// sx={{ width: '100%' }}
// >
// 超宽
// {docWidth === 'wide' && (
// <Icon
// type='icon-duihao1'
// sx={{
// color: 'primary.main',
// fontSize: 14,
// mr: -1,
// mt: -0.5,
// }}
// />
// )}
// </Stack>
// </StyledMenuSelect>
// ),
// onClick: () => {
// updateDocWidth('wide');
// },
// },
// {
// key: 'normal',
// label: (
// <StyledMenuSelect>
// <Stack
// direction={'row'}
// alignItems={'center'}
// justifyContent={'space-between'}
// sx={{ width: '100%' }}
// >
// 常规
// {docWidth === 'normal' && (
// <Icon
// type='icon-duihao1'
// sx={{
// color: 'primary.main',
// fontSize: 14,
// mr: -1,
// mt: -0.5,
// }}
// />
// )}
// </Stack>
// </StyledMenuSelect>
// ),
// onClick: () => {
// updateDocWidth('normal');
// },
// },
// ],
// },
{
key: 'copy',
textSx: { flex: 1 },
@ -329,6 +210,18 @@ const Header = ({
}
},
},
{
key: 'front_doc',
textSx: { flex: 1 },
label: <StyledMenuSelect></StyledMenuSelect>,
onClick: () => {
if (detail.status !== 2 && !detail.publisher_id) {
message.warning('当前文档未发布,无法查看前台文档');
return;
}
window.open(`${wikiUrl}/node/${detail.id}`, '_blank');
},
},
{
key: 'version',
textSx: { flex: 1 },
@ -367,7 +260,7 @@ const Header = ({
disabled={!detail.name}
sx={{ flexShrink: 0 }}
>
<Icon type='icon-gengduo' />
<IconGengduo sx={{ fontSize: 14 }} />
</IconButton>
}
/>
@ -435,7 +328,7 @@ const Header = ({
size='small'
variant='outlined'
disabled={!detail.name}
startIcon={<Icon type='icon-daochu' />}
startIcon={<IconDaochu sx={{ fontSize: 14 }} />}
>
</Button>
@ -511,7 +404,7 @@ const Header = ({
size='small'
variant='contained'
disabled={!detail.name}
startIcon={<Icon type='icon-baocun' />}
startIcon={<IconBaocun sx={{ fontSize: 14 }} />}
>
</Button>

View File

@ -1,22 +1,13 @@
import { useTiptap } from '@ctzhian/tiptap';
import { Icon } from '@ctzhian/ui';
import { Box, Skeleton, Stack } from '@mui/material';
import { useState } from 'react';
import { useOutletContext } from 'react-router-dom';
import { WrapContext } from '..';
import Header from './Header';
import Toolbar from './Toolbar';
import { IconAShijian2, IconZiti } from '@panda-wiki/icons';
const LoadingEditorWrap = () => {
const { catalogOpen } = useOutletContext<WrapContext>();
const [isSyncing] = useState(false);
const [collaborativeUsers] = useState<
Array<{
id: string;
name: string;
color: string;
}>
>([]);
const editorRef = useTiptap({
editable: false,
@ -62,11 +53,11 @@ const LoadingEditorWrap = () => {
</Stack>
<Stack direction={'row'} alignItems={'center'} gap={2} sx={{ mb: 4 }}>
<Stack direction={'row'} alignItems={'center'} gap={0.5}>
<Icon type='icon-a-shijian2' sx={{ color: 'text.tertiary' }} />
<IconAShijian2 sx={{ color: 'text.tertiary', fontSize: 12 }} />
<Skeleton variant='text' width={130} height={24} />
</Stack>
<Stack direction={'row'} alignItems={'center'} gap={0.5}>
<Icon type='icon-ziti' sx={{ color: 'text.tertiary' }} />
<IconZiti sx={{ color: 'text.tertiary', fontSize: 12 }} />
<Skeleton variant='text' width={80} height={24} />
</Stack>
</Stack>

View File

@ -4,11 +4,12 @@ import {
V1NodeDetailResp,
} from '@/request';
import { useAppSelector } from '@/store';
import { Icon, message, Modal } from '@ctzhian/ui';
import { message, Modal } from '@ctzhian/ui';
import { Button, CircularProgress, Stack, TextField } from '@mui/material';
import { useEffect, useState } from 'react';
import { useOutletContext } from 'react-router-dom';
import { WrapContext } from '..';
import { IconDJzhinengzhaiyao } from '@panda-wiki/icons';
interface SummaryProps {
open: boolean;
@ -95,7 +96,7 @@ const Summary = ({ open, onClose, updateDetail }: SummaryProps) => {
loading ? (
<CircularProgress size={16} />
) : (
<Icon type='icon-DJzhinengzhaiyao' sx={{ fontSize: 16 }} />
<IconDJzhinengzhaiyao sx={{ fontSize: 16 }} />
)
}
>

View File

@ -7,9 +7,10 @@ import {
H6Icon,
TocList,
} from '@ctzhian/tiptap';
import { Ellipsis, Icon } from '@ctzhian/ui';
import { Ellipsis } from '@ctzhian/ui';
import { Box, Drawer, IconButton, Stack } from '@mui/material';
import { useState } from 'react';
import { IconDingzi, IconIcon_tool_close } from '@panda-wiki/icons';
interface TocProps {
headings: TocList;
@ -139,10 +140,11 @@ const Toc = ({
setFixed(!fixed);
}}
>
<Icon
type={!fixed ? 'icon-dingzi' : 'icon-icon_tool_close'}
sx={{ fontSize: 18 }}
/>
{!fixed ? (
<IconDingzi sx={{ fontSize: 18 }} />
) : (
<IconIcon_tool_close sx={{ fontSize: 18 }} />
)}
</IconButton>
</Stack>
<Stack

View File

@ -11,7 +11,7 @@ import {
useTiptap,
UseTiptapReturn,
} from '@ctzhian/tiptap';
import { Icon, message } from '@ctzhian/ui';
import { message } from '@ctzhian/ui';
import { Box, Stack, TextField, Tooltip } from '@mui/material';
import dayjs from 'dayjs';
import { debounce } from 'lodash-es';
@ -30,6 +30,12 @@ import Summary from './Summary';
import Toc from './Toc';
import Toolbar from './Toolbar';
import { BUSINESS_VERSION_PERMISSION } from '@/constant/version';
import {
IconTianjiawendang,
IconAShijian2,
IconZiti,
IconDJzhinengzhaiyao,
} from '@panda-wiki/icons';
interface WrapProps {
detail: V1NodeDetailResp;
@ -379,7 +385,7 @@ const Wrap = ({ detail: defaultDetail }: WrapProps) => {
color: 'text.tertiary',
}}
>
<Icon type='icon-tianjiawendang' sx={{ fontSize: 9 }} />
<IconTianjiawendang sx={{ fontSize: 9 }} />
{nodeDetail?.editor_account}
</Stack>
</Tooltip>
@ -403,7 +409,7 @@ const Wrap = ({ detail: defaultDetail }: WrapProps) => {
}
}}
>
<Icon type='icon-a-shijian2' />
<IconAShijian2 sx={{ fontSize: 12 }} />
{dayjs(defaultDetail.created_at).format(
'YYYY-MM-DD HH:mm:ss',
)}{' '}
@ -416,7 +422,7 @@ const Wrap = ({ detail: defaultDetail }: WrapProps) => {
gap={0.5}
sx={{ fontSize: 12, color: 'text.tertiary' }}
>
<Icon type='icon-ziti' />
<IconZiti sx={{ fontSize: 12 }} />
{characterCount}
</Stack>
</Stack>
@ -463,7 +469,7 @@ const Wrap = ({ detail: defaultDetail }: WrapProps) => {
},
}}
>
<Icon type='icon-DJzhinengzhaiyao' sx={{ fontSize: 12 }} />
<IconDJzhinengzhaiyao sx={{ fontSize: 12 }} />
</Stack>
{nodeDetail?.meta?.summary ? (

View File

@ -8,7 +8,7 @@ import {
} from '@/request/pro';
import { useAppSelector } from '@/store';
import { Editor, useTiptap } from '@ctzhian/tiptap';
import { Ellipsis, Icon } from '@ctzhian/ui';
import { Ellipsis } from '@ctzhian/ui';
import {
alpha,
Box,
@ -22,6 +22,14 @@ import { useEffect, useState } from 'react';
import { useNavigate, useOutletContext, useParams } from 'react-router-dom';
import { WrapContext } from '..';
import VersionRollback from '../../component/VersionRollback';
import {
IconMuluzhankai,
IconChahao,
IconTianjiawendang,
IconZiti,
IconFabu,
IconAShijian2,
} from '@panda-wiki/icons';
const History = () => {
const { id = '' } = useParams();
@ -128,8 +136,7 @@ const History = () => {
},
}}
>
<Icon
type='icon-muluzhankai'
<IconMuluzhankai
sx={{
fontSize: 24,
}}
@ -144,7 +151,7 @@ const History = () => {
navigate(`/doc/editor/${id}`);
}}
>
<Icon type='icon-chahao' />
<IconChahao sx={{ fontSize: 16 }} />
</IconButton>
</Stack>
<Box sx={{ mt: '56px', mr: '292px' }}>
@ -211,17 +218,17 @@ const History = () => {
gap={0.5}
sx={{ cursor: 'pointer' }}
>
<Icon type='icon-tianjiawendang' sx={{ fontSize: 9 }} />
<IconTianjiawendang sx={{ fontSize: 9 }} />
{curNode.editor_account}
</Stack>
</Tooltip>
)}
<Stack direction={'row'} alignItems={'center'} gap={0.5}>
<Icon type='icon-a-shijian2' />
<IconAShijian2 sx={{ fontSize: 12 }} />
{curVersion?.release_message}
</Stack>
<Stack direction={'row'} alignItems={'center'} gap={0.5}>
<Icon type='icon-ziti' />
<IconZiti sx={{ fontSize: 12 }} />
{characterCount}
</Stack>
</Stack>
@ -338,7 +345,7 @@ const History = () => {
lineHeight: 1,
}}
>
<Icon type='icon-fabu' />
<IconFabu sx={{ fontSize: 16 }} />
{item.publisher_account}
</Stack>
) : (

View File

@ -17,17 +17,18 @@ import { useAppDispatch, useAppSelector } from '@/store';
import { setIsRefreshDocList } from '@/store/slices/config';
import { addOpacityToColor } from '@/utils';
import { collapseAllFolders, convertToTree } from '@/utils/drag';
import { Icon } from '@ctzhian/ui';
import { message } from '@ctzhian/ui';
import {
Box,
Button,
ButtonBase,
Checkbox,
IconButton,
Stack,
useTheme,
ButtonBase,
} from '@mui/material';
import { useCallback, useEffect, useRef, useState } from 'react';
import { IconGengduo } from '@panda-wiki/icons';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import VersionPublish from '../release/components/VersionPublish';
import AddDocBtn from './component/AddDocBtn';
import AddDocByType from './component/AddDocByType';
@ -41,7 +42,9 @@ import RagErrorReStart from './component/RagErrorReStart';
import Summary from './component/Summary';
const Content = () => {
const { kb_id, isRefreshDocList } = useAppSelector(state => state.config);
const { kb_id, isRefreshDocList, kbList } = useAppSelector(
state => state.config,
);
const dispatch = useAppDispatch();
const theme = useTheme();
const dragTreeRef = useRef<DragTreeHandle>(null);
@ -49,6 +52,7 @@ const Content = () => {
const [searchParams] = useURLSearchParams();
const search = searchParams.get('search') || '';
const [supportSelect, setBatchOpen] = useState(false);
const [wikiUrl, setWikiUrl] = useState<string>('');
const [ragReStartCount, setRagStartCount] = useState(0);
const [ragIds, setRagIds] = useState<string[]>([]);
@ -139,6 +143,15 @@ const Content = () => {
setIsBatch(false);
};
const handleFrontDoc = (id: string) => {
const currentNode = list.find(item => item.id === id);
if (currentNode?.status !== 2 && !currentNode?.publisher_id) {
message.warning('当前文档未发布,无法查看前台文档');
return;
}
window.open(`${wikiUrl}/node/${id}`, '_blank');
};
const menu = (opra: TreeMenuOptions): TreeMenuItem[] => {
const { item, createItem, renameItem, isEditing: isEditing } = opra;
return [
@ -292,6 +305,11 @@ const Content = () => {
key: 'properties',
onClick: () => handleProperties(item),
},
{
label: '前台查看',
key: 'front_doc',
onClick: () => handleFrontDoc(item.id),
},
]
: []),
...(!isEditing
@ -367,6 +385,10 @@ const Content = () => {
setData([...newData]);
}, []);
const currentKb = useMemo(() => {
return kbList?.find(item => item.id === kb_id);
}, [kbList, kb_id]);
useEffect(() => {
const handleVisibilityChange = () => {
if (document.visibilityState === 'visible' && kb_id) {
@ -379,6 +401,25 @@ const Content = () => {
};
}, [getData, kb_id]);
useEffect(() => {
if (currentKb?.access_settings?.base_url) {
setWikiUrl(currentKb.access_settings.base_url);
return;
}
const host = currentKb?.access_settings?.hosts?.[0] || '';
if (host === '') return;
const { ssl_ports = [], ports = [] } = currentKb?.access_settings || {};
if (ssl_ports) {
if (ssl_ports.includes(443)) setWikiUrl(`https://${host}`);
else if (ssl_ports.length > 0)
setWikiUrl(`https://${host}:${ssl_ports[0]}`);
} else if (ports) {
if (ports.includes(80)) setWikiUrl(`http://${host}`);
else if (ports.length > 0) setWikiUrl(`http://${host}:${ports[0]}`);
}
}, [currentKb]);
useEffect(() => {
if (kb_id) getData();
// eslint-disable-next-line react-hooks/exhaustive-deps
@ -529,7 +570,7 @@ const Content = () => {
context={
<Box>
<IconButton size='small'>
<Icon type='icon-gengduo' />
<IconGengduo sx={{ fontSize: '16px' }} />
</IconButton>
</Box>
}

View File

@ -27,7 +27,8 @@ import {
alpha,
ButtonBase,
} from '@mui/material';
import { Ellipsis, Table, Modal, Icon, message } from '@ctzhian/ui';
import { Ellipsis, Table, Modal, message } from '@ctzhian/ui';
import { IconGengduo } from '@panda-wiki/icons';
import { PROFESSION_VERSION_PERMISSION } from '@/constant/version';
import dayjs from 'dayjs';
import { useEffect, useState, useMemo } from 'react';
@ -129,7 +130,7 @@ const ActionMenu = ({
return (
<>
<IconButton size='small' onClick={handleClick}>
<Icon type='icon-gengduo' />
<IconGengduo sx={{ fontSize: 16 }} />
</IconButton>
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
{record.status! !== 1 && (

View File

@ -7,8 +7,13 @@ import { tableSx } from '@/constant/styles';
import { useURLSearchParams } from '@/hooks';
import { useAppSelector } from '@/store';
import { Box, Stack, Tooltip } from '@mui/material';
import { Ellipsis, Icon, Table } from '@ctzhian/ui';
import { Ellipsis, Table } from '@ctzhian/ui';
import { ColumnsType } from '@ctzhian/ui/dist/Table';
import {
IconDianzanXuanzhong1,
IconADiancaiWeixuanzhong2,
IconDianzanWeixuanzhong,
} from '@panda-wiki/icons';
import dayjs from 'dayjs';
import { useEffect, useState } from 'react';
import Detail from './Detail';
@ -33,15 +38,12 @@ const Evaluate = () => {
dataIndex: 'question',
title: '问题',
render: (text: string, record) => {
const AppIcon =
AppType[record.app_type as keyof typeof AppType]?.icon || '';
return (
<>
<Stack direction={'row'} alignItems={'center'} gap={1}>
<Icon
sx={{ fontSize: 12 }}
type={
AppType[record.app_type as keyof typeof AppType]?.icon || ''
}
/>
<AppIcon sx={{ fontSize: 12 }}></AppIcon>
<Ellipsis
className='primary-color'
sx={{ cursor: 'pointer', flex: 1, width: 0 }}
@ -94,19 +96,24 @@ const Evaluate = () => {
sx={{ cursor: 'pointer', fontSize: 14 }}
>
{value!.score === 1 ? (
<Icon
type='icon-dianzan-xuanzhong1'
sx={{ cursor: 'pointer', color: 'success.main' }}
<IconDianzanXuanzhong1
sx={{
fontSize: 14,
cursor: 'pointer',
color: 'success.main',
}}
/>
) : value!.score === -1 ? (
<Icon
type='icon-a-diancai-weixuanzhong2'
sx={{ cursor: 'pointer', color: 'error.main' }}
<IconADiancaiWeixuanzhong2
sx={{
fontSize: 14,
cursor: 'pointer',
color: 'error.main',
}}
/>
) : (
<Icon
type='icon-dianzan-weixuanzhong'
sx={{ color: 'text.disabled' }}
<IconDianzanWeixuanzhong
sx={{ fontSize: 14, color: 'text.disabled' }}
/>
)}
</Stack>

View File

@ -1,82 +0,0 @@
import { useURLSearchParams } from '@/hooks';
import { IconButton, InputAdornment, Stack, TextField } from '@mui/material';
import { Icon } from '@ctzhian/ui';
import { useState } from 'react';
const Search = () => {
const [searchParams, setSearchParams] = useURLSearchParams();
const oldSubject = searchParams.get('subject') || '';
const oldRemoteIp = searchParams.get('remote_ip') || '';
const [subject, setSubject] = useState(oldSubject);
const [remoteIp, setRemoteIp] = useState(oldRemoteIp);
return (
<Stack direction={'row'} alignItems={'center'} gap={2}>
<TextField
label='问题'
size='small'
sx={{ width: 200 }}
value={subject}
onKeyUp={event => {
if (event.key === 'Enter') {
setSearchParams({ subject: subject || '' });
}
}}
onBlur={event => setSearchParams({ subject: event.target.value })}
onChange={event => setSubject(event.target.value)}
InputProps={{
endAdornment: subject ? (
<InputAdornment position='end'>
<IconButton
onClick={() => {
setSubject('');
setSearchParams({ subject: '' });
}}
size='small'
>
<Icon
type='icon-icon_tool_close'
sx={{ fontSize: 14, color: 'text.tertiary' }}
/>
</IconButton>
</InputAdornment>
) : null,
}}
/>
<TextField
label='客户端'
size='small'
sx={{ width: 200 }}
value={remoteIp}
onKeyUp={event => {
if (event.key === 'Enter') {
setSearchParams({ remote_ip: remoteIp || '' });
}
}}
onBlur={event => setSearchParams({ remote_ip: event.target.value })}
onChange={event => setRemoteIp(event.target.value)}
InputProps={{
endAdornment: remoteIp ? (
<InputAdornment position='end'>
<IconButton
onClick={() => {
setRemoteIp('');
setSearchParams({ remote_ip: '' });
}}
size='small'
>
<Icon
type='icon-icon_tool_close'
sx={{ fontSize: 14, color: 'text.tertiary' }}
/>
</IconButton>
</InputAdornment>
) : null,
}}
/>
</Stack>
);
};
export default Search;

View File

@ -8,6 +8,13 @@ import { Box, Button, IconButton, Stack, TextField } from '@mui/material';
import { Icon, message } from '@ctzhian/ui';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
IconZhanghao,
IconIcon_tool_close,
IconMima,
IconKejian,
IconBukejian,
} from '@panda-wiki/icons';
const Login = () => {
const navigate = useNavigate();
@ -90,10 +97,7 @@ const Login = () => {
slotProps={{
input: {
startAdornment: (
<Icon
type='icon-zhanghao'
sx={{ fontSize: 16, mr: 2, flexShrink: 0 }}
/>
<IconZhanghao sx={{ fontSize: 16, mr: 2, flexShrink: 0 }} />
),
endAdornment: account ? (
<IconButton
@ -101,8 +105,7 @@ const Login = () => {
size='small'
tabIndex={-1}
>
<Icon
type='icon-icon_tool_close'
<IconIcon_tool_close
sx={{ fontSize: 14, color: 'text.tertiary' }}
/>
</IconButton>
@ -128,34 +131,31 @@ const Login = () => {
slotProps={{
input: {
startAdornment: (
<Icon
type='icon-mima'
sx={{ fontSize: 16, mr: 2, flexShrink: 0 }}
/>
<IconMima sx={{ fontSize: 16, mr: 2, flexShrink: 0 }} />
),
endAdornment: password ? (
<Stack
direction={'row'}
alignItems={'center'}
sx={{ mr: '14px' }}
>
<Stack direction={'row'} alignItems={'center'}>
<IconButton
onClick={() => setSee(!see)}
size='small'
tabIndex={-1}
>
<Icon
type={see ? 'icon-kejian' : 'icon-bukejian'}
{see ? (
<IconKejian
sx={{ fontSize: 18, color: 'text.tertiary' }}
/>
) : (
<IconBukejian
sx={{ fontSize: 18, color: 'text.tertiary' }}
/>
)}
</IconButton>
<IconButton
onClick={() => setPassword('')}
size='small'
tabIndex={-1}
>
<Icon
type='icon-icon_tool_close'
<IconIcon_tool_close
sx={{ fontSize: 14, color: 'text.tertiary' }}
/>
</IconButton>

View File

@ -262,31 +262,6 @@ const CardAuth = ({ kb, refresh }: CardAuthProps) => {
);
},
},
// {
// title: '',
// dataIndex: 'action',
// width: 60,
// render: (text: string, record) => {
// return (
// <IconButton
// size='small'
// sx={{ p: '2px' }}
// onClick={() => {
// onDeleteUser(record.id!);
// }}
// >
// <Icon
// type='icon-icon_tool_close'
// sx={{
// cursor: 'pointer',
// color: 'error.main',
// }}
// />
// </IconButton>
// );
// },
// },
];
const githubForm = () => {

View File

@ -1,245 +0,0 @@
import { putApiV1App } from '@/request/App';
import { FooterSetting } from '@/api/type';
import DragBrand from '@/components/Drag/DragBrand';
import UploadFile from '@/components/UploadFile';
import { DomainAppDetailResp, DomainBrandGroup } from '@/request/types';
import {
Box,
Button,
FormControlLabel,
Radio,
RadioGroup,
Stack,
TextField,
} from '@mui/material';
import { message } from '@ctzhian/ui';
import { useEffect, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { FormItem, SettingCardItem } from './Common';
import { useAppSelector } from '@/store';
interface CardFooterProps {
id: string;
data: DomainAppDetailResp;
refresh: (value: FooterSetting) => void;
}
const CardFooter = ({ id, data, refresh }: CardFooterProps) => {
const [isEdit, setIsEdit] = useState(false);
const { kb_id } = useAppSelector(state => state.config);
const {
control,
handleSubmit,
watch,
setValue,
formState: { errors },
} = useForm({
defaultValues: {
footer_style: 'simple',
corp_name: '',
icp: '',
brand_name: '',
brand_desc: '',
brand_logo: '',
brand_groups: [] as DomainBrandGroup[],
},
});
const footerStyle = watch('footer_style');
const onSubmit = handleSubmit(value => {
putApiV1App(
{ id },
{
kb_id,
settings: {
...data.settings,
footer_settings: {
...data.settings?.footer_settings,
...value,
},
},
},
).then(() => {
refresh(value as FooterSetting);
message.success('保存成功');
setIsEdit(false);
});
});
useEffect(() => {
setValue(
'footer_style',
data.settings?.footer_settings?.footer_style as 'simple' | 'complex',
);
setValue('corp_name', data.settings?.footer_settings?.corp_name || '');
setValue('icp', data.settings?.footer_settings?.icp || '');
setValue('brand_name', data.settings?.footer_settings?.brand_name || '');
setValue('brand_desc', data.settings?.footer_settings?.brand_desc || '');
setValue('brand_logo', data.settings?.footer_settings?.brand_logo || '');
setValue(
'brand_groups',
data.settings?.footer_settings?.brand_groups || [],
);
}, [data]);
return (
<SettingCardItem title='页脚' isEdit={isEdit} onSubmit={onSubmit}>
<FormItem label='页面模式'>
<Controller
control={control}
name='footer_style'
render={({ field }) => (
<RadioGroup
row
{...field}
onChange={e => {
field.onChange(e.target.value);
setIsEdit(true);
}}
>
<FormControlLabel
value={'simple'}
control={<Radio size='small' />}
label={<Box sx={{ width: 100 }}></Box>}
/>
<FormControlLabel
value={'complex'}
control={<Radio size='small' />}
label={<Box sx={{ width: 100 }}></Box>}
/>
</RadioGroup>
)}
/>
</FormItem>
<FormItem label='企业名称 / 组织名称'>
{' '}
<Controller
control={control}
name='corp_name'
render={({ field }) => (
<TextField
{...field}
fullWidth
placeholder='企业名称/组织名称'
onChange={e => {
field.onChange(e.target.value);
setIsEdit(true);
}}
error={!!errors.corp_name}
helperText={errors.corp_name?.message}
/>
)}
/>
</FormItem>
<FormItem label='ICP 备案编号'>
<Controller
control={control}
name='icp'
render={({ field }) => (
<TextField
{...field}
fullWidth
placeholder='ICP 备案编号'
onChange={e => {
field.onChange(e.target.value);
setIsEdit(true);
}}
error={!!errors.icp}
helperText={errors.icp?.message}
/>
)}
/>
</FormItem>
{footerStyle === 'complex' && (
<>
<FormItem label='品牌名称'>
<Controller
control={control}
name='brand_name'
render={({ field }) => (
<TextField
{...field}
fullWidth
placeholder='品牌名称'
onChange={e => {
field.onChange(e.target.value);
setIsEdit(true);
}}
error={!!errors.brand_name}
helperText={errors.brand_name?.message}
/>
)}
/>
</FormItem>
<FormItem label='品牌 Logo'>
<Controller
control={control}
name='brand_logo'
render={({ field }) => (
<UploadFile
{...field}
id='brand_logo'
type='url'
accept='image/*'
width={80}
onChange={url => {
field.onChange(url);
setIsEdit(true);
}}
/>
)}
/>
</FormItem>
<FormItem label='品牌介绍'>
<Controller
control={control}
name='brand_desc'
render={({ field }) => (
<TextField
{...field}
fullWidth
placeholder='品牌介绍'
onChange={e => {
field.onChange(e.target.value);
setIsEdit(true);
}}
error={!!errors.brand_desc}
helperText={errors.brand_desc?.message}
/>
)}
/>
</FormItem>
<Box>
<Box
sx={{
width: 156,
fontSize: 14,
lineHeight: '32px',
flexShrink: 0,
my: 1,
}}
>
</Box>
{/* 使用 DragBrand 组件替换原有的品牌链接组 */}
<DragBrand
// @ts-expect-error 类型不匹配
control={control}
errors={errors}
setIsEdit={setIsEdit}
/>
</Box>
</>
)}
</SettingCardItem>
);
};
export default CardFooter;

View File

@ -22,7 +22,9 @@ import {
import { useAppSelector } from '@/store';
import { setRefreshAdminRequest } from '@/store/slices/config';
import { copyText } from '@/utils';
import { Ellipsis, Icon, message, Modal } from '@ctzhian/ui';
import { Ellipsis, message, Modal } from '@ctzhian/ui';
import { IconIcon_tool_close, IconTianjiachengyuan } from '@panda-wiki/icons';
import { IconFuzhi } from '@panda-wiki/icons';
import InfoIcon from '@mui/icons-material/Info';
import {
Box,
@ -210,8 +212,7 @@ const ApiToken = () => {
}}
>
{maskString(it.token!)}
<Icon
type='icon-fuzhi'
<IconFuzhi
sx={{
cursor: 'pointer',
fontSize: 16,
@ -275,9 +276,9 @@ const ApiToken = () => {
</Stack>
<Tooltip title={user.role !== 'admin' && ''} placement='top' arrow>
<Icon
type='icon-icon_tool_close'
<IconIcon_tool_close
sx={{
fontSize: 16,
cursor:
!isBusiness ||
kbDetail?.perm !==
@ -457,7 +458,7 @@ const CardKB = () => {
extra={
<Button
size='small'
startIcon={<Icon type='icon-tianjiachengyuan' />}
startIcon={<IconTianjiachengyuan />}
onClick={() => setAddOpen(true)}
sx={{ color: 'primary.main' }}
>
@ -557,9 +558,9 @@ const CardKB = () => {
placement='top'
arrow
>
<Icon
type='icon-icon_tool_close'
<IconIcon_tool_close
sx={{
fontSize: 16,
cursor: it.role === 'admin' ? 'not-allowed' : 'pointer',
color: it.role === 'admin' ? 'text.disabled' : 'error.main',
}}

View File

@ -10,7 +10,8 @@ import {
DomainKnowledgeBaseDetail,
} from '@/request/types';
import { useAppSelector } from '@/store';
import { Icon, message } from '@ctzhian/ui';
import { message } from '@ctzhian/ui';
import { IconJinggao } from '@panda-wiki/icons';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import {
Box,
@ -235,7 +236,7 @@ const CardRobotWebComponent = ({ kb }: CardRobotWebComponentProps) => {
bgcolor: 'warning.light',
}}
>
<Icon type='icon-jinggao' />
<IconJinggao sx={{ fontSize: 16 }} />
<Box component={'span'} sx={{ fontWeight: 500 }}>
/

View File

@ -7,7 +7,8 @@ import {
DomainKnowledgeBaseDetail,
} from '@/request/types';
import { useAppSelector } from '@/store';
import { Icon, message } from '@ctzhian/ui';
import { message } from '@ctzhian/ui';
import { IconJinggao } from '@panda-wiki/icons';
import {
Box,
FormControlLabel,
@ -261,7 +262,7 @@ const CardRobotWecomService = ({
gap={1}
sx={{ fontSize: 14, fontWeight: 600, color: 'warning.main' }}
>
<Icon type='icon-jinggao' sx={{ fontSize: 18 }} />
<IconJinggao sx={{ fontSize: 18 }} />
</Stack>
<VersionMask permission={PROFESSION_VERSION_PERMISSION}>

Some files were not shown because too many files have changed in this diff Show More