safety-eval-service-frontend/src/pages/Container/SafetyEvalBusiness/EvalProject/Create/StaffPickerModal.js

250 lines
7.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import React, { useState, useEffect } from "react";
import {
Modal,
TreeSelect,
Input,
Table,
Button,
Tag,
Row,
Col,
Space,
} from "antd";
import { CloseOutlined } from "@ant-design/icons";
import { NS_STAFF_INFO, NS_ORG_DEPARTMENT } from "~/enumerate/namespace";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { QUALIFICATION_INDUSTRY_OPTIONS_MAP, TITLE_LEVEL_MAP } from "~/enumerate/enterpriseOptions";
import { renderCapabilityList } from "~/utils";
const StaffPickerModal = (props) => {
const { open, onCancel, onConfirm, selectedIds = [] } = props;
const { staffInfoList: dataSource, staffInfoTotal: total, staffInfoLoading: loading } = props.staffInfo || {};
const { orgDepartmentTreeData } = props.orgDepartment || {};
const [deptId, setDeptId] = useState(undefined);
const [keyword, setKeyword] = useState("");
const [page, setPage] = useState({ current: 1, size: 10 });
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
// 用 Map 存储所有已选行,翻页后不丢失
const [selectedRowMap, setSelectedRowMap] = useState({});
useEffect(() => {
if (open) {
props.orgDepartmentTree();
// 从父组件恢复已选数据
const initialIds = selectedIds;
const initialRows = props.selectedParticipants || [];
setSelectedRowKeys(initialIds);
const map = {};
initialRows.forEach((r) => { map[r.id] = r; });
setSelectedRowMap(map);
fetchData({ current: 1, size: 10 });
}
}, [open]);
const fetchData = (pagination) => {
const params = {
current: pagination?.current || page.current,
size: pagination?.size || page.size,
employmentStatusCode: 1,
};
if (deptId) params.deptId = deptId;
if (keyword) params.userName = keyword;
props.staffInfoList(params);
setPage(params);
};
const handleSearch = () => {
fetchData({ current: 1, size: 10 });
};
const handleTableChange = (pagination) => {
fetchData(pagination);
};
const handleSelectChange = (rowKeys, rows) => {
setSelectedRowKeys(rowKeys);
// rows 仅包含当前页的行,用 Map 合并新旧数据
setSelectedRowMap((prev) => {
const next = { ...prev };
// 移除取消勾选的项
Object.keys(next).forEach((key) => {
if (!rowKeys.includes(key)) {
delete next[key];
}
});
// 添加/更新当前页勾选的行
rows.forEach((row) => {
next[row.id] = row;
});
return next;
});
};
const handleRemoveSelected = (record) => {
const newKeys = selectedRowKeys.filter((id) => id !== record.id);
setSelectedRowKeys(newKeys);
setSelectedRowMap((prev) => {
const next = { ...prev };
delete next[record.id];
return next;
});
};
const handleConfirm = () => {
onConfirm(Object.values(selectedRowMap));
};
const selectedRows = Object.values(selectedRowMap);
const columns = [
{
title: "用户名称",
dataIndex: "userName",
width: 200,
render: (text, record) => {
const name = record.userName || "-";
return (
<Space size={4} wrap align="start">
<span style={{ maxWidth: 100, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", display: "inline-block" }} title={name}>{name}</span>
{record.technicalDirectorFlag && (
<Tag color="blue" style={{ marginRight: 0, fontSize: 11 }}>技术</Tag>
)}
{record.processControlLeaderFlag && (
<Tag color="purple" style={{ marginRight: 0, fontSize: 11 }}>过程</Tag>
)}
</Space>
);
},
},
{
title: "能力",
dataIndex: "capabilityAssessment",
ellipsis: true,
render: renderCapabilityList,
},
];
return (
<Modal
title="选择项目参与人员"
open={open}
onCancel={onCancel}
onOk={handleConfirm}
width={1000}
destroyOnHidden
>
<Row gutter={16}>
<Col span={18}>
<Space style={{ marginBottom: 12, width: "100%" }} size={12}>
<TreeSelect
placeholder="请选择部门"
allowClear
treeData={orgDepartmentTreeData}
fieldNames={{ label: "deptName", value: "id", key: "id" }}
treeDefaultExpandAll
showSearch
style={{ width: 240 }}
value={deptId}
onChange={(value) => {
setDeptId(value);
setTimeout(() => handleSearch(), 0);
}}
/>
<Input
placeholder="搜索用户名称"
allowClear
style={{ width: 240 }}
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
onPressEnter={handleSearch}
/>
<Button type="primary" onClick={handleSearch}>搜索</Button>
</Space>
<div style={{ marginBottom: 8, fontSize: 12, color: "#666" }}>
<Tag color="blue" style={{ fontSize: 11, marginRight: 4 }}>技术</Tag>
<Tag color="purple" style={{ fontSize: 11, margin: "0 4px 0 12px" }}>过程</Tag>
</div>
<Table
rowKey="id"
columns={columns}
dataSource={dataSource}
loading={loading}
size="small"
rowSelection={{
selectedRowKeys,
preserveSelectedRowKeys: true,
onChange: handleSelectChange,
}}
pagination={{
total,
current: page.current,
pageSize: page.size,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (t) => `${t}`,
onChange: (cur, size) => handleTableChange({ current: cur, size }),
}}
scroll={{ y: 400, x: 600 }}
/>
</Col>
<Col span={6}>
<div style={{ fontSize: 13, fontWeight: 500, marginBottom: 8 }}>
已选人员{selectedRows.length}
</div>
<div
style={{
border: "1px solid #d9d9d9",
borderRadius: 6,
padding: 8,
minHeight: 300,
maxHeight: 460,
overflow: "auto",
}}
>
{selectedRows.length === 0 && (
<div style={{ color: "#999", fontSize: 13, textAlign: "center", paddingTop: 120 }}>
请从左侧勾选人员
</div>
)}
{selectedRows.map((item) => (
<div
key={item.id}
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
padding: "6px 8px",
borderBottom: "1px solid #f0f0f0",
}}
>
<div style={{ fontSize: 13, maxWidth: 140, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={item.userName}>{item.userName}</div>
<Button
type="text"
size="small"
danger
icon={<CloseOutlined />}
onClick={() => handleRemoveSelected(item)}
/>
</div>
))}
</div>
</Col>
</Row>
</Modal>
);
};
export default Connect(
[NS_STAFF_INFO, NS_ORG_DEPARTMENT],
true,
)(StaffPickerModal);