完善文件上传的接口对接
This commit is contained in:
+105
-209
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user