完善文件上传的接口对接
This commit is contained in:
@@ -7,13 +7,13 @@ import { FilterPanel, FilterSelect, SearchFilter } from "~/components/ui/FilterP
|
||||
import { Pagination } from "~/components/ui/Pagination";
|
||||
import { Table } from "~/components/ui/Table";
|
||||
import { Tag } from "~/components/ui/Tag";
|
||||
import { getConfigLists, getConfigOptions, updateConfigStatus, type ConfigItem } from "~/api/system_setting/config-lists";
|
||||
import configListsStyles from "~/styles/pages/config-lists_index.css?url";
|
||||
|
||||
export const links = () => [
|
||||
{ rel: "stylesheet", href: configListsStyles }
|
||||
];
|
||||
|
||||
|
||||
export const meta: MetaFunction = () => {
|
||||
return [
|
||||
{ title: "系统配置管理 - 中国烟草AI合同及卷宗审核系统" },
|
||||
@@ -54,165 +54,55 @@ export const MODULE_LABELS: Record<ConfigModule, string> = {
|
||||
[ConfigModule.NOTIFICATION]: '通知'
|
||||
};
|
||||
|
||||
// 配置数据类型
|
||||
interface ConfigDataType {
|
||||
[key: string]: string | number | boolean | string[] | ConfigDataType | ConfigDataType[];
|
||||
}
|
||||
|
||||
// 配置项模型
|
||||
interface ConfigItem {
|
||||
id: string;
|
||||
configName: string;
|
||||
module: ConfigModule;
|
||||
environment: ConfigEnvironment;
|
||||
isActive: boolean;
|
||||
configData: ConfigDataType;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
interface LoaderData {
|
||||
configs: ConfigItem[];
|
||||
totalCount: number;
|
||||
currentPage: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
types: string[];
|
||||
environments: string[];
|
||||
}
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
const url = new URL(request.url);
|
||||
const configName = url.searchParams.get("configName") || "";
|
||||
const module = url.searchParams.get("module") || "";
|
||||
const name = url.searchParams.get("name") || "";
|
||||
const type = url.searchParams.get("type") || "";
|
||||
const environment = url.searchParams.get("environment") || "";
|
||||
const isActive = url.searchParams.get("isActive") || "";
|
||||
const is_active = url.searchParams.get("is_active") ? url.searchParams.get("is_active") === "true" : undefined;
|
||||
const currentPage = parseInt(url.searchParams.get("page") || "1", 10);
|
||||
const pageSize = parseInt(url.searchParams.get("pageSize") || "10", 10);
|
||||
|
||||
try {
|
||||
// 模拟数据,实际项目中应从API获取
|
||||
const mockConfigs: ConfigItem[] = [
|
||||
{
|
||||
id: "1",
|
||||
configName: "database_connection",
|
||||
module: ConfigModule.SYSTEM,
|
||||
environment: ConfigEnvironment.PROD,
|
||||
isActive: true,
|
||||
configData: {
|
||||
database: {
|
||||
host: "db.cluster.com",
|
||||
port: 5432,
|
||||
pool_size: 20,
|
||||
ssl: true
|
||||
},
|
||||
cache: {
|
||||
ttl: 3600,
|
||||
max_entries: 1000
|
||||
},
|
||||
feature_flags: ["new_ui", "analytics_v2"]
|
||||
},
|
||||
createdAt: "2023-07-10 10:15:23",
|
||||
updatedAt: "2023-07-15 14:30:26"
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
configName: "text_extraction_ai",
|
||||
module: ConfigModule.AI,
|
||||
environment: ConfigEnvironment.TEST,
|
||||
isActive: true,
|
||||
configData: {
|
||||
model: "gpt-4",
|
||||
parameters: {
|
||||
temperature: 0.7,
|
||||
max_tokens: 2000
|
||||
},
|
||||
api_key: "sk-**********",
|
||||
timeout: 30
|
||||
},
|
||||
createdAt: "2023-07-12 08:45:12",
|
||||
updatedAt: "2023-07-14 09:15:33"
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
configName: "notification_service",
|
||||
module: ConfigModule.NOTIFICATION,
|
||||
environment: ConfigEnvironment.DEV,
|
||||
isActive: false,
|
||||
configData: {
|
||||
email: {
|
||||
smtp_server: "smtp.example.com",
|
||||
port: 587,
|
||||
use_tls: true,
|
||||
sender: "noreply@example.com"
|
||||
},
|
||||
sms: {
|
||||
provider: "aliyun",
|
||||
region: "cn-hangzhou",
|
||||
sign_name: "AI审核系统"
|
||||
}
|
||||
},
|
||||
createdAt: "2023-07-05 13:20:45",
|
||||
updatedAt: "2023-07-10 16:45:19"
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
configName: "file_storage",
|
||||
module: ConfigModule.FILE,
|
||||
environment: ConfigEnvironment.PROD,
|
||||
isActive: true,
|
||||
configData: {
|
||||
type: "oss",
|
||||
region: "cn-shanghai",
|
||||
bucket: "contracts-ai-review",
|
||||
access_control: "private",
|
||||
lifecycle_rules: [
|
||||
{
|
||||
prefix: "temp/",
|
||||
ttl_days: 7
|
||||
}
|
||||
]
|
||||
},
|
||||
createdAt: "2023-06-28 09:30:18",
|
||||
updatedAt: "2023-07-08 11:22:07"
|
||||
}
|
||||
];
|
||||
|
||||
// 过滤数据
|
||||
let filteredConfigs = [...mockConfigs];
|
||||
|
||||
if (configName) {
|
||||
filteredConfigs = filteredConfigs.filter(config =>
|
||||
config.configName.toLowerCase().includes(configName.toLowerCase())
|
||||
);
|
||||
// 获取配置列表
|
||||
const configsResponse = await getConfigLists({
|
||||
name,
|
||||
type,
|
||||
environment,
|
||||
is_active,
|
||||
page: currentPage,
|
||||
pageSize
|
||||
});
|
||||
|
||||
if (configsResponse.error || !configsResponse.data) {
|
||||
throw new Error(configsResponse.error || "获取配置列表失败");
|
||||
}
|
||||
|
||||
if (module) {
|
||||
filteredConfigs = filteredConfigs.filter(config => config.module === module);
|
||||
|
||||
// 获取配置选项
|
||||
const optionsResponse = await getConfigOptions();
|
||||
|
||||
if (optionsResponse.error || !optionsResponse.data) {
|
||||
throw new Error(optionsResponse.error || "获取配置选项失败");
|
||||
}
|
||||
|
||||
if (environment) {
|
||||
filteredConfigs = filteredConfigs.filter(config => config.environment === environment);
|
||||
}
|
||||
|
||||
if (isActive) {
|
||||
const activeValue = isActive === 'true';
|
||||
filteredConfigs = filteredConfigs.filter(config => config.isActive === activeValue);
|
||||
}
|
||||
|
||||
// 计算分页信息
|
||||
const totalCount = filteredConfigs.length;
|
||||
const totalPages = Math.ceil(totalCount / pageSize);
|
||||
|
||||
// 分页截取
|
||||
const startIndex = (currentPage - 1) * pageSize;
|
||||
const endIndex = startIndex + pageSize;
|
||||
const paginatedConfigs = filteredConfigs.slice(startIndex, endIndex);
|
||||
|
||||
|
||||
return json<LoaderData>({
|
||||
configs: paginatedConfigs,
|
||||
totalCount,
|
||||
configs: configsResponse.data,
|
||||
totalCount: configsResponse.total,
|
||||
currentPage,
|
||||
pageSize,
|
||||
totalPages
|
||||
totalPages: Math.ceil(configsResponse.total / pageSize),
|
||||
types: optionsResponse.data.types,
|
||||
environments: optionsResponse.data.environments
|
||||
}, {
|
||||
headers: {
|
||||
"Cache-Control": "max-age=60, s-maxage=180"
|
||||
@@ -233,28 +123,18 @@ export async function action({ request }: ActionFunctionArgs) {
|
||||
return json({ success: false, error: "缺少配置ID" }, { status: 400 });
|
||||
}
|
||||
|
||||
// 进行更新启用和禁用的状态
|
||||
try {
|
||||
if (_action === 'toggleStatus') {
|
||||
const isActive = formData.get('isActive') === 'true';
|
||||
const newStatus = !isActive;
|
||||
const is_active = formData.get('is_active') === 'true';
|
||||
|
||||
// 实际项目中应调用API更新状态
|
||||
console.log(`切换配置 ${configId} 状态为: ${newStatus}`);
|
||||
const response = await updateConfigStatus(parseInt(configId as string), is_active);
|
||||
|
||||
// 模拟API调用
|
||||
// const response = await fetch(`/api/configs/${configId}/status`, {
|
||||
// method: 'PATCH',
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json',
|
||||
// },
|
||||
// body: JSON.stringify({ isActive: newStatus }),
|
||||
// });
|
||||
if (!response.success) {
|
||||
return json({ success: false, error: response.error }, { status: 500 });
|
||||
}
|
||||
|
||||
// if (!response.ok) {
|
||||
// throw new Error(`状态切换失败: ${response.status}`);
|
||||
// }
|
||||
|
||||
return json({ success: true, newStatus });
|
||||
return json({ success: true });
|
||||
}
|
||||
|
||||
return json({ success: false, error: "未知操作" }, { status: 400 });
|
||||
@@ -275,7 +155,7 @@ export function ErrorBoundary() {
|
||||
}
|
||||
|
||||
export default function ConfigListsIndex() {
|
||||
const { configs, totalCount, currentPage, pageSize } = useLoaderData<typeof loader>();
|
||||
const { configs, totalCount, currentPage, pageSize, types, environments } = useLoaderData<typeof loader>();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const submit = useSubmit();
|
||||
const [showDetailModal, setShowDetailModal] = useState(false);
|
||||
@@ -300,9 +180,9 @@ export default function ConfigListsIndex() {
|
||||
const handleConfigNameSearch = (value: string) => {
|
||||
const newParams = new URLSearchParams(searchParams);
|
||||
if (value) {
|
||||
newParams.set('configName', value);
|
||||
newParams.set('name', value);
|
||||
} else {
|
||||
newParams.delete('configName');
|
||||
newParams.delete('name');
|
||||
}
|
||||
|
||||
// 搜索时,重置到第一页
|
||||
@@ -312,11 +192,11 @@ export default function ConfigListsIndex() {
|
||||
};
|
||||
|
||||
const handleToggleStatus = (config: ConfigItem) => {
|
||||
if (window.confirm(`确定要${config.isActive ? '禁用' : '启用'}该配置吗?`)) {
|
||||
if (window.confirm(`确定要${config.is_active ? '禁用' : '启用'}该配置吗?`)) {
|
||||
const formData = new FormData();
|
||||
formData.append('_action', 'toggleStatus');
|
||||
formData.append('configId', config.id);
|
||||
formData.append('isActive', String(config.isActive));
|
||||
formData.append('configId', config.id.toString());
|
||||
formData.append('is_active', String(!config.is_active));
|
||||
|
||||
submit(formData, { method: 'post' });
|
||||
}
|
||||
@@ -342,10 +222,20 @@ export default function ConfigListsIndex() {
|
||||
|
||||
// 处理重置筛选
|
||||
const handleReset = () => {
|
||||
const nameInput = document.querySelector('input[placeholder="请输入配置名称"]') as HTMLInputElement;
|
||||
const typeSelect = document.querySelector('select[name="type"]') as HTMLInputElement;
|
||||
const environmentSelect = document.querySelector('select[name="environment"]') as HTMLInputElement;
|
||||
const statusSelect = document.querySelector('select[name="is_active"]') as HTMLInputElement;
|
||||
|
||||
setSearchParams(new URLSearchParams());
|
||||
|
||||
if(nameInput) nameInput.value = ''
|
||||
if(typeSelect) typeSelect.value = ''
|
||||
if(environmentSelect) environmentSelect.value = ''
|
||||
if(statusSelect) statusSelect.value = ''
|
||||
|
||||
};
|
||||
|
||||
|
||||
// 关闭详情模态框
|
||||
const closeDetailModal = () => {
|
||||
setShowDetailModal(false);
|
||||
@@ -356,43 +246,42 @@ export default function ConfigListsIndex() {
|
||||
const columns = [
|
||||
{
|
||||
title: "配置名称",
|
||||
dataIndex: "configName" as keyof ConfigItem,
|
||||
key: "configName",
|
||||
dataIndex: "name" as keyof ConfigItem,
|
||||
key: "name",
|
||||
width: "20%"
|
||||
},
|
||||
{
|
||||
title: "所属模块",
|
||||
key: "module",
|
||||
key: "type",
|
||||
width: "10%",
|
||||
render: (_: unknown, record: ConfigItem) => MODULE_LABELS[record.module]
|
||||
render: (_: unknown, record: ConfigItem) => record.type
|
||||
},
|
||||
{
|
||||
title: "环境",
|
||||
key: "environment",
|
||||
width: "15%",
|
||||
render: (_: unknown, record: ConfigItem) => {
|
||||
const envClass = `env-tag env-tag-${record.environment}`;
|
||||
return (
|
||||
<span className={envClass}>
|
||||
{ENVIRONMENT_LABELS[record.environment]}
|
||||
<span className="env-tag">
|
||||
{record.environment}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
key: "isActive",
|
||||
key: "is_active",
|
||||
width: "15%",
|
||||
render: (_: unknown, record: ConfigItem) => (
|
||||
<Tag color={record.isActive ? 'green' : 'red'}>
|
||||
{record.isActive ? '已启用' : '已禁用'}
|
||||
<Tag color={record.is_active ? 'green' : 'red'}>
|
||||
{record.is_active ? '已启用' : '已禁用'}
|
||||
</Tag>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "最后更新时间",
|
||||
dataIndex: "updatedAt" as keyof ConfigItem,
|
||||
key: "updatedAt",
|
||||
dataIndex: "updated_at" as keyof ConfigItem,
|
||||
key: "updated_at",
|
||||
width: "15%"
|
||||
},
|
||||
{
|
||||
@@ -417,29 +306,17 @@ export default function ConfigListsIndex() {
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
className={`operation-btn ${record.isActive ? '!text-[--color-warning]' : '!text-[--color-success]'}`}
|
||||
className={`operation-btn ${record.is_active ? '!text-[--color-warning]' : '!text-[--color-success]'}`}
|
||||
onClick={() => handleToggleStatus(record)}
|
||||
>
|
||||
<i className={record.isActive ? `ri-stop-circle-line` : `ri-play-circle-line`}></i>
|
||||
{record.isActive ? '禁用' : '启用'}
|
||||
<i className={record.is_active ? `ri-stop-circle-line` : `ri-play-circle-line`}></i>
|
||||
{record.is_active ? '禁用' : '启用'}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
// 生成环境选项
|
||||
const environmentOptions = Object.entries(ENVIRONMENT_LABELS).map(([value, label]) => ({
|
||||
value,
|
||||
label
|
||||
}));
|
||||
|
||||
// 生成模块选项
|
||||
const moduleOptions = Object.entries(MODULE_LABELS).map(([value, label]) => ({
|
||||
value,
|
||||
label
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="config-lists">
|
||||
{/* 页面头部 */}
|
||||
@@ -458,9 +335,9 @@ export default function ConfigListsIndex() {
|
||||
<Button type="default" icon="ri-refresh-line" onClick={handleReset} className="mr-2">
|
||||
重置
|
||||
</Button>
|
||||
<Button type="primary" icon="ri-search-line">
|
||||
{/* <Button type="primary" icon="ri-search-line">
|
||||
查询
|
||||
</Button>
|
||||
</Button> */}
|
||||
</>
|
||||
}
|
||||
noActionDivider={true}
|
||||
@@ -468,7 +345,7 @@ export default function ConfigListsIndex() {
|
||||
<SearchFilter
|
||||
label="配置名称"
|
||||
placeholder="请输入配置名称"
|
||||
value={searchParams.get('configName') || ''}
|
||||
value={searchParams.get('name') || ''}
|
||||
onSearch={handleConfigNameSearch}
|
||||
className="flex-1 min-w-[200px]"
|
||||
instantSearch={true}
|
||||
@@ -476,9 +353,9 @@ export default function ConfigListsIndex() {
|
||||
|
||||
<FilterSelect
|
||||
label="所属模块"
|
||||
name="module"
|
||||
value={searchParams.get('module') || ''}
|
||||
options={[{ value: '', label: '全部' }, ...moduleOptions]}
|
||||
name="type"
|
||||
value={searchParams.get('type') || ''}
|
||||
options={[ ...types.map(type => ({ value: type, label: type }))]}
|
||||
onChange={handleFilterChange}
|
||||
className="flex-1 min-w-[200px]"
|
||||
/>
|
||||
@@ -487,17 +364,16 @@ export default function ConfigListsIndex() {
|
||||
label="环境"
|
||||
name="environment"
|
||||
value={searchParams.get('environment') || ''}
|
||||
options={[{ value: '', label: '全部' }, ...environmentOptions]}
|
||||
options={[ ...environments.map(env => ({ value: env, label: env }))]}
|
||||
onChange={handleFilterChange}
|
||||
className="flex-1 min-w-[200px]"
|
||||
/>
|
||||
|
||||
<FilterSelect
|
||||
label="状态"
|
||||
name="isActive"
|
||||
value={searchParams.get('isActive') || ''}
|
||||
name="is_active"
|
||||
value={searchParams.get('is_active') || ''}
|
||||
options={[
|
||||
{ value: '', label: '全部' },
|
||||
{ value: 'true', label: '已启用' },
|
||||
{ value: 'false', label: '已禁用' }
|
||||
]}
|
||||
@@ -545,19 +421,19 @@ export default function ConfigListsIndex() {
|
||||
<div className="config-detail-content">
|
||||
<div className="config-detail-item">
|
||||
<div className="config-detail-label">配置名称</div>
|
||||
<div className="config-detail-value">{selectedConfig.configName}</div>
|
||||
<div className="config-detail-value">{selectedConfig.name}</div>
|
||||
</div>
|
||||
|
||||
<div className="config-detail-item">
|
||||
<div className="config-detail-label">所属模块</div>
|
||||
<div className="config-detail-value">{MODULE_LABELS[selectedConfig.module]}</div>
|
||||
<div className="config-detail-value">{selectedConfig.type}</div>
|
||||
</div>
|
||||
|
||||
<div className="config-detail-item">
|
||||
<div className="config-detail-label">环境</div>
|
||||
<div className="config-detail-value">
|
||||
<span className={`env-tag env-tag-${selectedConfig.environment}`}>
|
||||
{ENVIRONMENT_LABELS[selectedConfig.environment]}
|
||||
<span className="env-tag">
|
||||
{selectedConfig.environment}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -565,8 +441,8 @@ export default function ConfigListsIndex() {
|
||||
<div className="config-detail-item">
|
||||
<div className="config-detail-label">状态</div>
|
||||
<div className="config-detail-value">
|
||||
<Tag color={selectedConfig.isActive ? 'green' : 'red'}>
|
||||
{selectedConfig.isActive ? '已启用' : '已禁用'}
|
||||
<Tag color={selectedConfig.is_active ? 'green' : 'red'}>
|
||||
{selectedConfig.is_active ? '已启用' : '已禁用'}
|
||||
</Tag>
|
||||
</div>
|
||||
</div>
|
||||
@@ -574,19 +450,19 @@ export default function ConfigListsIndex() {
|
||||
<div className="config-detail-item">
|
||||
<div className="config-detail-label">配置数据</div>
|
||||
<pre className="config-detail-code">
|
||||
{JSON.stringify(selectedConfig.configData, null, 2)}
|
||||
{JSON.stringify(selectedConfig.config, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="config-detail-item">
|
||||
<div className="config-detail-label">创建时间</div>
|
||||
<div className="config-detail-value">{selectedConfig.createdAt}</div>
|
||||
<div className="config-detail-value">{selectedConfig.created_at}</div>
|
||||
</div>
|
||||
|
||||
<div className="config-detail-item">
|
||||
<div className="config-detail-label">更新时间</div>
|
||||
<div className="config-detail-value">{selectedConfig.updatedAt}</div>
|
||||
<div className="config-detail-value">{selectedConfig.updated_at}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user