封装公共组件,调整样式文件的布局,修改路由页面样式

This commit is contained in:
2025-03-27 19:58:58 +08:00
parent d9b9ce4676
commit 540618b8ca
33 changed files with 2613 additions and 987 deletions
+161 -194
View File
@@ -3,7 +3,6 @@ import { json, type MetaFunction, type LoaderFunctionArgs, redirect } from "@rem
import { useLoaderData, useSearchParams, useSubmit } from "@remix-run/react";
import { Button } from '~/components/ui/Button';
import { Card } from '~/components/ui/Card';
import { SearchBox } from '~/components/ui/SearchBox';
import { Tag } from '~/components/ui/Tag';
import { StatusDot } from '~/components/ui/StatusDot';
import rulesStyles from "~/styles/pages/rules_index.css?url";
@@ -11,14 +10,16 @@ import type { Rule } from '~/models/rule';
import { RULE_TYPE_LABELS, RULE_TYPE_COLORS, RULE_PRIORITY_LABELS, RULE_PRIORITY_COLORS } from '~/models/rule';
import type { TagColor } from '~/components/ui/Tag';
import { Link } from '@remix-run/react';
import { Table } from '~/components/ui/Table';
import { FilterPanel, FilterSelect, SearchFilter } from '~/components/ui/FilterPanel';
import { Pagination } from '~/components/ui/Pagination';
export const links = () => [
{ rel: "stylesheet", href: rulesStyles }
];
export const handle = {
breadcrumb: "评查点列表"
};
// export const handle = {
// breadcrumb: "评查点列表"
// };
export const meta: MetaFunction = () => {
return [
@@ -301,8 +302,8 @@ const priorityLabels = {
'low': '低'
};
export default function RulesList() {
const { rules, groups, totalCount, currentPage, pageSize, totalPages } = useLoaderData<typeof loader>();
export default function RulesIndex() {
const { rules, groups, totalCount, currentPage, pageSize } = useLoaderData<typeof loader>();
const [searchParams, setSearchParams] = useSearchParams();
const submit = useSubmit();
@@ -371,213 +372,179 @@ export default function RulesList() {
setSearchParams(newParams);
};
const handlePageSizeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const newPageSize = e.target.value;
const handlePageSizeChange = (size: number) => {
const newParams = new URLSearchParams(searchParams);
newParams.set('pageSize', newPageSize);
newParams.set('pageSize', size.toString());
newParams.set('page', '1'); // 更改每页条数时,重置到第一页
setSearchParams(newParams);
};
// 处理重置筛选
const handleReset = () => {
setSearchParams(new URLSearchParams());
};
// 定义表格列配置
const columns = [
{
title: "评查点编码",
dataIndex: "code" as keyof Rule,
key: "code",
align: "center" as const
},
{
title: "评查点名称",
dataIndex: "name" as keyof Rule,
key: "name",
align: "center" as const
},
{
title: "评查点类型",
key: "ruleType",
align: "center" as const,
render: (_: unknown, record: Rule) => {
const typeColor = RULE_TYPE_COLORS[record.ruleType] as TagColor;
return (
<Tag color={typeColor}>
{typeLabels[record.ruleType as keyof typeof typeLabels] || RULE_TYPE_LABELS[record.ruleType]}
</Tag>
);
}
},
{
title: "所属规则组",
dataIndex: "groupName" as keyof Rule,
key: "groupName",
align: "center" as const
},
{
title: "优先级",
key: "priority",
align: "center" as const,
render: (_: unknown, record: Rule) => {
const priorityColor = RULE_PRIORITY_COLORS[record.priority] as TagColor;
return (
<Tag color={priorityColor}>
{priorityLabels[record.priority as keyof typeof priorityLabels] || RULE_PRIORITY_LABELS[record.priority]}
</Tag>
);
}
},
{
title: "状态",
key: "isActive",
align: "center" as const,
className: "status-column",
render: (_: unknown, record: Rule) => (
<StatusDot status={record.isActive} text={record.isActive ? "启用" : "禁用"} />
)
},
{
title: "创建时间",
dataIndex: "createdAt" as keyof Rule,
key: "createdAt",
align: "center" as const
},
{
title: "操作",
key: "operation",
align: "center" as const,
render: (_: unknown, record: Rule) => (
<div className="operations-cell">
<Link to={`/rules/${record.id}`} className="operation-btn">
<i className="ri-edit-line"></i>
</Link>
<button className="operation-btn" onClick={() => handleCopy(record)}>
<i className="ri-file-copy-line"></i>
</button>
<button className="operation-btn operation-btn-danger" onClick={() => handleDeleteClick(record)}>
<i className="ri-delete-bin-line"></i>
</button>
</div>
)
}
];
return (
<div className="p-6 rules-page">
{/* 页面头部 */}
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-medium"></h2>
<Button type="primary" icon="ri-add-line" to="/rules/new">
<Button type="primary" icon="ri-add-line" to="/rules/new" className="btn-add-rule">
</Button>
</div>
{/* 筛选区域 */}
<Card className="card-container">
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div>
<label htmlFor="ruleType" className="form-label"></label>
<select
id="ruleType"
className="form-select"
name="ruleType"
value={searchParams.get('ruleType') || ''}
onChange={handleFilterChange}
>
<option value=""></option>
<option value="essential"></option>
<option value="content"></option>
<option value="format"></option>
<option value="legal"></option>
<option value="business"></option>
</select>
</div>
<div>
<label htmlFor="groupId" className="form-label"></label>
<select
id="groupId"
className="form-select"
name="groupId"
value={searchParams.get('groupId') || ''}
onChange={handleFilterChange}
>
<option value=""></option>
{groups.map((group) => (
<option key={group.id} value={group.id}>{group.name}</option>
))}
</select>
</div>
<div>
<label htmlFor="isActive" className="form-label"></label>
<select
id="isActive"
className="form-select"
name="isActive"
value={searchParams.get('isActive') || ''}
onChange={handleFilterChange}
>
<option value=""></option>
<option value="true"></option>
<option value="false"></option>
</select>
</div>
<div>
<label htmlFor="keyword" className="form-label"></label>
<SearchBox
placeholder="输入评查点名称或编码"
defaultValue={searchParams.get('keyword') || ''}
onSearch={handleSearch}
/>
</div>
</div>
</Card>
<FilterPanel>
<FilterSelect
label="评查点类型"
name="ruleType"
value={searchParams.get('ruleType') || ''}
options={[
{ value: "essential", label: "基本要素类" },
{ value: "content", label: "内容合规类" },
{ value: "format", label: "格式规范类" },
{ value: "legal", label: "法律风险类" },
{ value: "business", label: "业务专项类" }
]}
onChange={handleFilterChange}
className="mr-3 w-80 "
/>
<FilterSelect
label="所属规则组"
name="groupId"
value={searchParams.get('groupId') || ''}
options={groups.map(group => ({ value: group.id, label: group.name }))}
onChange={handleFilterChange}
className="mr-3 w-80"
/>
<FilterSelect
label="状态"
name="isActive"
value={searchParams.get('isActive') || ''}
options={[
{ value: "true", label: "启用" },
{ value: "false", label: "禁用" }
]}
onChange={handleFilterChange}
className="mr-3 w-80"
/>
<SearchFilter
label="搜索"
placeholder="输入评查点名称或编码"
value={searchParams.get('keyword') || ''}
onSearch={handleSearch}
className="flex-1"
/>
</FilterPanel>
{/* 评查点列表 */}
{/* 评查点列表 - 使用Table组件 */}
<Card className="ant-card">
<div className="overflow-x-auto">
<table className="ant-table">
<thead>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{rules.length > 0 ? (
rules.map((rule) => {
const typeColor = RULE_TYPE_COLORS[rule.ruleType] as TagColor;
const priorityColor = RULE_PRIORITY_COLORS[rule.priority] as TagColor;
return (
<tr key={rule.id}>
<td>{rule.code}</td>
<td>{rule.name}</td>
<td>
<Tag color={typeColor}>
{typeLabels[rule.ruleType as keyof typeof typeLabels] || RULE_TYPE_LABELS[rule.ruleType]}
</Tag>
</td>
<td>{rule.groupName}</td>
<td>
<Tag color={priorityColor}>
{priorityLabels[rule.priority as keyof typeof priorityLabels] || RULE_PRIORITY_LABELS[rule.priority]}
</Tag>
</td>
<td>
<StatusDot status={rule.isActive} />
</td>
<td>{rule.createdAt}</td>
<td className="operations-cell">
<Link to={`/rules/${rule.id}`} className="operation-btn">
<i className="ri-edit-line"></i>
</Link>
<button className="operation-btn" onClick={() => handleCopy(rule)}>
<i className="ri-file-copy-line"></i>
</button>
<button className="operation-btn operation-btn-danger" onClick={() => handleDeleteClick(rule)}>
<i className="ri-delete-bin-line"></i>
</button>
</td>
</tr>
);
})
) : (
<tr>
<td colSpan={8} className="text-center py-8 text-gray-500">
</td>
</tr>
)}
</tbody>
</table>
</div>
<Table
columns={columns}
dataSource={rules}
rowKey="id"
emptyText="暂无评查点数据"
className="rules-table"
/>
{/* 分页 */}
{totalCount > 0 && (
<div className="ant-pagination">
<div className="ant-pagination-options">
<span className="text-sm mr-2"> {totalCount} </span>
<select
className="form-select ant-pagination-options-size-changer"
style={{ width: "100px" }}
value={pageSize}
onChange={handlePageSizeChange}
>
<option value="10">10 /</option>
<option value="20">20 /</option>
<option value="50">50 /</option>
</select>
</div>
<div className="ant-pagination-right">
<button
className={`ant-pagination-item ant-pagination-prev ${currentPage <= 1 ? 'ant-pagination-disabled' : ''}`}
onClick={() => currentPage > 1 && handlePageChange(currentPage - 1)}
disabled={currentPage <= 1}
>
<i className="ri-arrow-left-s-line"></i>
</button>
{Array.from({ length: Math.min(totalPages, 5) }, (_, i) => {
// 显示当前页附近的页码,最多显示5个
let pageNum;
if (totalPages <= 5) {
// 总页数少于5,直接显示所有页码
pageNum = i + 1;
} else if (currentPage <= 3) {
// 当前页靠近开始
pageNum = i + 1;
} else if (currentPage >= totalPages - 2) {
// 当前页靠近结尾
pageNum = totalPages - 4 + i;
} else {
// 当前页在中间
pageNum = currentPage - 2 + i;
}
return (
<button
key={pageNum}
className={`ant-pagination-item ${pageNum === currentPage ? 'ant-pagination-item-active' : ''}`}
onClick={() => handlePageChange(pageNum)}
>
{pageNum}
</button>
);
})}
<button
className={`ant-pagination-item ant-pagination-next ${currentPage >= totalPages ? 'ant-pagination-disabled' : ''}`}
onClick={() => currentPage < totalPages && handlePageChange(currentPage + 1)}
disabled={currentPage >= totalPages}
>
<i className="ri-arrow-right-s-line"></i>
</button>
</div>
</div>
<Pagination
currentPage={currentPage}
total={totalCount}
pageSize={pageSize}
onChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
showTotal={true}
showPageSizeChanger={true}
pageSizeOptions={[10, 20, 30, 50]}
/>
)}
</Card>