fix: 1. 接入入口模块的管理接口,优化样式。
2. 将查看文档评查结果详情对接接口,采用接口的方式进行查询。
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useNavigate, useSearchParams, useLoaderData } from "@remix-run/react";
|
||||
import { ActionFunctionArgs, LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
|
||||
import { ClientLoaderFunctionArgs, MetaFunction } from "@remix-run/react";
|
||||
import { Card } from "~/components/ui/Card";
|
||||
import { Button } from "~/components/ui/Button";
|
||||
import { toastService } from "~/components/ui/Toast";
|
||||
@@ -9,9 +9,18 @@ import {
|
||||
getEntryModuleById,
|
||||
createEntryModule,
|
||||
updateEntryModule,
|
||||
type EntryModule
|
||||
type EntryModule,
|
||||
type AreaConfig
|
||||
} from "~/api/entry-modules/entry-modules";
|
||||
import { API_BASE_URL, DOCUMENT_URL } from "~/config/api-config";
|
||||
import entryModulesStyles from "~/styles/pages/entry-modules.css?url";
|
||||
|
||||
// 引入CSS样式
|
||||
export function links() {
|
||||
return [
|
||||
{ rel: "stylesheet", href: entryModulesStyles }
|
||||
];
|
||||
}
|
||||
|
||||
// 页面元数据
|
||||
export const meta: MetaFunction = () => {
|
||||
@@ -33,38 +42,31 @@ export const handle = {
|
||||
interface LoaderData {
|
||||
module?: EntryModule;
|
||||
error?: string;
|
||||
frontendJWT?: string | null;
|
||||
}
|
||||
|
||||
// 加载函数 - 获取入口模块数据(编辑模式)
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
// 🔑 客户端加载函数 - 在浏览器端执行,axios-client 会自动添加 JWT
|
||||
export async function clientLoader({ request }: ClientLoaderFunctionArgs) {
|
||||
try {
|
||||
const { getUserSession } = await import("~/api/login/auth.server");
|
||||
const { frontendJWT } = await getUserSession(request);
|
||||
|
||||
const url = new URL(request.url);
|
||||
const id = url.searchParams.get('id');
|
||||
|
||||
if (id) {
|
||||
const moduleResponse = await getEntryModuleById(parseInt(id), frontendJWT);
|
||||
// ✅ 不需要传递 JWT,axios-client 会自动处理
|
||||
const moduleResponse = await getEntryModuleById(parseInt(id));
|
||||
if (moduleResponse.error) {
|
||||
throw new Error(moduleResponse.error);
|
||||
}
|
||||
return Response.json({
|
||||
module: moduleResponse.data,
|
||||
frontendJWT
|
||||
});
|
||||
return {
|
||||
module: moduleResponse.data
|
||||
};
|
||||
}
|
||||
|
||||
return Response.json({ frontendJWT });
|
||||
return {};
|
||||
} catch (error) {
|
||||
console.error("加载入口模块失败:", error);
|
||||
return Response.json(
|
||||
{
|
||||
error: error || "加载入口模块失败",
|
||||
status: 500
|
||||
}
|
||||
);
|
||||
return {
|
||||
error: error instanceof Error ? error.message : "加载入口模块失败"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +83,7 @@ const AREA_OPTIONS = [
|
||||
export default function EntryModuleNew() {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const { module, error, frontendJWT } = useLoaderData<LoaderData>();
|
||||
const { module, error } = useLoaderData<LoaderData>();
|
||||
|
||||
const id = searchParams.get('id');
|
||||
const isEditMode = !!id;
|
||||
@@ -89,7 +91,10 @@ export default function EntryModuleNew() {
|
||||
// 表单状态
|
||||
const [name, setName] = useState(module?.name || '');
|
||||
const [description, setDescription] = useState(module?.description || '');
|
||||
const [selectedAreas, setSelectedAreas] = useState<string[]>(module?.areas || []);
|
||||
// 🔑 从 AreaConfig[] 提取地区名称数组
|
||||
const [selectedAreas, setSelectedAreas] = useState<string[]>(
|
||||
module?.areas ? module.areas.map(a => a.area) : []
|
||||
);
|
||||
const [logoFile, setLogoFile] = useState<File | null>(null);
|
||||
const [logoPreview, setLogoPreview] = useState<string | null>(
|
||||
module?.path ? `${DOCUMENT_URL}${module.path}` : null
|
||||
@@ -168,10 +173,14 @@ export default function EntryModuleNew() {
|
||||
formData.append('file', logoFile);
|
||||
formData.append('folder', 'entryModule');
|
||||
|
||||
// ✅ 不需要手动添加 Authorization 头
|
||||
// fetch 可以自动使用浏览器的认证信息,或者我们也可以使用 axios
|
||||
const token = localStorage.getItem('access_token');
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/admin/upload`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${frontendJWT}`
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: formData
|
||||
});
|
||||
@@ -210,18 +219,21 @@ export default function EntryModuleNew() {
|
||||
logoPath = await uploadLogo();
|
||||
}
|
||||
|
||||
// 🔑 准备提交数据
|
||||
// areas 字段会在 API 层自动转换为 AreaConfig[] 格式
|
||||
const moduleData = {
|
||||
name: name.trim(),
|
||||
description: description.trim() || undefined,
|
||||
path: logoPath,
|
||||
areas: selectedAreas
|
||||
areas: selectedAreas // 字符串数组,API会自动转换
|
||||
};
|
||||
|
||||
// ✅ 不需要传递 JWT,axios-client 会自动处理
|
||||
let result;
|
||||
if (isEditMode) {
|
||||
result = await updateEntryModule(parseInt(id!), moduleData, frontendJWT);
|
||||
result = await updateEntryModule(parseInt(id!), moduleData);
|
||||
} else {
|
||||
result = await createEntryModule(moduleData, frontendJWT);
|
||||
result = await createEntryModule(moduleData);
|
||||
}
|
||||
|
||||
if (result.error) {
|
||||
@@ -280,7 +292,7 @@ export default function EntryModuleNew() {
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="请输入模块名称,如:合同管理"
|
||||
maxLength={255}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -291,7 +303,7 @@ export default function EntryModuleNew() {
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
placeholder="请输入模块描述"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md"
|
||||
rows={4}
|
||||
/>
|
||||
</div>
|
||||
@@ -349,7 +361,7 @@ export default function EntryModuleNew() {
|
||||
type="checkbox"
|
||||
checked={selectedAreas.includes(option.value)}
|
||||
onChange={() => handleAreaToggle(option.value)}
|
||||
className="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
||||
className="w-4 h-4 border-gray-300 rounded cursor-pointer"
|
||||
/>
|
||||
<span className="text-sm text-gray-700">{option.label}</span>
|
||||
</label>
|
||||
|
||||
Reference in New Issue
Block a user