完善文件上传的接口对接

This commit is contained in:
2025-04-08 22:07:14 +08:00
parent 5cf05eca40
commit fda6515891
9 changed files with 1309 additions and 892 deletions
+105 -209
View File
@@ -4,6 +4,7 @@ import { useEffect, useState } from "react";
import { Button } from "~/components/ui/Button";
import { Card } from "~/components/ui/Card";
import { ConfigModule, MODULE_LABELS, ENVIRONMENT_LABELS } from "./config-lists._index";
import { getConfigOptions, getConfigDetail, createConfig, updateConfig } from "~/api/system_setting/config-lists";
import configNewStyles from "~/styles/pages/config-lists_new.css?url";
export const links = () => [
@@ -40,17 +41,19 @@ export const EXTENDED_ENVIRONMENT_LABELS: Record<string, string> = {
interface ConfigData {
id: string;
configName: string;
module: ConfigModule;
environment: string; // 使用扩展的环境类型
isActive: boolean;
configData: string; // JSON字符串
remarks?: string; // 添加备注字段
name: string;
type: string;
environment: string;
is_active: boolean;
config: Record<string, unknown>;
remarks?: string;
}
interface LoaderData {
config?: ConfigData;
isEdit: boolean;
types: string[];
environments: string[];
}
export async function loader({ request }: LoaderFunctionArgs) {
@@ -58,156 +61,72 @@ export async function loader({ request }: LoaderFunctionArgs) {
const id = url.searchParams.get("id");
let config: ConfigData | undefined = undefined;
if (id) {
try {
// 实际应用中,应从API获取配置详情
// const response = await fetch(`${process.env.API_BASE_URL}/api/configs/${id}`);
// if (!response.ok) throw new Error(`获取配置详情失败: ${response.status}`);
// config = await response.json();
// config.configData = JSON.stringify(config.configData, null, 2);
// 使用模拟数据
if (id === "1") {
config = {
id: "1",
configName: "database_connection",
module: ConfigModule.SYSTEM,
environment: ExtendedConfigEnvironment.PROD,
isActive: true,
remarks: "数据库连接配置,包含主库和从库配置",
configData: JSON.stringify({
database: {
host: "db.cluster.com",
port: 5432,
pool_size: 20,
ssl: true
},
cache: {
ttl: 3600,
max_entries: 1000
},
feature_flags: ["new_ui", "analytics_v2"]
}, null, 2)
};
} else if (id === "2") {
config = {
id: "2",
configName: "text_extraction_ai",
module: ConfigModule.AI,
environment: ExtendedConfigEnvironment.TEST,
isActive: true,
remarks: "AI文本抽取服务配置",
configData: JSON.stringify({
model: "gpt-4",
parameters: {
temperature: 0.7,
max_tokens: 2000
},
api_key: "sk-**********",
timeout: 30
}, null, 2)
};
} else if (id === "3") {
config = {
id: "3",
configName: "notification_service",
module: ConfigModule.NOTIFICATION,
environment: ExtendedConfigEnvironment.DEV,
isActive: false,
remarks: "通知服务配置,目前处于开发测试阶段",
configData: JSON.stringify({
email: {
smtp_server: "smtp.example.com",
port: 587,
use_tls: true,
sender: "noreply@example.com"
},
sms: {
provider: "aliyun",
region: "cn-hangzhou",
sign_name: "AI审核系统"
}
}, null, 2)
};
} else if (id === "4") {
config = {
id: "4",
configName: "file_storage",
module: ConfigModule.FILE,
environment: ExtendedConfigEnvironment.COMMON,
isActive: true,
remarks: "文件存储通用配置,适用于所有环境",
configData: JSON.stringify({
type: "oss",
region: "cn-shanghai",
bucket: "contracts-ai-review",
access_control: "private",
lifecycle_rules: [
{
prefix: "temp/",
ttl_days: 7
}
]
}, null, 2)
};
}
} catch (error) {
console.error("获取配置详情失败:", error);
// 在实际应用中,应该将错误信息返回给客户端
// 这里简单处理,返回空config
}
// 获取配置选项
const optionsResponse = await getConfigOptions();
if (optionsResponse.error) {
throw new Error(optionsResponse.error);
}
return Response.json({
if (id) {
// 获取配置详情
const detailResponse = await getConfigDetail(id);
if (detailResponse.error) {
throw new Error(detailResponse.error);
}
config = detailResponse.data;
}
return json<LoaderData>({
config,
isEdit: !!config
isEdit: !!config,
types: optionsResponse.data.types,
environments: optionsResponse.data.environments
});
}
interface ActionData {
success?: boolean;
errors?: {
configName?: string;
module?: string;
name?: string;
type?: string;
environment?: string;
configData?: string;
config?: string;
general?: string;
};
}
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const configId = formData.get("id") as string;
const configName = formData.get("configName") as string;
const module = formData.get("module") as string;
const id = formData.get("id") as string;
const name = formData.get("name") as string;
const type = formData.get("type") as string;
const environment = formData.get("environment") as string;
const configData = formData.get("configData") as string;
const isActive = formData.get("isActive") === "true";
const config = formData.get("config") as string;
const is_active = formData.get("is_active") === "true";
const remarks = formData.get("remarks") as string;
const errors: ActionData["errors"] = {};
// 表单验证
if (!configName || configName.trim() === "") {
errors.configName = "配置名称不能为空";
if (!name || name.trim() === "") {
errors.name = "配置名称不能为空";
}
if (!module) {
errors.module = "请选择所属模块";
if (!type) {
errors.type = "请选择所属模块";
}
if (!environment) {
errors.environment = "请选择环境";
}
if (!configData || configData.trim() === "") {
errors.configData = "配置数据不能为空";
if (!config || config.trim() === "") {
errors.config = "配置数据不能为空";
} else {
try {
JSON.parse(configData);
JSON.parse(config);
} catch (e) {
errors.configData = "配置数据必须是有效的JSON格式";
errors.config = "配置数据必须是有效的JSON格式";
}
}
@@ -216,31 +135,29 @@ export async function action({ request }: ActionFunctionArgs) {
}
try {
// 实际应用中,应调用API保存数据
console.log("保存配置:", { configId, configName, module, environment, configData, isActive, remarks });
const configData = {
name,
type,
environment,
config: JSON.parse(config),
is_active,
remarks
};
// 模拟API调用
// const response = await fetch(`${process.env.API_BASE_URL}/api/configs${configId ? `/${configId}` : ''}`, {
// method: configId ? "PUT" : "POST",
// headers: {
// "Content-Type": "application/json",
// },
// body: JSON.stringify({
// id: configId,
// configName,
// module,
// environment,
// configData: JSON.parse(configData),
// isActive,
// remarks,
// }),
// });
//
// if (!response.ok) {
// throw new Error(`保存失败: ${response.status}`);
// }
if (id) {
// 更新配置
const response = await updateConfig(id, configData);
if (response.error) {
throw new Error(response.error);
}
} else {
// 创建配置
const response = await createConfig(configData);
if (response.error) {
throw new Error(response.error);
}
}
// 保存成功后重定向到列表页
return redirect("/config-lists");
} catch (error) {
console.error("保存配置失败:", error);
@@ -290,8 +207,7 @@ const JSON_TEMPLATES = {
};
export default function ConfigNew() {
const { config, isEdit } = useLoaderData<typeof loader>();
const { config, isEdit, types, environments } = useLoaderData<typeof loader>();
const actionData = useActionData<typeof action>();
const navigation = useNavigation();
const isSubmitting = navigation.state === "submitting";
@@ -306,16 +222,9 @@ export default function ConfigNew() {
useEffect(() => {
// 初始化配置数据
if (config?.configData) {
setConfigDataValue(config.configData);
}
// 初始化模块和环境的选中状态
if (config?.module) {
setSelectedModule(config.module);
}
if (config?.environment) {
if (config) {
setConfigDataValue(JSON.stringify(config.config, null, 2));
setSelectedModule(config.type);
setSelectedEnvironment(config.environment);
}
@@ -333,7 +242,6 @@ export default function ConfigNew() {
},
feature_flags: ["new_ui", "analytics_v2"]
}, null, 2));
}, [config]);
// 处理JSON数据变更
@@ -382,16 +290,6 @@ export default function ConfigNew() {
setJsonError(null);
};
// 模块标签点击
const handleModuleTagClick = (module: string) => {
setSelectedModule(module);
};
// 环境标签点击
const handleEnvironmentTagClick = (env: string) => {
setSelectedEnvironment(env);
};
// 显示JSON语法高亮
const renderJsonWithSyntaxHighlight = (json: string) => {
try {
@@ -452,18 +350,18 @@ export default function ConfigNew() {
{/* 配置名称和状态 */}
<div className="form-row">
<div className="form-group">
<label htmlFor="configName" className="form-label required"></label>
<label htmlFor="name" className="form-label required"></label>
<input
type="text"
id="configName"
name="configName"
className={`form-input ${actionData?.errors?.configName ? 'input-error' : ''}`}
defaultValue={config?.configName || ''}
id="name"
name="name"
className={`form-input ${actionData?.errors?.name ? 'input-error' : ''}`}
defaultValue={config?.name || ''}
placeholder="请输入配置名称,如database_connection"
required
/>
{actionData?.errors?.configName && (
<div className="error-message">{actionData.errors.configName}</div>
{actionData?.errors?.name && (
<div className="error-message">{actionData.errors.name}</div>
)}
<div className="form-help">
使使线
@@ -471,18 +369,18 @@ export default function ConfigNew() {
</div>
<div className="form-group">
<label htmlFor="isActive" className="form-label"></label>
<label htmlFor="is_active" className="form-label"></label>
<div className="mt-2">
<div className="flex items-center">
<input
type="checkbox"
id="isActive"
name="isActive"
id="is_active"
name="is_active"
value="true"
className="form-checkbox"
defaultChecked={config?.isActive !== false}
defaultChecked={config?.is_active !== false}
/>
<label htmlFor="isActive" className="form-checkbox-label">
<label htmlFor="is_active" className="form-checkbox-label">
</label>
</div>
@@ -495,33 +393,33 @@ export default function ConfigNew() {
{/* 所属模块 */}
<div className="form-group">
<label htmlFor="module" className="form-label required"></label>
<label htmlFor="type" className="form-label required"></label>
<input
type="hidden"
name="module"
name="type"
value={selectedModule}
/>
<input
type="text"
id="moduleDisplay"
className={`form-input ${actionData?.errors?.module ? 'input-error' : ''}`}
value={selectedModule ? MODULE_LABELS[selectedModule as ConfigModule] || selectedModule : ''}
id="typeDisplay"
className={`form-input ${actionData?.errors?.type ? 'input-error' : ''}`}
value={selectedModule}
onChange={(e) => setSelectedModule(e.target.value)}
placeholder="请输入或选择所属模块"
readOnly
required
/>
{actionData?.errors?.module && (
<div className="error-message">{actionData.errors.module}</div>
{actionData?.errors?.type && (
<div className="error-message">{actionData.errors.type}</div>
)}
<div className="tag-buttons mt-2">
{Object.entries(MODULE_LABELS).map(([value, label]) => (
{types.map(type => (
<button
key={value}
key={type}
type="button"
className={`tag-button ${selectedModule === value ? 'active' : ''}`}
onClick={() => handleModuleTagClick(value)}
className={`tag-button ${selectedModule === type ? 'active' : ''}`}
onClick={() => setSelectedModule(type)}
>
{label}
{type}
</button>
))}
</div>
@@ -542,23 +440,23 @@ export default function ConfigNew() {
type="text"
id="environmentDisplay"
className={`form-input ${actionData?.errors?.environment ? 'input-error' : ''}`}
value={selectedEnvironment ? EXTENDED_ENVIRONMENT_LABELS[selectedEnvironment] || selectedEnvironment : ''}
value={selectedEnvironment}
onChange={(e) => setSelectedEnvironment(e.target.value)}
placeholder="请输入或选择环境"
readOnly
required
/>
{actionData?.errors?.environment && (
<div className="error-message">{actionData.errors.environment}</div>
)}
<div className="tag-buttons mt-2">
{Object.entries(EXTENDED_ENVIRONMENT_LABELS).map(([value, label]) => (
{environments.map(env => (
<button
key={value}
key={env}
type="button"
className={`tag-button ${selectedEnvironment === value ? 'active' : ''}`}
onClick={() => handleEnvironmentTagClick(value)}
className={`tag-button ${selectedEnvironment === env ? 'active' : ''}`}
onClick={() => setSelectedEnvironment(env)}
>
{label}
{env}
</button>
))}
</div>
@@ -569,14 +467,14 @@ export default function ConfigNew() {
{/* 配置数据 */}
<div className="form-group">
<label htmlFor="configData" className="form-label required"> (JSON)</label>
<label htmlFor="config" className="form-label required"> (JSON)</label>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4" style={{ minHeight: '390px' }}>
{/* 左侧JSON编辑区 */}
<div className="h-full">
<textarea
id="configData"
name="configData"
className={`json-editor ${(actionData?.errors?.configData || jsonError) ? 'input-error' : ''}`}
id="config"
name="config"
className={`json-editor ${(actionData?.errors?.config || jsonError) ? 'input-error' : ''}`}
value={configDataValue}
onChange={handleConfigDataChange}
required
@@ -591,8 +489,8 @@ export default function ConfigNew() {
<i className="ri-braces-line mr-1"></i> JSON
</Button>
</div>
{(actionData?.errors?.configData || jsonError) && (
<div className="error-message">{actionData?.errors?.configData || jsonError}</div>
{(actionData?.errors?.config || jsonError) && (
<div className="error-message">{actionData?.errors?.config || jsonError}</div>
)}
</div>
@@ -663,8 +561,6 @@ export default function ConfigNew() {
<div className="error-message general-error">{actionData.errors.general}</div>
</div>
)}
</Form>
</Card>
</div>