feat: 1.修改提示词模板的不用角色的操作权限。
2. 对接数据看板的数据。 3. 添加入口模块管理的页面。
This commit is contained in:
@@ -0,0 +1,430 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSearchParams, useNavigate, useLoaderData, useRouteLoaderData } from "@remix-run/react";
|
||||
import { ActionFunctionArgs, LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
|
||||
import { Table } from "~/components/ui/Table";
|
||||
import { Card } from "~/components/ui/Card";
|
||||
import { Button } from "~/components/ui/Button";
|
||||
import { Pagination } from "~/components/ui/Pagination";
|
||||
import { FilterPanel, FilterSelect, SearchFilter } from "~/components/ui/FilterPanel";
|
||||
import { toastService } from "~/components/ui/Toast";
|
||||
import {
|
||||
getEntryModules,
|
||||
deleteEntryModule,
|
||||
type EntryModule,
|
||||
type EntryModuleSearchParams
|
||||
} from "~/api/entry-modules/entry-modules";
|
||||
import entryModulesStyles from "~/styles/pages/entry-modules.css?url";
|
||||
import { DOCUMENT_URL } from "~/config/api-config";
|
||||
|
||||
// 引入CSS样式
|
||||
export function links() {
|
||||
return [
|
||||
{ rel: "stylesheet", href: entryModulesStyles }
|
||||
];
|
||||
}
|
||||
|
||||
// 页面元数据
|
||||
export const meta: MetaFunction = () => {
|
||||
return [
|
||||
{ title: "入口模块管理 - 中国烟草AI合同及卷宗审核系统" },
|
||||
{ name: "description", content: "管理入口模块,包括查看、编辑和删除入口模块" },
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
// 定义加载器返回的数据类型
|
||||
interface LoaderData {
|
||||
modules: EntryModule[];
|
||||
total: number;
|
||||
pageSize: number;
|
||||
currentPage: number;
|
||||
error?: string;
|
||||
frontendJWT?: string | null;
|
||||
}
|
||||
|
||||
// 加载函数 - 获取入口模块列表
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
try {
|
||||
// 获取用户会话信息
|
||||
const { getUserSession } = await import("~/api/login/auth.server");
|
||||
const { frontendJWT } = await getUserSession(request);
|
||||
|
||||
const url = new URL(request.url);
|
||||
const name = url.searchParams.get('name') || undefined;
|
||||
const area = url.searchParams.get('area') || undefined;
|
||||
const page = parseInt(url.searchParams.get('page') || '1', 10);
|
||||
const pageSize = parseInt(url.searchParams.get('pageSize') || '10', 10);
|
||||
|
||||
// 构建搜索参数
|
||||
const searchParams: EntryModuleSearchParams = {
|
||||
name,
|
||||
area,
|
||||
page,
|
||||
pageSize
|
||||
};
|
||||
|
||||
const modulesResponse = await getEntryModules(searchParams, frontendJWT);
|
||||
if (modulesResponse.error) {
|
||||
console.error("获取入口模块失败:", modulesResponse.error);
|
||||
throw new Error(modulesResponse.error);
|
||||
}
|
||||
const modulesResult = modulesResponse.data?.modules || [];
|
||||
|
||||
return Response.json({
|
||||
modules: modulesResult,
|
||||
total: modulesResponse.data?.total || modulesResult.length,
|
||||
pageSize,
|
||||
currentPage: page,
|
||||
frontendJWT
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("加载入口模块列表失败:", error);
|
||||
return Response.json(
|
||||
{
|
||||
modules: [],
|
||||
total: 0,
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
error: error instanceof Error ? error.message : "加载入口模块列表失败"
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 动作函数 - 处理删除请求
|
||||
export async function action({ request }: ActionFunctionArgs) {
|
||||
// 获取表单数据
|
||||
const formData = await request.formData();
|
||||
const id = formData.get("id") as string;
|
||||
const intent = formData.get("intent") as string;
|
||||
const { getUserSession } = await import("~/api/login/auth.server");
|
||||
const { frontendJWT } = await getUserSession(request);
|
||||
|
||||
if (intent === "delete" && id) {
|
||||
try {
|
||||
const result = await deleteEntryModule(parseInt(id), frontendJWT || undefined);
|
||||
|
||||
if (result.error) {
|
||||
return Response.json({ success: false, error: result.error }, { status: 500 });
|
||||
}
|
||||
|
||||
return Response.json({ success: true });
|
||||
} catch (error) {
|
||||
return Response.json(
|
||||
{ success: false, error: error instanceof Error ? error.message : "删除入口模块失败" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Response.json({ success: false, error: "无效的操作" }, { status: 400 });
|
||||
}
|
||||
|
||||
// 地区选项
|
||||
const AREA_OPTIONS = [
|
||||
{ value: "", label: "全部地区" },
|
||||
{ value: "梅州", label: "梅州" },
|
||||
{ value: "云浮", label: "云浮" },
|
||||
{ value: "揭阳", label: "揭阳" },
|
||||
{ value: "潮州", label: "潮州" },
|
||||
{ value: "省局", label: "省局" }
|
||||
];
|
||||
|
||||
// 入口模块列表组件
|
||||
export default function EntryModulesList() {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
|
||||
// 获取加载器数据
|
||||
const { modules, total, error, frontendJWT } = useLoaderData<LoaderData>();
|
||||
|
||||
// 获取用户角色并判断权限
|
||||
const rootData = useRouteLoaderData("root") as { userRole: string };
|
||||
const userRole = rootData?.userRole || 'common';
|
||||
const hasEditPermission = userRole.toLowerCase().includes('admin') || userRole.toLowerCase().includes('developer');
|
||||
|
||||
// 调试信息
|
||||
useEffect(() => {
|
||||
console.log('📋 [EntryModules] 用户角色:', userRole);
|
||||
console.log('📋 [EntryModules] 是否有编辑权限:', hasEditPermission);
|
||||
}, [userRole, hasEditPermission]);
|
||||
|
||||
// 获取搜索参数
|
||||
const name = searchParams.get('name') || '';
|
||||
const area = searchParams.get('area') || '';
|
||||
const currentPage = parseInt(searchParams.get('page') || String(1), 10);
|
||||
const pageSize = parseInt(searchParams.get('pageSize') || String(10), 10);
|
||||
|
||||
// 处理loader加载数据的时候的错误
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
toastService.error(error);
|
||||
}
|
||||
}, [error]);
|
||||
|
||||
// 处理名称搜索
|
||||
const handleNameSearch = (value: string) => {
|
||||
const newParams = new URLSearchParams(searchParams);
|
||||
if (value) {
|
||||
newParams.set('name', value);
|
||||
} else {
|
||||
newParams.delete('name');
|
||||
}
|
||||
newParams.set('page', '1');
|
||||
setSearchParams(newParams);
|
||||
};
|
||||
|
||||
// 处理筛选变更
|
||||
const handleFilterChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const { name, value } = e.target;
|
||||
const newParams = new URLSearchParams(searchParams);
|
||||
|
||||
if (value) {
|
||||
newParams.set(name, value);
|
||||
} else {
|
||||
newParams.delete(name);
|
||||
}
|
||||
|
||||
// 切换筛选条件时,重置到第一页
|
||||
newParams.set('page', '1');
|
||||
|
||||
setSearchParams(newParams);
|
||||
};
|
||||
|
||||
// 处理重置筛选
|
||||
const handleReset = () => {
|
||||
const nameInput = document.querySelector('input[placeholder="请输入入口模块名称"]');
|
||||
if (nameInput) {
|
||||
(nameInput as HTMLInputElement).value = '';
|
||||
}
|
||||
|
||||
// 重置所有筛选条件
|
||||
setSearchParams(new URLSearchParams());
|
||||
};
|
||||
|
||||
// 处理删除入口模块
|
||||
const handleDelete = async (id: number) => {
|
||||
if (confirm('确定要删除该入口模块吗?此操作不可撤销。')) {
|
||||
setIsDeleting(true);
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('id', id.toString());
|
||||
formData.append('intent', 'delete');
|
||||
|
||||
const response = await fetch('/entry-modules', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toastService.success('删除成功!');
|
||||
// 刷新页面
|
||||
window.location.reload();
|
||||
} else {
|
||||
toastService.error(`删除失败: ${result.error || '未知错误'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
toastService.error(`删除失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 处理编辑入口模块
|
||||
const handleEdit = (id: number) => {
|
||||
navigate(`/entry-modules/new?id=${id}`);
|
||||
};
|
||||
|
||||
// 处理分页变更
|
||||
const handlePageChange = (page: number) => {
|
||||
const newParams = new URLSearchParams(searchParams);
|
||||
newParams.set('page', page.toString());
|
||||
setSearchParams(newParams);
|
||||
};
|
||||
|
||||
// 处理每页条数变更
|
||||
const handlePageSizeChange = (size: number) => {
|
||||
const newParams = new URLSearchParams(searchParams);
|
||||
newParams.set('pageSize', size.toString());
|
||||
newParams.set('page', '1');
|
||||
setSearchParams(newParams);
|
||||
};
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
key: 'id',
|
||||
title: 'ID',
|
||||
width: '80px',
|
||||
render: (row: EntryModule) => row.id
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
title: '模块名称',
|
||||
width: '200px',
|
||||
render: (row: EntryModule) => (
|
||||
<span className="font-medium text-gray-900">{row.name}</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
title: '描述',
|
||||
width: '250px',
|
||||
render: (row: EntryModule) => (
|
||||
<span className="text-gray-600">{row.description || '-'}</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'logo',
|
||||
title: 'Logo图片',
|
||||
width: '150px',
|
||||
render: (row: EntryModule) => {
|
||||
if (!row.path) {
|
||||
return <span className="text-gray-400">未上传</span>;
|
||||
}
|
||||
const logoUrl = `${DOCUMENT_URL}${row.path}`;
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
src={logoUrl}
|
||||
alt={row.name}
|
||||
className="h-8 w-8 object-contain rounded"
|
||||
onError={(e) => {
|
||||
(e.target as HTMLImageElement).style.display = 'none';
|
||||
(e.target as HTMLImageElement).parentElement!.innerHTML = '<span class="text-red-500">加载失败</span>';
|
||||
}}
|
||||
/>
|
||||
<a
|
||||
href={logoUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="ml-2 text-blue-600 hover:underline text-sm"
|
||||
>
|
||||
查看
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'areas',
|
||||
title: '适用地区',
|
||||
width: '200px',
|
||||
render: (row: EntryModule) => (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{row.areas && row.areas.length > 0 ? (
|
||||
row.areas.map((area, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="inline-block px-2 py-1 text-xs bg-blue-100 text-blue-800 rounded"
|
||||
>
|
||||
{area}
|
||||
</span>
|
||||
))
|
||||
) : (
|
||||
<span className="text-gray-400">未设置</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'created_at',
|
||||
title: '创建时间',
|
||||
width: '180px',
|
||||
render: (row: EntryModule) =>
|
||||
row.created_at ? new Date(row.created_at).toLocaleString('zh-CN') : '-'
|
||||
},
|
||||
{
|
||||
key: 'actions',
|
||||
title: '操作',
|
||||
width: '150px',
|
||||
render: (row: EntryModule) => (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => handleEdit(row.id!)}
|
||||
disabled={!hasEditPermission}
|
||||
title={hasEditPermission ? "编辑" : "无权限"}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
onClick={() => handleDelete(row.id!)}
|
||||
disabled={isDeleting || !hasEditPermission}
|
||||
title={hasEditPermission ? "删除" : "无权限"}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="entry-modules-page">
|
||||
<Card>
|
||||
{/* 页面头部 */}
|
||||
<div className="page-header">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">入口模块管理</h1>
|
||||
<p className="text-sm text-gray-600 mt-1">管理系统入口模块,包括Logo图片和适用地区设置</p>
|
||||
</div>
|
||||
{hasEditPermission && (
|
||||
<Button
|
||||
type="primary"
|
||||
icon="ri-add-line"
|
||||
to="/entry-modules/new"
|
||||
>
|
||||
新建入口模块
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 筛选面板 */}
|
||||
<FilterPanel onReset={handleReset}>
|
||||
<SearchFilter
|
||||
placeholder="请输入入口模块名称"
|
||||
defaultValue={name}
|
||||
onSearch={handleNameSearch}
|
||||
/>
|
||||
<FilterSelect
|
||||
label="适用地区"
|
||||
name="area"
|
||||
value={area}
|
||||
options={AREA_OPTIONS}
|
||||
onChange={handleFilterChange}
|
||||
/>
|
||||
</FilterPanel>
|
||||
|
||||
{/* 表格 */}
|
||||
<Table
|
||||
columns={columns}
|
||||
data={modules || []}
|
||||
loading={false}
|
||||
emptyText="暂无入口模块数据"
|
||||
/>
|
||||
|
||||
{/* 分页 */}
|
||||
{total > 0 && (
|
||||
<Pagination
|
||||
current={currentPage}
|
||||
pageSize={pageSize}
|
||||
total={total}
|
||||
onPageChange={handlePageChange}
|
||||
onPageSizeChange={handlePageSizeChange}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user