import { useState, useEffect, useRef } from "react"; import { useNavigate, useSearchParams, useLoaderData } from "@remix-run/react"; 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"; import { Modal } from "~/components/ui/Modal"; import { usePermission } from "~/hooks/usePermission"; import { getEntryModuleById, createEntryModule, updateEntryModule, 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 = () => { return [ { title: "入口模块编辑 - 中国烟草AI合同及卷宗审核系统" }, { name: "description", content: "创建或编辑入口模块" }, ]; }; export const handle = { breadcrumb: "新建/编辑入口模块", previousRoute: { title: "入口模块管理", to: "/entry-modules" } }; // 定义加载器返回的数据类型 interface LoaderData { module?: EntryModule; error?: string; } // 🔑 客户端加载函数 - 在浏览器端执行,axios-client 会自动添加 JWT export async function clientLoader({ request }: ClientLoaderFunctionArgs) { try { const url = new URL(request.url); const id = url.searchParams.get('id'); if (id) { // ✅ 不需要传递 JWT,axios-client 会自动处理 const moduleResponse = await getEntryModuleById(parseInt(id)); if (moduleResponse.error) { throw new Error(moduleResponse.error); } return { module: moduleResponse.data }; } return {}; } catch (error) { console.error("加载入口模块失败:", error); return { error: error instanceof Error ? error.message : "加载入口模块失败" }; } } // 地区选项 const AREA_OPTIONS = [ { value: "梅州", label: "梅州" }, { value: "云浮", label: "云浮" }, { value: "揭阳", label: "揭阳" }, { value: "潮州", label: "潮州" }, { value: "省局", label: "省局" } ]; // 入口模块新建/编辑组件 export default function EntryModuleNew() { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const { module, error } = useLoaderData(); const id = searchParams.get('id'); const isEditMode = !!id; // ✅ 使用权限 Hook const { canCreate, canUpdate } = usePermission(); const canCreateModule = canCreate('entry_module'); const canUpdateModule = canUpdate('entry_module'); // ✅ 根据当前操作类型判断权限 const hasEditPermission = isEditMode ? canUpdateModule : canCreateModule; const isReadOnly = !hasEditPermission; // 表单状态 const [name, setName] = useState(module?.name || ''); const [description, setDescription] = useState(module?.description || ''); // 🔑 从 AreaConfig[] 提取地区名称数组 const [selectedAreas, setSelectedAreas] = useState( module?.areas ? module.areas.map(a => a.area) : [] ); const [logoFile, setLogoFile] = useState(null); const [logoPreview, setLogoPreview] = useState( module?.path ? `${DOCUMENT_URL}${module.path}` : null ); const [isSubmitting, setIsSubmitting] = useState(false); const [showConfirmModal, setShowConfirmModal] = useState(false); const fileInputRef = useRef(null); // ✅ 页面加载时检查权限并提示(仅在只读模式下提示) useEffect(() => { if (isReadOnly) { if (isEditMode) { toastService.info('当前为查看模式,您没有编辑权限'); } else { toastService.warning('您没有创建入口模块的权限'); } } }, [isReadOnly, isEditMode]); // 处理loader加载数据的时候的错误 useEffect(() => { if (error) { toastService.error(error); } }, [error]); // 处理logo文件选择 const handleLogoChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { // 验证文件类型 if (!file.type.startsWith('image/')) { toastService.error('请选择图片文件'); return; } // 验证文件大小(限制5MB) if (file.size > 5 * 1024 * 1024) { toastService.error('图片大小不能超过5MB'); return; } setLogoFile(file); // 生成预览 const reader = new FileReader(); reader.onload = (event) => { setLogoPreview(event.target?.result as string); }; reader.readAsDataURL(file); } }; // 处理地区选择 const handleAreaToggle = (area: string) => { setSelectedAreas(prev => { if (prev.includes(area)) { return prev.filter(a => a !== area); } else { return [...prev, area]; } }); }; // 验证表单 const validateForm = () => { if (!name.trim()) { toastService.error('请输入模块名称'); return false; } if (selectedAreas.length === 0) { toastService.error('请至少选择一个适用地区'); return false; } return true; }; // 上传logo图片 const uploadLogo = async (): Promise => { if (!logoFile) return module?.path || null; try { const formData = new FormData(); 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 ${token}` }, body: formData }); if (!response.ok) { throw new Error('图片上传失败'); } const result = await response.json(); console.log('图片上传结果:', result); // 根据后端返回的数据结构提取路径 if (result.data?.path) { return result.data.path; } else if (result.path) { return result.path; } else { throw new Error('未获取到图片路径'); } } catch (error) { console.error('上传logo失败:', error); throw error; } }; // 处理表单提交 const handleSubmit = async () => { // ✅ Runtime permission check if (isEditMode && !canUpdateModule) { toastService.warning('您没有修改权限,无法保存更改'); return; } if (!isEditMode && !canCreateModule) { toastService.warning('您没有创建权限,无法新增入口模块'); return; } if (!validateForm()) return; setIsSubmitting(true); try { // 上传logo let logoPath = module?.path || null; if (logoFile) { logoPath = await uploadLogo(); } // 🔑 准备提交数据 // areas 字段会在 API 层自动转换为 AreaConfig[] 格式 const moduleData = { name: name.trim(), description: description.trim() || undefined, path: logoPath, areas: selectedAreas // 字符串数组,API会自动转换 }; // ✅ 不需要传递 JWT,axios-client 会自动处理 let result; if (isEditMode) { result = await updateEntryModule(parseInt(id!), moduleData); } else { result = await createEntryModule(moduleData); } if (result.error) { toastService.error(result.error); return; } toastService.success(isEditMode ? '更新成功!' : '创建成功!'); setTimeout(() => { navigate('/entry-modules'); }, 1000); } catch (error) { console.error('提交失败:', error); toastService.error(error instanceof Error ? error.message : '操作失败,请重试'); } finally { setIsSubmitting(false); } }; // 处理取消 const handleCancel = () => { setShowConfirmModal(true); }; // 确认取消 const confirmCancel = () => { navigate('/entry-modules'); }; return (
{/* 页面头部 */}

{isEditMode ? (isReadOnly ? '查看入口模块' : '编辑入口模块') : '新建入口模块'}

{isEditMode ? (isReadOnly ? '查看入口模块信息' : '修改入口模块信息') : '创建新的入口模块'}

{/* 表单内容 */}
{/* 模块名称 */}
setName(e.target.value)} placeholder="请输入模块名称,如:合同管理" maxLength={255} className="w-full px-3 py-2 border border-gray-300 rounded-md" disabled={isReadOnly} />
{/* 描述 */}