🎨 chore(web): apply ESLint and Prettier auto-fixes (baseline)
- Ran: bun run eslint:fix && bun run lint:fix - Inserted AGPL license header via eslint-plugin-header - Enforced no-multiple-empty-lines and other lint rules - Formatted code using Prettier v3 (@so1ve/prettier-config) - No functional changes; formatting-only baseline across JS/JSX files
This commit is contained in:
@@ -29,19 +29,13 @@ import {
|
||||
Avatar,
|
||||
Modal,
|
||||
Tag,
|
||||
Switch
|
||||
Switch,
|
||||
} from '@douyinfe/semi-ui';
|
||||
import {
|
||||
IllustrationNoResult,
|
||||
IllustrationNoResultDark
|
||||
IllustrationNoResultDark,
|
||||
} from '@douyinfe/semi-illustrations';
|
||||
import {
|
||||
Plus,
|
||||
Edit,
|
||||
Trash2,
|
||||
Save,
|
||||
Settings
|
||||
} from 'lucide-react';
|
||||
import { Plus, Edit, Trash2, Save, Settings } from 'lucide-react';
|
||||
import { API, showError, showSuccess } from '../../../helpers';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -62,7 +56,7 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
url: '',
|
||||
description: '',
|
||||
route: '',
|
||||
color: 'blue'
|
||||
color: 'blue',
|
||||
});
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
@@ -87,7 +81,7 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
{ value: 'light-blue', label: 'light-blue' },
|
||||
{ value: 'indigo', label: 'indigo' },
|
||||
{ value: 'violet', label: 'violet' },
|
||||
{ value: 'grey', label: 'grey' }
|
||||
{ value: 'grey', label: 'grey' },
|
||||
];
|
||||
|
||||
const updateOption = async (key, value) => {
|
||||
@@ -124,7 +118,7 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
url: '',
|
||||
description: '',
|
||||
route: '',
|
||||
color: 'blue'
|
||||
color: 'blue',
|
||||
});
|
||||
setShowApiModal(true);
|
||||
};
|
||||
@@ -135,7 +129,7 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
url: api.url,
|
||||
description: api.description,
|
||||
route: api.route,
|
||||
color: api.color
|
||||
color: api.color,
|
||||
});
|
||||
setShowApiModal(true);
|
||||
};
|
||||
@@ -147,7 +141,7 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
|
||||
const confirmDeleteApi = () => {
|
||||
if (deletingApi) {
|
||||
const newList = apiInfoList.filter(api => api.id !== deletingApi.id);
|
||||
const newList = apiInfoList.filter((api) => api.id !== deletingApi.id);
|
||||
setApiInfoList(newList);
|
||||
setHasChanges(true);
|
||||
showSuccess('API信息已删除,请及时点击“保存设置”进行保存');
|
||||
@@ -167,16 +161,14 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
|
||||
let newList;
|
||||
if (editingApi) {
|
||||
newList = apiInfoList.map(api =>
|
||||
api.id === editingApi.id
|
||||
? { ...api, ...apiForm }
|
||||
: api
|
||||
newList = apiInfoList.map((api) =>
|
||||
api.id === editingApi.id ? { ...api, ...apiForm } : api,
|
||||
);
|
||||
} else {
|
||||
const newId = Math.max(...apiInfoList.map(api => api.id), 0) + 1;
|
||||
const newId = Math.max(...apiInfoList.map((api) => api.id), 0) + 1;
|
||||
const newApi = {
|
||||
id: newId,
|
||||
...apiForm
|
||||
...apiForm,
|
||||
};
|
||||
newList = [...apiInfoList, newApi];
|
||||
}
|
||||
@@ -184,7 +176,11 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
setApiInfoList(newList);
|
||||
setHasChanges(true);
|
||||
setShowApiModal(false);
|
||||
showSuccess(editingApi ? 'API信息已更新,请及时点击“保存设置”进行保存' : 'API信息已添加,请及时点击“保存设置”进行保存');
|
||||
showSuccess(
|
||||
editingApi
|
||||
? 'API信息已更新,请及时点击“保存设置”进行保存'
|
||||
: 'API信息已添加,请及时点击“保存设置”进行保存',
|
||||
);
|
||||
} catch (error) {
|
||||
showError('操作失败: ' + error.message);
|
||||
} finally {
|
||||
@@ -216,7 +212,11 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
|
||||
useEffect(() => {
|
||||
const enabledStr = options['console_setting.api_info_enabled'];
|
||||
setPanelEnabled(enabledStr === undefined ? true : enabledStr === 'true' || enabledStr === true);
|
||||
setPanelEnabled(
|
||||
enabledStr === undefined
|
||||
? true
|
||||
: enabledStr === 'true' || enabledStr === true,
|
||||
);
|
||||
}, [options['console_setting.api_info_enabled']]);
|
||||
|
||||
const handleToggleEnabled = async (checked) => {
|
||||
@@ -247,11 +247,7 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
title: t('API地址'),
|
||||
dataIndex: 'url',
|
||||
render: (text, record) => (
|
||||
<Tag
|
||||
color={record.color}
|
||||
shape='circle'
|
||||
style={{ maxWidth: '280px' }}
|
||||
>
|
||||
<Tag color={record.color} shape='circle' style={{ maxWidth: '280px' }}>
|
||||
{text}
|
||||
</Tag>
|
||||
),
|
||||
@@ -259,31 +255,18 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
{
|
||||
title: t('线路描述'),
|
||||
dataIndex: 'route',
|
||||
render: (text, record) => (
|
||||
<Tag shape='circle'>
|
||||
{text}
|
||||
</Tag>
|
||||
),
|
||||
render: (text, record) => <Tag shape='circle'>{text}</Tag>,
|
||||
},
|
||||
{
|
||||
title: t('说明'),
|
||||
dataIndex: 'description',
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<Tag shape='circle'>
|
||||
{text || '-'}
|
||||
</Tag>
|
||||
),
|
||||
render: (text, record) => <Tag shape='circle'>{text || '-'}</Tag>,
|
||||
},
|
||||
{
|
||||
title: t('颜色'),
|
||||
dataIndex: 'color',
|
||||
render: (color) => (
|
||||
<Avatar
|
||||
size="extra-extra-small"
|
||||
color={color}
|
||||
/>
|
||||
),
|
||||
render: (color) => <Avatar size='extra-extra-small' color={color} />,
|
||||
},
|
||||
{
|
||||
title: t('操作'),
|
||||
@@ -320,31 +303,39 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const newList = apiInfoList.filter(api => !selectedRowKeys.includes(api.id));
|
||||
const newList = apiInfoList.filter(
|
||||
(api) => !selectedRowKeys.includes(api.id),
|
||||
);
|
||||
setApiInfoList(newList);
|
||||
setSelectedRowKeys([]);
|
||||
setHasChanges(true);
|
||||
showSuccess(`已删除 ${selectedRowKeys.length} 个API信息,请及时点击“保存设置”进行保存`);
|
||||
showSuccess(
|
||||
`已删除 ${selectedRowKeys.length} 个API信息,请及时点击“保存设置”进行保存`,
|
||||
);
|
||||
};
|
||||
|
||||
const renderHeader = () => (
|
||||
<div className="flex flex-col w-full">
|
||||
<div className="mb-2">
|
||||
<div className="flex items-center text-blue-500">
|
||||
<Settings size={16} className="mr-2" />
|
||||
<Text>{t('API信息管理,可以配置多个API地址用于状态展示和负载均衡(最多50个)')}</Text>
|
||||
<div className='flex flex-col w-full'>
|
||||
<div className='mb-2'>
|
||||
<div className='flex items-center text-blue-500'>
|
||||
<Settings size={16} className='mr-2' />
|
||||
<Text>
|
||||
{t(
|
||||
'API信息管理,可以配置多个API地址用于状态展示和负载均衡(最多50个)',
|
||||
)}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider margin="12px" />
|
||||
<Divider margin='12px' />
|
||||
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-4 w-full">
|
||||
<div className="flex gap-2 w-full md:w-auto order-2 md:order-1">
|
||||
<div className='flex flex-col md:flex-row justify-between items-center gap-4 w-full'>
|
||||
<div className='flex gap-2 w-full md:w-auto order-2 md:order-1'>
|
||||
<Button
|
||||
theme='light'
|
||||
type='primary'
|
||||
icon={<Plus size={14} />}
|
||||
className="w-full md:w-auto"
|
||||
className='w-full md:w-auto'
|
||||
onClick={handleAddApi}
|
||||
>
|
||||
{t('添加API')}
|
||||
@@ -355,9 +346,10 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
theme='light'
|
||||
onClick={handleBatchDelete}
|
||||
disabled={selectedRowKeys.length === 0}
|
||||
className="w-full md:w-auto"
|
||||
className='w-full md:w-auto'
|
||||
>
|
||||
{t('批量删除')} {selectedRowKeys.length > 0 && `(${selectedRowKeys.length})`}
|
||||
{t('批量删除')}{' '}
|
||||
{selectedRowKeys.length > 0 && `(${selectedRowKeys.length})`}
|
||||
</Button>
|
||||
<Button
|
||||
icon={<Save size={14} />}
|
||||
@@ -365,18 +357,15 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
loading={loading}
|
||||
disabled={!hasChanges}
|
||||
type='secondary'
|
||||
className="w-full md:w-auto"
|
||||
className='w-full md:w-auto'
|
||||
>
|
||||
{t('保存设置')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 启用开关 */}
|
||||
<div className="order-1 md:order-2 flex items-center gap-2">
|
||||
<Switch
|
||||
checked={panelEnabled}
|
||||
onChange={handleToggleEnabled}
|
||||
/>
|
||||
<div className='order-1 md:order-2 flex items-center gap-2'>
|
||||
<Switch checked={panelEnabled} onChange={handleToggleEnabled} />
|
||||
<Text>{panelEnabled ? t('已启用') : t('已禁用')}</Text>
|
||||
</div>
|
||||
</div>
|
||||
@@ -414,7 +403,7 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
columns={columns}
|
||||
dataSource={getCurrentPageData()}
|
||||
rowSelection={rowSelection}
|
||||
rowKey="id"
|
||||
rowKey='id'
|
||||
scroll={{ x: 'max-content' }}
|
||||
pagination={{
|
||||
currentPage: currentPage,
|
||||
@@ -430,19 +419,23 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
onShowSizeChange: (current, size) => {
|
||||
setCurrentPage(1);
|
||||
setPageSize(size);
|
||||
}
|
||||
},
|
||||
}}
|
||||
size='middle'
|
||||
loading={loading}
|
||||
empty={
|
||||
<Empty
|
||||
image={<IllustrationNoResult style={{ width: 150, height: 150 }} />}
|
||||
darkModeImage={<IllustrationNoResultDark style={{ width: 150, height: 150 }} />}
|
||||
image={
|
||||
<IllustrationNoResult style={{ width: 150, height: 150 }} />
|
||||
}
|
||||
darkModeImage={
|
||||
<IllustrationNoResultDark style={{ width: 150, height: 150 }} />
|
||||
}
|
||||
description={t('暂无API信息')}
|
||||
style={{ padding: 30 }}
|
||||
/>
|
||||
}
|
||||
className="overflow-hidden"
|
||||
className='overflow-hidden'
|
||||
/>
|
||||
</Form.Section>
|
||||
|
||||
@@ -455,7 +448,11 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
cancelText={t('取消')}
|
||||
confirmLoading={modalLoading}
|
||||
>
|
||||
<Form layout='vertical' initValues={apiForm} key={editingApi ? editingApi.id : 'new'}>
|
||||
<Form
|
||||
layout='vertical'
|
||||
initValues={apiForm}
|
||||
key={editingApi ? editingApi.id : 'new'}
|
||||
>
|
||||
<Form.Input
|
||||
field='url'
|
||||
label={t('API地址')}
|
||||
@@ -484,10 +481,7 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
onChange={(value) => setApiForm({ ...apiForm, color: value })}
|
||||
render={(option) => (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<Avatar
|
||||
size="extra-extra-small"
|
||||
color={option.value}
|
||||
/>
|
||||
<Avatar size='extra-extra-small' color={option.value} />
|
||||
{option.label}
|
||||
</div>
|
||||
)}
|
||||
@@ -505,10 +499,10 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
}}
|
||||
okText={t('确认删除')}
|
||||
cancelText={t('取消')}
|
||||
type="warning"
|
||||
type='warning'
|
||||
okButtonProps={{
|
||||
type: 'danger',
|
||||
theme: 'solid'
|
||||
theme: 'solid',
|
||||
}}
|
||||
>
|
||||
<Text>{t('确定要删除此API信息吗?')}</Text>
|
||||
@@ -517,4 +511,4 @@ const SettingsAPIInfo = ({ options, refresh }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsAPIInfo;
|
||||
export default SettingsAPIInfo;
|
||||
|
||||
@@ -30,21 +30,20 @@ import {
|
||||
Tag,
|
||||
Switch,
|
||||
TextArea,
|
||||
Tooltip
|
||||
Tooltip,
|
||||
} from '@douyinfe/semi-ui';
|
||||
import {
|
||||
IllustrationNoResult,
|
||||
IllustrationNoResultDark
|
||||
IllustrationNoResultDark,
|
||||
} from '@douyinfe/semi-illustrations';
|
||||
import { Plus, Edit, Trash2, Save, Bell, Maximize2 } from 'lucide-react';
|
||||
import {
|
||||
Plus,
|
||||
Edit,
|
||||
Trash2,
|
||||
Save,
|
||||
Bell,
|
||||
Maximize2
|
||||
} from 'lucide-react';
|
||||
import { API, showError, showSuccess, getRelativeTime, formatDateTimeString } from '../../../helpers';
|
||||
API,
|
||||
showError,
|
||||
showSuccess,
|
||||
getRelativeTime,
|
||||
formatDateTimeString,
|
||||
} from '../../../helpers';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const { Text } = Typography;
|
||||
@@ -65,7 +64,7 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
content: '',
|
||||
publishDate: new Date(),
|
||||
type: 'default',
|
||||
extra: ''
|
||||
extra: '',
|
||||
});
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
@@ -81,7 +80,7 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
{ value: 'ongoing', label: t('进行中') },
|
||||
{ value: 'success', label: t('成功') },
|
||||
{ value: 'warning', label: t('警告') },
|
||||
{ value: 'error', label: t('错误') }
|
||||
{ value: 'error', label: t('错误') },
|
||||
];
|
||||
|
||||
const getTypeColor = (type) => {
|
||||
@@ -90,7 +89,7 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
ongoing: 'blue',
|
||||
success: 'green',
|
||||
warning: 'orange',
|
||||
error: 'red'
|
||||
error: 'red',
|
||||
};
|
||||
return colorMap[type] || 'grey';
|
||||
};
|
||||
@@ -102,16 +101,18 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
key: 'content',
|
||||
render: (text) => (
|
||||
<Tooltip content={text} position='topLeft' showArrow>
|
||||
<div style={{
|
||||
maxWidth: '300px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap'
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
maxWidth: '300px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('发布时间'),
|
||||
@@ -123,15 +124,17 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
<div style={{ fontWeight: 'bold' }}>
|
||||
{getRelativeTime(publishDate)}
|
||||
</div>
|
||||
<div style={{
|
||||
fontSize: '12px',
|
||||
color: 'var(--semi-color-text-2)',
|
||||
marginTop: '2px'
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '12px',
|
||||
color: 'var(--semi-color-text-2)',
|
||||
marginTop: '2px',
|
||||
}}
|
||||
>
|
||||
{publishDate ? formatDateTimeString(new Date(publishDate)) : '-'}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('类型'),
|
||||
@@ -140,9 +143,9 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
width: 100,
|
||||
render: (type) => (
|
||||
<Tag color={getTypeColor(type)} shape='circle'>
|
||||
{typeOptions.find(opt => opt.value === type)?.label || type}
|
||||
{typeOptions.find((opt) => opt.value === type)?.label || type}
|
||||
</Tag>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('说明'),
|
||||
@@ -150,17 +153,19 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
key: 'extra',
|
||||
render: (text) => (
|
||||
<Tooltip content={text || '-'} showArrow>
|
||||
<div style={{
|
||||
maxWidth: '200px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
color: 'var(--semi-color-text-2)'
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
maxWidth: '200px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
color: 'var(--semi-color-text-2)',
|
||||
}}
|
||||
>
|
||||
{text || '-'}
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('操作'),
|
||||
@@ -188,8 +193,8 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
{t('删除')}
|
||||
</Button>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const updateOption = async (key, value) => {
|
||||
@@ -226,7 +231,7 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
content: '',
|
||||
publishDate: new Date(),
|
||||
type: 'default',
|
||||
extra: ''
|
||||
extra: '',
|
||||
});
|
||||
setShowAnnouncementModal(true);
|
||||
};
|
||||
@@ -235,9 +240,11 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
setEditingAnnouncement(announcement);
|
||||
setAnnouncementForm({
|
||||
content: announcement.content,
|
||||
publishDate: announcement.publishDate ? new Date(announcement.publishDate) : new Date(),
|
||||
publishDate: announcement.publishDate
|
||||
? new Date(announcement.publishDate)
|
||||
: new Date(),
|
||||
type: announcement.type || 'default',
|
||||
extra: announcement.extra || ''
|
||||
extra: announcement.extra || '',
|
||||
});
|
||||
setShowAnnouncementModal(true);
|
||||
};
|
||||
@@ -249,7 +256,9 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
|
||||
const confirmDeleteAnnouncement = () => {
|
||||
if (deletingAnnouncement) {
|
||||
const newList = announcementsList.filter(item => item.id !== deletingAnnouncement.id);
|
||||
const newList = announcementsList.filter(
|
||||
(item) => item.id !== deletingAnnouncement.id,
|
||||
);
|
||||
setAnnouncementsList(newList);
|
||||
setHasChanges(true);
|
||||
showSuccess('公告已删除,请及时点击“保存设置”进行保存');
|
||||
@@ -270,21 +279,20 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
// 将publishDate转换为ISO字符串保存
|
||||
const formData = {
|
||||
...announcementForm,
|
||||
publishDate: announcementForm.publishDate.toISOString()
|
||||
publishDate: announcementForm.publishDate.toISOString(),
|
||||
};
|
||||
|
||||
let newList;
|
||||
if (editingAnnouncement) {
|
||||
newList = announcementsList.map(item =>
|
||||
item.id === editingAnnouncement.id
|
||||
? { ...item, ...formData }
|
||||
: item
|
||||
newList = announcementsList.map((item) =>
|
||||
item.id === editingAnnouncement.id ? { ...item, ...formData } : item,
|
||||
);
|
||||
} else {
|
||||
const newId = Math.max(...announcementsList.map(item => item.id), 0) + 1;
|
||||
const newId =
|
||||
Math.max(...announcementsList.map((item) => item.id), 0) + 1;
|
||||
const newAnnouncement = {
|
||||
id: newId,
|
||||
...formData
|
||||
...formData,
|
||||
};
|
||||
newList = [...announcementsList, newAnnouncement];
|
||||
}
|
||||
@@ -292,7 +300,11 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
setAnnouncementsList(newList);
|
||||
setHasChanges(true);
|
||||
setShowAnnouncementModal(false);
|
||||
showSuccess(editingAnnouncement ? '公告已更新,请及时点击“保存设置”进行保存' : '公告已添加,请及时点击“保存设置”进行保存');
|
||||
showSuccess(
|
||||
editingAnnouncement
|
||||
? '公告已更新,请及时点击“保存设置”进行保存'
|
||||
: '公告已添加,请及时点击“保存设置”进行保存',
|
||||
);
|
||||
} catch (error) {
|
||||
showError('操作失败: ' + error.message);
|
||||
} finally {
|
||||
@@ -312,7 +324,7 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
// 确保每个项目都有id
|
||||
const listWithIds = list.map((item, index) => ({
|
||||
...item,
|
||||
id: item.id || index + 1
|
||||
id: item.id || index + 1,
|
||||
}));
|
||||
setAnnouncementsList(listWithIds);
|
||||
} catch (error) {
|
||||
@@ -322,7 +334,8 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const annStr = options['console_setting.announcements'] ?? options.Announcements;
|
||||
const annStr =
|
||||
options['console_setting.announcements'] ?? options.Announcements;
|
||||
if (annStr !== undefined) {
|
||||
parseAnnouncements(annStr);
|
||||
}
|
||||
@@ -330,7 +343,11 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
|
||||
useEffect(() => {
|
||||
const enabledStr = options['console_setting.announcements_enabled'];
|
||||
setPanelEnabled(enabledStr === undefined ? true : enabledStr === 'true' || enabledStr === true);
|
||||
setPanelEnabled(
|
||||
enabledStr === undefined
|
||||
? true
|
||||
: enabledStr === 'true' || enabledStr === true,
|
||||
);
|
||||
}, [options['console_setting.announcements_enabled']]);
|
||||
|
||||
const handleToggleEnabled = async (checked) => {
|
||||
@@ -358,31 +375,39 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const newList = announcementsList.filter(item => !selectedRowKeys.includes(item.id));
|
||||
const newList = announcementsList.filter(
|
||||
(item) => !selectedRowKeys.includes(item.id),
|
||||
);
|
||||
setAnnouncementsList(newList);
|
||||
setSelectedRowKeys([]);
|
||||
setHasChanges(true);
|
||||
showSuccess(`已删除 ${selectedRowKeys.length} 个系统公告,请及时点击“保存设置”进行保存`);
|
||||
showSuccess(
|
||||
`已删除 ${selectedRowKeys.length} 个系统公告,请及时点击“保存设置”进行保存`,
|
||||
);
|
||||
};
|
||||
|
||||
const renderHeader = () => (
|
||||
<div className="flex flex-col w-full">
|
||||
<div className="mb-2">
|
||||
<div className="flex items-center text-blue-500">
|
||||
<Bell size={16} className="mr-2" />
|
||||
<Text>{t('系统公告管理,可以发布系统通知和重要消息(最多100个,前端显示最新20条)')}</Text>
|
||||
<div className='flex flex-col w-full'>
|
||||
<div className='mb-2'>
|
||||
<div className='flex items-center text-blue-500'>
|
||||
<Bell size={16} className='mr-2' />
|
||||
<Text>
|
||||
{t(
|
||||
'系统公告管理,可以发布系统通知和重要消息(最多100个,前端显示最新20条)',
|
||||
)}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider margin="12px" />
|
||||
<Divider margin='12px' />
|
||||
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-4 w-full">
|
||||
<div className="flex gap-2 w-full md:w-auto order-2 md:order-1">
|
||||
<div className='flex flex-col md:flex-row justify-between items-center gap-4 w-full'>
|
||||
<div className='flex gap-2 w-full md:w-auto order-2 md:order-1'>
|
||||
<Button
|
||||
theme='light'
|
||||
type='primary'
|
||||
icon={<Plus size={14} />}
|
||||
className="w-full md:w-auto"
|
||||
className='w-full md:w-auto'
|
||||
onClick={handleAddAnnouncement}
|
||||
>
|
||||
{t('添加公告')}
|
||||
@@ -393,9 +418,10 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
theme='light'
|
||||
onClick={handleBatchDelete}
|
||||
disabled={selectedRowKeys.length === 0}
|
||||
className="w-full md:w-auto"
|
||||
className='w-full md:w-auto'
|
||||
>
|
||||
{t('批量删除')} {selectedRowKeys.length > 0 && `(${selectedRowKeys.length})`}
|
||||
{t('批量删除')}{' '}
|
||||
{selectedRowKeys.length > 0 && `(${selectedRowKeys.length})`}
|
||||
</Button>
|
||||
<Button
|
||||
icon={<Save size={14} />}
|
||||
@@ -403,14 +429,14 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
loading={loading}
|
||||
disabled={!hasChanges}
|
||||
type='secondary'
|
||||
className="w-full md:w-auto"
|
||||
className='w-full md:w-auto'
|
||||
>
|
||||
{t('保存设置')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 启用开关 */}
|
||||
<div className="order-1 md:order-2 flex items-center gap-2">
|
||||
<div className='order-1 md:order-2 flex items-center gap-2'>
|
||||
<Switch checked={panelEnabled} onChange={handleToggleEnabled} />
|
||||
<Text>{panelEnabled ? t('已启用') : t('已禁用')}</Text>
|
||||
</div>
|
||||
@@ -455,7 +481,7 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
columns={columns}
|
||||
dataSource={getCurrentPageData()}
|
||||
rowSelection={rowSelection}
|
||||
rowKey="id"
|
||||
rowKey='id'
|
||||
scroll={{ x: 'max-content' }}
|
||||
pagination={{
|
||||
currentPage: currentPage,
|
||||
@@ -471,19 +497,23 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
onShowSizeChange: (current, size) => {
|
||||
setCurrentPage(1);
|
||||
setPageSize(size);
|
||||
}
|
||||
},
|
||||
}}
|
||||
size='middle'
|
||||
loading={loading}
|
||||
empty={
|
||||
<Empty
|
||||
image={<IllustrationNoResult style={{ width: 150, height: 150 }} />}
|
||||
darkModeImage={<IllustrationNoResultDark style={{ width: 150, height: 150 }} />}
|
||||
image={
|
||||
<IllustrationNoResult style={{ width: 150, height: 150 }} />
|
||||
}
|
||||
darkModeImage={
|
||||
<IllustrationNoResultDark style={{ width: 150, height: 150 }} />
|
||||
}
|
||||
description={t('暂无系统公告')}
|
||||
style={{ padding: 30 }}
|
||||
/>
|
||||
}
|
||||
className="overflow-hidden"
|
||||
className='overflow-hidden'
|
||||
/>
|
||||
</Form.Section>
|
||||
|
||||
@@ -509,7 +539,9 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
maxCount={500}
|
||||
rows={3}
|
||||
rules={[{ required: true, message: t('请输入公告内容') }]}
|
||||
onChange={(value) => setAnnouncementForm({ ...announcementForm, content: value })}
|
||||
onChange={(value) =>
|
||||
setAnnouncementForm({ ...announcementForm, content: value })
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
theme='light'
|
||||
@@ -526,19 +558,25 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
label={t('发布日期')}
|
||||
type='dateTime'
|
||||
rules={[{ required: true, message: t('请选择发布日期') }]}
|
||||
onChange={(value) => setAnnouncementForm({ ...announcementForm, publishDate: value })}
|
||||
onChange={(value) =>
|
||||
setAnnouncementForm({ ...announcementForm, publishDate: value })
|
||||
}
|
||||
/>
|
||||
<Form.Select
|
||||
field='type'
|
||||
label={t('公告类型')}
|
||||
optionList={typeOptions}
|
||||
onChange={(value) => setAnnouncementForm({ ...announcementForm, type: value })}
|
||||
onChange={(value) =>
|
||||
setAnnouncementForm({ ...announcementForm, type: value })
|
||||
}
|
||||
/>
|
||||
<Form.Input
|
||||
field='extra'
|
||||
label={t('说明信息')}
|
||||
placeholder={t('可选,公告的补充说明')}
|
||||
onChange={(value) => setAnnouncementForm({ ...announcementForm, extra: value })}
|
||||
onChange={(value) =>
|
||||
setAnnouncementForm({ ...announcementForm, extra: value })
|
||||
}
|
||||
/>
|
||||
</Form>
|
||||
</Modal>
|
||||
@@ -553,10 +591,10 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
}}
|
||||
okText={t('确认删除')}
|
||||
cancelText={t('取消')}
|
||||
type="warning"
|
||||
type='warning'
|
||||
okButtonProps={{
|
||||
type: 'danger',
|
||||
theme: 'solid'
|
||||
theme: 'solid',
|
||||
}}
|
||||
>
|
||||
<Text>{t('确定要删除此公告吗?')}</Text>
|
||||
@@ -584,11 +622,13 @@ const SettingsAnnouncements = ({ options, refresh }) => {
|
||||
maxCount={500}
|
||||
rows={15}
|
||||
style={{ width: '100%' }}
|
||||
onChange={(value) => setAnnouncementForm({ ...announcementForm, content: value })}
|
||||
onChange={(value) =>
|
||||
setAnnouncementForm({ ...announcementForm, content: value })
|
||||
}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsAnnouncements;
|
||||
export default SettingsAnnouncements;
|
||||
|
||||
@@ -28,19 +28,13 @@ import {
|
||||
Divider,
|
||||
Modal,
|
||||
Switch,
|
||||
Tooltip
|
||||
Tooltip,
|
||||
} from '@douyinfe/semi-ui';
|
||||
import {
|
||||
IllustrationNoResult,
|
||||
IllustrationNoResultDark
|
||||
IllustrationNoResultDark,
|
||||
} from '@douyinfe/semi-illustrations';
|
||||
import {
|
||||
Plus,
|
||||
Edit,
|
||||
Trash2,
|
||||
Save,
|
||||
HelpCircle
|
||||
} from 'lucide-react';
|
||||
import { Plus, Edit, Trash2, Save, HelpCircle } from 'lucide-react';
|
||||
import { API, showError, showSuccess } from '../../../helpers';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -59,7 +53,7 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
const [faqForm, setFaqForm] = useState({
|
||||
question: '',
|
||||
answer: ''
|
||||
answer: '',
|
||||
});
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
@@ -75,17 +69,19 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
key: 'question',
|
||||
render: (text) => (
|
||||
<Tooltip content={text} showArrow>
|
||||
<div style={{
|
||||
maxWidth: '300px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
fontWeight: 'bold'
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
maxWidth: '300px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
fontWeight: 'bold',
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('回答内容'),
|
||||
@@ -93,17 +89,19 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
key: 'answer',
|
||||
render: (text) => (
|
||||
<Tooltip content={text} showArrow>
|
||||
<div style={{
|
||||
maxWidth: '400px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
color: 'var(--semi-color-text-1)'
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
maxWidth: '400px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
color: 'var(--semi-color-text-1)',
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('操作'),
|
||||
@@ -131,8 +129,8 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
{t('删除')}
|
||||
</Button>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const updateOption = async (key, value) => {
|
||||
@@ -167,7 +165,7 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
setEditingFaq(null);
|
||||
setFaqForm({
|
||||
question: '',
|
||||
answer: ''
|
||||
answer: '',
|
||||
});
|
||||
setShowFaqModal(true);
|
||||
};
|
||||
@@ -176,7 +174,7 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
setEditingFaq(faq);
|
||||
setFaqForm({
|
||||
question: faq.question,
|
||||
answer: faq.answer
|
||||
answer: faq.answer,
|
||||
});
|
||||
setShowFaqModal(true);
|
||||
};
|
||||
@@ -188,7 +186,7 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
|
||||
const confirmDeleteFaq = () => {
|
||||
if (deletingFaq) {
|
||||
const newList = faqList.filter(item => item.id !== deletingFaq.id);
|
||||
const newList = faqList.filter((item) => item.id !== deletingFaq.id);
|
||||
setFaqList(newList);
|
||||
setHasChanges(true);
|
||||
showSuccess('问答已删除,请及时点击“保存设置”进行保存');
|
||||
@@ -208,16 +206,14 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
|
||||
let newList;
|
||||
if (editingFaq) {
|
||||
newList = faqList.map(item =>
|
||||
item.id === editingFaq.id
|
||||
? { ...item, ...faqForm }
|
||||
: item
|
||||
newList = faqList.map((item) =>
|
||||
item.id === editingFaq.id ? { ...item, ...faqForm } : item,
|
||||
);
|
||||
} else {
|
||||
const newId = Math.max(...faqList.map(item => item.id), 0) + 1;
|
||||
const newId = Math.max(...faqList.map((item) => item.id), 0) + 1;
|
||||
const newFaq = {
|
||||
id: newId,
|
||||
...faqForm
|
||||
...faqForm,
|
||||
};
|
||||
newList = [...faqList, newFaq];
|
||||
}
|
||||
@@ -225,7 +221,11 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
setFaqList(newList);
|
||||
setHasChanges(true);
|
||||
setShowFaqModal(false);
|
||||
showSuccess(editingFaq ? '问答已更新,请及时点击“保存设置”进行保存' : '问答已添加,请及时点击“保存设置”进行保存');
|
||||
showSuccess(
|
||||
editingFaq
|
||||
? '问答已更新,请及时点击“保存设置”进行保存'
|
||||
: '问答已添加,请及时点击“保存设置”进行保存',
|
||||
);
|
||||
} catch (error) {
|
||||
showError('操作失败: ' + error.message);
|
||||
} finally {
|
||||
@@ -245,7 +245,7 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
// 确保每个项目都有id
|
||||
const listWithIds = list.map((item, index) => ({
|
||||
...item,
|
||||
id: item.id || index + 1
|
||||
id: item.id || index + 1,
|
||||
}));
|
||||
setFaqList(listWithIds);
|
||||
} catch (error) {
|
||||
@@ -262,7 +262,11 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
|
||||
useEffect(() => {
|
||||
const enabledStr = options['console_setting.faq_enabled'];
|
||||
setPanelEnabled(enabledStr === undefined ? true : enabledStr === 'true' || enabledStr === true);
|
||||
setPanelEnabled(
|
||||
enabledStr === undefined
|
||||
? true
|
||||
: enabledStr === 'true' || enabledStr === true,
|
||||
);
|
||||
}, [options['console_setting.faq_enabled']]);
|
||||
|
||||
const handleToggleEnabled = async (checked) => {
|
||||
@@ -290,31 +294,39 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const newList = faqList.filter(item => !selectedRowKeys.includes(item.id));
|
||||
const newList = faqList.filter(
|
||||
(item) => !selectedRowKeys.includes(item.id),
|
||||
);
|
||||
setFaqList(newList);
|
||||
setSelectedRowKeys([]);
|
||||
setHasChanges(true);
|
||||
showSuccess(`已删除 ${selectedRowKeys.length} 个常见问答,请及时点击“保存设置”进行保存`);
|
||||
showSuccess(
|
||||
`已删除 ${selectedRowKeys.length} 个常见问答,请及时点击“保存设置”进行保存`,
|
||||
);
|
||||
};
|
||||
|
||||
const renderHeader = () => (
|
||||
<div className="flex flex-col w-full">
|
||||
<div className="mb-2">
|
||||
<div className="flex items-center text-blue-500">
|
||||
<HelpCircle size={16} className="mr-2" />
|
||||
<Text>{t('常见问答管理,为用户提供常见问题的答案(最多50个,前端显示最新20条)')}</Text>
|
||||
<div className='flex flex-col w-full'>
|
||||
<div className='mb-2'>
|
||||
<div className='flex items-center text-blue-500'>
|
||||
<HelpCircle size={16} className='mr-2' />
|
||||
<Text>
|
||||
{t(
|
||||
'常见问答管理,为用户提供常见问题的答案(最多50个,前端显示最新20条)',
|
||||
)}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider margin="12px" />
|
||||
<Divider margin='12px' />
|
||||
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-4 w-full">
|
||||
<div className="flex gap-2 w-full md:w-auto order-2 md:order-1">
|
||||
<div className='flex flex-col md:flex-row justify-between items-center gap-4 w-full'>
|
||||
<div className='flex gap-2 w-full md:w-auto order-2 md:order-1'>
|
||||
<Button
|
||||
theme='light'
|
||||
type='primary'
|
||||
icon={<Plus size={14} />}
|
||||
className="w-full md:w-auto"
|
||||
className='w-full md:w-auto'
|
||||
onClick={handleAddFaq}
|
||||
>
|
||||
{t('添加问答')}
|
||||
@@ -325,9 +337,10 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
theme='light'
|
||||
onClick={handleBatchDelete}
|
||||
disabled={selectedRowKeys.length === 0}
|
||||
className="w-full md:w-auto"
|
||||
className='w-full md:w-auto'
|
||||
>
|
||||
{t('批量删除')} {selectedRowKeys.length > 0 && `(${selectedRowKeys.length})`}
|
||||
{t('批量删除')}{' '}
|
||||
{selectedRowKeys.length > 0 && `(${selectedRowKeys.length})`}
|
||||
</Button>
|
||||
<Button
|
||||
icon={<Save size={14} />}
|
||||
@@ -335,14 +348,14 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
loading={loading}
|
||||
disabled={!hasChanges}
|
||||
type='secondary'
|
||||
className="w-full md:w-auto"
|
||||
className='w-full md:w-auto'
|
||||
>
|
||||
{t('保存设置')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 启用开关 */}
|
||||
<div className="order-1 md:order-2 flex items-center gap-2">
|
||||
<div className='order-1 md:order-2 flex items-center gap-2'>
|
||||
<Switch checked={panelEnabled} onChange={handleToggleEnabled} />
|
||||
<Text>{panelEnabled ? t('已启用') : t('已禁用')}</Text>
|
||||
</div>
|
||||
@@ -381,7 +394,7 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
columns={columns}
|
||||
dataSource={getCurrentPageData()}
|
||||
rowSelection={rowSelection}
|
||||
rowKey="id"
|
||||
rowKey='id'
|
||||
scroll={{ x: 'max-content' }}
|
||||
pagination={{
|
||||
currentPage: currentPage,
|
||||
@@ -397,19 +410,23 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
onShowSizeChange: (current, size) => {
|
||||
setCurrentPage(1);
|
||||
setPageSize(size);
|
||||
}
|
||||
},
|
||||
}}
|
||||
size='middle'
|
||||
loading={loading}
|
||||
empty={
|
||||
<Empty
|
||||
image={<IllustrationNoResult style={{ width: 150, height: 150 }} />}
|
||||
darkModeImage={<IllustrationNoResultDark style={{ width: 150, height: 150 }} />}
|
||||
image={
|
||||
<IllustrationNoResult style={{ width: 150, height: 150 }} />
|
||||
}
|
||||
darkModeImage={
|
||||
<IllustrationNoResultDark style={{ width: 150, height: 150 }} />
|
||||
}
|
||||
description={t('暂无常见问答')}
|
||||
style={{ padding: 30 }}
|
||||
/>
|
||||
}
|
||||
className="overflow-hidden"
|
||||
className='overflow-hidden'
|
||||
/>
|
||||
</Form.Section>
|
||||
|
||||
@@ -423,7 +440,11 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
confirmLoading={modalLoading}
|
||||
width={800}
|
||||
>
|
||||
<Form layout='vertical' initValues={faqForm} key={editingFaq ? editingFaq.id : 'new'}>
|
||||
<Form
|
||||
layout='vertical'
|
||||
initValues={faqForm}
|
||||
key={editingFaq ? editingFaq.id : 'new'}
|
||||
>
|
||||
<Form.Input
|
||||
field='question'
|
||||
label={t('问题标题')}
|
||||
@@ -454,10 +475,10 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
}}
|
||||
okText={t('确认删除')}
|
||||
cancelText={t('取消')}
|
||||
type="warning"
|
||||
type='warning'
|
||||
okButtonProps={{
|
||||
type: 'danger',
|
||||
theme: 'solid'
|
||||
theme: 'solid',
|
||||
}}
|
||||
>
|
||||
<Text>{t('确定要删除此问答吗?')}</Text>
|
||||
@@ -466,4 +487,4 @@ const SettingsFAQ = ({ options, refresh }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsFAQ;
|
||||
export default SettingsFAQ;
|
||||
|
||||
@@ -27,19 +27,13 @@ import {
|
||||
Empty,
|
||||
Divider,
|
||||
Modal,
|
||||
Switch
|
||||
Switch,
|
||||
} from '@douyinfe/semi-ui';
|
||||
import {
|
||||
IllustrationNoResult,
|
||||
IllustrationNoResultDark
|
||||
IllustrationNoResultDark,
|
||||
} from '@douyinfe/semi-illustrations';
|
||||
import {
|
||||
Plus,
|
||||
Edit,
|
||||
Trash2,
|
||||
Save,
|
||||
Activity
|
||||
} from 'lucide-react';
|
||||
import { Plus, Edit, Trash2, Save, Activity } from 'lucide-react';
|
||||
import { API, showError, showSuccess } from '../../../helpers';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -72,41 +66,47 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
dataIndex: 'categoryName',
|
||||
key: 'categoryName',
|
||||
render: (text) => (
|
||||
<div style={{
|
||||
fontWeight: 'bold',
|
||||
color: 'var(--semi-color-text-0)'
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
fontWeight: 'bold',
|
||||
color: 'var(--semi-color-text-0)',
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Uptime Kuma地址'),
|
||||
dataIndex: 'url',
|
||||
key: 'url',
|
||||
render: (text) => (
|
||||
<div style={{
|
||||
maxWidth: '300px',
|
||||
wordBreak: 'break-all',
|
||||
fontFamily: 'monospace',
|
||||
color: 'var(--semi-color-primary)'
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
maxWidth: '300px',
|
||||
wordBreak: 'break-all',
|
||||
fontFamily: 'monospace',
|
||||
color: 'var(--semi-color-primary)',
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('状态页面Slug'),
|
||||
dataIndex: 'slug',
|
||||
key: 'slug',
|
||||
render: (text) => (
|
||||
<div style={{
|
||||
fontFamily: 'monospace',
|
||||
color: 'var(--semi-color-text-1)'
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
fontFamily: 'monospace',
|
||||
color: 'var(--semi-color-text-1)',
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('操作'),
|
||||
@@ -134,8 +134,8 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
{t('删除')}
|
||||
</Button>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const updateOption = async (key, value) => {
|
||||
@@ -193,7 +193,9 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
|
||||
const confirmDeleteGroup = () => {
|
||||
if (deletingGroup) {
|
||||
const newList = uptimeGroupsList.filter(item => item.id !== deletingGroup.id);
|
||||
const newList = uptimeGroupsList.filter(
|
||||
(item) => item.id !== deletingGroup.id,
|
||||
);
|
||||
setUptimeGroupsList(newList);
|
||||
setHasChanges(true);
|
||||
showSuccess('分类已删除,请及时点击“保存设置”进行保存');
|
||||
@@ -225,16 +227,15 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
|
||||
let newList;
|
||||
if (editingGroup) {
|
||||
newList = uptimeGroupsList.map(item =>
|
||||
item.id === editingGroup.id
|
||||
? { ...item, ...uptimeForm }
|
||||
: item
|
||||
newList = uptimeGroupsList.map((item) =>
|
||||
item.id === editingGroup.id ? { ...item, ...uptimeForm } : item,
|
||||
);
|
||||
} else {
|
||||
const newId = Math.max(...uptimeGroupsList.map(item => item.id), 0) + 1;
|
||||
const newId =
|
||||
Math.max(...uptimeGroupsList.map((item) => item.id), 0) + 1;
|
||||
const newGroup = {
|
||||
id: newId,
|
||||
...uptimeForm
|
||||
...uptimeForm,
|
||||
};
|
||||
newList = [...uptimeGroupsList, newGroup];
|
||||
}
|
||||
@@ -242,7 +243,11 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
setUptimeGroupsList(newList);
|
||||
setHasChanges(true);
|
||||
setShowUptimeModal(false);
|
||||
showSuccess(editingGroup ? '分类已更新,请及时点击“保存设置”进行保存' : '分类已添加,请及时点击“保存设置”进行保存');
|
||||
showSuccess(
|
||||
editingGroup
|
||||
? '分类已更新,请及时点击“保存设置”进行保存'
|
||||
: '分类已添加,请及时点击“保存设置”进行保存',
|
||||
);
|
||||
} catch (error) {
|
||||
showError('操作失败: ' + error.message);
|
||||
} finally {
|
||||
@@ -261,7 +266,7 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
const list = Array.isArray(parsed) ? parsed : [];
|
||||
const listWithIds = list.map((item, index) => ({
|
||||
...item,
|
||||
id: item.id || index + 1
|
||||
id: item.id || index + 1,
|
||||
}));
|
||||
setUptimeGroupsList(listWithIds);
|
||||
} catch (error) {
|
||||
@@ -279,7 +284,11 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
|
||||
useEffect(() => {
|
||||
const enabledStr = options['console_setting.uptime_kuma_enabled'];
|
||||
setPanelEnabled(enabledStr === undefined ? true : enabledStr === 'true' || enabledStr === true);
|
||||
setPanelEnabled(
|
||||
enabledStr === undefined
|
||||
? true
|
||||
: enabledStr === 'true' || enabledStr === true,
|
||||
);
|
||||
}, [options['console_setting.uptime_kuma_enabled']]);
|
||||
|
||||
const handleToggleEnabled = async (checked) => {
|
||||
@@ -307,31 +316,39 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const newList = uptimeGroupsList.filter(item => !selectedRowKeys.includes(item.id));
|
||||
const newList = uptimeGroupsList.filter(
|
||||
(item) => !selectedRowKeys.includes(item.id),
|
||||
);
|
||||
setUptimeGroupsList(newList);
|
||||
setSelectedRowKeys([]);
|
||||
setHasChanges(true);
|
||||
showSuccess(`已删除 ${selectedRowKeys.length} 个分类,请及时点击“保存设置”进行保存`);
|
||||
showSuccess(
|
||||
`已删除 ${selectedRowKeys.length} 个分类,请及时点击“保存设置”进行保存`,
|
||||
);
|
||||
};
|
||||
|
||||
const renderHeader = () => (
|
||||
<div className="flex flex-col w-full">
|
||||
<div className="mb-2">
|
||||
<div className="flex items-center text-blue-500">
|
||||
<Activity size={16} className="mr-2" />
|
||||
<Text>{t('Uptime Kuma监控分类管理,可以配置多个监控分类用于服务状态展示(最多20个)')}</Text>
|
||||
<div className='flex flex-col w-full'>
|
||||
<div className='mb-2'>
|
||||
<div className='flex items-center text-blue-500'>
|
||||
<Activity size={16} className='mr-2' />
|
||||
<Text>
|
||||
{t(
|
||||
'Uptime Kuma监控分类管理,可以配置多个监控分类用于服务状态展示(最多20个)',
|
||||
)}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider margin="12px" />
|
||||
<Divider margin='12px' />
|
||||
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-4 w-full">
|
||||
<div className="flex gap-2 w-full md:w-auto order-2 md:order-1">
|
||||
<div className='flex flex-col md:flex-row justify-between items-center gap-4 w-full'>
|
||||
<div className='flex gap-2 w-full md:w-auto order-2 md:order-1'>
|
||||
<Button
|
||||
theme='light'
|
||||
type='primary'
|
||||
icon={<Plus size={14} />}
|
||||
className="w-full md:w-auto"
|
||||
className='w-full md:w-auto'
|
||||
onClick={handleAddGroup}
|
||||
>
|
||||
{t('添加分类')}
|
||||
@@ -342,9 +359,10 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
theme='light'
|
||||
onClick={handleBatchDelete}
|
||||
disabled={selectedRowKeys.length === 0}
|
||||
className="w-full md:w-auto"
|
||||
className='w-full md:w-auto'
|
||||
>
|
||||
{t('批量删除')} {selectedRowKeys.length > 0 && `(${selectedRowKeys.length})`}
|
||||
{t('批量删除')}{' '}
|
||||
{selectedRowKeys.length > 0 && `(${selectedRowKeys.length})`}
|
||||
</Button>
|
||||
<Button
|
||||
icon={<Save size={14} />}
|
||||
@@ -352,14 +370,14 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
loading={loading}
|
||||
disabled={!hasChanges}
|
||||
type='secondary'
|
||||
className="w-full md:w-auto"
|
||||
className='w-full md:w-auto'
|
||||
>
|
||||
{t('保存设置')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 启用开关 */}
|
||||
<div className="order-1 md:order-2 flex items-center gap-2">
|
||||
<div className='order-1 md:order-2 flex items-center gap-2'>
|
||||
<Switch checked={panelEnabled} onChange={handleToggleEnabled} />
|
||||
<Text>{panelEnabled ? t('已启用') : t('已禁用')}</Text>
|
||||
</div>
|
||||
@@ -397,7 +415,7 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
columns={columns}
|
||||
dataSource={getCurrentPageData()}
|
||||
rowSelection={rowSelection}
|
||||
rowKey="id"
|
||||
rowKey='id'
|
||||
scroll={{ x: 'max-content' }}
|
||||
pagination={{
|
||||
currentPage: currentPage,
|
||||
@@ -413,19 +431,23 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
onShowSizeChange: (current, size) => {
|
||||
setCurrentPage(1);
|
||||
setPageSize(size);
|
||||
}
|
||||
},
|
||||
}}
|
||||
size='middle'
|
||||
loading={loading}
|
||||
empty={
|
||||
<Empty
|
||||
image={<IllustrationNoResult style={{ width: 150, height: 150 }} />}
|
||||
darkModeImage={<IllustrationNoResultDark style={{ width: 150, height: 150 }} />}
|
||||
image={
|
||||
<IllustrationNoResult style={{ width: 150, height: 150 }} />
|
||||
}
|
||||
darkModeImage={
|
||||
<IllustrationNoResultDark style={{ width: 150, height: 150 }} />
|
||||
}
|
||||
description={t('暂无监控数据')}
|
||||
style={{ padding: 30 }}
|
||||
/>
|
||||
}
|
||||
className="overflow-hidden"
|
||||
className='overflow-hidden'
|
||||
/>
|
||||
</Form.Section>
|
||||
|
||||
@@ -439,19 +461,27 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
confirmLoading={modalLoading}
|
||||
width={600}
|
||||
>
|
||||
<Form layout='vertical' initValues={uptimeForm} key={editingGroup ? editingGroup.id : 'new'}>
|
||||
<Form
|
||||
layout='vertical'
|
||||
initValues={uptimeForm}
|
||||
key={editingGroup ? editingGroup.id : 'new'}
|
||||
>
|
||||
<Form.Input
|
||||
field='categoryName'
|
||||
label={t('分类名称')}
|
||||
placeholder={t('请输入分类名称,如:OpenAI、Claude等')}
|
||||
maxLength={50}
|
||||
rules={[{ required: true, message: t('请输入分类名称') }]}
|
||||
onChange={(value) => setUptimeForm({ ...uptimeForm, categoryName: value })}
|
||||
onChange={(value) =>
|
||||
setUptimeForm({ ...uptimeForm, categoryName: value })
|
||||
}
|
||||
/>
|
||||
<Form.Input
|
||||
field='url'
|
||||
label={t('Uptime Kuma地址')}
|
||||
placeholder={t('请输入Uptime Kuma服务地址,如:https://status.example.com')}
|
||||
placeholder={t(
|
||||
'请输入Uptime Kuma服务地址,如:https://status.example.com',
|
||||
)}
|
||||
maxLength={500}
|
||||
rules={[{ required: true, message: t('请输入Uptime Kuma地址') }]}
|
||||
onChange={(value) => setUptimeForm({ ...uptimeForm, url: value })}
|
||||
@@ -477,10 +507,10 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
}}
|
||||
okText={t('确认删除')}
|
||||
cancelText={t('取消')}
|
||||
type="warning"
|
||||
type='warning'
|
||||
okButtonProps={{
|
||||
type: 'danger',
|
||||
theme: 'solid'
|
||||
theme: 'solid',
|
||||
}}
|
||||
>
|
||||
<Text>{t('确定要删除此分类吗?')}</Text>
|
||||
@@ -489,4 +519,4 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsUptimeKuma;
|
||||
export default SettingsUptimeKuma;
|
||||
|
||||
Reference in New Issue
Block a user