接入feat(cross-checking): 整合组织架构数据并优化意见列表功能
- 更新 API 配置,使用新的后端服务地址- 移除前端模拟数据,改为从后端获取真实数据- 优化意见列表接口,支持分页和用户身份验证 - 调整前端界面,适应新的数据结构和功能需求
This commit is contained in:
@@ -653,8 +653,8 @@ export default function CrossCheckingIndex() {
|
||||
pageSize={pageSize}
|
||||
onChange={handlePageChange}
|
||||
onPageSizeChange={handlePageSizeChange}
|
||||
showTotal={true}
|
||||
showPageSizeChanger={true}
|
||||
showTotal={false}
|
||||
showPageSizeChanger={false}
|
||||
pageSizeOptions={[10, 20, 30, 50]}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useRef } from "react";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { type MetaFunction, type ActionFunctionArgs } from "@remix-run/node";
|
||||
import { Form, useNavigation, useNavigate } from "@remix-run/react";
|
||||
import { UploadArea, type UploadAreaRef } from "~/components/ui/UploadArea";
|
||||
@@ -16,6 +16,10 @@ import {
|
||||
formatFileSize,
|
||||
batchUploadCrossCheckingFiles
|
||||
} from "~/api/cross-checking/cross-files-upload";
|
||||
import {
|
||||
getOrganizationTree,
|
||||
convertToTreeData
|
||||
} from "~/api/user";
|
||||
import React from "react"; // Added for React.useState
|
||||
|
||||
export const meta: MetaFunction = () => {
|
||||
@@ -50,310 +54,15 @@ export interface TreeNode {
|
||||
children?: TreeNode[];
|
||||
}
|
||||
|
||||
// 无限层级组织架构数据结构
|
||||
const MOCK_TREE: TreeNode[] = [
|
||||
{
|
||||
label: "梅州市",
|
||||
value: "梅州市",
|
||||
children: [
|
||||
{
|
||||
label: "梅州市烟草局", // 市级局
|
||||
value: "梅州市烟草局",
|
||||
children: [
|
||||
{ label: "李局长", value: "梅州市-梅州市烟草局-李局长" },
|
||||
{ label: "王副局长", value: "梅州市-梅州市烟草局-王副局长" },
|
||||
{
|
||||
label: "市场监管科", // 市级局下的科室
|
||||
value: "梅州市烟草局-市场监管科",
|
||||
children: [
|
||||
{ label: "张科长", value: "梅州市-梅州市烟草局-市场监管科-张科长" },
|
||||
{ label: "陈主任", value: "梅州市-梅州市烟草局-市场监管科-陈主任" }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "法规科",
|
||||
value: "梅州市烟草局-法规科",
|
||||
children: [
|
||||
{ label: "刘科长", value: "梅州市-梅州市烟草局-法规科-刘科长" },
|
||||
{ label: "周专员", value: "梅州市-梅州市烟草局-法规科-周专员" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "梅江区", // 区级
|
||||
value: "梅江区",
|
||||
children: [
|
||||
{
|
||||
label: "梅江区烟草分局", // 区级分局
|
||||
value: "梅江区烟草分局",
|
||||
children: [
|
||||
{ label: "张分局长", value: "梅州市-梅江区-梅江区烟草分局-张分局长" },
|
||||
{ label: "李副分局长", value: "梅州市-梅江区-梅江区烟草分局-李副分局长" },
|
||||
{
|
||||
label: "执法大队", // 分局下的大队
|
||||
value: "梅江区烟草分局-执法大队",
|
||||
children: [
|
||||
{ label: "王队长", value: "梅州市-梅江区-梅江区烟草分局-执法大队-王队长" },
|
||||
{ label: "陈副队长", value: "梅州市-梅江区-梅江区烟草分局-执法大队-陈副队长" },
|
||||
{
|
||||
label: "第一中队", // 大队下的中队
|
||||
value: "梅江区烟草分局-执法大队-第一中队",
|
||||
children: [
|
||||
{ label: "赵中队长", value: "梅州市-梅江区-梅江区烟草分局-执法大队-第一中队-赵中队长" },
|
||||
{ label: "孙执法员", value: "梅州市-梅江区-梅江区烟草分局-执法大队-第一中队-孙执法员" },
|
||||
{ label: "钱执法员", value: "梅州市-梅江区-梅江区烟草分局-执法大队-第一中队-钱执法员" }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "第二中队",
|
||||
value: "梅江区烟草分局-执法大队-第二中队",
|
||||
children: [
|
||||
{ label: "吴中队长", value: "梅州市-梅江区-梅江区烟草分局-执法大队-第二中队-吴中队长" },
|
||||
{ label: "郑执法员", value: "梅州市-梅江区-梅江区烟草分局-执法大队-第二中队-郑执法员" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "办公室",
|
||||
value: "梅江区烟草分局-办公室",
|
||||
children: [
|
||||
{ label: "林主任", value: "梅州市-梅江区-梅江区烟草分局-办公室-林主任" },
|
||||
{ label: "黄秘书", value: "梅州市-梅江区-梅江区烟草分局-办公室-黄秘书" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "梅江区市场监管局",
|
||||
value: "梅江区市场监管局",
|
||||
children: [
|
||||
{ label: "刘局长", value: "梅州市-梅江区-梅江区市场监管局-刘局长" },
|
||||
{ label: "周副局长", value: "梅州市-梅江区-梅江区市场监管局-周副局长" },
|
||||
{
|
||||
label: "执法监察科",
|
||||
value: "梅江区市场监管局-执法监察科",
|
||||
children: [
|
||||
{ label: "谢科长", value: "梅州市-梅江区-梅江区市场监管局-执法监察科-谢科长" },
|
||||
{ label: "何专员", value: "梅州市-梅江区-梅江区市场监管局-执法监察科-何专员" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "梅县区", // 另一个区
|
||||
value: "梅县区",
|
||||
children: [
|
||||
{
|
||||
label: "梅县区烟草分局",
|
||||
value: "梅县区烟草分局",
|
||||
children: [
|
||||
{ label: "黄分局长", value: "梅州市-梅县区-梅县区烟草分局-黄分局长" },
|
||||
{ label: "林副分局长", value: "梅州市-梅县区-梅县区烟草分局-林副分局长" },
|
||||
{
|
||||
label: "稽查队",
|
||||
value: "梅县区烟草分局-稽查队",
|
||||
children: [
|
||||
{ label: "吴队长", value: "梅州市-梅县区-梅县区烟草分局-稽查队-吴队长" },
|
||||
{ label: "郑稽查员", value: "梅州市-梅县区-梅县区烟草分局-稽查队-郑稽查员" },
|
||||
{ label: "谢稽查员", value: "梅州市-梅县区-梅县区烟草分局-稽查队-谢稽查员" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "丰顺县", // 县级
|
||||
value: "丰顺县",
|
||||
children: [
|
||||
{
|
||||
label: "丰顺县烟草分局",
|
||||
value: "丰顺县烟草分局",
|
||||
children: [
|
||||
{ label: "曾分局长", value: "梅州市-丰顺县-丰顺县烟草分局-曾分局长" },
|
||||
{
|
||||
label: "专卖管理所",
|
||||
value: "丰顺县烟草分局-专卖管理所",
|
||||
children: [
|
||||
{ label: "邓所长", value: "梅州市-丰顺县-丰顺县烟草分局-专卖管理所-邓所长" },
|
||||
{ label: "罗管理员", value: "梅州市-丰顺县-丰顺县烟草分局-专卖管理所-罗管理员" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "揭阳市",
|
||||
value: "揭阳市",
|
||||
children: [
|
||||
{
|
||||
label: "揭阳市烟草局", // 市级局
|
||||
value: "揭阳市烟草局",
|
||||
children: [
|
||||
{ label: "苏局长", value: "揭阳市-揭阳市烟草局-苏局长" },
|
||||
{ label: "叶副局长", value: "揭阳市-揭阳市烟草局-叶副局长" },
|
||||
{
|
||||
label: "专卖监督管理处",
|
||||
value: "揭阳市烟草局-专卖监督管理处",
|
||||
children: [
|
||||
{ label: "潘处长", value: "揭阳市-揭阳市烟草局-专卖监督管理处-潘处长" },
|
||||
{ label: "方副处长", value: "揭阳市-揭阳市烟草局-专卖监督管理处-方副处长" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "榕城区",
|
||||
value: "榕城区",
|
||||
children: [
|
||||
{
|
||||
label: "榕城区烟草分局",
|
||||
value: "榕城区烟草分局",
|
||||
children: [
|
||||
{ label: "王分局长", value: "揭阳市-榕城区-榕城区烟草分局-王分局长" },
|
||||
{ label: "李明华", value: "揭阳市-榕城区-榕城区烟草分局-李明华" },
|
||||
{ label: "张丽萍", value: "揭阳市-榕城区-榕城区烟草分局-张丽萍" },
|
||||
{
|
||||
label: "市场检查组",
|
||||
value: "榕城区烟草分局-市场检查组",
|
||||
children: [
|
||||
{ label: "陈组长", value: "揭阳市-榕城区-榕城区烟草分局-市场检查组-陈组长" },
|
||||
{ label: "林检查员", value: "揭阳市-榕城区-榕城区烟草分局-市场检查组-林检查员" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "榕城区质监局",
|
||||
value: "榕城区质监局",
|
||||
children: [
|
||||
{ label: "陈国强", value: "揭阳市-榕城区-榕城区质监局-陈国强" },
|
||||
{ label: "林小芳", value: "揭阳市-榕城区-榕城区质监局-林小芳" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "揭东区",
|
||||
value: "揭东区",
|
||||
children: [
|
||||
{
|
||||
label: "揭东区烟草分局",
|
||||
value: "揭东区烟草分局",
|
||||
children: [
|
||||
{ label: "黄建军", value: "揭阳市-揭东区-揭东区烟草分局-黄建军" },
|
||||
{ label: "吴秀英", value: "揭阳市-揭东区-揭东区烟草分局-吴秀英" },
|
||||
{ label: "刘志华", value: "揭阳市-揭东区-揭东区烟草分局-刘志华" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "惠来县", // 县级
|
||||
value: "惠来县",
|
||||
children: [
|
||||
{
|
||||
label: "惠来县烟草分局",
|
||||
value: "惠来县烟草分局",
|
||||
children: [
|
||||
{ label: "杨分局长", value: "揭阳市-惠来县-惠来县烟草分局-杨分局长" },
|
||||
{
|
||||
label: "案件审理室",
|
||||
value: "惠来县烟草分局-案件审理室",
|
||||
children: [
|
||||
{ label: "蔡主任", value: "揭阳市-惠来县-惠来县烟草分局-案件审理室-蔡主任" },
|
||||
{ label: "郭审理员", value: "揭阳市-惠来县-惠来县烟草分局-案件审理室-郭审理员" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "汕头市",
|
||||
value: "汕头市",
|
||||
children: [
|
||||
{
|
||||
label: "汕头市烟草局", // 市级局
|
||||
value: "汕头市烟草局",
|
||||
children: [
|
||||
{ label: "何局长", value: "汕头市-汕头市烟草局-何局长" },
|
||||
{ label: "许副局长", value: "汕头市-汕头市烟草局-许副局长" }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "龙湖区",
|
||||
value: "龙湖区",
|
||||
children: [
|
||||
{
|
||||
label: "龙湖区烟草分局",
|
||||
value: "龙湖区烟草分局",
|
||||
children: [
|
||||
{ label: "许志明", value: "汕头市-龙湖区-龙湖区烟草分局-许志明" },
|
||||
{ label: "蔡丽娜", value: "汕头市-龙湖区-龙湖区烟草分局-蔡丽娜" },
|
||||
{ label: "郭建华", value: "汕头市-龙湖区-龙湖区烟草分局-郭建华" },
|
||||
{ label: "何美霞", value: "汕头市-龙湖区-龙湖区烟草分局-何美霞" }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "龙湖区工商局",
|
||||
value: "龙湖区工商局",
|
||||
children: [
|
||||
{ label: "方国庆", value: "汕头市-龙湖区-龙湖区工商局-方国庆" },
|
||||
{ label: "杨小红", value: "汕头市-龙湖区-龙湖区工商局-杨小红" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "金平区",
|
||||
value: "金平区",
|
||||
children: [
|
||||
{
|
||||
label: "金平区烟草分局",
|
||||
value: "金平区烟草分局",
|
||||
children: [
|
||||
{ label: "邓志强", value: "汕头市-金平区-金平区烟草分局-邓志强" },
|
||||
{ label: "罗美玲", value: "汕头市-金平区-金平区烟草分局-罗美玲" }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "金平区市场监管局",
|
||||
value: "金平区市场监管局",
|
||||
children: [
|
||||
{ label: "苏建国", value: "汕头市-金平区-金平区市场监管局-苏建国" },
|
||||
{ label: "叶丽华", value: "汕头市-金平区-金平区市场监管局-叶丽华" },
|
||||
{ label: "潘志明", value: "汕头市-金平区-金平区市场监管局-潘志明" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "南澳县", // 县级
|
||||
value: "南澳县",
|
||||
children: [
|
||||
{
|
||||
label: "南澳县烟草分局",
|
||||
value: "南澳县烟草分局",
|
||||
children: [
|
||||
{ label: "陈分局长", value: "汕头市-南澳县-南澳县烟草分局-陈分局长" },
|
||||
{ label: "林管理员", value: "汕头市-南澳县-南澳县烟草分局-林管理员" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
// 默认的空组织架构数据(作为备用)
|
||||
const DEFAULT_TREE: TreeNode[] = [];
|
||||
|
||||
// 用户选择状态管理
|
||||
interface UserSelectionState {
|
||||
treeData: TreeNode[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
|
||||
function isAllChildrenChecked(node: TreeNode, checked: string[]): boolean {
|
||||
@@ -440,6 +149,11 @@ export default function CrossCheckingUpload() {
|
||||
});
|
||||
// 步骤2状态
|
||||
const [groupChecked, setGroupChecked] = useState<string[]>([]);
|
||||
const [userSelectionState, setUserSelectionState] = useState<UserSelectionState>({
|
||||
treeData: DEFAULT_TREE,
|
||||
loading: false,
|
||||
error: null
|
||||
});
|
||||
|
||||
// 上传配置状态 - 设置默认值
|
||||
const [priority] = useState<string>("normal");
|
||||
@@ -696,6 +410,65 @@ export default function CrossCheckingUpload() {
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
// 加载组织架构数据
|
||||
useEffect(() => {
|
||||
const loadOrganizationData = async () => {
|
||||
// 只在步骤2且数据为空且未在加载时执行
|
||||
if (currentStep === 2 && userSelectionState.treeData.length === 0 && !userSelectionState.loading) {
|
||||
setUserSelectionState(prev => ({ ...prev, loading: true, error: null }));
|
||||
|
||||
try {
|
||||
console.log('开始加载组织架构数据');
|
||||
const response = await getOrganizationTree(true);
|
||||
|
||||
if (response.success && response.data) {
|
||||
console.log('原始API数据:', response.data);
|
||||
const treeData = convertToTreeData(response.data.organizations);
|
||||
console.log('转换后的树形数据:', treeData);
|
||||
|
||||
// 验证数据转换是否正确
|
||||
treeData.forEach(org => {
|
||||
console.log(`组织: ${org.label} (${org.value})`);
|
||||
if (org.children) {
|
||||
org.children.forEach(child => {
|
||||
if (child.isUser) {
|
||||
console.log(` - 用户: ${child.label} (${child.value})`);
|
||||
} else {
|
||||
console.log(` - 子组织: ${child.label} (${child.value})`);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
setUserSelectionState({
|
||||
treeData,
|
||||
loading: false,
|
||||
error: null
|
||||
});
|
||||
} else {
|
||||
console.error('获取组织架构失败:', response.error);
|
||||
setUserSelectionState({
|
||||
treeData: DEFAULT_TREE,
|
||||
loading: false,
|
||||
error: response.error || '获取组织架构失败'
|
||||
});
|
||||
toastService.error('获取组织架构失败,请刷新页面重试');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载组织架构数据失败:', error);
|
||||
setUserSelectionState({
|
||||
treeData: DEFAULT_TREE,
|
||||
loading: false,
|
||||
error: error instanceof Error ? error.message : '加载组织架构数据失败'
|
||||
});
|
||||
toastService.error('加载组织架构数据失败,请刷新页面重试');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
loadOrganizationData();
|
||||
}, [currentStep]); // 只依赖 currentStep,避免无限循环
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 py-8">
|
||||
<div className="max-w-6xl mx-auto px-4">
|
||||
@@ -751,7 +524,10 @@ export default function CrossCheckingUpload() {
|
||||
<Button
|
||||
type="default"
|
||||
icon="ri-arrow-left-line"
|
||||
onClick={() => navigate('/cross-checking')}
|
||||
onClick={() => {
|
||||
console.log('点击返回列表按钮');
|
||||
navigate('/cross-checking');
|
||||
}}
|
||||
>
|
||||
返回列表
|
||||
</Button>
|
||||
@@ -771,14 +547,26 @@ export default function CrossCheckingUpload() {
|
||||
<div style={{ minWidth: 300, width: '40%' }}>
|
||||
<div className="form-group">
|
||||
<label htmlFor="review-group" className="form-label required">评查小组</label>
|
||||
<MultiCascader
|
||||
options={MOCK_TREE}
|
||||
placeholder="请选择评查小组"
|
||||
value={groupChecked}
|
||||
onChange={(values: string[]) => {
|
||||
setGroupChecked(values);
|
||||
}}
|
||||
/>
|
||||
{userSelectionState.loading ? (
|
||||
<div className="flex items-center justify-center p-4 border border-gray-200 rounded-md bg-gray-50">
|
||||
<i className="ri-loader-4-line ri-spin animate-spin text-xl mr-2 text-blue-600"></i>
|
||||
<span className="text-gray-600">正在加载组织架构...</span>
|
||||
</div>
|
||||
) : userSelectionState.error ? (
|
||||
<div className="flex items-center justify-center p-4 border border-red-200 rounded-md bg-red-50">
|
||||
<i className="ri-error-warning-line text-xl mr-2 text-red-600"></i>
|
||||
<span className="text-red-600">加载失败: {userSelectionState.error}</span>
|
||||
</div>
|
||||
) : (
|
||||
<MultiCascader
|
||||
options={userSelectionState.treeData}
|
||||
placeholder="请选择评查小组成员"
|
||||
value={groupChecked}
|
||||
onChange={(values: string[]) => {
|
||||
setGroupChecked(values);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{/* 右侧已选择成员显示区域 */}
|
||||
@@ -788,13 +576,25 @@ export default function CrossCheckingUpload() {
|
||||
{groupChecked.length > 0 ? (
|
||||
<div className="space-y-2 max-h-64 overflow-y-auto">
|
||||
{groupChecked.map((member, index) => {
|
||||
const parts = member.split('-');
|
||||
const name = parts[parts.length - 1];
|
||||
const org = parts.slice(0, -1).join(' - ');
|
||||
// 处理用户选择值,支持新的API格式
|
||||
let displayName = member;
|
||||
let displayOrg = '';
|
||||
|
||||
if (member.startsWith('user_')) {
|
||||
// 用户选择,格式为 user_123
|
||||
displayName = `用户ID: ${member.replace('user_', '')}`;
|
||||
displayOrg = '用户';
|
||||
} else {
|
||||
// 组织选择,格式为 ou_id 或 ou_id-ou_id
|
||||
const parts = member.split('-');
|
||||
displayName = parts[parts.length - 1];
|
||||
displayOrg = parts.slice(0, -1).join(' - ') || '组织';
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={index} className="bg-white p-2 rounded text-xs border">
|
||||
<div className="font-medium text-gray-800">{name}</div>
|
||||
<div className="text-gray-500 mt-1">{org}</div>
|
||||
<div className="font-medium text-gray-800">{displayName}</div>
|
||||
<div className="text-gray-500 mt-1">{displayOrg}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@@ -819,7 +619,10 @@ export default function CrossCheckingUpload() {
|
||||
<Button
|
||||
type="default"
|
||||
icon="ri-arrow-left-line"
|
||||
onClick={() => navigate('/cross-checking')}
|
||||
onClick={() => {
|
||||
console.log('点击返回列表按钮');
|
||||
navigate('/cross-checking');
|
||||
}}
|
||||
>
|
||||
返回列表
|
||||
</Button>
|
||||
@@ -989,7 +792,10 @@ export default function CrossCheckingUpload() {
|
||||
<Button
|
||||
type="default"
|
||||
icon="ri-arrow-left-line"
|
||||
onClick={() => navigate('/cross-checking')}
|
||||
onClick={() => {
|
||||
console.log('点击返回列表按钮');
|
||||
navigate('/cross-checking');
|
||||
}}
|
||||
>
|
||||
返回列表
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user