🎨 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:
t0ng7u
2025-08-30 21:15:10 +08:00
parent 105b86c660
commit 6a87808612
274 changed files with 11025 additions and 7659 deletions

View File

@@ -290,7 +290,9 @@ const EditModelModal = (props) => {
</Avatar>
<div>
<Text className='text-lg font-medium'>{t('基本信息')}</Text>
<div className='text-xs text-gray-600'>{t('设置模型的基本信息')}</div>
<div className='text-xs text-gray-600'>
{t('设置模型的基本信息')}
</div>
</div>
</div>
<Row gutter={12}>
@@ -309,9 +311,16 @@ const EditModelModal = (props) => {
field='name_rule'
label={t('名称匹配类型')}
placeholder={t('请选择名称匹配类型')}
optionList={nameRuleOptions.map(o => ({ label: t(o.label), value: o.value }))}
rules={[{ required: true, message: t('请选择名称匹配类型') }]}
extraText={t('根据模型名称和匹配规则查找模型元数据,优先级:精确 > 前缀 > 后缀 > 包含')}
optionList={nameRuleOptions.map((o) => ({
label: t(o.label),
value: o.value,
}))}
rules={[
{ required: true, message: t('请选择名称匹配类型') },
]}
extraText={t(
'根据模型名称和匹配规则查找模型元数据,优先级:精确 > 前缀 > 后缀 > 包含',
)}
style={{ width: '100%' }}
/>
</Col>
@@ -323,9 +332,14 @@ const EditModelModal = (props) => {
placeholder={t('请输入图标名称')}
extraText={
<span>
{t('图标使用@lobehub/icons库OpenAI、Claude.Color支持链式参数OpenAI.Avatar.type={\'platform\'}、OpenRouter.Avatar.shape={\'square\'},查询所有可用图标请 ')}
{t(
"图标使用@lobehub/icons库OpenAI、Claude.Color支持链式参数OpenAI.Avatar.type={'platform'}、OpenRouter.Avatar.shape={'square'},查询所有可用图标请 ",
)}
<Typography.Text
link={{ href: 'https://icons.lobehub.com/components/lobe-hub', target: '_blank' }}
link={{
href: 'https://icons.lobehub.com/components/lobe-hub',
target: '_blank',
}}
icon={<IconLink />}
underline
>
@@ -357,7 +371,16 @@ const EditModelModal = (props) => {
if (!formApiRef.current) return;
const normalize = (tags) => {
if (!Array.isArray(tags)) return [];
return [...new Set(tags.flatMap(tag => tag.split(',').map(t => t.trim()).filter(Boolean)))];
return [
...new Set(
tags.flatMap((tag) =>
tag
.split(',')
.map((t) => t.trim())
.filter(Boolean),
),
),
];
};
const normalized = normalize(newTags);
formApiRef.current.setValue('tags', normalized);
@@ -366,17 +389,24 @@ const EditModelModal = (props) => {
{...(tagGroups.length > 0 && {
extraText: (
<Space wrap>
{tagGroups.map(group => (
{tagGroups.map((group) => (
<Button
key={group.id}
size='small'
type='primary'
onClick={() => {
if (formApiRef.current) {
const currentTags = formApiRef.current.getValue('tags') || [];
const newTags = [...currentTags, ...(group.items || [])];
const currentTags =
formApiRef.current.getValue('tags') || [];
const newTags = [
...currentTags,
...(group.items || []),
];
const uniqueTags = [...new Set(newTags)];
formApiRef.current.setValue('tags', uniqueTags);
formApiRef.current.setValue(
'tags',
uniqueTags,
);
}
}}
>
@@ -384,7 +414,7 @@ const EditModelModal = (props) => {
</Button>
))}
</Space>
)
),
})}
/>
</Col>
@@ -393,13 +423,19 @@ const EditModelModal = (props) => {
field='vendor_id'
label={t('供应商')}
placeholder={t('选择模型供应商')}
optionList={vendors.map(v => ({ label: v.name, value: v.id }))}
optionList={vendors.map((v) => ({
label: v.name,
value: v.id,
}))}
filter
showClear
onChange={(value) => {
const vendorInfo = vendors.find(v => v.id === value);
const vendorInfo = vendors.find((v) => v.id === value);
if (vendorInfo && formApiRef.current) {
formApiRef.current.setValue('vendor', vendorInfo.name);
formApiRef.current.setValue(
'vendor',
vendorInfo.name,
);
}
}}
style={{ width: '100%' }}
@@ -409,49 +445,71 @@ const EditModelModal = (props) => {
<JSONEditor
field='endpoints'
label={t('端点映射')}
placeholder={'{\n "openai": {"path": "/v1/chat/completions", "method": "POST"}\n}'}
placeholder={
'{\n "openai": {"path": "/v1/chat/completions", "method": "POST"}\n}'
}
value={values.endpoints}
onChange={(val) => formApiRef.current?.setValue('endpoints', val)}
onChange={(val) =>
formApiRef.current?.setValue('endpoints', val)
}
formApi={formApiRef.current}
editorType='object'
template={ENDPOINT_TEMPLATE}
templateLabel={t('填入模板')}
extraText={t('留空则使用默认端点;支持 {path, method}')}
extraFooter={endpointGroups.length > 0 && (
<Space wrap>
{endpointGroups.map(group => (
<Button
key={group.id}
size='small'
type='primary'
onClick={() => {
try {
const current = formApiRef.current?.getValue('endpoints') || '';
let base = {};
if (current && current.trim()) base = JSON.parse(current);
const groupObj = typeof group.items === 'string' ? JSON.parse(group.items || '{}') : (group.items || {});
const merged = { ...base, ...groupObj };
formApiRef.current?.setValue('endpoints', JSON.stringify(merged, null, 2));
} catch (e) {
extraFooter={
endpointGroups.length > 0 && (
<Space wrap>
{endpointGroups.map((group) => (
<Button
key={group.id}
size='small'
type='primary'
onClick={() => {
try {
const groupObj = typeof group.items === 'string' ? JSON.parse(group.items || '{}') : (group.items || {});
formApiRef.current?.setValue('endpoints', JSON.stringify(groupObj, null, 2));
} catch { }
}
}}
>
{group.name}
</Button>
))}
</Space>
)}
const current =
formApiRef.current?.getValue(
'endpoints',
) || '';
let base = {};
if (current && current.trim())
base = JSON.parse(current);
const groupObj =
typeof group.items === 'string'
? JSON.parse(group.items || '{}')
: group.items || {};
const merged = { ...base, ...groupObj };
formApiRef.current?.setValue(
'endpoints',
JSON.stringify(merged, null, 2),
);
} catch (e) {
try {
const groupObj =
typeof group.items === 'string'
? JSON.parse(group.items || '{}')
: group.items || {};
formApiRef.current?.setValue(
'endpoints',
JSON.stringify(groupObj, null, 2),
);
} catch {}
}
}}
>
{group.name}
</Button>
))}
</Space>
)
}
/>
</Col>
<Col span={24}>
<Form.Switch
field='status'
label={t('状态')}
size="large"
size='large'
/>
</Col>
</Row>
@@ -464,4 +522,4 @@ const EditModelModal = (props) => {
);
};
export default EditModelModal;
export default EditModelModal;

View File

@@ -32,11 +32,7 @@ import {
Avatar,
Spin,
} from '@douyinfe/semi-ui';
import {
IconLayers,
IconSave,
IconClose,
} from '@douyinfe/semi-icons';
import { IconLayers, IconSave, IconClose } from '@douyinfe/semi-icons';
import { API, showError, showSuccess } from '../../../../helpers';
import { useTranslation } from 'react-i18next';
import { useIsMobile } from '../../../../hooks/common/useIsMobile';
@@ -53,7 +49,12 @@ const ENDPOINT_TEMPLATE = {
'image-generation': { path: '/v1/images/generations', method: 'POST' },
};
const EditPrefillGroupModal = ({ visible, onClose, editingGroup, onSuccess }) => {
const EditPrefillGroupModal = ({
visible,
onClose,
editingGroup,
onSuccess,
}) => {
const { t } = useTranslation();
const isMobile = useIsMobile();
const [loading, setLoading] = useState(false);
@@ -112,7 +113,7 @@ const EditPrefillGroupModal = ({ visible, onClose, editingGroup, onSuccess }) =>
return (
<SideSheet
placement="left"
placement='left'
title={
<Space>
{isEdit ? (
@@ -193,13 +194,15 @@ const EditPrefillGroupModal = ({ visible, onClose, editingGroup, onSuccess }) =>
</Avatar>
<div>
<Text className='text-lg font-medium'>{t('基本信息')}</Text>
<div className='text-xs text-gray-600'>{t('设置预填组的基本信息')}</div>
<div className='text-xs text-gray-600'>
{t('设置预填组的基本信息')}
</div>
</div>
</div>
<Row gutter={12}>
<Col span={24}>
<Form.Input
field="name"
field='name'
label={t('组名')}
placeholder={t('请输入组名')}
rules={[{ required: true, message: t('请输入组名') }]}
@@ -208,7 +211,7 @@ const EditPrefillGroupModal = ({ visible, onClose, editingGroup, onSuccess }) =>
</Col>
<Col span={24}>
<Form.Select
field="type"
field='type'
label={t('类型')}
placeholder={t('选择组类型')}
optionList={typeOptions}
@@ -219,7 +222,7 @@ const EditPrefillGroupModal = ({ visible, onClose, editingGroup, onSuccess }) =>
</Col>
<Col span={24}>
<Form.TextArea
field="description"
field='description'
label={t('描述')}
placeholder={t('请输入组描述')}
rows={3}
@@ -229,19 +232,28 @@ const EditPrefillGroupModal = ({ visible, onClose, editingGroup, onSuccess }) =>
<Col span={24}>
{selectedType === 'endpoint' ? (
<JSONEditor
field="items"
field='items'
label={t('端点映射')}
value={formRef.current?.getValue('items') ?? (typeof editingGroup?.items === 'string' ? editingGroup.items : JSON.stringify(editingGroup.items || {}, null, 2))}
onChange={(val) => formRef.current?.setValue('items', val)}
value={
formRef.current?.getValue('items') ??
(typeof editingGroup?.items === 'string'
? editingGroup.items
: JSON.stringify(editingGroup.items || {}, null, 2))
}
onChange={(val) =>
formRef.current?.setValue('items', val)
}
editorType='object'
placeholder={'{\n "openai": {"path": "/v1/chat/completions", "method": "POST"}\n}'}
placeholder={
'{\n "openai": {"path": "/v1/chat/completions", "method": "POST"}\n}'
}
template={ENDPOINT_TEMPLATE}
templateLabel={t('填入模板')}
extraText={t('键为端点类型,值为路径和方法对象')}
/>
) : (
<Form.TagInput
field="items"
field='items'
label={t('项目')}
placeholder={t('输入项目名称,按回车添加')}
addOnBlur
@@ -259,4 +271,4 @@ const EditPrefillGroupModal = ({ visible, onClose, editingGroup, onSuccess }) =>
);
};
export default EditPrefillGroupModal;
export default EditPrefillGroupModal;

View File

@@ -18,12 +18,7 @@ For commercial licensing, please contact support@quantumnous.com
*/
import React, { useState, useRef, useEffect } from 'react';
import {
Modal,
Form,
Col,
Row,
} from '@douyinfe/semi-ui';
import { Modal, Form, Col, Row } from '@douyinfe/semi-ui';
import { API, showError, showSuccess } from '../../../../helpers';
import { Typography } from '@douyinfe/semi-ui';
import { IconLink } from '@douyinfe/semi-icons';
@@ -138,7 +133,7 @@ const EditVendorModal = ({ visible, handleClose, refresh, editingVendor }) => {
<Row gutter={12}>
<Col span={24}>
<Form.Input
field="name"
field='name'
label={t('供应商名称')}
placeholder={t('请输入供应商名称OpenAI')}
rules={[{ required: true, message: t('请输入供应商名称') }]}
@@ -147,7 +142,7 @@ const EditVendorModal = ({ visible, handleClose, refresh, editingVendor }) => {
</Col>
<Col span={24}>
<Form.TextArea
field="description"
field='description'
label={t('描述')}
placeholder={t('请输入供应商描述')}
rows={3}
@@ -156,14 +151,19 @@ const EditVendorModal = ({ visible, handleClose, refresh, editingVendor }) => {
</Col>
<Col span={24}>
<Form.Input
field="icon"
field='icon'
label={t('供应商图标')}
placeholder={t("请输入图标名称")}
placeholder={t('请输入图标名称')}
extraText={
<span>
{t('图标使用@lobehub/icons库OpenAI、Claude.Color支持链式参数OpenAI.Avatar.type={\'platform\'}、OpenRouter.Avatar.shape={\'square\'},查询所有可用图标请 ')}
{t(
"图标使用@lobehub/icons库OpenAI、Claude.Color支持链式参数OpenAI.Avatar.type={'platform'}、OpenRouter.Avatar.shape={'square'},查询所有可用图标请 ",
)}
<Typography.Text
link={{ href: 'https://icons.lobehub.com/components/lobe-hub', target: '_blank' }}
link={{
href: 'https://icons.lobehub.com/components/lobe-hub',
target: '_blank',
}}
icon={<IconLink />}
underline
>
@@ -175,11 +175,7 @@ const EditVendorModal = ({ visible, handleClose, refresh, editingVendor }) => {
/>
</Col>
<Col span={24}>
<Form.Switch
field="status"
label={t('状态')}
initValue={true}
/>
<Form.Switch field='status' label={t('状态')} initValue={true} />
</Col>
</Row>
</Form>
@@ -187,4 +183,4 @@ const EditVendorModal = ({ visible, handleClose, refresh, editingVendor }) => {
);
};
export default EditVendorModal;
export default EditVendorModal;

View File

@@ -18,19 +18,25 @@ For commercial licensing, please contact support@quantumnous.com
*/
import React, { useEffect, useState } from 'react';
import { Modal, Table, Spin, Button, Typography, Empty, Input } from '@douyinfe/semi-ui';
import { IllustrationNoResult, IllustrationNoResultDark } from '@douyinfe/semi-illustrations';
import {
Modal,
Table,
Spin,
Button,
Typography,
Empty,
Input,
} from '@douyinfe/semi-ui';
import {
IllustrationNoResult,
IllustrationNoResultDark,
} from '@douyinfe/semi-illustrations';
import { IconSearch } from '@douyinfe/semi-icons';
import { API, showError } from '../../../../helpers';
import { MODEL_TABLE_PAGE_SIZE } from '../../../../constants';
import { useIsMobile } from '../../../../hooks/common/useIsMobile';
const MissingModelsModal = ({
visible,
onClose,
onConfigureModel,
t,
}) => {
const MissingModelsModal = ({ visible, onClose, onConfigureModel, t }) => {
const [loading, setLoading] = useState(false);
const [missingModels, setMissingModels] = useState([]);
const [searchKeyword, setSearchKeyword] = useState('');
@@ -64,7 +70,7 @@ const MissingModelsModal = ({
// 过滤和分页逻辑
const filteredModels = missingModels.filter((model) =>
model.toLowerCase().includes(searchKeyword.toLowerCase())
model.toLowerCase().includes(searchKeyword.toLowerCase()),
);
const dataSource = (() => {
@@ -81,10 +87,10 @@ const MissingModelsModal = ({
title: t('模型名称'),
dataIndex: 'model',
render: (text) => (
<div className="flex items-center">
<div className='flex items-center'>
<Typography.Text strong>{text}</Typography.Text>
</div>
)
),
},
{
title: '',
@@ -93,25 +99,28 @@ const MissingModelsModal = ({
width: 100,
render: (text, record) => (
<Button
type="primary"
size="small"
type='primary'
size='small'
onClick={() => onConfigureModel(record.model)}
>
{t('配置')}
</Button>
)
}
),
},
];
return (
<Modal
title={
<div className="flex flex-col gap-2 w-full">
<div className="flex items-center gap-2">
<Typography.Text strong className="!text-[var(--semi-color-text-0)] !text-base">
<div className='flex flex-col gap-2 w-full'>
<div className='flex items-center gap-2'>
<Typography.Text
strong
className='!text-[var(--semi-color-text-0)] !text-base'
>
{t('未配置的模型列表')}
</Typography.Text>
<Typography.Text type="tertiary" size="small">
<Typography.Text type='tertiary' size='small'>
{t('共')} {missingModels.length} {t('个未配置模型')}
</Typography.Text>
</div>
@@ -121,20 +130,22 @@ const MissingModelsModal = ({
onCancel={onClose}
footer={null}
size={isMobile ? 'full-width' : 'medium'}
className="!rounded-lg"
className='!rounded-lg'
>
<Spin spinning={loading}>
{missingModels.length === 0 && !loading ? (
<Empty
image={<IllustrationNoResult style={{ width: 150, height: 150 }} />}
darkModeImage={<IllustrationNoResultDark style={{ width: 150, height: 150 }} />}
darkModeImage={
<IllustrationNoResultDark style={{ width: 150, height: 150 }} />
}
description={t('暂无缺失模型')}
style={{ padding: 30 }}
/>
) : (
<div className="missing-models-content">
<div className='missing-models-content'>
{/* 搜索框 */}
<div className="flex items-center justify-end gap-2 w-full mb-4">
<div className='flex items-center justify-end gap-2 w-full mb-4'>
<Input
placeholder={t('搜索模型...')}
value={searchKeyword}
@@ -142,7 +153,7 @@ const MissingModelsModal = ({
setSearchKeyword(v);
setCurrentPage(1);
}}
className="!w-full"
className='!w-full'
prefix={<IconSearch />}
showClear
/>
@@ -163,9 +174,17 @@ const MissingModelsModal = ({
/>
) : (
<Empty
image={<IllustrationNoResult style={{ width: 100, height: 100 }} />}
darkModeImage={<IllustrationNoResultDark style={{ width: 100, height: 100 }} />}
description={searchKeyword ? t('未找到匹配的模型') : t('暂无缺失模型')}
image={
<IllustrationNoResult style={{ width: 100, height: 100 }} />
}
darkModeImage={
<IllustrationNoResultDark
style={{ width: 100, height: 100 }}
/>
}
description={
searchKeyword ? t('未找到匹配的模型') : t('暂无缺失模型')
}
style={{ padding: 20 }}
/>
)}

View File

@@ -30,20 +30,25 @@ import {
Spin,
Empty,
} from '@douyinfe/semi-ui';
import {
IconPlus,
IconLayers,
} from '@douyinfe/semi-icons';
import { IconPlus, IconLayers } from '@douyinfe/semi-icons';
import {
IllustrationNoResult,
IllustrationNoResultDark,
} from '@douyinfe/semi-illustrations';
import { API, showError, showSuccess, stringToColor } from '../../../../helpers';
import {
API,
showError,
showSuccess,
stringToColor,
} from '../../../../helpers';
import { useTranslation } from 'react-i18next';
import { useIsMobile } from '../../../../hooks/common/useIsMobile';
import CardTable from '../../../common/ui/CardTable';
import EditPrefillGroupModal from './EditPrefillGroupModal';
import { renderLimitedItems, renderDescription } from '../../../common/ui/RenderUtils';
import {
renderLimitedItems,
renderDescription,
} from '../../../common/ui/RenderUtils';
const { Text, Title } = Typography;
@@ -121,8 +126,9 @@ const PrefillGroupManagement = ({ visible, onClose }) => {
render: (text, record) => (
<Space>
<Text strong>{text}</Text>
<Tag color="white" shape="circle" size="small">
{typeOptions.find(opt => opt.value === record.type)?.label || record.type}
<Tag color='white' shape='circle' size='small'>
{typeOptions.find((opt) => opt.value === record.type)?.label ||
record.type}
</Tag>
</Space>
),
@@ -140,34 +146,49 @@ const PrefillGroupManagement = ({ visible, onClose }) => {
render: (items, record) => {
try {
if (record.type === 'endpoint') {
const obj = typeof items === 'string' ? JSON.parse(items || '{}') : (items || {});
const obj =
typeof items === 'string'
? JSON.parse(items || '{}')
: items || {};
const keys = Object.keys(obj);
if (keys.length === 0) return <Text type="tertiary">{t('暂无项目')}</Text>;
if (keys.length === 0)
return <Text type='tertiary'>{t('暂无项目')}</Text>;
return renderLimitedItems({
items: keys,
renderItem: (key, idx) => (
<Tag key={idx} size="small" shape='circle' color={stringToColor(key)}>
<Tag
key={idx}
size='small'
shape='circle'
color={stringToColor(key)}
>
{key}
</Tag>
),
maxDisplay: 3,
});
}
const itemsArray = typeof items === 'string' ? JSON.parse(items) : items;
const itemsArray =
typeof items === 'string' ? JSON.parse(items) : items;
if (!Array.isArray(itemsArray) || itemsArray.length === 0) {
return <Text type="tertiary">{t('暂无项目')}</Text>;
return <Text type='tertiary'>{t('暂无项目')}</Text>;
}
return renderLimitedItems({
items: itemsArray,
renderItem: (item, idx) => (
<Tag key={idx} size="small" shape='circle' color={stringToColor(item)}>
<Tag
key={idx}
size='small'
shape='circle'
color={stringToColor(item)}
>
{item}
</Tag>
),
maxDisplay: 3,
});
} catch {
return <Text type="tertiary">{t('数据格式错误')}</Text>;
return <Text type='tertiary'>{t('数据格式错误')}</Text>;
}
},
},
@@ -178,20 +199,14 @@ const PrefillGroupManagement = ({ visible, onClose }) => {
width: 140,
render: (_, record) => (
<Space>
<Button
size="small"
onClick={() => handleEdit(record)}
>
<Button size='small' onClick={() => handleEdit(record)}>
{t('编辑')}
</Button>
<Popconfirm
title={t('确定删除此组?')}
onConfirm={() => deleteGroup(record.id)}
>
<Button
size="small"
type="danger"
>
<Button size='small' type='danger'>
{t('删除')}
</Button>
</Popconfirm>
@@ -209,7 +224,7 @@ const PrefillGroupManagement = ({ visible, onClose }) => {
return (
<>
<SideSheet
placement="left"
placement='left'
title={
<Space>
<Tag color='blue' shape='circle'>
@@ -235,14 +250,16 @@ const PrefillGroupManagement = ({ visible, onClose }) => {
</Avatar>
<div>
<Text className='text-lg font-medium'>{t('组列表')}</Text>
<div className='text-xs text-gray-600'>{t('管理模型、标签、端点等预填组')}</div>
<div className='text-xs text-gray-600'>
{t('管理模型、标签、端点等预填组')}
</div>
</div>
</div>
<div className="flex justify-end mb-4">
<div className='flex justify-end mb-4'>
<Button
type="primary"
type='primary'
theme='solid'
size="small"
size='small'
icon={<IconPlus />}
onClick={() => handleEdit()}
>
@@ -253,15 +270,21 @@ const PrefillGroupManagement = ({ visible, onClose }) => {
<CardTable
columns={columns}
dataSource={groups}
rowKey="id"
rowKey='id'
hidePagination={true}
size="small"
size='small'
scroll={{ x: 'max-content' }}
/>
) : (
<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 }}
/>
@@ -282,4 +305,4 @@ const PrefillGroupManagement = ({ visible, onClose }) => {
);
};
export default PrefillGroupManagement;
export default PrefillGroupManagement;