mirror of https://github.com/chaitin/PandaWiki.git
Compare commits
1 Commits
282dc63242
...
5078e93a4a
| Author | SHA1 | Date |
|---|---|---|
|
|
5078e93a4a |
|
|
@ -1,59 +0,0 @@
|
||||||
import { useSortable } from '@dnd-kit/sortable';
|
|
||||||
import { CSS } from '@dnd-kit/utilities';
|
|
||||||
import { ComponentType } from 'react';
|
|
||||||
|
|
||||||
export interface SortableItemProps<T extends { id?: string | null }> {
|
|
||||||
id: string;
|
|
||||||
item: T;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
ItemComponent: ComponentType<any>;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SortableItem<T extends { id?: string | null }>({
|
|
||||||
id,
|
|
||||||
item,
|
|
||||||
ItemComponent,
|
|
||||||
...rest
|
|
||||||
}: SortableItemProps<T>) {
|
|
||||||
const {
|
|
||||||
isDragging,
|
|
||||||
attributes,
|
|
||||||
listeners,
|
|
||||||
setNodeRef,
|
|
||||||
transform,
|
|
||||||
transition,
|
|
||||||
} = useSortable({ id });
|
|
||||||
|
|
||||||
const style = {
|
|
||||||
transform: CSS.Transform.toString(transform),
|
|
||||||
transition: transition || undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ItemComponent
|
|
||||||
ref={setNodeRef}
|
|
||||||
style={style}
|
|
||||||
withOpacity={isDragging}
|
|
||||||
dragHandleProps={{
|
|
||||||
...attributes,
|
|
||||||
...listeners,
|
|
||||||
}}
|
|
||||||
item={item}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export function createSortableItem(ItemComponent: ComponentType<any>) {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const WrappedComponent = (props: any) => (
|
|
||||||
<SortableItem {...props} ItemComponent={ItemComponent} />
|
|
||||||
);
|
|
||||||
WrappedComponent.displayName = `SortableItem(${ItemComponent.displayName || ItemComponent.name || 'Component'})`;
|
|
||||||
return WrappedComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SortableItem;
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
export { default as DragList } from './DragList';
|
|
||||||
export type { DragListProps } from './DragList';
|
|
||||||
|
|
||||||
export { default as SortableItem, createSortableItem } from './SortableItem';
|
|
||||||
export type { SortableItemProps } from './SortableItem';
|
|
||||||
|
|
@ -13,53 +13,26 @@ import {
|
||||||
arrayMove,
|
arrayMove,
|
||||||
rectSortingStrategy,
|
rectSortingStrategy,
|
||||||
SortableContext,
|
SortableContext,
|
||||||
SortingStrategy,
|
|
||||||
} from '@dnd-kit/sortable';
|
} from '@dnd-kit/sortable';
|
||||||
import { Stack, SxProps, Theme } from '@mui/material';
|
import { Stack } from '@mui/material';
|
||||||
import {
|
import { Dispatch, FC, SetStateAction, useCallback, useState } from 'react';
|
||||||
ComponentType,
|
import Item from './Item';
|
||||||
CSSProperties,
|
import SortableItem from './SortableItem';
|
||||||
Dispatch,
|
|
||||||
SetStateAction,
|
|
||||||
useCallback,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
export interface DragListProps<T extends { id?: string | null }> {
|
type ItemType = {
|
||||||
data: T[];
|
id: string;
|
||||||
onChange: (data: T[]) => void;
|
text: string;
|
||||||
|
type: 'contained' | 'outlined' | 'text';
|
||||||
|
href: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface DragListProps {
|
||||||
|
data: ItemType[];
|
||||||
|
onChange: (data: ItemType[]) => void;
|
||||||
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
||||||
SortableItemComponent: ComponentType<{
|
|
||||||
id: string;
|
|
||||||
item: T;
|
|
||||||
handleRemove: (id: string) => void;
|
|
||||||
handleUpdateItem: (item: T) => void;
|
|
||||||
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
|
||||||
}>;
|
|
||||||
ItemComponent: ComponentType<{
|
|
||||||
isDragging?: boolean;
|
|
||||||
item: T;
|
|
||||||
style?: CSSProperties;
|
|
||||||
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
|
||||||
handleUpdateItem?: (item: T) => void;
|
|
||||||
}>;
|
|
||||||
containerSx?: SxProps<Theme>;
|
|
||||||
sortingStrategy?: SortingStrategy;
|
|
||||||
direction?: 'row' | 'column';
|
|
||||||
gap?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function DragList<T extends { id?: string | null }>({
|
const DragList: FC<DragListProps> = ({ data, onChange, setIsEdit }) => {
|
||||||
data,
|
|
||||||
onChange,
|
|
||||||
setIsEdit,
|
|
||||||
SortableItemComponent,
|
|
||||||
ItemComponent,
|
|
||||||
containerSx,
|
|
||||||
sortingStrategy = rectSortingStrategy,
|
|
||||||
direction = 'row',
|
|
||||||
gap = 2,
|
|
||||||
}: DragListProps<T>) {
|
|
||||||
const [activeId, setActiveId] = useState<string | null>(null);
|
const [activeId, setActiveId] = useState<string | null>(null);
|
||||||
const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor));
|
const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor));
|
||||||
|
|
||||||
|
|
@ -71,8 +44,8 @@ function DragList<T extends { id?: string | null }>({
|
||||||
(event: DragEndEvent) => {
|
(event: DragEndEvent) => {
|
||||||
const { active, over } = event;
|
const { active, over } = event;
|
||||||
if (active.id !== over?.id) {
|
if (active.id !== over?.id) {
|
||||||
const oldIndex = data.findIndex(item => (item.id || '') === active.id);
|
const oldIndex = data.findIndex(item => item.id === active.id);
|
||||||
const newIndex = data.findIndex(item => (item.id || '') === over!.id);
|
const newIndex = data.findIndex(item => item.id === over!.id);
|
||||||
const newData = arrayMove(data, oldIndex, newIndex);
|
const newData = arrayMove(data, oldIndex, newIndex);
|
||||||
onChange(newData);
|
onChange(newData);
|
||||||
}
|
}
|
||||||
|
|
@ -87,16 +60,21 @@ function DragList<T extends { id?: string | null }>({
|
||||||
|
|
||||||
const handleRemove = useCallback(
|
const handleRemove = useCallback(
|
||||||
(id: string) => {
|
(id: string) => {
|
||||||
const newData = data.filter(item => (item.id || '') !== id);
|
const newData = data.filter(item => item.id !== id);
|
||||||
onChange(newData);
|
onChange(newData);
|
||||||
},
|
},
|
||||||
[data, onChange],
|
[data, onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleUpdateItem = useCallback(
|
const handleUpdateItem = useCallback(
|
||||||
(updatedItem: T) => {
|
(updatedItem: {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
type: 'contained' | 'outlined' | 'text';
|
||||||
|
href: string;
|
||||||
|
}) => {
|
||||||
const newData = data.map(item =>
|
const newData = data.map(item =>
|
||||||
(item.id || '') === (updatedItem.id || '') ? updatedItem : item,
|
item.id === updatedItem.id ? updatedItem : item,
|
||||||
);
|
);
|
||||||
onChange(newData);
|
onChange(newData);
|
||||||
},
|
},
|
||||||
|
|
@ -114,19 +92,14 @@ function DragList<T extends { id?: string | null }>({
|
||||||
onDragCancel={handleDragCancel}
|
onDragCancel={handleDragCancel}
|
||||||
>
|
>
|
||||||
<SortableContext
|
<SortableContext
|
||||||
items={data.map(item => item.id || '')}
|
items={data.map(item => item.id)}
|
||||||
strategy={sortingStrategy}
|
strategy={rectSortingStrategy}
|
||||||
>
|
>
|
||||||
<Stack
|
<Stack direction={'row'} flexWrap={'wrap'} gap={2}>
|
||||||
direction={direction}
|
|
||||||
flexWrap={'wrap'}
|
|
||||||
gap={gap}
|
|
||||||
sx={containerSx}
|
|
||||||
>
|
|
||||||
{data.map(item => (
|
{data.map(item => (
|
||||||
<SortableItemComponent
|
<SortableItem
|
||||||
key={item.id || ''}
|
key={item.id}
|
||||||
id={item.id || ''}
|
id={item.id}
|
||||||
item={item}
|
item={item}
|
||||||
handleRemove={handleRemove}
|
handleRemove={handleRemove}
|
||||||
handleUpdateItem={handleUpdateItem}
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
|
@ -137,9 +110,9 @@ function DragList<T extends { id?: string | null }>({
|
||||||
</SortableContext>
|
</SortableContext>
|
||||||
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
|
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
|
||||||
{activeId ? (
|
{activeId ? (
|
||||||
<ItemComponent
|
<Item
|
||||||
isDragging
|
isDragging
|
||||||
item={data.find(item => (item.id || '') === activeId)!}
|
item={data.find(item => item.id === activeId)!}
|
||||||
setIsEdit={setIsEdit}
|
setIsEdit={setIsEdit}
|
||||||
handleUpdateItem={handleUpdateItem}
|
handleUpdateItem={handleUpdateItem}
|
||||||
/>
|
/>
|
||||||
|
|
@ -147,6 +120,6 @@ function DragList<T extends { id?: string | null }>({
|
||||||
</DragOverlay>
|
</DragOverlay>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default DragList;
|
export default DragList;
|
||||||
|
|
@ -1,141 +0,0 @@
|
||||||
import { Box, IconButton, Stack, TextField } from '@mui/material';
|
|
||||||
import { Icon } from '@ctzhian/ui';
|
|
||||||
import {
|
|
||||||
CSSProperties,
|
|
||||||
Dispatch,
|
|
||||||
forwardRef,
|
|
||||||
HTMLAttributes,
|
|
||||||
SetStateAction,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
type HotSearchItem = {
|
|
||||||
id: string;
|
|
||||||
text: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type HotSearchItemProps = Omit<
|
|
||||||
HTMLAttributes<HTMLDivElement>,
|
|
||||||
'onChange'
|
|
||||||
> & {
|
|
||||||
item: HotSearchItem;
|
|
||||||
withOpacity?: boolean;
|
|
||||||
isDragging?: boolean;
|
|
||||||
dragHandleProps?: React.HTMLAttributes<HTMLDivElement>;
|
|
||||||
handleRemove?: (id: string) => void;
|
|
||||||
handleUpdateItem?: (item: HotSearchItem) => void;
|
|
||||||
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const HotSearchItem = forwardRef<HTMLDivElement, HotSearchItemProps>(
|
|
||||||
(
|
|
||||||
{
|
|
||||||
item,
|
|
||||||
withOpacity,
|
|
||||||
isDragging,
|
|
||||||
style,
|
|
||||||
dragHandleProps,
|
|
||||||
handleRemove,
|
|
||||||
handleUpdateItem,
|
|
||||||
setIsEdit,
|
|
||||||
...props
|
|
||||||
},
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
const inlineStyles: CSSProperties = {
|
|
||||||
opacity: withOpacity ? '0.5' : '1',
|
|
||||||
borderRadius: '10px',
|
|
||||||
cursor: isDragging ? 'grabbing' : 'grab',
|
|
||||||
backgroundColor: '#ffffff',
|
|
||||||
width: '100%',
|
|
||||||
...style,
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Box ref={ref} style={inlineStyles} {...props}>
|
|
||||||
<Stack
|
|
||||||
direction={'row'}
|
|
||||||
alignItems={'center'}
|
|
||||||
justifyContent={'space-between'}
|
|
||||||
gap={0.5}
|
|
||||||
sx={{
|
|
||||||
py: 1.5,
|
|
||||||
px: 1,
|
|
||||||
border: '1px solid',
|
|
||||||
borderColor: 'divider',
|
|
||||||
borderRadius: '10px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
direction={'column'}
|
|
||||||
gap={'20px'}
|
|
||||||
sx={{
|
|
||||||
flex: 1,
|
|
||||||
p: 1.5,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextField
|
|
||||||
label='搜索关键词'
|
|
||||||
slotProps={{
|
|
||||||
inputLabel: {
|
|
||||||
shrink: true,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
height: '36px',
|
|
||||||
'& .MuiOutlinedInput-root': {
|
|
||||||
height: '36px',
|
|
||||||
padding: '0 12px',
|
|
||||||
'& .MuiOutlinedInput-input': {
|
|
||||||
padding: '8px 0',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
fullWidth
|
|
||||||
placeholder='请输入搜索关键词'
|
|
||||||
variant='outlined'
|
|
||||||
value={item.text}
|
|
||||||
onChange={e => {
|
|
||||||
const updatedItem = { ...item, text: e.target.value };
|
|
||||||
handleUpdateItem?.(updatedItem);
|
|
||||||
setIsEdit(true);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Stack
|
|
||||||
direction={'column'}
|
|
||||||
sx={{ justifyContent: 'space-between', alignSelf: 'stretch' }}
|
|
||||||
>
|
|
||||||
<IconButton
|
|
||||||
size='small'
|
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleRemove?.(item.id);
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
color: 'text.tertiary',
|
|
||||||
':hover': { color: 'error.main' },
|
|
||||||
width: '28px',
|
|
||||||
height: '28px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon type='icon-shanchu2' sx={{ fontSize: '12px' }} />
|
|
||||||
</IconButton>
|
|
||||||
<IconButton
|
|
||||||
size='small'
|
|
||||||
sx={{
|
|
||||||
cursor: 'grab',
|
|
||||||
color: 'text.secondary',
|
|
||||||
'&:hover': { color: 'primary.main' },
|
|
||||||
}}
|
|
||||||
{...(dragHandleProps as any)}
|
|
||||||
>
|
|
||||||
<Icon type='icon-drag' />
|
|
||||||
</IconButton>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export default HotSearchItem;
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import Item, { ItemProps } from './Item';
|
||||||
|
|
||||||
|
type FaqSortableItemProps = ItemProps & {};
|
||||||
|
|
||||||
|
const FaqSortableItem: FC<FaqSortableItemProps> = ({ 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 FaqSortableItem;
|
||||||
|
|
@ -1,21 +1,18 @@
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { TextField } from '@mui/material';
|
import { TextField, Chip, Autocomplete, Box } from '@mui/material';
|
||||||
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
||||||
import type { ConfigProps } from '../type';
|
import type { ConfigProps } from '../type';
|
||||||
import { useForm, Controller } from 'react-hook-form';
|
import { useForm, Controller } from 'react-hook-form';
|
||||||
import { useAppSelector } from '@/store';
|
import { useAppSelector } from '@/store';
|
||||||
import DragList from '../../components/DragList';
|
import DragList from './DragList';
|
||||||
import SortableItem from '../../components/SortableItem';
|
|
||||||
import Item from './Item';
|
|
||||||
import HotSearchItem from './HotSearchItem';
|
|
||||||
import UploadFile from '@/components/UploadFile';
|
import UploadFile from '@/components/UploadFile';
|
||||||
import { DEFAULT_DATA } from '../../../constants';
|
import { DEFAULT_DATA } from '../../../constants';
|
||||||
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
||||||
import { handleLandingConfigs, findConfigById } from '../../../utils';
|
import { handleLandingConfigs, findConfigById } from '../../../utils';
|
||||||
import { Empty } from '@ctzhian/ui';
|
|
||||||
|
|
||||||
const Config: React.FC<ConfigProps> = ({ setIsEdit, id }) => {
|
const Config: React.FC<ConfigProps> = ({ setIsEdit, id }) => {
|
||||||
const { appPreviewData } = useAppSelector(state => state.config);
|
const { appPreviewData } = useAppSelector(state => state.config);
|
||||||
|
const [inputValue, setInputValue] = useState('');
|
||||||
const { control, watch, setValue, subscribe } = useForm<
|
const { control, watch, setValue, subscribe } = useForm<
|
||||||
typeof DEFAULT_DATA.banner
|
typeof DEFAULT_DATA.banner
|
||||||
>({
|
>({
|
||||||
|
|
@ -27,49 +24,6 @@ const Config: React.FC<ConfigProps> = ({ setIsEdit, id }) => {
|
||||||
|
|
||||||
const debouncedDispatch = useDebounceAppPreviewData();
|
const debouncedDispatch = useDebounceAppPreviewData();
|
||||||
const btns = watch('btns') || [];
|
const btns = watch('btns') || [];
|
||||||
const hotSearch = watch('hot_search') || [];
|
|
||||||
|
|
||||||
// 使用 ref 来维护稳定的 ID 映射
|
|
||||||
const idMapRef = useRef<Map<number, string>>(new Map());
|
|
||||||
|
|
||||||
// 将string[]转换为对象数组用于显示,保持 ID 稳定
|
|
||||||
const hotSearchList = Array.isArray(hotSearch)
|
|
||||||
? hotSearch.map((text, index) => {
|
|
||||||
// 如果该索引没有 ID,生成一个新的
|
|
||||||
if (!idMapRef.current.has(index)) {
|
|
||||||
idMapRef.current.set(
|
|
||||||
index,
|
|
||||||
`${Date.now()}-${index}-${Math.random()}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
id: idMapRef.current.get(index)!,
|
|
||||||
text: String(text),
|
|
||||||
};
|
|
||||||
})
|
|
||||||
: [];
|
|
||||||
|
|
||||||
// 清理不再使用的 ID,并确保所有索引都有 ID
|
|
||||||
useEffect(() => {
|
|
||||||
const currentIndexes = new Set(hotSearch.map((_, index) => index));
|
|
||||||
|
|
||||||
// 清理不存在的索引
|
|
||||||
const keysToDelete: number[] = [];
|
|
||||||
idMapRef.current.forEach((_, key) => {
|
|
||||||
if (!currentIndexes.has(key)) {
|
|
||||||
keysToDelete.push(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
keysToDelete.forEach(key => idMapRef.current.delete(key));
|
|
||||||
|
|
||||||
// 确保每个索引都有 ID
|
|
||||||
hotSearch.forEach((_, index) => {
|
|
||||||
if (!idMapRef.current.has(index)) {
|
|
||||||
idMapRef.current.set(index, `${Date.now()}-${index}-${Math.random()}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [hotSearch.length]);
|
|
||||||
|
|
||||||
const handleAddButton = () => {
|
const handleAddButton = () => {
|
||||||
const nextId = `${Date.now()}`;
|
const nextId = `${Date.now()}`;
|
||||||
|
|
@ -79,31 +33,6 @@ const Config: React.FC<ConfigProps> = ({ setIsEdit, id }) => {
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddHotSearch = () => {
|
|
||||||
const newIndex = hotSearch.length;
|
|
||||||
const nextId = `${Date.now()}-${newIndex}-${Math.random()}`;
|
|
||||||
idMapRef.current.set(newIndex, nextId);
|
|
||||||
// 转换回string[]格式
|
|
||||||
setValue('hot_search', [...hotSearch, '']);
|
|
||||||
setIsEdit(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleHotSearchChange = (newList: { id: string; text: string }[]) => {
|
|
||||||
// 重建 ID 映射关系
|
|
||||||
const newIdMap = new Map<number, string>();
|
|
||||||
newList.forEach((item, index) => {
|
|
||||||
newIdMap.set(index, item.id);
|
|
||||||
});
|
|
||||||
idMapRef.current = newIdMap;
|
|
||||||
|
|
||||||
// 转换回string[]格式
|
|
||||||
setValue(
|
|
||||||
'hot_search',
|
|
||||||
newList.map(item => item.text),
|
|
||||||
);
|
|
||||||
setIsEdit(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const callback = subscribe({
|
const callback = subscribe({
|
||||||
formState: {
|
formState: {
|
||||||
|
|
@ -130,7 +59,6 @@ const Config: React.FC<ConfigProps> = ({ setIsEdit, id }) => {
|
||||||
return () => {
|
return () => {
|
||||||
callback();
|
callback();
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [subscribe]);
|
}, [subscribe]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -204,20 +132,60 @@ const Config: React.FC<ConfigProps> = ({ setIsEdit, id }) => {
|
||||||
render={({ field }) => <TextField {...field} placeholder='请输入' />}
|
render={({ field }) => <TextField {...field} placeholder='请输入' />}
|
||||||
/>
|
/>
|
||||||
</CommonItem>
|
</CommonItem>
|
||||||
<CommonItem title='热门搜索' onAdd={handleAddHotSearch}>
|
<CommonItem title='热门搜索'>
|
||||||
{hotSearchList.length === 0 ? (
|
<Controller
|
||||||
<Empty />
|
control={control}
|
||||||
) : (
|
name='hot_search'
|
||||||
<DragList
|
render={({ field }) => (
|
||||||
data={hotSearchList}
|
<Autocomplete
|
||||||
onChange={handleHotSearchChange}
|
{...field}
|
||||||
setIsEdit={setIsEdit}
|
value={field.value || []}
|
||||||
SortableItemComponent={sortableProps => (
|
multiple
|
||||||
<SortableItem {...sortableProps} ItemComponent={HotSearchItem} />
|
freeSolo
|
||||||
)}
|
fullWidth
|
||||||
ItemComponent={HotSearchItem}
|
options={[]}
|
||||||
/>
|
inputValue={inputValue}
|
||||||
)}
|
onInputChange={(_, newInputValue) => setInputValue(newInputValue)}
|
||||||
|
onChange={(_, newValue) => {
|
||||||
|
setIsEdit(true);
|
||||||
|
const newValues = [...new Set(newValue as string[])];
|
||||||
|
field.onChange(newValues);
|
||||||
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
setIsEdit(true);
|
||||||
|
const trimmedValue = inputValue.trim();
|
||||||
|
if (trimmedValue && !field.value?.includes(trimmedValue)) {
|
||||||
|
field.onChange([...(field.value || []), trimmedValue]);
|
||||||
|
}
|
||||||
|
setInputValue('');
|
||||||
|
}}
|
||||||
|
renderValue={(value, getTagProps) => {
|
||||||
|
return value.map((option, index: number) => {
|
||||||
|
return (
|
||||||
|
<Chip
|
||||||
|
variant='outlined'
|
||||||
|
size='small'
|
||||||
|
label={
|
||||||
|
<Box sx={{ fontSize: '12px' }}>
|
||||||
|
{option as React.ReactNode}
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
{...getTagProps({ index })}
|
||||||
|
key={index}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
renderInput={params => (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
placeholder='回车确认,填写下一个热门搜索'
|
||||||
|
variant='outlined'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</CommonItem>
|
</CommonItem>
|
||||||
<CommonItem title='主按钮' onAdd={handleAddButton}>
|
<CommonItem title='主按钮' onAdd={handleAddButton}>
|
||||||
<DragList
|
<DragList
|
||||||
|
|
@ -227,10 +195,6 @@ const Config: React.FC<ConfigProps> = ({ setIsEdit, id }) => {
|
||||||
setIsEdit(true);
|
setIsEdit(true);
|
||||||
}}
|
}}
|
||||||
setIsEdit={setIsEdit}
|
setIsEdit={setIsEdit}
|
||||||
SortableItemComponent={sortableProps => (
|
|
||||||
<SortableItem {...sortableProps} ItemComponent={Item} />
|
|
||||||
)}
|
|
||||||
ItemComponent={Item}
|
|
||||||
/>
|
/>
|
||||||
</CommonItem>
|
</CommonItem>
|
||||||
</StyledCommonWrapper>
|
</StyledCommonWrapper>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
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 { Dispatch, FC, SetStateAction, useCallback, useState } from 'react';
|
||||||
|
import Item from './Item';
|
||||||
|
import { DomainRecommendNodeListResp } from '@/request/types';
|
||||||
|
import SortableItem from './SortableItem';
|
||||||
|
|
||||||
|
interface DragListProps {
|
||||||
|
data: DomainRecommendNodeListResp[];
|
||||||
|
onChange: (data: DomainRecommendNodeListResp[]) => void;
|
||||||
|
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DragList: FC<DragListProps> = ({ data, onChange, setIsEdit }) => {
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleUpdateItem = useCallback(
|
||||||
|
(updatedItem: DomainRecommendNodeListResp) => {
|
||||||
|
const newData = data.map(item =>
|
||||||
|
item.id === updatedItem.id ? updatedItem : item,
|
||||||
|
);
|
||||||
|
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={2}>
|
||||||
|
{data.map(item => (
|
||||||
|
<SortableItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
item={item}
|
||||||
|
handleRemove={handleRemove}
|
||||||
|
// handleUpdateItem={handleUpdateItem}
|
||||||
|
// setIsEdit={setIsEdit}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</SortableContext>
|
||||||
|
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
|
||||||
|
{activeId ? (
|
||||||
|
<Item
|
||||||
|
isDragging
|
||||||
|
item={data.find(item => item.id === activeId)!}
|
||||||
|
// setIsEdit={setIsEdit}
|
||||||
|
// handleUpdateItem={handleUpdateItem}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DragList;
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import FaqItem, { ItemProps } from './Item';
|
||||||
|
|
||||||
|
type SortableItemProps = ItemProps & {};
|
||||||
|
|
||||||
|
const FaqSortableItem: 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 (
|
||||||
|
<FaqItem
|
||||||
|
ref={setNodeRef}
|
||||||
|
style={style}
|
||||||
|
withOpacity={isDragging}
|
||||||
|
dragHandleProps={{
|
||||||
|
...attributes,
|
||||||
|
...listeners,
|
||||||
|
}}
|
||||||
|
item={item}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FaqSortableItem;
|
||||||
|
|
@ -2,9 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||||
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
||||||
import { TextField } from '@mui/material';
|
import { TextField } from '@mui/material';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import DragList from '../../components/DragList';
|
import BasicDocDragList from './DragList';
|
||||||
import SortableItem from '../../components/SortableItem';
|
|
||||||
import Item from './Item';
|
|
||||||
import type { ConfigProps } from '../type';
|
import type { ConfigProps } from '../type';
|
||||||
import { Empty } from '@ctzhian/ui';
|
import { Empty } from '@ctzhian/ui';
|
||||||
import { useAppSelector } from '@/store';
|
import { useAppSelector } from '@/store';
|
||||||
|
|
@ -118,17 +116,13 @@ const BasicDocConfig = ({ setIsEdit, id }: ConfigProps) => {
|
||||||
{nodes.length === 0 ? (
|
{nodes.length === 0 ? (
|
||||||
<Empty />
|
<Empty />
|
||||||
) : (
|
) : (
|
||||||
<DragList
|
<BasicDocDragList
|
||||||
data={nodes}
|
data={nodes}
|
||||||
onChange={value => {
|
onChange={value => {
|
||||||
setIsEdit(true);
|
setIsEdit(true);
|
||||||
setValue('nodes', value);
|
setValue('nodes', value);
|
||||||
}}
|
}}
|
||||||
setIsEdit={setIsEdit}
|
setIsEdit={setIsEdit}
|
||||||
SortableItemComponent={sortableProps => (
|
|
||||||
<SortableItem {...sortableProps} ItemComponent={Item} />
|
|
||||||
)}
|
|
||||||
ItemComponent={Item}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</CommonItem>
|
</CommonItem>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
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 { Dispatch, FC, SetStateAction, useCallback, useState } from 'react';
|
||||||
|
import Item, { type ItemType } from './Item';
|
||||||
|
import SortableItem from './SortableItem';
|
||||||
|
|
||||||
|
interface DragListProps {
|
||||||
|
data: ItemType[];
|
||||||
|
onChange: (data: ItemType[]) => void;
|
||||||
|
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DragList: FC<DragListProps> = ({ data, onChange, setIsEdit }) => {
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleUpdateItem = useCallback(
|
||||||
|
(updatedItem: ItemType) => {
|
||||||
|
const newData = data.map(item =>
|
||||||
|
item.id === updatedItem.id ? updatedItem : item,
|
||||||
|
);
|
||||||
|
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={2}>
|
||||||
|
{data.map(item => (
|
||||||
|
<SortableItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
item={item}
|
||||||
|
handleRemove={handleRemove}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</SortableContext>
|
||||||
|
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
|
||||||
|
{activeId ? (
|
||||||
|
<Item
|
||||||
|
isDragging
|
||||||
|
item={data.find(item => item.id === activeId)!}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DragList;
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import Item, { ItemProps } from './Item';
|
||||||
|
|
||||||
|
type SortableItemProps = ItemProps & {};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
@ -2,9 +2,7 @@ import React, { useEffect } from 'react';
|
||||||
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
||||||
import { TextField } from '@mui/material';
|
import { TextField } from '@mui/material';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import DragList from '../../components/DragList';
|
import DragList from './DragList';
|
||||||
import SortableItem from '../../components/SortableItem';
|
|
||||||
import Item from './Item';
|
|
||||||
import type { ConfigProps } from '../type';
|
import type { ConfigProps } from '../type';
|
||||||
import { useAppSelector } from '@/store';
|
import { useAppSelector } from '@/store';
|
||||||
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
||||||
|
|
@ -93,10 +91,6 @@ const Config = ({ setIsEdit, id }: ConfigProps) => {
|
||||||
data={list}
|
data={list}
|
||||||
onChange={handleListChange}
|
onChange={handleListChange}
|
||||||
setIsEdit={setIsEdit}
|
setIsEdit={setIsEdit}
|
||||||
SortableItemComponent={sortableProps => (
|
|
||||||
<SortableItem {...sortableProps} ItemComponent={Item} />
|
|
||||||
)}
|
|
||||||
ItemComponent={Item}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</CommonItem>
|
</CommonItem>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
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 { Dispatch, FC, SetStateAction, useCallback, useState } from 'react';
|
||||||
|
import Item from './Item';
|
||||||
|
import FaqSortableItem from './SortableItem';
|
||||||
|
|
||||||
|
type ItemType = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
url: string;
|
||||||
|
desc: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface DragListProps {
|
||||||
|
data: ItemType[];
|
||||||
|
onChange: (data: ItemType[]) => void;
|
||||||
|
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DragList: FC<DragListProps> = ({ data, onChange, setIsEdit }) => {
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleUpdateItem = useCallback(
|
||||||
|
(updatedItem: ItemType) => {
|
||||||
|
const newData = data.map(item =>
|
||||||
|
item.id === updatedItem.id ? updatedItem : item,
|
||||||
|
);
|
||||||
|
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={2}>
|
||||||
|
{data.map(item => (
|
||||||
|
<FaqSortableItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
item={item}
|
||||||
|
handleRemove={handleRemove}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</SortableContext>
|
||||||
|
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
|
||||||
|
{activeId ? (
|
||||||
|
<Item
|
||||||
|
isDragging
|
||||||
|
item={data.find(item => item.id === activeId)!}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DragList;
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import FaqItem, { ItemProps } from './Item';
|
||||||
|
|
||||||
|
type FaqSortableItemProps = ItemProps & {};
|
||||||
|
|
||||||
|
const FaqSortableItem: FC<FaqSortableItemProps> = ({ item, ...rest }) => {
|
||||||
|
const {
|
||||||
|
isDragging,
|
||||||
|
attributes,
|
||||||
|
listeners,
|
||||||
|
setNodeRef,
|
||||||
|
transform,
|
||||||
|
transition,
|
||||||
|
} = useSortable({ id: item.id });
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
transform: CSS.Transform.toString(transform),
|
||||||
|
transition: transition || undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FaqItem
|
||||||
|
ref={setNodeRef}
|
||||||
|
style={style}
|
||||||
|
withOpacity={isDragging}
|
||||||
|
dragHandleProps={{
|
||||||
|
...attributes,
|
||||||
|
...listeners,
|
||||||
|
}}
|
||||||
|
item={item}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FaqSortableItem;
|
||||||
|
|
@ -2,9 +2,7 @@ import React, { useEffect } from 'react';
|
||||||
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
||||||
import { TextField } from '@mui/material';
|
import { TextField } from '@mui/material';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import DragList from '../../components/DragList';
|
import DragList from './DragList';
|
||||||
import SortableItem from '../../components/SortableItem';
|
|
||||||
import Item from './Item';
|
|
||||||
import type { ConfigProps } from '../type';
|
import type { ConfigProps } from '../type';
|
||||||
import { useAppSelector } from '@/store';
|
import { useAppSelector } from '@/store';
|
||||||
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
||||||
|
|
@ -123,10 +121,6 @@ const Config = ({ setIsEdit, id }: ConfigProps) => {
|
||||||
data={list}
|
data={list}
|
||||||
onChange={handleListChange}
|
onChange={handleListChange}
|
||||||
setIsEdit={setIsEdit}
|
setIsEdit={setIsEdit}
|
||||||
SortableItemComponent={sortableProps => (
|
|
||||||
<SortableItem {...sortableProps} ItemComponent={Item} />
|
|
||||||
)}
|
|
||||||
ItemComponent={Item}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</CommonItem>
|
</CommonItem>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
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 { Dispatch, FC, SetStateAction, useCallback, useState } from 'react';
|
||||||
|
import Item, { type ItemType } from './Item';
|
||||||
|
import SortableItem from './SortableItem';
|
||||||
|
|
||||||
|
interface DragListProps {
|
||||||
|
data: ItemType[];
|
||||||
|
onChange: (data: ItemType[]) => void;
|
||||||
|
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DragList: FC<DragListProps> = ({ data, onChange, setIsEdit }) => {
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleUpdateItem = useCallback(
|
||||||
|
(updatedItem: ItemType) => {
|
||||||
|
const newData = data.map(item =>
|
||||||
|
item.id === updatedItem.id ? updatedItem : item,
|
||||||
|
);
|
||||||
|
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={2}>
|
||||||
|
{data.map(item => (
|
||||||
|
<SortableItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
item={item}
|
||||||
|
handleRemove={handleRemove}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</SortableContext>
|
||||||
|
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
|
||||||
|
{activeId ? (
|
||||||
|
<Item
|
||||||
|
isDragging
|
||||||
|
item={data.find(item => item.id === activeId)!}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DragList;
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import FaqItem, { ItemTypeProps } from './Item';
|
||||||
|
|
||||||
|
type SortableItemProps = ItemTypeProps & {};
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<FaqItem
|
||||||
|
ref={setNodeRef}
|
||||||
|
style={style}
|
||||||
|
withOpacity={isDragging}
|
||||||
|
dragHandleProps={{
|
||||||
|
...attributes,
|
||||||
|
...listeners,
|
||||||
|
}}
|
||||||
|
item={item}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SortableItem;
|
||||||
|
|
@ -2,9 +2,7 @@ import React, { useEffect } from 'react';
|
||||||
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
||||||
import { TextField } from '@mui/material';
|
import { TextField } from '@mui/material';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import DragList from '../../components/DragList';
|
import DragList from './DragList';
|
||||||
import SortableItem from '../../components/SortableItem';
|
|
||||||
import Item from './Item';
|
|
||||||
import type { ConfigProps } from '../type';
|
import type { ConfigProps } from '../type';
|
||||||
import { useAppSelector } from '@/store';
|
import { useAppSelector } from '@/store';
|
||||||
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
||||||
|
|
@ -92,10 +90,6 @@ const CaseConfig = ({ setIsEdit, id }: ConfigProps) => {
|
||||||
data={list}
|
data={list}
|
||||||
onChange={handleListChange}
|
onChange={handleListChange}
|
||||||
setIsEdit={setIsEdit}
|
setIsEdit={setIsEdit}
|
||||||
SortableItemComponent={sortableProps => (
|
|
||||||
<SortableItem {...sortableProps} ItemComponent={Item} />
|
|
||||||
)}
|
|
||||||
ItemComponent={Item}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</CommonItem>
|
</CommonItem>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
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 { Dispatch, FC, SetStateAction, useCallback, useState } from 'react';
|
||||||
|
import Item, { type ItemType } from './Item';
|
||||||
|
import SortableItem from './SortableItem';
|
||||||
|
|
||||||
|
interface DragListProps {
|
||||||
|
data: ItemType[];
|
||||||
|
onChange: (data: ItemType[]) => void;
|
||||||
|
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DragList: FC<DragListProps> = ({ data, onChange, setIsEdit }) => {
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleUpdateItem = useCallback(
|
||||||
|
(updatedItem: ItemType) => {
|
||||||
|
const newData = data.map(item =>
|
||||||
|
item.id === updatedItem.id ? updatedItem : item,
|
||||||
|
);
|
||||||
|
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={2}>
|
||||||
|
{data.map(item => (
|
||||||
|
<SortableItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
item={item}
|
||||||
|
handleRemove={handleRemove}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</SortableContext>
|
||||||
|
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
|
||||||
|
{activeId ? (
|
||||||
|
<Item
|
||||||
|
isDragging
|
||||||
|
item={data.find(item => item.id === activeId)!}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DragList;
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import FaqItem, { ItemTypeProps } from './Item';
|
||||||
|
|
||||||
|
type SortableItemProps = ItemTypeProps & {};
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<FaqItem
|
||||||
|
ref={setNodeRef}
|
||||||
|
style={style}
|
||||||
|
withOpacity={isDragging}
|
||||||
|
dragHandleProps={{
|
||||||
|
...attributes,
|
||||||
|
...listeners,
|
||||||
|
}}
|
||||||
|
item={item}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SortableItem;
|
||||||
|
|
@ -2,9 +2,7 @@ import React, { useEffect } from 'react';
|
||||||
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
||||||
import { TextField } from '@mui/material';
|
import { TextField } from '@mui/material';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import DragList from '../../components/DragList';
|
import DragList from './DragList';
|
||||||
import SortableItem from '../../components/SortableItem';
|
|
||||||
import Item from './Item';
|
|
||||||
import type { ConfigProps } from '../type';
|
import type { ConfigProps } from '../type';
|
||||||
import { useAppSelector } from '@/store';
|
import { useAppSelector } from '@/store';
|
||||||
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
||||||
|
|
@ -95,10 +93,6 @@ const Config = ({ setIsEdit, id }: ConfigProps) => {
|
||||||
data={list}
|
data={list}
|
||||||
onChange={handleListChange}
|
onChange={handleListChange}
|
||||||
setIsEdit={setIsEdit}
|
setIsEdit={setIsEdit}
|
||||||
SortableItemComponent={sortableProps => (
|
|
||||||
<SortableItem {...sortableProps} ItemComponent={Item} />
|
|
||||||
)}
|
|
||||||
ItemComponent={Item}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</CommonItem>
|
</CommonItem>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
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 { Dispatch, FC, SetStateAction, useCallback, useState } from 'react';
|
||||||
|
import Item from './Item';
|
||||||
|
import { DomainRecommendNodeListResp } from '@/request/types';
|
||||||
|
import SortableItem from './SortableItem';
|
||||||
|
|
||||||
|
interface DragListProps {
|
||||||
|
data: DomainRecommendNodeListResp[];
|
||||||
|
onChange: (data: DomainRecommendNodeListResp[]) => void;
|
||||||
|
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DragList: FC<DragListProps> = ({ data, onChange, setIsEdit }) => {
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleUpdateItem = useCallback(
|
||||||
|
(updatedItem: DomainRecommendNodeListResp) => {
|
||||||
|
const newData = data.map(item =>
|
||||||
|
item.id === updatedItem.id ? updatedItem : item,
|
||||||
|
);
|
||||||
|
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={2}>
|
||||||
|
{data.map(item => (
|
||||||
|
<SortableItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
item={item}
|
||||||
|
handleRemove={handleRemove}
|
||||||
|
// handleUpdateItem={handleUpdateItem}
|
||||||
|
// setIsEdit={setIsEdit}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</SortableContext>
|
||||||
|
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
|
||||||
|
{activeId ? (
|
||||||
|
<Item
|
||||||
|
isDragging
|
||||||
|
item={data.find(item => item.id === activeId)!}
|
||||||
|
// setIsEdit={setIsEdit}
|
||||||
|
// handleUpdateItem={handleUpdateItem}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DragList;
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import Item, { ItemProps } from './Item';
|
||||||
|
|
||||||
|
type SortableItemProps = ItemProps & {};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
@ -2,9 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||||
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
||||||
import { TextField } from '@mui/material';
|
import { TextField } from '@mui/material';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import DragList from '../../components/DragList';
|
import BasicDocDragList from './DragList';
|
||||||
import SortableItem from '../../components/SortableItem';
|
|
||||||
import Item from './Item';
|
|
||||||
import { Empty } from '@ctzhian/ui';
|
import { Empty } from '@ctzhian/ui';
|
||||||
import type { ConfigProps } from '../type';
|
import type { ConfigProps } from '../type';
|
||||||
import { useAppSelector } from '@/store';
|
import { useAppSelector } from '@/store';
|
||||||
|
|
@ -119,17 +117,13 @@ const DirDocConfig = ({ setIsEdit, id }: ConfigProps) => {
|
||||||
{nodes.length === 0 ? (
|
{nodes.length === 0 ? (
|
||||||
<Empty />
|
<Empty />
|
||||||
) : (
|
) : (
|
||||||
<DragList
|
<BasicDocDragList
|
||||||
data={nodes}
|
data={nodes}
|
||||||
onChange={value => {
|
onChange={value => {
|
||||||
setIsEdit(true);
|
setIsEdit(true);
|
||||||
setValue('nodes', value);
|
setValue('nodes', value);
|
||||||
}}
|
}}
|
||||||
setIsEdit={setIsEdit}
|
setIsEdit={setIsEdit}
|
||||||
SortableItemComponent={sortableProps => (
|
|
||||||
<SortableItem {...sortableProps} ItemComponent={Item} />
|
|
||||||
)}
|
|
||||||
ItemComponent={Item}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</CommonItem>
|
</CommonItem>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
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 { Dispatch, FC, SetStateAction, useCallback, useState } from 'react';
|
||||||
|
import FaqItem from './FaqItem';
|
||||||
|
import FaqSortableItem from './FaqSortableItem';
|
||||||
|
|
||||||
|
type FaqItemType = {
|
||||||
|
id: string;
|
||||||
|
question: string;
|
||||||
|
link: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface FaqDragListProps {
|
||||||
|
data: FaqItemType[];
|
||||||
|
onChange: (data: FaqItemType[]) => void;
|
||||||
|
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FaqDragList: FC<FaqDragListProps> = ({ data, onChange, setIsEdit }) => {
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleUpdateItem = useCallback(
|
||||||
|
(updatedItem: FaqItemType) => {
|
||||||
|
const newData = data.map(item =>
|
||||||
|
item.id === updatedItem.id ? updatedItem : item,
|
||||||
|
);
|
||||||
|
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={2}>
|
||||||
|
{data.map(item => (
|
||||||
|
<FaqSortableItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
item={item}
|
||||||
|
handleRemove={handleRemove}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</SortableContext>
|
||||||
|
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
|
||||||
|
{activeId ? (
|
||||||
|
<FaqItem
|
||||||
|
isDragging
|
||||||
|
item={data.find(item => item.id === activeId)!}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FaqDragList;
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import FaqItem, { FaqItemProps } from './FaqItem';
|
||||||
|
|
||||||
|
type FaqSortableItemProps = FaqItemProps & {};
|
||||||
|
|
||||||
|
const FaqSortableItem: FC<FaqSortableItemProps> = ({ item, ...rest }) => {
|
||||||
|
const {
|
||||||
|
isDragging,
|
||||||
|
attributes,
|
||||||
|
listeners,
|
||||||
|
setNodeRef,
|
||||||
|
transform,
|
||||||
|
transition,
|
||||||
|
} = useSortable({ id: item.id });
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
transform: CSS.Transform.toString(transform),
|
||||||
|
transition: transition || undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FaqItem
|
||||||
|
ref={setNodeRef}
|
||||||
|
style={style}
|
||||||
|
withOpacity={isDragging}
|
||||||
|
dragHandleProps={{
|
||||||
|
...attributes,
|
||||||
|
...listeners,
|
||||||
|
}}
|
||||||
|
item={item}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FaqSortableItem;
|
||||||
|
|
@ -2,14 +2,13 @@ import React, { useEffect } from 'react';
|
||||||
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
||||||
import { TextField } from '@mui/material';
|
import { TextField } from '@mui/material';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import DragList from '../../components/DragList';
|
import FaqDragList from './FaqDragList';
|
||||||
import SortableItem from '../../components/SortableItem';
|
|
||||||
import FaqItem from './Item';
|
|
||||||
import type { ConfigProps } from '../type';
|
import type { ConfigProps } from '../type';
|
||||||
import { useAppSelector } from '@/store';
|
import { useAppSelector } from '@/store';
|
||||||
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
||||||
import { Empty } from '@ctzhian/ui';
|
import { Empty } from '@ctzhian/ui';
|
||||||
import { DEFAULT_DATA } from '../../../constants';
|
import { DEFAULT_DATA } from '../../../constants';
|
||||||
|
import ColorPickerField from '../../components/ColorPickerField';
|
||||||
import { findConfigById, handleLandingConfigs } from '../../../utils';
|
import { findConfigById, handleLandingConfigs } from '../../../utils';
|
||||||
|
|
||||||
const FaqConfig = ({ setIsEdit, id }: ConfigProps) => {
|
const FaqConfig = ({ setIsEdit, id }: ConfigProps) => {
|
||||||
|
|
@ -112,14 +111,10 @@ const FaqConfig = ({ setIsEdit, id }: ConfigProps) => {
|
||||||
{list.length === 0 ? (
|
{list.length === 0 ? (
|
||||||
<Empty />
|
<Empty />
|
||||||
) : (
|
) : (
|
||||||
<DragList
|
<FaqDragList
|
||||||
data={list}
|
data={list}
|
||||||
onChange={handleListChange}
|
onChange={handleListChange}
|
||||||
setIsEdit={setIsEdit}
|
setIsEdit={setIsEdit}
|
||||||
SortableItemComponent={sortableProps => (
|
|
||||||
<SortableItem {...sortableProps} ItemComponent={FaqItem} />
|
|
||||||
)}
|
|
||||||
ItemComponent={FaqItem}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</CommonItem>
|
</CommonItem>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
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 { Dispatch, FC, SetStateAction, useCallback, useState } from 'react';
|
||||||
|
import Item, { type ItemType } from './Item';
|
||||||
|
import SortableItem from './SortableItem';
|
||||||
|
|
||||||
|
interface DragListProps {
|
||||||
|
data: ItemType[];
|
||||||
|
onChange: (data: ItemType[]) => void;
|
||||||
|
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DragList: FC<DragListProps> = ({ data, onChange, setIsEdit }) => {
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleUpdateItem = useCallback(
|
||||||
|
(updatedItem: ItemType) => {
|
||||||
|
const newData = data.map(item =>
|
||||||
|
item.id === updatedItem.id ? updatedItem : item,
|
||||||
|
);
|
||||||
|
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={2}>
|
||||||
|
{data.map(item => (
|
||||||
|
<SortableItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
item={item}
|
||||||
|
handleRemove={handleRemove}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</SortableContext>
|
||||||
|
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
|
||||||
|
{activeId ? (
|
||||||
|
<Item
|
||||||
|
isDragging
|
||||||
|
item={data.find(item => item.id === activeId)!}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DragList;
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import Item, { ItemProps } from './Item';
|
||||||
|
|
||||||
|
type SortableItemProps = ItemProps & {};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
@ -2,9 +2,7 @@ import React, { useEffect } from 'react';
|
||||||
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
||||||
import { TextField } from '@mui/material';
|
import { TextField } from '@mui/material';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import DragList from '../../components/DragList';
|
import DragList from './DragList';
|
||||||
import SortableItem from '../../components/SortableItem';
|
|
||||||
import Item from './Item';
|
|
||||||
import type { ConfigProps } from '../type';
|
import type { ConfigProps } from '../type';
|
||||||
import { useAppSelector } from '@/store';
|
import { useAppSelector } from '@/store';
|
||||||
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
||||||
|
|
@ -91,10 +89,6 @@ const Config = ({ setIsEdit, id }: ConfigProps) => {
|
||||||
data={list}
|
data={list}
|
||||||
onChange={handleListChange}
|
onChange={handleListChange}
|
||||||
setIsEdit={setIsEdit}
|
setIsEdit={setIsEdit}
|
||||||
SortableItemComponent={sortableProps => (
|
|
||||||
<SortableItem {...sortableProps} ItemComponent={Item} />
|
|
||||||
)}
|
|
||||||
ItemComponent={Item}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</CommonItem>
|
</CommonItem>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
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 { Dispatch, FC, SetStateAction, useCallback, useState } from 'react';
|
||||||
|
import Item, { type ItemType } from './Item';
|
||||||
|
import SortableItem from './SortableItem';
|
||||||
|
|
||||||
|
interface DragListProps {
|
||||||
|
data: ItemType[];
|
||||||
|
onChange: (data: ItemType[]) => void;
|
||||||
|
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DragList: FC<DragListProps> = ({ data, onChange, setIsEdit }) => {
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleUpdateItem = useCallback(
|
||||||
|
(updatedItem: ItemType) => {
|
||||||
|
const newData = data.map(item =>
|
||||||
|
item.id === updatedItem.id ? updatedItem : item,
|
||||||
|
);
|
||||||
|
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={2}>
|
||||||
|
{data.map(item => (
|
||||||
|
<SortableItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
item={item}
|
||||||
|
handleRemove={handleRemove}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</SortableContext>
|
||||||
|
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
|
||||||
|
{activeId ? (
|
||||||
|
<Item
|
||||||
|
isDragging
|
||||||
|
item={data.find(item => item.id === activeId)!}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DragList;
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import FaqItem, { ItemTypeProps } from './Item';
|
||||||
|
|
||||||
|
type SortableItemProps = ItemTypeProps & {};
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<FaqItem
|
||||||
|
ref={setNodeRef}
|
||||||
|
style={style}
|
||||||
|
withOpacity={isDragging}
|
||||||
|
dragHandleProps={{
|
||||||
|
...attributes,
|
||||||
|
...listeners,
|
||||||
|
}}
|
||||||
|
item={item}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SortableItem;
|
||||||
|
|
@ -2,9 +2,7 @@ import React, { useEffect } from 'react';
|
||||||
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
||||||
import { TextField } from '@mui/material';
|
import { TextField } from '@mui/material';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import DragList from '../../components/DragList';
|
import DragList from './DragList';
|
||||||
import SortableItem from '../../components/SortableItem';
|
|
||||||
import Item from './Item';
|
|
||||||
import type { ConfigProps } from '../type';
|
import type { ConfigProps } from '../type';
|
||||||
import { useAppSelector } from '@/store';
|
import { useAppSelector } from '@/store';
|
||||||
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
||||||
|
|
@ -92,10 +90,6 @@ const MetricsConfig = ({ setIsEdit, id }: ConfigProps) => {
|
||||||
data={list}
|
data={list}
|
||||||
onChange={handleListChange}
|
onChange={handleListChange}
|
||||||
setIsEdit={setIsEdit}
|
setIsEdit={setIsEdit}
|
||||||
SortableItemComponent={sortableProps => (
|
|
||||||
<SortableItem {...sortableProps} ItemComponent={Item} />
|
|
||||||
)}
|
|
||||||
ItemComponent={Item}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</CommonItem>
|
</CommonItem>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
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 { Dispatch, FC, SetStateAction, useCallback, useState } from 'react';
|
||||||
|
import Item, { ItemType } from './Item';
|
||||||
|
import SortableItem from './SortableItem';
|
||||||
|
|
||||||
|
interface FaqDragListProps {
|
||||||
|
data: ItemType[];
|
||||||
|
onChange: (data: ItemType[]) => void;
|
||||||
|
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FaqDragList: FC<FaqDragListProps> = ({ data, onChange, setIsEdit }) => {
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleUpdateItem = useCallback(
|
||||||
|
(updatedItem: ItemType) => {
|
||||||
|
const newData = data.map(item =>
|
||||||
|
item.id === updatedItem.id ? updatedItem : item,
|
||||||
|
);
|
||||||
|
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={2}>
|
||||||
|
{data.map(item => (
|
||||||
|
<SortableItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
item={item}
|
||||||
|
handleRemove={handleRemove}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</SortableContext>
|
||||||
|
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
|
||||||
|
{activeId ? (
|
||||||
|
<Item
|
||||||
|
isDragging
|
||||||
|
item={data.find(item => item.id === activeId)!}
|
||||||
|
setIsEdit={setIsEdit}
|
||||||
|
handleUpdateItem={handleUpdateItem}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FaqDragList;
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import Item, { ItemProps } from './Item';
|
||||||
|
|
||||||
|
type SortableItemProps = ItemProps & {};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
@ -2,9 +2,7 @@ import React, { useEffect } from 'react';
|
||||||
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
||||||
import { TextField } from '@mui/material';
|
import { TextField } from '@mui/material';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import DragList from '../../components/DragList';
|
import FaqDragList from './DragList';
|
||||||
import SortableItem from '../../components/SortableItem';
|
|
||||||
import Item from './Item';
|
|
||||||
import type { ConfigProps } from '../type';
|
import type { ConfigProps } from '../type';
|
||||||
import { useAppSelector } from '@/store';
|
import { useAppSelector } from '@/store';
|
||||||
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
import useDebounceAppPreviewData from '@/hooks/useDebounceAppPreviewData';
|
||||||
|
|
@ -90,14 +88,10 @@ const FaqConfig = ({ setIsEdit, id }: ConfigProps) => {
|
||||||
{list.length === 0 ? (
|
{list.length === 0 ? (
|
||||||
<Empty />
|
<Empty />
|
||||||
) : (
|
) : (
|
||||||
<DragList
|
<FaqDragList
|
||||||
data={list}
|
data={list}
|
||||||
onChange={handleListChange}
|
onChange={handleListChange}
|
||||||
setIsEdit={setIsEdit}
|
setIsEdit={setIsEdit}
|
||||||
SortableItemComponent={sortableProps => (
|
|
||||||
<SortableItem {...sortableProps} ItemComponent={Item} />
|
|
||||||
)}
|
|
||||||
ItemComponent={Item}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</CommonItem>
|
</CommonItem>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
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 { Dispatch, FC, SetStateAction, useCallback, useState } from 'react';
|
||||||
|
import Item from './Item';
|
||||||
|
import { DomainRecommendNodeListResp } from '@/request/types';
|
||||||
|
import SortableItem from './SortableItem';
|
||||||
|
|
||||||
|
interface DragListProps {
|
||||||
|
data: DomainRecommendNodeListResp[];
|
||||||
|
onChange: (data: DomainRecommendNodeListResp[]) => void;
|
||||||
|
setIsEdit: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DragList: FC<DragListProps> = ({ data, onChange, setIsEdit }) => {
|
||||||
|
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],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleUpdateItem = useCallback(
|
||||||
|
(updatedItem: DomainRecommendNodeListResp) => {
|
||||||
|
const newData = data.map(item =>
|
||||||
|
item.id === updatedItem.id ? updatedItem : item,
|
||||||
|
);
|
||||||
|
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={2}>
|
||||||
|
{data.map(item => (
|
||||||
|
<SortableItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
item={item}
|
||||||
|
handleRemove={handleRemove}
|
||||||
|
// handleUpdateItem={handleUpdateItem}
|
||||||
|
// setIsEdit={setIsEdit}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</SortableContext>
|
||||||
|
<DragOverlay adjustScale style={{ transformOrigin: '0 0' }}>
|
||||||
|
{activeId ? (
|
||||||
|
<Item
|
||||||
|
isDragging
|
||||||
|
item={data.find(item => item.id === activeId)!}
|
||||||
|
// setIsEdit={setIsEdit}
|
||||||
|
// handleUpdateItem={handleUpdateItem}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DragList;
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import FaqItem, { ItemProps } from './Item';
|
||||||
|
|
||||||
|
type SortableItemProps = ItemProps & {};
|
||||||
|
|
||||||
|
const FaqSortableItem: 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 (
|
||||||
|
<FaqItem
|
||||||
|
ref={setNodeRef}
|
||||||
|
style={style}
|
||||||
|
withOpacity={isDragging}
|
||||||
|
dragHandleProps={{
|
||||||
|
...attributes,
|
||||||
|
...listeners,
|
||||||
|
}}
|
||||||
|
item={item}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FaqSortableItem;
|
||||||
|
|
@ -2,9 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||||
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
import { CommonItem, StyledCommonWrapper } from '../../components/StyledCommon';
|
||||||
import { TextField } from '@mui/material';
|
import { TextField } from '@mui/material';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import DragList from '../../components/DragList';
|
import DragList from './DragList';
|
||||||
import SortableItem from '../../components/SortableItem';
|
|
||||||
import Item from './Item';
|
|
||||||
import { Empty } from '@ctzhian/ui';
|
import { Empty } from '@ctzhian/ui';
|
||||||
import type { ConfigProps } from '../type';
|
import type { ConfigProps } from '../type';
|
||||||
import { useAppSelector } from '@/store';
|
import { useAppSelector } from '@/store';
|
||||||
|
|
@ -126,10 +124,6 @@ const SimpleDocConfigConfig = ({ setIsEdit, id }: ConfigProps) => {
|
||||||
setValue('nodes', value);
|
setValue('nodes', value);
|
||||||
}}
|
}}
|
||||||
setIsEdit={setIsEdit}
|
setIsEdit={setIsEdit}
|
||||||
SortableItemComponent={sortableProps => (
|
|
||||||
<SortableItem {...sortableProps} ItemComponent={Item} />
|
|
||||||
)}
|
|
||||||
ItemComponent={Item}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</CommonItem>
|
</CommonItem>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { useURLSearchParams } from '@/hooks';
|
||||||
import { ConstsUserRole } from '@/request/types';
|
import { ConstsUserRole } from '@/request/types';
|
||||||
import { useAppDispatch, useAppSelector } from '@/store';
|
import { useAppDispatch, useAppSelector } from '@/store';
|
||||||
import { setKbC, setKbId } from '@/store/slices/config';
|
import { setKbC, setKbId } from '@/store/slices/config';
|
||||||
|
import custom from '@/themes/custom';
|
||||||
import { Ellipsis, Icon, message } from '@ctzhian/ui';
|
import { Ellipsis, Icon, message } from '@ctzhian/ui';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
|
@ -116,7 +117,7 @@ const KBSelect = () => {
|
||||||
borderRadius: '5px',
|
borderRadius: '5px',
|
||||||
bgcolor: 'background.paper3',
|
bgcolor: 'background.paper3',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
bgcolor: 'rgba(50,72,242,0.1)',
|
bgcolor: custom.selectedMenuItemBgColor,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import custom from './custom';
|
||||||
|
import dark from './dark';
|
||||||
|
import light from './light';
|
||||||
|
|
||||||
|
export { custom, dark, light };
|
||||||
|
export type ThemeColor = typeof light;
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
export default {
|
||||||
|
selectPopupBoxShadow: '0px 10px 20px 0px rgba(54,59,76,0.2)',
|
||||||
|
selectedMenuItemBgColor: 'rgba(50,72,242,0.1)',
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
const dark = {
|
||||||
|
cssVariables: true,
|
||||||
|
primary: {
|
||||||
|
main: '#fdfdfd',
|
||||||
|
contrastText: '#000',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#2196F3',
|
||||||
|
lighter: '#D6E4FF',
|
||||||
|
light: '#84A9FF',
|
||||||
|
dark: '#1939B7',
|
||||||
|
darker: '#091A7A',
|
||||||
|
contrastText: '#fff',
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
main: '#1890FF',
|
||||||
|
lighter: '#D0F2FF',
|
||||||
|
light: '#74CAFF',
|
||||||
|
dark: '#0C53B7',
|
||||||
|
darker: '#04297A',
|
||||||
|
contrastText: '#fff',
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
main: '#00DF98',
|
||||||
|
lighter: '#E9FCD4',
|
||||||
|
light: '#AAF27F',
|
||||||
|
dark: '#229A16',
|
||||||
|
darker: '#08660D',
|
||||||
|
contrastText: 'rgba(0,0,0,0.7)',
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
main: '#F7B500',
|
||||||
|
lighter: '#FFF7CD',
|
||||||
|
light: '#FFE16A',
|
||||||
|
dark: '#B78103',
|
||||||
|
darker: '#7A4F01',
|
||||||
|
contrastText: 'rgba(0,0,0,0.7)',
|
||||||
|
},
|
||||||
|
neutral: {
|
||||||
|
main: '#1A1A1A',
|
||||||
|
contrastText: 'rgba(255, 255, 255, 0.60)',
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
main: '#D93940',
|
||||||
|
lighter: '#FFE7D9',
|
||||||
|
light: '#FFA48D',
|
||||||
|
dark: '#B72136',
|
||||||
|
darker: '#7A0C2E',
|
||||||
|
contrastText: '#fff',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: '#fff',
|
||||||
|
secondary: 'rgba(255,255,255,0.7)',
|
||||||
|
auxiliary: 'rgba(255,255,255,0.5)',
|
||||||
|
disabled: 'rgba(255,255,255,0.26)',
|
||||||
|
slave: 'rgba(255,255,255,0.05)',
|
||||||
|
inverseAuxiliary: 'rgba(0,0,0,0.5)',
|
||||||
|
inverseDisabled: 'rgba(0,0,0,0.15)',
|
||||||
|
},
|
||||||
|
divider: '#ededed',
|
||||||
|
background: {
|
||||||
|
paper: '#18181b',
|
||||||
|
paper2: '#060608',
|
||||||
|
paper3: '#27272a',
|
||||||
|
default: 'rgba(255,255,255,0.6)',
|
||||||
|
disabled: 'rgba(15,15,15,0.8)',
|
||||||
|
chip: 'rgba(145,147,171,0.16)',
|
||||||
|
circle: '#3B476A',
|
||||||
|
focus: '#542996',
|
||||||
|
},
|
||||||
|
common: {},
|
||||||
|
shadows: 'transparent',
|
||||||
|
table: {
|
||||||
|
head: {
|
||||||
|
backgroundColor: '#484848',
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
hoverColor: 'rgba(48, 58, 70, 0.4)',
|
||||||
|
},
|
||||||
|
cell: {
|
||||||
|
borderColor: '#484848',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
charts: {
|
||||||
|
color: ['#7267EF', '#36B37E'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default dark;
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
const light = {
|
||||||
|
cssVariables: true,
|
||||||
|
primary: {
|
||||||
|
main: '#3248F2',
|
||||||
|
contrastText: '#fff',
|
||||||
|
lighter: '#E6E8EC',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#3366FF',
|
||||||
|
lighter: '#D6E4FF',
|
||||||
|
light: '#84A9FF',
|
||||||
|
dark: '#1939B7',
|
||||||
|
darker: '#091A7A',
|
||||||
|
contrastText: '#fff',
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
main: '#0063FF',
|
||||||
|
lighter: '#D0F2FF',
|
||||||
|
light: '#74CAFF',
|
||||||
|
dark: '#0C53B7',
|
||||||
|
darker: '#04297A',
|
||||||
|
contrastText: '#fff',
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
main: '#82DDAF',
|
||||||
|
lighter: '#E9FCD4',
|
||||||
|
light: '#AAF27F',
|
||||||
|
mainShadow: '#36B37E',
|
||||||
|
dark: '#229A16',
|
||||||
|
darker: '#08660D',
|
||||||
|
contrastText: 'rgba(0,0,0,0.7)',
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
main: '#FEA145',
|
||||||
|
lighter: '#FFF7CD',
|
||||||
|
light: '#FFE16A',
|
||||||
|
shadow: 'rgba(255, 171, 0, 0.15)',
|
||||||
|
dark: '#B78103',
|
||||||
|
darker: '#7A4F01',
|
||||||
|
contrastText: 'rgba(0,0,0,0.7)',
|
||||||
|
},
|
||||||
|
neutral: {
|
||||||
|
main: '#FFFFFF',
|
||||||
|
contrastText: 'rgba(0, 0, 0, 0.60)',
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
main: '#FE4545',
|
||||||
|
lighter: '#FFE7D9',
|
||||||
|
light: '#FFA48D',
|
||||||
|
shadow: 'rgba(255, 86, 48, 0.15)',
|
||||||
|
dark: '#B72136',
|
||||||
|
darker: '#7A0C2E',
|
||||||
|
contrastText: '#FFFFFF',
|
||||||
|
},
|
||||||
|
divider: '#ECEEF1',
|
||||||
|
text: {
|
||||||
|
primary: '#21222D',
|
||||||
|
secondary: 'rgba(33,34,35,0.7)',
|
||||||
|
auxiliary: 'rgba(33,34,35,0.5)',
|
||||||
|
slave: 'rgba(33,34,35,0.3)',
|
||||||
|
disabled: 'rgba(33,34,35,0.2)',
|
||||||
|
inverse: '#FFFFFF',
|
||||||
|
inverseAuxiliary: 'rgba(255,255,255,0.5)',
|
||||||
|
inverseDisabled: 'rgba(255,255,255,0.15)',
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
paper: '#FFFFFF',
|
||||||
|
paper2: '#F1F2F8',
|
||||||
|
paper3: '#F8F9FA',
|
||||||
|
default: '#FFFFFF',
|
||||||
|
chip: '#FFFFFF',
|
||||||
|
circle: '#E6E8EC',
|
||||||
|
hover: 'rgba(243, 244, 245, 0.5)',
|
||||||
|
},
|
||||||
|
shadows: 'rgba(68, 80 ,91, 0.1)',
|
||||||
|
table: {
|
||||||
|
head: {
|
||||||
|
height: '50px',
|
||||||
|
backgroundColor: '#FFFFFF',
|
||||||
|
color: '#000',
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
hoverColor: '#F8F9FA',
|
||||||
|
},
|
||||||
|
cell: {
|
||||||
|
height: '72px',
|
||||||
|
borderColor: '#ECEEF1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
charts: {
|
||||||
|
color: ['#673AB7', '#36B37E'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default light;
|
||||||
|
|
@ -0,0 +1,460 @@
|
||||||
|
import { addOpacityToColor } from '@/utils';
|
||||||
|
import { custom, ThemeColor } from './color';
|
||||||
|
|
||||||
|
declare module '@mui/material/styles' {}
|
||||||
|
|
||||||
|
declare module '@mui/material/styles' {
|
||||||
|
interface TypeBackground {
|
||||||
|
paper0?: string;
|
||||||
|
paper2?: string;
|
||||||
|
chip?: string;
|
||||||
|
circle?: string;
|
||||||
|
hover?: string;
|
||||||
|
focus?: string;
|
||||||
|
disabled?: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const componentStyleOverrides = (theme: ThemeColor) => {
|
||||||
|
return {
|
||||||
|
MuiTabs: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
borderRadius: '10px !important',
|
||||||
|
overflow: 'hidden',
|
||||||
|
minHeight: '36px',
|
||||||
|
height: '36px',
|
||||||
|
padding: '0px !important',
|
||||||
|
},
|
||||||
|
indicator: {
|
||||||
|
borderRadius: '0px !important',
|
||||||
|
overflow: 'hidden',
|
||||||
|
backgroundColor: '#21222D !important',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiTab: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
borderRadius: '0px !important',
|
||||||
|
fontWeight: 'normal',
|
||||||
|
fontSize: '14px !important',
|
||||||
|
lineHeight: '34px',
|
||||||
|
padding: '0 16px !important',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiFormLabel: {
|
||||||
|
styleOverrides: {
|
||||||
|
asterisk: {
|
||||||
|
color: theme.error.main,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiButton: {
|
||||||
|
styleOverrides: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
root: ({ ownerState }: { ownerState: any }) => {
|
||||||
|
return {
|
||||||
|
height: '36px',
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: '36px',
|
||||||
|
paddingLeft: '16px',
|
||||||
|
paddingRight: '16px',
|
||||||
|
boxShadow: 'none',
|
||||||
|
transition: 'all 0.2s ease-in-out',
|
||||||
|
borderRadius: '10px',
|
||||||
|
fontWeight: '400',
|
||||||
|
...(ownerState.variant === 'contained' && {
|
||||||
|
color: theme.text.inverse,
|
||||||
|
backgroundColor: theme.text.primary,
|
||||||
|
}),
|
||||||
|
...(ownerState.variant === 'text' && {}),
|
||||||
|
...(ownerState.variant === 'outlined' && {
|
||||||
|
color: theme.text.primary,
|
||||||
|
border: `1px solid ${theme.text.primary}`,
|
||||||
|
}),
|
||||||
|
...(ownerState.disabled === true && {
|
||||||
|
cursor: 'not-allowed !important',
|
||||||
|
}),
|
||||||
|
...(ownerState.size === 'small' && {
|
||||||
|
height: '32px',
|
||||||
|
lineHeight: '32px',
|
||||||
|
}),
|
||||||
|
'&:hover': {
|
||||||
|
boxShadow: 'none',
|
||||||
|
...(ownerState.variant === 'contained' && {
|
||||||
|
backgroundColor: addOpacityToColor(theme.text.primary, 0.9),
|
||||||
|
}),
|
||||||
|
...(ownerState.variant === 'text' && {
|
||||||
|
backgroundColor: theme.background.paper3,
|
||||||
|
}),
|
||||||
|
...(ownerState.variant === 'outlined' && {
|
||||||
|
backgroundColor: theme.background.paper3,
|
||||||
|
}),
|
||||||
|
...(ownerState.color === 'neutral' && {
|
||||||
|
color: theme.text.primary,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
startIcon: {
|
||||||
|
marginLeft: 0,
|
||||||
|
marginRight: 8,
|
||||||
|
'>*:nth-of-type(1)': {
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiTooltip: {
|
||||||
|
styleOverrides: {
|
||||||
|
tooltip: {
|
||||||
|
borderRadius: '10px',
|
||||||
|
maxWidth: '600px',
|
||||||
|
padding: '8px 16px',
|
||||||
|
backgroundColor: theme.text.primary,
|
||||||
|
fontSize: '12px',
|
||||||
|
lineHeight: '20px',
|
||||||
|
color: theme.primary.contrastText,
|
||||||
|
},
|
||||||
|
arrow: {
|
||||||
|
color: theme.text.primary,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiFormHelperText: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
color: theme.error.main,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiFormControl: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'.MuiFormLabel-asterisk': {
|
||||||
|
color: theme.error.main,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiFormControlLabel: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
marginLeft: '0 !important',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiTableBody: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'.MuiTableRow-root:hover': {
|
||||||
|
'.MuiTableCell-root:not(.cx-table-empty-td)': {
|
||||||
|
backgroundColor: theme.table.row.hoverColor,
|
||||||
|
overflowX: 'hidden',
|
||||||
|
'.primary-color': {
|
||||||
|
color: theme.primary.main,
|
||||||
|
},
|
||||||
|
'.no-title-url': {
|
||||||
|
color: `${theme.primary.main} !important`,
|
||||||
|
},
|
||||||
|
'.error-color': {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiCheckbox: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
padding: 0,
|
||||||
|
svg: {
|
||||||
|
fontSize: '18px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiTableCell: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
background: theme.background.paper,
|
||||||
|
lineHeight: 1.5,
|
||||||
|
height: theme.table.cell.height,
|
||||||
|
fontSize: '14px',
|
||||||
|
paddingTop: '16px !important',
|
||||||
|
paddingBottom: '16px !important',
|
||||||
|
paddingLeft: 0,
|
||||||
|
'&:first-of-type': {
|
||||||
|
paddingLeft: '0px',
|
||||||
|
},
|
||||||
|
'&:not(:first-of-type)': {
|
||||||
|
paddingLeft: '0px',
|
||||||
|
},
|
||||||
|
'.MuiCheckbox-root': {
|
||||||
|
color: '#CCCCCC',
|
||||||
|
svg: {
|
||||||
|
fontSize: '16px',
|
||||||
|
},
|
||||||
|
'&.Mui-checked': {
|
||||||
|
color: theme.text.primary,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
head: {
|
||||||
|
backgroundColor: theme.background.paper3,
|
||||||
|
color: theme.table.head.color,
|
||||||
|
fontSize: '12px',
|
||||||
|
height: theme.table.head.height,
|
||||||
|
paddingTop: '0 !important',
|
||||||
|
paddingBottom: '0 !important',
|
||||||
|
borderSpacing: '12px',
|
||||||
|
zIndex: 100,
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
borderBottom: '1px dashed',
|
||||||
|
borderColor: theme.table.cell.borderColor,
|
||||||
|
borderSpacing: '12px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiPopover: {
|
||||||
|
styleOverrides: {
|
||||||
|
paper: {
|
||||||
|
borderRadius: '10px',
|
||||||
|
boxShadow: custom.selectPopupBoxShadow,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiMenu: {
|
||||||
|
styleOverrides: {
|
||||||
|
paper: {
|
||||||
|
padding: '4px',
|
||||||
|
borderRadius: '10px',
|
||||||
|
backgroundColor: theme.background.paper,
|
||||||
|
boxShadow: custom.selectPopupBoxShadow,
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
paddingTop: '0px !important',
|
||||||
|
paddingBottom: '0px !important',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultProps: {
|
||||||
|
elevation: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiMenuItem: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
height: '40px',
|
||||||
|
borderRadius: '5px',
|
||||||
|
':hover': {
|
||||||
|
backgroundColor: theme.background.paper3,
|
||||||
|
},
|
||||||
|
'&.Mui-selected': {
|
||||||
|
fontWeight: '500',
|
||||||
|
backgroundColor: `${custom.selectedMenuItemBgColor} !important`,
|
||||||
|
color: theme.primary.main,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiPaper: {
|
||||||
|
defaultProps: {
|
||||||
|
elevation: 1,
|
||||||
|
},
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ ownerState }: { ownerState: { elevation?: number } }) => {
|
||||||
|
return {
|
||||||
|
...(ownerState.elevation === 0 && {
|
||||||
|
backgroundColor: theme.background.paper2,
|
||||||
|
}),
|
||||||
|
...(ownerState.elevation === 2 && {
|
||||||
|
backgroundColor: theme.background.paper3,
|
||||||
|
}),
|
||||||
|
backgroundImage: 'none',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiChip: {
|
||||||
|
styleOverrides: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
root: ({ ownerState }: { ownerState: any }) => {
|
||||||
|
return {
|
||||||
|
height: '24px',
|
||||||
|
lineHeight: '24px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
'.MuiChip-label': {
|
||||||
|
padding: '0 8px 0 4px',
|
||||||
|
},
|
||||||
|
...(ownerState.color === 'default' && {
|
||||||
|
backgroundColor: theme.background.chip,
|
||||||
|
borderColor: theme.text.disabled,
|
||||||
|
'.Mui-focusVisible': {
|
||||||
|
backgroundColor: theme.background.chip,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
...(ownerState.color === 'error' && {
|
||||||
|
backgroundColor: addOpacityToColor(theme.error.main, 0.1),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
label: ({ ownerState }: { ownerState: any }) => {
|
||||||
|
return {
|
||||||
|
padding: '0 14px',
|
||||||
|
fontSize: '14px',
|
||||||
|
lineHeight: '24px',
|
||||||
|
...(ownerState.color === 'default' && {
|
||||||
|
color: theme.text.primary,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
deleteIcon: {
|
||||||
|
fontSize: '14px',
|
||||||
|
color: theme.text.disabled,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiAppBar: {
|
||||||
|
defaultProps: {
|
||||||
|
elevation: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiDialog: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'h2.MuiTypography-root button': {
|
||||||
|
marginRight: '2px',
|
||||||
|
},
|
||||||
|
'.MuiDialogActions-root': {
|
||||||
|
paddingTop: '24px',
|
||||||
|
button: {
|
||||||
|
width: '88px',
|
||||||
|
height: '36px !important',
|
||||||
|
},
|
||||||
|
'.MuiButton-text': {
|
||||||
|
width: 'auto',
|
||||||
|
minWidth: 'auto',
|
||||||
|
color: `${theme.text.primary} !important`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
height: '100vh',
|
||||||
|
bgcolor: theme.text.secondary,
|
||||||
|
backdropFilter: 'blur(5px)',
|
||||||
|
},
|
||||||
|
paper: {
|
||||||
|
pb: 1,
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: theme.divider,
|
||||||
|
borderRadius: '10px',
|
||||||
|
backgroundColor: theme.background.paper,
|
||||||
|
textarea: {
|
||||||
|
borderRadius: '8px 8px 0 8px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiDialogTitle: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
paddingTop: '24px',
|
||||||
|
'> button': {
|
||||||
|
top: '20px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiAlert: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
lineHeight: '22px',
|
||||||
|
paddingTop: '1px',
|
||||||
|
paddingBottom: '1px',
|
||||||
|
borderRadius: '10px',
|
||||||
|
boxShadow: 'none',
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
padding: '10px 0',
|
||||||
|
},
|
||||||
|
standardInfo: {
|
||||||
|
backgroundColor: addOpacityToColor(theme.primary.main, 0.1),
|
||||||
|
color: theme.text.primary,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiRadio: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
padding: 0,
|
||||||
|
marginRight: '8px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiTextField: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
label: {
|
||||||
|
color: theme.text.secondary,
|
||||||
|
},
|
||||||
|
'label.Mui-focused': {
|
||||||
|
color: theme.text.primary,
|
||||||
|
},
|
||||||
|
'& .MuiInputBase-input::placeholder': {
|
||||||
|
fontSize: '12px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiInputBase: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
borderRadius: '10px !important',
|
||||||
|
backgroundColor: theme.background.paper3,
|
||||||
|
'.MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: `${theme.background.paper3} !important`,
|
||||||
|
borderWidth: '1px !important',
|
||||||
|
},
|
||||||
|
'&.Mui-focused': {
|
||||||
|
'.MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: `${theme.text.primary} !important`,
|
||||||
|
borderWidth: '1px !important',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'&:hover': {
|
||||||
|
'.MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: `${theme.text.primary} !important`,
|
||||||
|
borderWidth: '1px !important',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
height: '19px',
|
||||||
|
'&.Mui-disabled': {
|
||||||
|
color: `${theme.text.secondary} !important`,
|
||||||
|
WebkitTextFillColor: `${theme.text.secondary} !important`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiSelect: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
height: '36px',
|
||||||
|
borderRadius: '10px !important',
|
||||||
|
backgroundColor: theme.background.paper3,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
paddingRight: '0 !important',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default componentStyleOverrides;
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
"html-react-parser": "^5.2.5",
|
"html-react-parser": "^5.2.5",
|
||||||
"html-to-image": "^1.11.13",
|
"html-to-image": "^1.11.13",
|
||||||
|
"import-in-the-middle": "^1.14.2",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"katex": "^0.16.22",
|
"katex": "^0.16.22",
|
||||||
"markdown-it": "13.0.1",
|
"markdown-it": "13.0.1",
|
||||||
|
|
@ -41,6 +42,7 @@
|
||||||
"remark-breaks": "^4.0.0",
|
"remark-breaks": "^4.0.0",
|
||||||
"remark-gfm": "^4.0.1",
|
"remark-gfm": "^4.0.1",
|
||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
|
"require-in-the-middle": "^7.5.2",
|
||||||
"uuid": "^11.1.0"
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
||||||
|
|
@ -97,198 +97,199 @@ const Footer = React.memo(
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Box
|
{showBrand && (
|
||||||
pt={
|
<Box
|
||||||
customStyle?.footer_show_intro
|
pt={
|
||||||
? 5
|
customStyle?.footer_show_intro
|
||||||
: (footerSetting?.brand_groups?.length || 0) > 0
|
|
||||||
? 5
|
? 5
|
||||||
: 0
|
: (footerSetting?.brand_groups?.length || 0) > 0
|
||||||
}
|
? 5
|
||||||
>
|
: 0
|
||||||
{customStyle?.footer_show_intro !== false && (
|
}
|
||||||
<Box sx={{ mb: 3 }}>
|
>
|
||||||
<Stack direction={'row'} alignItems={'center'} gap={1}>
|
{customStyle?.footer_show_intro !== false && (
|
||||||
{footerSetting?.brand_logo && (
|
<Box sx={{ mb: 3 }}>
|
||||||
<img
|
<Stack direction={'row'} alignItems={'center'} gap={1}>
|
||||||
src={footerSetting.brand_logo}
|
{footerSetting?.brand_logo && (
|
||||||
alt='PandaWiki'
|
<img
|
||||||
height={24}
|
src={footerSetting.brand_logo}
|
||||||
/>
|
alt='PandaWiki'
|
||||||
|
height={24}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
fontWeight: 'bold',
|
||||||
|
lineHeight: '32px',
|
||||||
|
fontSize: 24,
|
||||||
|
color: 'white',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{footerSetting?.brand_name}
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
{footerSetting?.brand_desc && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
fontSize: 12,
|
||||||
|
lineHeight: '26px',
|
||||||
|
mt: 2,
|
||||||
|
color: 'rgba(255, 255, 255, 0.70)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{footerSetting.brand_desc}
|
||||||
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Box
|
<Stack direction={'column'} gap={2.5} mt={2}>
|
||||||
sx={{
|
{customStyle?.social_media_accounts?.map(
|
||||||
fontWeight: 'bold',
|
(account, index) => {
|
||||||
lineHeight: '32px',
|
return (
|
||||||
fontSize: 24,
|
<Stack
|
||||||
color: 'white',
|
direction={'row'}
|
||||||
}}
|
key={index}
|
||||||
>
|
sx={theme => ({
|
||||||
{footerSetting?.brand_name}
|
position: 'relative',
|
||||||
</Box>
|
color: alpha(theme.palette.text.primary, 0.7),
|
||||||
</Stack>
|
})}
|
||||||
{footerSetting?.brand_desc && (
|
gap={1}
|
||||||
<Box
|
onClick={() => {
|
||||||
sx={{
|
setCurOverlayType(account.channel || '');
|
||||||
fontSize: 12,
|
if (account.channel === 'phone') {
|
||||||
lineHeight: '26px',
|
setPhoneData({
|
||||||
mt: 2,
|
phone: account.phone || '',
|
||||||
color: 'rgba(255, 255, 255, 0.70)',
|
text: account.text || '',
|
||||||
}}
|
});
|
||||||
>
|
setOpen(true);
|
||||||
{footerSetting.brand_desc}
|
}
|
||||||
</Box>
|
if (account.channel === 'wechat_oa') {
|
||||||
)}
|
setWechatData({
|
||||||
<Stack direction={'column'} gap={2.5} mt={2}>
|
src: account.icon || '',
|
||||||
{customStyle?.social_media_accounts?.map(
|
text: account.text || '',
|
||||||
(account, index) => {
|
});
|
||||||
return (
|
setOpen(true);
|
||||||
<Stack
|
}
|
||||||
direction={'row'}
|
|
||||||
key={index}
|
|
||||||
sx={theme => ({
|
|
||||||
position: 'relative',
|
|
||||||
color: alpha(theme.palette.text.primary, 0.7),
|
|
||||||
})}
|
|
||||||
gap={1}
|
|
||||||
onClick={() => {
|
|
||||||
setCurOverlayType(account.channel || '');
|
|
||||||
if (account.channel === 'phone') {
|
|
||||||
setPhoneData({
|
|
||||||
phone: account.phone || '',
|
|
||||||
text: account.text || '',
|
|
||||||
});
|
|
||||||
setOpen(true);
|
|
||||||
}
|
|
||||||
if (account.channel === 'wechat_oa') {
|
|
||||||
setWechatData({
|
|
||||||
src: account.icon || '',
|
|
||||||
text: account.text || '',
|
|
||||||
});
|
|
||||||
setOpen(true);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{account.channel === 'wechat_oa' && (
|
|
||||||
<IconWeixingongzhonghao
|
|
||||||
sx={{ fontSize: '20px', color: 'inherit' }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{account.channel === 'phone' && (
|
|
||||||
<IconDianhua
|
|
||||||
sx={{ fontSize: '20px', color: 'inherit' }}
|
|
||||||
></IconDianhua>
|
|
||||||
)}
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
lineHeight: '24px',
|
|
||||||
fontSize: '14px',
|
|
||||||
color: 'inherit',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{account.text}
|
{account.channel === 'wechat_oa' && (
|
||||||
</Box>
|
<IconWeixingongzhonghao
|
||||||
{account.channel === 'wechat_oa' &&
|
sx={{ fontSize: '20px', color: 'inherit' }}
|
||||||
(account?.text || account?.icon) && (
|
/>
|
||||||
<Stack
|
|
||||||
direction={'column'}
|
|
||||||
alignItems={'center'}
|
|
||||||
bgcolor={'#fff'}
|
|
||||||
p={1.5}
|
|
||||||
sx={theme => ({
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: '100%',
|
|
||||||
transform: 'translateY(-10px)',
|
|
||||||
left: 0,
|
|
||||||
boxShadow:
|
|
||||||
' 0px 4px 8px 0px ' +
|
|
||||||
alpha(theme.palette.text.primary, 0.25),
|
|
||||||
borderRadius: '4px',
|
|
||||||
bgcolor: theme.palette.background.default,
|
|
||||||
})}
|
|
||||||
gap={1}
|
|
||||||
display={'none'}
|
|
||||||
zIndex={999}
|
|
||||||
>
|
|
||||||
{account.icon && (
|
|
||||||
<img
|
|
||||||
src={account.icon}
|
|
||||||
width={83}
|
|
||||||
height={83}
|
|
||||||
></img>
|
|
||||||
)}
|
|
||||||
{account.text && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
fontSize: '12px',
|
|
||||||
lineHeight: '16px',
|
|
||||||
color: '#21222D',
|
|
||||||
maxWidth: '83px',
|
|
||||||
|
|
||||||
textAlign: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{account.text}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
)}
|
)}
|
||||||
</Stack>
|
{account.channel === 'phone' && (
|
||||||
);
|
<IconDianhua
|
||||||
},
|
sx={{ fontSize: '20px', color: 'inherit' }}
|
||||||
)}
|
></IconDianhua>
|
||||||
</Stack>
|
)}
|
||||||
</Box>
|
<Box
|
||||||
)}
|
sx={{
|
||||||
|
lineHeight: '24px',
|
||||||
|
fontSize: '14px',
|
||||||
|
color: 'inherit',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{account.text}
|
||||||
|
</Box>
|
||||||
|
{account.channel === 'wechat_oa' &&
|
||||||
|
(account?.text || account?.icon) && (
|
||||||
|
<Stack
|
||||||
|
direction={'column'}
|
||||||
|
alignItems={'center'}
|
||||||
|
bgcolor={'#fff'}
|
||||||
|
p={1.5}
|
||||||
|
sx={theme => ({
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '100%',
|
||||||
|
transform: 'translateY(-10px)',
|
||||||
|
left: 0,
|
||||||
|
boxShadow:
|
||||||
|
' 0px 4px 8px 0px ' +
|
||||||
|
alpha(theme.palette.text.primary, 0.25),
|
||||||
|
borderRadius: '4px',
|
||||||
|
bgcolor: theme.palette.background.default,
|
||||||
|
})}
|
||||||
|
gap={1}
|
||||||
|
display={'none'}
|
||||||
|
zIndex={999}
|
||||||
|
>
|
||||||
|
{account.icon && (
|
||||||
|
<img
|
||||||
|
src={account.icon}
|
||||||
|
width={83}
|
||||||
|
height={83}
|
||||||
|
></img>
|
||||||
|
)}
|
||||||
|
{account.text && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
fontSize: '12px',
|
||||||
|
lineHeight: '16px',
|
||||||
|
color: '#21222D',
|
||||||
|
maxWidth: '83px',
|
||||||
|
|
||||||
<Stack direction={'row'} flexWrap={'wrap'} gap={2}>
|
textAlign: 'center',
|
||||||
{footerSetting?.brand_groups?.map((group, idx) => (
|
}}
|
||||||
<Stack
|
>
|
||||||
gap={1}
|
{account.text}
|
||||||
key={group.name}
|
</Box>
|
||||||
sx={{
|
)}
|
||||||
fontSize: 14,
|
</Stack>
|
||||||
lineHeight: '22px',
|
)}
|
||||||
width: 'calc(50% - 8px)',
|
</Stack>
|
||||||
...(idx > 1 && {
|
);
|
||||||
mt: 1,
|
},
|
||||||
}),
|
)}
|
||||||
'& a:hover': {
|
</Stack>
|
||||||
color: 'primary.main',
|
</Box>
|
||||||
},
|
)}
|
||||||
}}
|
|
||||||
>
|
<Stack direction={'row'} flexWrap={'wrap'} gap={2}>
|
||||||
<Box
|
{footerSetting?.brand_groups?.map((group, idx) => (
|
||||||
|
<Stack
|
||||||
|
gap={1}
|
||||||
|
key={group.name}
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: 16,
|
fontSize: 14,
|
||||||
lineHeight: '24px',
|
lineHeight: '22px',
|
||||||
mb: 1,
|
width: 'calc(50% - 8px)',
|
||||||
color: '#ffffff',
|
...(idx > 1 && {
|
||||||
|
mt: 1,
|
||||||
|
}),
|
||||||
|
'& a:hover': {
|
||||||
|
color: 'primary.main',
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{group.name}
|
|
||||||
</Box>
|
|
||||||
{group.links?.map(link => (
|
|
||||||
<Box
|
<Box
|
||||||
sx={theme => ({
|
sx={{
|
||||||
color: alpha(theme.palette.text.primary, 0.5),
|
fontSize: 16,
|
||||||
})}
|
lineHeight: '24px',
|
||||||
key={link.name}
|
mb: 1,
|
||||||
|
color: '#ffffff',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Link
|
{group.name}
|
||||||
href={link?.url || ''}
|
</Box>
|
||||||
target='_blank'
|
{group.links?.map(link => (
|
||||||
|
<Box
|
||||||
|
sx={theme => ({
|
||||||
|
color: alpha(theme.palette.text.primary, 0.5),
|
||||||
|
})}
|
||||||
key={link.name}
|
key={link.name}
|
||||||
>
|
>
|
||||||
{link.name}
|
<Link
|
||||||
</Link>
|
href={link?.url || ''}
|
||||||
</Box>
|
target='_blank'
|
||||||
))}
|
key={link.name}
|
||||||
</Stack>
|
>
|
||||||
))}
|
{link.name}
|
||||||
</Stack>
|
</Link>
|
||||||
</Box>
|
</Box>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
{!(
|
{!(
|
||||||
customStyle?.footer_show_intro === false &&
|
customStyle?.footer_show_intro === false &&
|
||||||
footerSetting?.brand_groups?.length === 0
|
footerSetting?.brand_groups?.length === 0
|
||||||
|
|
@ -477,239 +478,241 @@ const Footer = React.memo(
|
||||||
// }),
|
// }),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
{showBrand && (
|
||||||
py={
|
<Box
|
||||||
customStyle?.footer_show_intro
|
py={
|
||||||
? 6
|
customStyle?.footer_show_intro
|
||||||
: (footerSetting?.brand_groups?.length || 0) > 0
|
|
||||||
? 6
|
? 6
|
||||||
: 0
|
: (footerSetting?.brand_groups?.length || 0) > 0
|
||||||
}
|
? 6
|
||||||
>
|
: 0
|
||||||
<Stack
|
|
||||||
direction={'row'}
|
|
||||||
gap={10}
|
|
||||||
justifyContent={
|
|
||||||
customStyle?.footer_show_intro === false
|
|
||||||
? 'center'
|
|
||||||
: 'flex-start'
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{customStyle?.footer_show_intro !== false && (
|
|
||||||
<Stack
|
|
||||||
direction={'column'}
|
|
||||||
sx={{ width: '30%', minWidth: 200 }}
|
|
||||||
gap={3}
|
|
||||||
>
|
|
||||||
<Stack direction={'row'} alignItems={'center'} gap={1}>
|
|
||||||
{footerSetting?.brand_logo && (
|
|
||||||
<img
|
|
||||||
src={footerSetting.brand_logo}
|
|
||||||
alt='PandaWiki'
|
|
||||||
height={36}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
fontWeight: 'bold',
|
|
||||||
lineHeight: '32px',
|
|
||||||
fontSize: 24,
|
|
||||||
color: 'text.primary',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{footerSetting?.brand_name}
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
{footerSetting?.brand_desc && (
|
|
||||||
<Box
|
|
||||||
sx={theme => ({
|
|
||||||
fontSize: 14,
|
|
||||||
lineHeight: '26px',
|
|
||||||
color: alpha(theme.palette.text.primary, 0.7),
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{footerSetting.brand_desc}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<Stack direction={'column'} gap={'26px'}>
|
|
||||||
{customStyle?.social_media_accounts?.map(
|
|
||||||
(account, index) => {
|
|
||||||
return (
|
|
||||||
<Stack
|
|
||||||
direction={'row'}
|
|
||||||
key={index}
|
|
||||||
sx={theme => ({
|
|
||||||
position: 'relative',
|
|
||||||
'&:hover': {
|
|
||||||
color: theme.palette.primary.main,
|
|
||||||
},
|
|
||||||
'&:hover .popup': {
|
|
||||||
display: 'flex !important',
|
|
||||||
},
|
|
||||||
color: alpha(theme.palette.text.primary, 0.7),
|
|
||||||
cursor: 'default',
|
|
||||||
})}
|
|
||||||
gap={1}
|
|
||||||
>
|
|
||||||
{account.channel === 'wechat_oa' && (
|
|
||||||
<IconWeixingongzhonghao
|
|
||||||
sx={{ fontSize: '20px', color: 'inherit' }}
|
|
||||||
></IconWeixingongzhonghao>
|
|
||||||
)}
|
|
||||||
{account.channel === 'phone' && (
|
|
||||||
<IconDianhua
|
|
||||||
sx={{ fontSize: '20px', color: 'inherit' }}
|
|
||||||
></IconDianhua>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
lineHeight: '24px',
|
|
||||||
fontSize: '14px',
|
|
||||||
color: 'inherit',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{account.text}
|
|
||||||
</Box>
|
|
||||||
{account.channel === 'wechat_oa' &&
|
|
||||||
(account?.text || account?.icon) && (
|
|
||||||
<Stack
|
|
||||||
className={'popup'}
|
|
||||||
direction={'column'}
|
|
||||||
alignItems={'center'}
|
|
||||||
bgcolor={'#fff'}
|
|
||||||
p={1.5}
|
|
||||||
sx={theme => ({
|
|
||||||
position: 'absolute',
|
|
||||||
top: '40px',
|
|
||||||
left: 0,
|
|
||||||
boxShadow:
|
|
||||||
' 0px 4px 8px 0px ' +
|
|
||||||
alpha(theme.palette.text.primary, 0.25),
|
|
||||||
borderRadius: '4px',
|
|
||||||
bgcolor: theme.palette.background.default,
|
|
||||||
})}
|
|
||||||
gap={1}
|
|
||||||
display={'none'}
|
|
||||||
zIndex={999}
|
|
||||||
>
|
|
||||||
{account.icon && (
|
|
||||||
<img
|
|
||||||
src={account.icon}
|
|
||||||
width={120}
|
|
||||||
height={120}
|
|
||||||
></img>
|
|
||||||
)}
|
|
||||||
{account.text && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
fontSize: '12px',
|
|
||||||
lineHeight: '16px',
|
|
||||||
color: 'text.primary',
|
|
||||||
maxWidth: '120px',
|
|
||||||
textAlign: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{account.text}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
{account.channel === 'phone' && account?.phone && (
|
|
||||||
<Stack
|
|
||||||
className={'popup'}
|
|
||||||
bgcolor={'#fff'}
|
|
||||||
px={1.5}
|
|
||||||
py={1}
|
|
||||||
sx={theme => ({
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: '100%',
|
|
||||||
transform: 'translateY(-10px)',
|
|
||||||
left: 0,
|
|
||||||
boxShadow:
|
|
||||||
'0px 4px 8px 0px ' +
|
|
||||||
alpha(theme.palette.text.primary, 0.25),
|
|
||||||
borderRadius: '4px',
|
|
||||||
bgcolor: theme.palette.background.default,
|
|
||||||
})}
|
|
||||||
display={'none'}
|
|
||||||
zIndex={999}
|
|
||||||
>
|
|
||||||
{account.phone && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
fontSize: '12px',
|
|
||||||
lineHeight: '16px',
|
|
||||||
color: 'text.primary',
|
|
||||||
textAlign: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{account.phone}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Stack
|
<Stack
|
||||||
direction={'row'}
|
direction={'row'}
|
||||||
width={'100%'}
|
gap={10}
|
||||||
justifyContent={'flex-start'}
|
justifyContent={
|
||||||
flexWrap='wrap'
|
customStyle?.footer_show_intro === false
|
||||||
|
? 'center'
|
||||||
|
: 'flex-start'
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{footerSetting?.brand_groups?.map(group => (
|
{customStyle?.footer_show_intro !== false && (
|
||||||
<Stack
|
<Stack
|
||||||
gap={1.5}
|
direction={'column'}
|
||||||
key={group.name}
|
sx={{ width: '30%', minWidth: 200 }}
|
||||||
sx={{
|
gap={3}
|
||||||
flex: '0 0 33.33%',
|
|
||||||
fontSize: 14,
|
|
||||||
lineHeight: '22px',
|
|
||||||
minWidth: '100px',
|
|
||||||
'& a:hover': {
|
|
||||||
color: 'primary.main',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Box
|
<Stack direction={'row'} alignItems={'center'} gap={1}>
|
||||||
sx={{
|
{footerSetting?.brand_logo && (
|
||||||
fontSize: 16,
|
<img
|
||||||
lineHeight: '24px',
|
src={footerSetting.brand_logo}
|
||||||
mb: 1,
|
alt='PandaWiki'
|
||||||
color: 'text.primary',
|
height={36}
|
||||||
}}
|
/>
|
||||||
>
|
)}
|
||||||
{group.name}
|
<Box
|
||||||
</Box>
|
sx={{
|
||||||
{group.links?.map(link => (
|
fontWeight: 'bold',
|
||||||
|
lineHeight: '32px',
|
||||||
|
fontSize: 24,
|
||||||
|
color: 'text.primary',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{footerSetting?.brand_name}
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{footerSetting?.brand_desc && (
|
||||||
<Box
|
<Box
|
||||||
sx={theme => ({
|
sx={theme => ({
|
||||||
color: alpha(theme.palette.text.primary, 0.5),
|
fontSize: 14,
|
||||||
|
lineHeight: '26px',
|
||||||
|
color: alpha(theme.palette.text.primary, 0.7),
|
||||||
})}
|
})}
|
||||||
key={link.name}
|
|
||||||
>
|
>
|
||||||
<Link
|
{footerSetting.brand_desc}
|
||||||
href={link?.url || ''}
|
</Box>
|
||||||
target='_blank'
|
)}
|
||||||
|
<Stack direction={'column'} gap={'26px'}>
|
||||||
|
{customStyle?.social_media_accounts?.map(
|
||||||
|
(account, index) => {
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
direction={'row'}
|
||||||
|
key={index}
|
||||||
|
sx={theme => ({
|
||||||
|
position: 'relative',
|
||||||
|
'&:hover': {
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
},
|
||||||
|
'&:hover .popup': {
|
||||||
|
display: 'flex !important',
|
||||||
|
},
|
||||||
|
color: alpha(theme.palette.text.primary, 0.7),
|
||||||
|
cursor: 'default',
|
||||||
|
})}
|
||||||
|
gap={1}
|
||||||
|
>
|
||||||
|
{account.channel === 'wechat_oa' && (
|
||||||
|
<IconWeixingongzhonghao
|
||||||
|
sx={{ fontSize: '20px', color: 'inherit' }}
|
||||||
|
></IconWeixingongzhonghao>
|
||||||
|
)}
|
||||||
|
{account.channel === 'phone' && (
|
||||||
|
<IconDianhua
|
||||||
|
sx={{ fontSize: '20px', color: 'inherit' }}
|
||||||
|
></IconDianhua>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
lineHeight: '24px',
|
||||||
|
fontSize: '14px',
|
||||||
|
color: 'inherit',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{account.text}
|
||||||
|
</Box>
|
||||||
|
{account.channel === 'wechat_oa' &&
|
||||||
|
(account?.text || account?.icon) && (
|
||||||
|
<Stack
|
||||||
|
className={'popup'}
|
||||||
|
direction={'column'}
|
||||||
|
alignItems={'center'}
|
||||||
|
bgcolor={'#fff'}
|
||||||
|
p={1.5}
|
||||||
|
sx={theme => ({
|
||||||
|
position: 'absolute',
|
||||||
|
top: '40px',
|
||||||
|
left: 0,
|
||||||
|
boxShadow:
|
||||||
|
' 0px 4px 8px 0px ' +
|
||||||
|
alpha(theme.palette.text.primary, 0.25),
|
||||||
|
borderRadius: '4px',
|
||||||
|
bgcolor: theme.palette.background.default,
|
||||||
|
})}
|
||||||
|
gap={1}
|
||||||
|
display={'none'}
|
||||||
|
zIndex={999}
|
||||||
|
>
|
||||||
|
{account.icon && (
|
||||||
|
<img
|
||||||
|
src={account.icon}
|
||||||
|
width={120}
|
||||||
|
height={120}
|
||||||
|
></img>
|
||||||
|
)}
|
||||||
|
{account.text && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
fontSize: '12px',
|
||||||
|
lineHeight: '16px',
|
||||||
|
color: 'text.primary',
|
||||||
|
maxWidth: '120px',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{account.text}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{account.channel === 'phone' &&
|
||||||
|
account?.phone && (
|
||||||
|
<Stack
|
||||||
|
className={'popup'}
|
||||||
|
bgcolor={'#fff'}
|
||||||
|
px={1.5}
|
||||||
|
py={1}
|
||||||
|
sx={theme => ({
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '100%',
|
||||||
|
transform: 'translateY(-10px)',
|
||||||
|
left: 0,
|
||||||
|
boxShadow:
|
||||||
|
'0px 4px 8px 0px ' +
|
||||||
|
alpha(theme.palette.text.primary, 0.25),
|
||||||
|
borderRadius: '4px',
|
||||||
|
bgcolor: theme.palette.background.default,
|
||||||
|
})}
|
||||||
|
display={'none'}
|
||||||
|
zIndex={999}
|
||||||
|
>
|
||||||
|
{account.phone && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
fontSize: '12px',
|
||||||
|
lineHeight: '16px',
|
||||||
|
color: 'text.primary',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{account.phone}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Stack
|
||||||
|
direction={'row'}
|
||||||
|
width={'100%'}
|
||||||
|
justifyContent={'flex-start'}
|
||||||
|
flexWrap='wrap'
|
||||||
|
>
|
||||||
|
{footerSetting?.brand_groups?.map(group => (
|
||||||
|
<Stack
|
||||||
|
gap={1.5}
|
||||||
|
key={group.name}
|
||||||
|
sx={{
|
||||||
|
flex: '0 0 33.33%',
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: '22px',
|
||||||
|
minWidth: '100px',
|
||||||
|
'& a:hover': {
|
||||||
|
color: 'primary.main',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
fontSize: 16,
|
||||||
|
lineHeight: '24px',
|
||||||
|
mb: 1,
|
||||||
|
color: 'text.primary',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{group.name}
|
||||||
|
</Box>
|
||||||
|
{group.links?.map(link => (
|
||||||
|
<Box
|
||||||
|
sx={theme => ({
|
||||||
|
color: alpha(theme.palette.text.primary, 0.5),
|
||||||
|
})}
|
||||||
key={link.name}
|
key={link.name}
|
||||||
>
|
>
|
||||||
{link.name}
|
<Link
|
||||||
</Link>
|
href={link?.url || ''}
|
||||||
</Box>
|
target='_blank'
|
||||||
))}
|
key={link.name}
|
||||||
</Stack>
|
>
|
||||||
))}
|
{link.name}
|
||||||
|
</Link>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Box>
|
||||||
</Box>
|
)}
|
||||||
|
|
||||||
{!(
|
{!(
|
||||||
customStyle?.footer_show_intro === false &&
|
customStyle?.footer_show_intro === false &&
|
||||||
footerSetting?.brand_groups?.length === 0
|
footerSetting?.brand_groups?.length === 0
|
||||||
|
|
|
||||||
|
|
@ -94,196 +94,197 @@ const Footer = React.memo(
|
||||||
bgcolor: alpha(theme.palette.text.primary, 0.05),
|
bgcolor: alpha(theme.palette.text.primary, 0.05),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Box
|
{showBrand && (
|
||||||
pt={
|
<Box
|
||||||
customStyle?.footer_show_intro
|
pt={
|
||||||
? 5
|
customStyle?.footer_show_intro
|
||||||
: (footerSetting?.brand_groups?.length || 0) > 0
|
|
||||||
? 5
|
? 5
|
||||||
: 0
|
: (footerSetting?.brand_groups?.length || 0) > 0
|
||||||
}
|
? 5
|
||||||
>
|
: 0
|
||||||
{customStyle?.footer_show_intro !== false && (
|
}
|
||||||
<Box sx={{ mb: 3 }}>
|
>
|
||||||
<Stack direction={'row'} alignItems={'center'} gap={1}>
|
{customStyle?.footer_show_intro !== false && (
|
||||||
{footerSetting?.brand_logo && (
|
<Box sx={{ mb: 3 }}>
|
||||||
<img
|
<Stack direction={'row'} alignItems={'center'} gap={1}>
|
||||||
src={footerSetting.brand_logo}
|
{footerSetting?.brand_logo && (
|
||||||
alt='PandaWiki'
|
<img
|
||||||
height={24}
|
src={footerSetting.brand_logo}
|
||||||
/>
|
alt='PandaWiki'
|
||||||
)}
|
height={24}
|
||||||
<Box
|
/>
|
||||||
sx={{
|
)}
|
||||||
fontWeight: 'bold',
|
<Box
|
||||||
lineHeight: '32px',
|
sx={{
|
||||||
fontSize: 24,
|
fontWeight: 'bold',
|
||||||
color: 'text.primary',
|
lineHeight: '32px',
|
||||||
}}
|
fontSize: 24,
|
||||||
>
|
color: 'text.primary',
|
||||||
{footerSetting?.brand_name}
|
}}
|
||||||
</Box>
|
>
|
||||||
</Stack>
|
{footerSetting?.brand_name}
|
||||||
{footerSetting?.brand_desc && (
|
</Box>
|
||||||
<Box
|
</Stack>
|
||||||
sx={theme => ({
|
{footerSetting?.brand_desc && (
|
||||||
fontSize: 12,
|
|
||||||
lineHeight: '26px',
|
|
||||||
mt: 2,
|
|
||||||
color: alpha(theme.palette.text.primary, 0.7),
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{footerSetting.brand_desc}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<Stack direction={'column'} gap={2.5} mt={2}>
|
|
||||||
{customStyle?.social_media_accounts?.map(
|
|
||||||
(account, index) => {
|
|
||||||
return (
|
|
||||||
<Stack
|
|
||||||
direction={'row'}
|
|
||||||
key={index}
|
|
||||||
sx={theme => ({
|
|
||||||
position: 'relative',
|
|
||||||
color: alpha(theme.palette.text.primary, 0.7),
|
|
||||||
})}
|
|
||||||
gap={1}
|
|
||||||
onClick={() => {
|
|
||||||
setCurOverlayType(account.channel || '');
|
|
||||||
if (account.channel === 'phone') {
|
|
||||||
setPhoneData({
|
|
||||||
phone: account.phone || '',
|
|
||||||
text: account.text || '',
|
|
||||||
});
|
|
||||||
setOpen(true);
|
|
||||||
}
|
|
||||||
if (account.channel === 'wechat_oa') {
|
|
||||||
setWechatData({
|
|
||||||
src: account.icon || '',
|
|
||||||
text: account.text || '',
|
|
||||||
});
|
|
||||||
setOpen(true);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{account.channel === 'wechat_oa' && (
|
|
||||||
<IconWeixingongzhonghao
|
|
||||||
sx={{ fontSize: '20px', color: 'inherit' }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{account.channel === 'phone' && (
|
|
||||||
<IconDianhua
|
|
||||||
sx={{ fontSize: '20px', color: 'inherit' }}
|
|
||||||
></IconDianhua>
|
|
||||||
)}
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
lineHeight: '24px',
|
|
||||||
fontSize: '12px',
|
|
||||||
color: 'inherit',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{account.text}
|
|
||||||
</Box>
|
|
||||||
{account.channel === 'wechat_oa' &&
|
|
||||||
(account?.text || account?.icon) && (
|
|
||||||
<Stack
|
|
||||||
direction={'column'}
|
|
||||||
alignItems={'center'}
|
|
||||||
p={1.5}
|
|
||||||
sx={theme => ({
|
|
||||||
position: 'absolute',
|
|
||||||
top: '40px',
|
|
||||||
left: 0,
|
|
||||||
boxShadow:
|
|
||||||
' 0px 4px 8px 0px ' +
|
|
||||||
alpha(theme.palette.text.primary, 0.25),
|
|
||||||
borderRadius: '4px',
|
|
||||||
bgcolor: theme.palette.background.default,
|
|
||||||
})}
|
|
||||||
gap={1}
|
|
||||||
display={'none'}
|
|
||||||
zIndex={999}
|
|
||||||
>
|
|
||||||
{account.icon && (
|
|
||||||
<img
|
|
||||||
src={account.icon}
|
|
||||||
width={83}
|
|
||||||
height={83}
|
|
||||||
></img>
|
|
||||||
)}
|
|
||||||
{account.text && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
fontSize: '12px',
|
|
||||||
lineHeight: '16px',
|
|
||||||
color: 'text.primary',
|
|
||||||
maxWidth: '83px',
|
|
||||||
|
|
||||||
textAlign: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{account.text}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Stack direction={'row'} flexWrap={'wrap'} gap={2}>
|
|
||||||
{footerSetting?.brand_groups?.map((group, idx) => (
|
|
||||||
<Stack
|
|
||||||
gap={1}
|
|
||||||
key={group.name}
|
|
||||||
sx={{
|
|
||||||
fontSize: 14,
|
|
||||||
lineHeight: '22px',
|
|
||||||
width: 'calc(50% - 8px)',
|
|
||||||
...(idx > 1 && {
|
|
||||||
mt: 1,
|
|
||||||
}),
|
|
||||||
'& a:hover': {
|
|
||||||
color: 'primary.main',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
fontSize: 14,
|
|
||||||
lineHeight: '24px',
|
|
||||||
mb: 1,
|
|
||||||
color: 'text.primary',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{group.name}
|
|
||||||
</Box>
|
|
||||||
{group.links?.map(link => (
|
|
||||||
<Box
|
<Box
|
||||||
sx={theme => ({
|
sx={theme => ({
|
||||||
color: alpha(theme.palette.text.primary, 0.5),
|
fontSize: 12,
|
||||||
|
lineHeight: '26px',
|
||||||
|
mt: 2,
|
||||||
|
color: alpha(theme.palette.text.primary, 0.7),
|
||||||
})}
|
})}
|
||||||
key={link.name}
|
|
||||||
>
|
>
|
||||||
<Link
|
{footerSetting.brand_desc}
|
||||||
href={link?.url || ''}
|
</Box>
|
||||||
target='_blank'
|
)}
|
||||||
|
<Stack direction={'column'} gap={2.5} mt={2}>
|
||||||
|
{customStyle?.social_media_accounts?.map(
|
||||||
|
(account, index) => {
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
direction={'row'}
|
||||||
|
key={index}
|
||||||
|
sx={theme => ({
|
||||||
|
position: 'relative',
|
||||||
|
color: alpha(theme.palette.text.primary, 0.7),
|
||||||
|
})}
|
||||||
|
gap={1}
|
||||||
|
onClick={() => {
|
||||||
|
setCurOverlayType(account.channel || '');
|
||||||
|
if (account.channel === 'phone') {
|
||||||
|
setPhoneData({
|
||||||
|
phone: account.phone || '',
|
||||||
|
text: account.text || '',
|
||||||
|
});
|
||||||
|
setOpen(true);
|
||||||
|
}
|
||||||
|
if (account.channel === 'wechat_oa') {
|
||||||
|
setWechatData({
|
||||||
|
src: account.icon || '',
|
||||||
|
text: account.text || '',
|
||||||
|
});
|
||||||
|
setOpen(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{account.channel === 'wechat_oa' && (
|
||||||
|
<IconWeixingongzhonghao
|
||||||
|
sx={{ fontSize: '20px', color: 'inherit' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{account.channel === 'phone' && (
|
||||||
|
<IconDianhua
|
||||||
|
sx={{ fontSize: '20px', color: 'inherit' }}
|
||||||
|
></IconDianhua>
|
||||||
|
)}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
lineHeight: '24px',
|
||||||
|
fontSize: '12px',
|
||||||
|
color: 'inherit',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{account.text}
|
||||||
|
</Box>
|
||||||
|
{account.channel === 'wechat_oa' &&
|
||||||
|
(account?.text || account?.icon) && (
|
||||||
|
<Stack
|
||||||
|
direction={'column'}
|
||||||
|
alignItems={'center'}
|
||||||
|
p={1.5}
|
||||||
|
sx={theme => ({
|
||||||
|
position: 'absolute',
|
||||||
|
top: '40px',
|
||||||
|
left: 0,
|
||||||
|
boxShadow:
|
||||||
|
' 0px 4px 8px 0px ' +
|
||||||
|
alpha(theme.palette.text.primary, 0.25),
|
||||||
|
borderRadius: '4px',
|
||||||
|
bgcolor: theme.palette.background.default,
|
||||||
|
})}
|
||||||
|
gap={1}
|
||||||
|
display={'none'}
|
||||||
|
zIndex={999}
|
||||||
|
>
|
||||||
|
{account.icon && (
|
||||||
|
<img
|
||||||
|
src={account.icon}
|
||||||
|
width={83}
|
||||||
|
height={83}
|
||||||
|
></img>
|
||||||
|
)}
|
||||||
|
{account.text && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
fontSize: '12px',
|
||||||
|
lineHeight: '16px',
|
||||||
|
color: 'text.primary',
|
||||||
|
maxWidth: '83px',
|
||||||
|
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{account.text}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Stack direction={'row'} flexWrap={'wrap'} gap={2}>
|
||||||
|
{footerSetting?.brand_groups?.map((group, idx) => (
|
||||||
|
<Stack
|
||||||
|
gap={1}
|
||||||
|
key={group.name}
|
||||||
|
sx={{
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: '22px',
|
||||||
|
width: 'calc(50% - 8px)',
|
||||||
|
...(idx > 1 && {
|
||||||
|
mt: 1,
|
||||||
|
}),
|
||||||
|
'& a:hover': {
|
||||||
|
color: 'primary.main',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: '24px',
|
||||||
|
mb: 1,
|
||||||
|
color: 'text.primary',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{group.name}
|
||||||
|
</Box>
|
||||||
|
{group.links?.map(link => (
|
||||||
|
<Box
|
||||||
|
sx={theme => ({
|
||||||
|
color: alpha(theme.palette.text.primary, 0.5),
|
||||||
|
})}
|
||||||
key={link.name}
|
key={link.name}
|
||||||
>
|
>
|
||||||
{link.name}
|
<Link
|
||||||
</Link>
|
href={link?.url || ''}
|
||||||
</Box>
|
target='_blank'
|
||||||
))}
|
key={link.name}
|
||||||
</Stack>
|
>
|
||||||
))}
|
{link.name}
|
||||||
</Stack>
|
</Link>
|
||||||
</Box>
|
</Box>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
{!(
|
{!(
|
||||||
customStyle?.footer_show_intro === false &&
|
customStyle?.footer_show_intro === false &&
|
||||||
footerSetting?.brand_groups?.length === 0
|
footerSetting?.brand_groups?.length === 0
|
||||||
|
|
@ -455,237 +456,239 @@ const Footer = React.memo(
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
{showBrand && (
|
||||||
py={
|
<Box
|
||||||
customStyle?.footer_show_intro
|
py={
|
||||||
? 6
|
customStyle?.footer_show_intro
|
||||||
: (footerSetting?.brand_groups?.length || 0) > 0
|
|
||||||
? 6
|
? 6
|
||||||
: 0
|
: (footerSetting?.brand_groups?.length || 0) > 0
|
||||||
}
|
? 6
|
||||||
>
|
: 0
|
||||||
<Stack
|
|
||||||
direction={'row'}
|
|
||||||
gap={10}
|
|
||||||
justifyContent={
|
|
||||||
customStyle?.footer_show_intro === false
|
|
||||||
? 'center'
|
|
||||||
: 'flex-start'
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{customStyle?.footer_show_intro !== false && (
|
|
||||||
<Stack
|
|
||||||
direction={'column'}
|
|
||||||
sx={{ width: '30%', minWidth: 200 }}
|
|
||||||
gap={3}
|
|
||||||
>
|
|
||||||
<Stack direction={'row'} alignItems={'center'} gap={1}>
|
|
||||||
{footerSetting?.brand_logo && (
|
|
||||||
<img
|
|
||||||
src={footerSetting.brand_logo}
|
|
||||||
alt='PandaWiki'
|
|
||||||
height={36}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
fontWeight: 'bold',
|
|
||||||
lineHeight: '32px',
|
|
||||||
fontSize: 24,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{footerSetting?.brand_name}
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
{footerSetting?.brand_desc && (
|
|
||||||
<Box
|
|
||||||
sx={theme => ({
|
|
||||||
fontSize: 14,
|
|
||||||
lineHeight: '26px',
|
|
||||||
color: alpha(theme.palette.text.primary, 0.7),
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{footerSetting.brand_desc}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<Stack direction={'column'} gap={'26px'}>
|
|
||||||
{customStyle?.social_media_accounts?.map(
|
|
||||||
(account, index) => {
|
|
||||||
return (
|
|
||||||
<Stack
|
|
||||||
direction={'row'}
|
|
||||||
key={index}
|
|
||||||
alignItems='center'
|
|
||||||
sx={theme => ({
|
|
||||||
position: 'relative',
|
|
||||||
'&:hover': {
|
|
||||||
color: theme.palette.primary.main,
|
|
||||||
},
|
|
||||||
'&:hover .popup': {
|
|
||||||
display: 'flex !important',
|
|
||||||
},
|
|
||||||
color: alpha(theme.palette.text.primary, 0.7),
|
|
||||||
cursor: 'default',
|
|
||||||
})}
|
|
||||||
gap={1}
|
|
||||||
>
|
|
||||||
{account.channel === 'wechat_oa' && (
|
|
||||||
<IconWeixingongzhonghao
|
|
||||||
sx={{ fontSize: '18px', color: 'inherit' }}
|
|
||||||
></IconWeixingongzhonghao>
|
|
||||||
)}
|
|
||||||
{account.channel === 'phone' && (
|
|
||||||
<IconDianhua
|
|
||||||
sx={{ fontSize: '16px', color: 'inherit' }}
|
|
||||||
></IconDianhua>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
lineHeight: '24px',
|
|
||||||
fontSize: '14px',
|
|
||||||
color: 'inherit',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{account.text}
|
|
||||||
</Box>
|
|
||||||
{account.channel === 'wechat_oa' &&
|
|
||||||
(account?.text || account?.icon) && (
|
|
||||||
<Stack
|
|
||||||
className={'popup'}
|
|
||||||
direction={'column'}
|
|
||||||
alignItems={'center'}
|
|
||||||
p={1.5}
|
|
||||||
sx={theme => ({
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: '100%',
|
|
||||||
transform: 'translateY(-10px)',
|
|
||||||
left: 0,
|
|
||||||
boxShadow:
|
|
||||||
' 0px 4px 8px 0px ' +
|
|
||||||
alpha(theme.palette.text.primary, 0.25),
|
|
||||||
borderRadius: '4px',
|
|
||||||
bgcolor: theme.palette.background.default,
|
|
||||||
})}
|
|
||||||
gap={1}
|
|
||||||
display={'none'}
|
|
||||||
zIndex={999}
|
|
||||||
>
|
|
||||||
{account.icon && (
|
|
||||||
<img
|
|
||||||
src={account.icon}
|
|
||||||
width={120}
|
|
||||||
height={120}
|
|
||||||
></img>
|
|
||||||
)}
|
|
||||||
{account.text && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
fontSize: '12px',
|
|
||||||
lineHeight: '16px',
|
|
||||||
color: 'text.primary',
|
|
||||||
maxWidth: '120px',
|
|
||||||
textAlign: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{account.text}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
{account.channel === 'phone' && account?.phone && (
|
|
||||||
<Stack
|
|
||||||
className={'popup'}
|
|
||||||
px={1.5}
|
|
||||||
py={1}
|
|
||||||
sx={theme => ({
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: '100%',
|
|
||||||
transform: 'translateY(-10px)',
|
|
||||||
left: 0,
|
|
||||||
boxShadow:
|
|
||||||
'0px 4px 8px 0px ' +
|
|
||||||
alpha(theme.palette.text.primary, 0.25),
|
|
||||||
borderRadius: '4px',
|
|
||||||
bgcolor: theme.palette.background.default,
|
|
||||||
})}
|
|
||||||
display={'none'}
|
|
||||||
zIndex={999}
|
|
||||||
>
|
|
||||||
{account.phone && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
fontSize: '14px',
|
|
||||||
lineHeight: '16px',
|
|
||||||
color: 'text.primary',
|
|
||||||
textAlign: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{account.phone}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Stack
|
<Stack
|
||||||
direction={'row'}
|
direction={'row'}
|
||||||
width={'100%'}
|
gap={10}
|
||||||
justifyContent={'flex-start'}
|
justifyContent={
|
||||||
flexWrap='wrap'
|
customStyle?.footer_show_intro === false
|
||||||
|
? 'center'
|
||||||
|
: 'flex-start'
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{footerSetting?.brand_groups?.map(group => (
|
{customStyle?.footer_show_intro !== false && (
|
||||||
<Stack
|
<Stack
|
||||||
gap={1.5}
|
direction={'column'}
|
||||||
key={group.name}
|
sx={{ width: '30%', minWidth: 200 }}
|
||||||
sx={{
|
gap={3}
|
||||||
flex: '0 0 33.33%',
|
|
||||||
fontSize: 14,
|
|
||||||
lineHeight: '22px',
|
|
||||||
minWidth: '100px',
|
|
||||||
'& a:hover': {
|
|
||||||
color: 'primary.main',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Box
|
<Stack direction={'row'} alignItems={'center'} gap={1}>
|
||||||
sx={{
|
{footerSetting?.brand_logo && (
|
||||||
fontSize: 16,
|
<img
|
||||||
lineHeight: '24px',
|
src={footerSetting.brand_logo}
|
||||||
mb: 1,
|
alt='PandaWiki'
|
||||||
}}
|
height={36}
|
||||||
>
|
/>
|
||||||
{group.name}
|
)}
|
||||||
</Box>
|
<Box
|
||||||
{group.links?.map(link => (
|
sx={{
|
||||||
|
fontWeight: 'bold',
|
||||||
|
lineHeight: '32px',
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{footerSetting?.brand_name}
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{footerSetting?.brand_desc && (
|
||||||
<Box
|
<Box
|
||||||
sx={theme => ({
|
sx={theme => ({
|
||||||
color: alpha(theme.palette.text.primary, 0.5),
|
fontSize: 14,
|
||||||
|
lineHeight: '26px',
|
||||||
|
color: alpha(theme.palette.text.primary, 0.7),
|
||||||
})}
|
})}
|
||||||
key={link.name}
|
|
||||||
>
|
>
|
||||||
<Link
|
{footerSetting.brand_desc}
|
||||||
href={link?.url || ''}
|
</Box>
|
||||||
target='_blank'
|
)}
|
||||||
|
<Stack direction={'column'} gap={'26px'}>
|
||||||
|
{customStyle?.social_media_accounts?.map(
|
||||||
|
(account, index) => {
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
direction={'row'}
|
||||||
|
key={index}
|
||||||
|
alignItems='center'
|
||||||
|
sx={theme => ({
|
||||||
|
position: 'relative',
|
||||||
|
'&:hover': {
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
},
|
||||||
|
'&:hover .popup': {
|
||||||
|
display: 'flex !important',
|
||||||
|
},
|
||||||
|
color: alpha(theme.palette.text.primary, 0.7),
|
||||||
|
cursor: 'default',
|
||||||
|
})}
|
||||||
|
gap={1}
|
||||||
|
>
|
||||||
|
{account.channel === 'wechat_oa' && (
|
||||||
|
<IconWeixingongzhonghao
|
||||||
|
sx={{ fontSize: '18px', color: 'inherit' }}
|
||||||
|
></IconWeixingongzhonghao>
|
||||||
|
)}
|
||||||
|
{account.channel === 'phone' && (
|
||||||
|
<IconDianhua
|
||||||
|
sx={{ fontSize: '16px', color: 'inherit' }}
|
||||||
|
></IconDianhua>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
lineHeight: '24px',
|
||||||
|
fontSize: '14px',
|
||||||
|
color: 'inherit',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{account.text}
|
||||||
|
</Box>
|
||||||
|
{account.channel === 'wechat_oa' &&
|
||||||
|
(account?.text || account?.icon) && (
|
||||||
|
<Stack
|
||||||
|
className={'popup'}
|
||||||
|
direction={'column'}
|
||||||
|
alignItems={'center'}
|
||||||
|
p={1.5}
|
||||||
|
sx={theme => ({
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '100%',
|
||||||
|
transform: 'translateY(-10px)',
|
||||||
|
left: 0,
|
||||||
|
boxShadow:
|
||||||
|
' 0px 4px 8px 0px ' +
|
||||||
|
alpha(theme.palette.text.primary, 0.25),
|
||||||
|
borderRadius: '4px',
|
||||||
|
bgcolor: theme.palette.background.default,
|
||||||
|
})}
|
||||||
|
gap={1}
|
||||||
|
display={'none'}
|
||||||
|
zIndex={999}
|
||||||
|
>
|
||||||
|
{account.icon && (
|
||||||
|
<img
|
||||||
|
src={account.icon}
|
||||||
|
width={120}
|
||||||
|
height={120}
|
||||||
|
></img>
|
||||||
|
)}
|
||||||
|
{account.text && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
fontSize: '12px',
|
||||||
|
lineHeight: '16px',
|
||||||
|
color: 'text.primary',
|
||||||
|
maxWidth: '120px',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{account.text}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{account.channel === 'phone' &&
|
||||||
|
account?.phone && (
|
||||||
|
<Stack
|
||||||
|
className={'popup'}
|
||||||
|
px={1.5}
|
||||||
|
py={1}
|
||||||
|
sx={theme => ({
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '100%',
|
||||||
|
transform: 'translateY(-10px)',
|
||||||
|
left: 0,
|
||||||
|
boxShadow:
|
||||||
|
'0px 4px 8px 0px ' +
|
||||||
|
alpha(theme.palette.text.primary, 0.25),
|
||||||
|
borderRadius: '4px',
|
||||||
|
bgcolor: theme.palette.background.default,
|
||||||
|
})}
|
||||||
|
display={'none'}
|
||||||
|
zIndex={999}
|
||||||
|
>
|
||||||
|
{account.phone && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
fontSize: '14px',
|
||||||
|
lineHeight: '16px',
|
||||||
|
color: 'text.primary',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{account.phone}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Stack
|
||||||
|
direction={'row'}
|
||||||
|
width={'100%'}
|
||||||
|
justifyContent={'flex-start'}
|
||||||
|
flexWrap='wrap'
|
||||||
|
>
|
||||||
|
{footerSetting?.brand_groups?.map(group => (
|
||||||
|
<Stack
|
||||||
|
gap={1.5}
|
||||||
|
key={group.name}
|
||||||
|
sx={{
|
||||||
|
flex: '0 0 33.33%',
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: '22px',
|
||||||
|
minWidth: '100px',
|
||||||
|
'& a:hover': {
|
||||||
|
color: 'primary.main',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
fontSize: 16,
|
||||||
|
lineHeight: '24px',
|
||||||
|
mb: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{group.name}
|
||||||
|
</Box>
|
||||||
|
{group.links?.map(link => (
|
||||||
|
<Box
|
||||||
|
sx={theme => ({
|
||||||
|
color: alpha(theme.palette.text.primary, 0.5),
|
||||||
|
})}
|
||||||
key={link.name}
|
key={link.name}
|
||||||
>
|
>
|
||||||
{link.name}
|
<Link
|
||||||
</Link>
|
href={link?.url || ''}
|
||||||
</Box>
|
target='_blank'
|
||||||
))}
|
key={link.name}
|
||||||
</Stack>
|
>
|
||||||
))}
|
{link.name}
|
||||||
|
</Link>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Box>
|
||||||
</Box>
|
)}
|
||||||
|
|
||||||
{!(
|
{!(
|
||||||
customStyle?.footer_show_intro === false &&
|
customStyle?.footer_show_intro === false &&
|
||||||
footerSetting?.brand_groups?.length === 0
|
footerSetting?.brand_groups?.length === 0
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,9 @@ importers:
|
||||||
html-to-image:
|
html-to-image:
|
||||||
specifier: ^1.11.13
|
specifier: ^1.11.13
|
||||||
version: 1.11.13
|
version: 1.11.13
|
||||||
|
import-in-the-middle:
|
||||||
|
specifier: ^1.14.2
|
||||||
|
version: 1.15.0
|
||||||
js-cookie:
|
js-cookie:
|
||||||
specifier: ^3.0.5
|
specifier: ^3.0.5
|
||||||
version: 3.0.5
|
version: 3.0.5
|
||||||
|
|
@ -310,6 +313,9 @@ importers:
|
||||||
remark-math:
|
remark-math:
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.0.0
|
version: 6.0.0
|
||||||
|
require-in-the-middle:
|
||||||
|
specifier: ^7.5.2
|
||||||
|
version: 7.5.2
|
||||||
uuid:
|
uuid:
|
||||||
specifier: ^11.1.0
|
specifier: ^11.1.0
|
||||||
version: 11.1.0
|
version: 11.1.0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue