first commit
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
import React from 'react';
|
||||
import {debounce} from 'lodash';
|
||||
import {Button, Col, ColorPicker, Divider, Drawer, InputNumber, Row, Select, Switch, theme, Tooltip} from 'antd';
|
||||
import useGlobalStore from "@/stores/global";
|
||||
import {configTheme, darkColorTheme, defaultColorTheme} from "@/layout/theme.ts";
|
||||
import {algorithmOptions} from "@/layout/algorithm.ts";
|
||||
|
||||
import {useTranslation} from 'react-i18next';
|
||||
|
||||
const {useToken} = theme;
|
||||
|
||||
|
||||
// 主题配置项
|
||||
const THEME_CONFIGS = [
|
||||
{key: 'colorPrimary', label: 'layout.colorPrimary'},
|
||||
{key: 'colorText', label: 'layout.colorText'},
|
||||
{key: 'colorBg', label: 'layout.colorBg'},
|
||||
{key: 'colorSuccess', label: 'layout.colorSuccess'},
|
||||
{key: 'colorWarning', label: 'layout.colorWarning'},
|
||||
{key: 'colorError', label: 'layout.colorError'},
|
||||
{key: 'bodyBg', label: 'layout.bodyBg'},
|
||||
{key: 'footerBg', label: 'layout.footerBg'},
|
||||
{key: 'headerBg', label: 'layout.headerBg'},
|
||||
{key: 'headerColor', label: 'layout.headerColor'},
|
||||
{key: 'siderBg', label: 'layout.siderBg'},
|
||||
{key: 'siderColor', label: 'layout.siderColor'},
|
||||
{key: 'colorBorder', label: 'layout.colorBorder'},
|
||||
];
|
||||
|
||||
// 风格配置项
|
||||
const STYLE_CONFIGS = [
|
||||
{key: 'fixedFooter', label: 'layout.fixedFooter', type: 'switch'},
|
||||
{key: 'borderRadius', label: 'layout.borderRadius', type: 'number', min: 0, max: 30},
|
||||
{key: 'controlHeight', label: 'layout.controlHeight', type: 'number', min: 0},
|
||||
{key: 'headerPadding', label: 'layout.headerPadding', type: 'number', min: 0},
|
||||
{key: 'headerHeight', label: 'layout.headerHeight', type: 'number', min: 0},
|
||||
{key: 'siderWeight', label: 'layout.siderWeight', type: 'number', min: 0},
|
||||
{key: 'bodyPadding', label: 'layout.bodyPadding', type: 'number', min: 0},
|
||||
];
|
||||
|
||||
// 预设主题列表
|
||||
const THEME_LIST = [
|
||||
{background: '/static/theme/default.svg', name: 'light', title: 'layout.themeLight'},
|
||||
{background: '/static/theme/dark.svg', name: 'dark', title: 'layout.themeDark'},
|
||||
];
|
||||
|
||||
// 布局配置
|
||||
const LAYOUT_CONFIGS = [
|
||||
{
|
||||
key: 'side', title: 'layout.layoutSide', render: (token: any) => (
|
||||
<>
|
||||
<div className="rounded-sm w-full h-6 mb-1.5" style={{background: token.colorPrimaryBorder}}/>
|
||||
<div className="flex">
|
||||
<div className="rounded-sm h-16 w-6" style={{background: token.colorPrimary}}/>
|
||||
<div className="rounded-sm flex-1 ml-1.5" style={{background: token.colorPrimaryBg}}/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'top', title: 'layout.layoutTop', render: (token: any) => (
|
||||
<>
|
||||
<div className="rounded-sm h-6 w-full mb-1.5" style={{background: token.colorPrimary}}/>
|
||||
<div className="rounded-sm h-16" style={{background: token.colorPrimaryBg}}/>
|
||||
</>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'mix', title: 'layout.layoutMix', render: (token: any) => (
|
||||
<>
|
||||
<div className="rounded-sm w-full h-6 mb-1.5" style={{background: token.colorPrimary}}/>
|
||||
<div className="flex">
|
||||
<div className="rounded-sm h-16 w-6" style={{background: token.colorPrimary}}/>
|
||||
<div className="rounded-sm flex-1 ml-1.5" style={{background: token.colorPrimaryBg}}/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'columns', title: 'layout.layoutColumns', render: (token: any) => (
|
||||
<div className="flex">
|
||||
<div className="rounded-sm mr-1.5 w-3 h-24" style={{background: token.colorPrimary}}/>
|
||||
<div className="rounded-sm mr-1.5 w-6 h-24" style={{background: token.colorPrimaryHover}}/>
|
||||
<div className="rounded-sm flex-auto h-24" style={{background: token.colorPrimaryBg}}/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
];
|
||||
|
||||
const SettingDrawer: React.FC = () => {
|
||||
const {t} = useTranslation();
|
||||
const {token} = useToken();
|
||||
const themeDrawer = useGlobalStore(state => state.themeDrawer);
|
||||
const setThemeDrawer = useGlobalStore(state => state.setThemeDrawer);
|
||||
const setThemeConfig = useGlobalStore(state => state.setThemeConfig);
|
||||
const themeConfig = useGlobalStore(state => state.themeConfig);
|
||||
const layout = useGlobalStore(state => state.layout);
|
||||
const setLayout = useGlobalStore(state => state.setLayout);
|
||||
|
||||
// 翻译后的算法选项
|
||||
const translatedAlgorithmOptions = algorithmOptions?.map(option => ({
|
||||
...option,
|
||||
label: t(option.label as string)
|
||||
})) || [];
|
||||
|
||||
// 防抖更新主题配置(带过渡动画)
|
||||
const changeSetting = debounce((key: string, value: any) => {
|
||||
setThemeConfig({...themeConfig, [key]: value});
|
||||
}, 400, {leading: true, trailing: false});
|
||||
|
||||
// 处理主题切换(带圆形扩散动画)
|
||||
const handleThemeChange = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const target = (e.target as HTMLElement).closest('[data-theme]');
|
||||
if (!target) return;
|
||||
|
||||
const themeName = (target as HTMLElement).dataset.theme;
|
||||
const themeMap = {
|
||||
dark: darkColorTheme,
|
||||
light: defaultColorTheme
|
||||
};
|
||||
|
||||
if (themeMap[themeName as keyof typeof themeMap]) {
|
||||
setThemeConfig({...themeConfig, ...themeMap[themeName as keyof typeof themeMap]});
|
||||
}
|
||||
};
|
||||
|
||||
// 重置主题(带过渡动画)
|
||||
const resetTheme = () => {
|
||||
setThemeConfig({...configTheme, ...defaultColorTheme});
|
||||
};
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
styles={{body: {paddingTop: 10}}}
|
||||
placement="right"
|
||||
closable={false}
|
||||
onClose={() => setThemeDrawer(false)}
|
||||
open={themeDrawer}
|
||||
footer={(
|
||||
<div className="flex w-full justify-between">
|
||||
<Button onClick={resetTheme}>
|
||||
{t('layout.resetTheme')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{/* 布局样式 */}
|
||||
<Divider>{t('layout.layoutStyle')}</Divider>
|
||||
<Row gutter={[20, 20]}>
|
||||
{LAYOUT_CONFIGS.map(({key, title, render}) => (
|
||||
<Col span={12} key={key}>
|
||||
<Tooltip title={t(title)}>
|
||||
<div
|
||||
className="p-2 rounded-lg cursor-pointer"
|
||||
style={{
|
||||
boxShadow: token.boxShadow,
|
||||
borderRadius: token.borderRadius,
|
||||
border: layout === key ? `2px solid ${token.colorPrimary}` : '2px solid transparent',
|
||||
}}
|
||||
onClick={() => setLayout(key as any)}
|
||||
>
|
||||
{render(token)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
|
||||
{/* 预设主题 */}
|
||||
<Divider>{t('layout.presetTheme')}</Divider>
|
||||
<Row gutter={20} onClick={handleThemeChange}>
|
||||
{THEME_LIST.map((item) => (
|
||||
<Col span={8} key={item.name} className="mb-2.5">
|
||||
<div
|
||||
data-theme={item.name}
|
||||
className="cursor-pointer overflow-hidden border-solid"
|
||||
style={{
|
||||
borderRadius: token.borderRadius,
|
||||
borderWidth: themeConfig.themeScheme === item.name ? '2px' : '0px',
|
||||
borderColor: themeConfig.themeScheme === item.name ? token.colorPrimary : 'transparent'
|
||||
}}
|
||||
>
|
||||
<img src={item.background} alt={item.name}/>
|
||||
</div>
|
||||
<div className="text-center mt-1.5">{t(item.title)}</div>
|
||||
</Col>
|
||||
))}
|
||||
<Col span={24} className="mb-2.5">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>{t('layout.themeAlgorithm')}</div>
|
||||
<Select
|
||||
value={themeConfig.algorithm}
|
||||
style={{ width: 160 }}
|
||||
onChange={(value) => changeSetting('algorithm', value)}
|
||||
options={translatedAlgorithmOptions}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* 主题颜色 */}
|
||||
<Divider>{t('layout.themeColor')}</Divider>
|
||||
{THEME_CONFIGS.map(({key, label}) => (
|
||||
<div key={key} className="flex justify-between items-center mb-2.5">
|
||||
<div>{t(label)}</div>
|
||||
<ColorPicker
|
||||
showText
|
||||
value={themeConfig[key as keyof typeof themeConfig] as string}
|
||||
onChange={(value) => changeSetting(key, value.toCssString())}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* 风格配置 */}
|
||||
<Divider>{t('layout.styleConfig')}</Divider>
|
||||
{STYLE_CONFIGS.map(({key, label, type, ...rest}) => (
|
||||
<div key={key} className="flex justify-between items-center mb-2.5">
|
||||
<div>{t(label)}</div>
|
||||
{type === 'switch' ? (
|
||||
<Switch
|
||||
value={themeConfig[key as keyof typeof themeConfig] as boolean}
|
||||
onChange={(value) => changeSetting(key, value)}
|
||||
/>
|
||||
) : (
|
||||
<InputNumber
|
||||
value={themeConfig[key as keyof typeof themeConfig] as number}
|
||||
onChange={(value) => changeSetting(key, value)}
|
||||
{...rest}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingDrawer;
|
||||
Reference in New Issue
Block a user