2026-07-02 17:50:43 +08:00
|
|
|
import React, { useEffect, useState } from "react";
|
2026-07-07 17:45:27 +08:00
|
|
|
import {
|
|
|
|
|
Button,
|
|
|
|
|
Descriptions,
|
|
|
|
|
Form,
|
|
|
|
|
Input,
|
|
|
|
|
message,
|
|
|
|
|
Modal,
|
|
|
|
|
Select,
|
|
|
|
|
Table,
|
|
|
|
|
} from "antd";
|
2026-07-02 17:50:43 +08:00
|
|
|
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
|
|
|
|
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
|
|
|
|
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
|
|
|
|
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
|
|
|
|
|
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
|
|
|
|
|
import { tools } from "@cqsjjb/jjb-common-lib";
|
|
|
|
|
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
|
|
|
|
import { NS_QUAL_EXPERT } from "~/enumerate/namespace";
|
2026-07-07 15:19:16 +08:00
|
|
|
import { GENDER_OPTIONS, GENDER_MAP } from "~/enumerate/constant";
|
2026-07-07 17:45:27 +08:00
|
|
|
import AttachmentUpload from "~/components/AttachmentUpload";
|
2026-07-08 09:53:55 +08:00
|
|
|
import { idCardRule } from "~/utils/validators";
|
2026-07-02 17:50:43 +08:00
|
|
|
|
|
|
|
|
const { router } = tools;
|
|
|
|
|
|
|
|
|
|
const ExperManage = (props) => {
|
|
|
|
|
const [searchForm] = Form.useForm();
|
|
|
|
|
const [editForm] = Form.useForm();
|
|
|
|
|
const [detailVisible, setDetailVisible] = useState(false);
|
|
|
|
|
const [editVisible, setEditVisible] = useState(false);
|
|
|
|
|
const [editingId, setEditingId] = useState(null);
|
|
|
|
|
const [submiting, setSubmiting] = useState(false);
|
|
|
|
|
|
2026-07-07 17:45:27 +08:00
|
|
|
const {
|
|
|
|
|
qualExpert,
|
|
|
|
|
queryExpertPage,
|
|
|
|
|
getExpertDetail,
|
|
|
|
|
saveExpert,
|
|
|
|
|
modifyExpert,
|
|
|
|
|
deleteExpert,
|
|
|
|
|
} = props;
|
|
|
|
|
const {
|
|
|
|
|
qualExpertList,
|
|
|
|
|
qualExpertTotal,
|
|
|
|
|
qualExpertLoading,
|
|
|
|
|
qualExpertDetail,
|
|
|
|
|
qualExpertDetailLoading,
|
|
|
|
|
} = qualExpert || {};
|
2026-07-02 17:50:43 +08:00
|
|
|
|
|
|
|
|
const getData = () => {
|
|
|
|
|
queryExpertPage({
|
|
|
|
|
...router.query,
|
|
|
|
|
current: router.query.current || 1,
|
|
|
|
|
size: router.query.size || 10,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleReset = (values) => {
|
|
|
|
|
router.query = {
|
|
|
|
|
...router.query,
|
|
|
|
|
...values,
|
|
|
|
|
current: 1,
|
|
|
|
|
size: 10,
|
|
|
|
|
};
|
|
|
|
|
getData();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
searchForm.setFieldsValue(router.query);
|
|
|
|
|
getData();
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// ─── 查看详情 ───
|
|
|
|
|
|
|
|
|
|
const openDetail = async (id) => {
|
|
|
|
|
setDetailVisible(true);
|
|
|
|
|
await getExpertDetail({ id });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ─── 新增 / 编辑 ───
|
|
|
|
|
|
|
|
|
|
const openAdd = () => {
|
|
|
|
|
editForm.resetFields();
|
|
|
|
|
setEditingId(null);
|
|
|
|
|
setEditVisible(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const openEdit = async (id) => {
|
|
|
|
|
setEditingId(id);
|
|
|
|
|
setEditVisible(true);
|
|
|
|
|
const res = await getExpertDetail({ id });
|
|
|
|
|
if (res?.success !== false) {
|
2026-07-07 17:45:27 +08:00
|
|
|
const params = res.data;
|
|
|
|
|
if (params.certificate) {
|
|
|
|
|
params.certificate = JSON.parse(params.certificate);
|
|
|
|
|
}
|
|
|
|
|
editForm.setFieldsValue(params);
|
2026-07-02 17:50:43 +08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleEditSubmit = async () => {
|
|
|
|
|
const values = await editForm.validateFields();
|
|
|
|
|
setSubmiting(true);
|
|
|
|
|
const action = editingId ? modifyExpert : saveExpert;
|
|
|
|
|
const params = editingId ? { ...values, id: editingId } : values;
|
2026-07-07 17:45:27 +08:00
|
|
|
if (values.certificate) {
|
|
|
|
|
params.certificate = JSON.stringify(values.certificate);
|
|
|
|
|
}
|
2026-07-02 17:50:43 +08:00
|
|
|
const res = await action(params);
|
|
|
|
|
setSubmiting(false);
|
|
|
|
|
if (res?.success !== false) {
|
|
|
|
|
message.success(editingId ? "修改成功" : "新增成功");
|
|
|
|
|
setEditVisible(false);
|
|
|
|
|
getData();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ─── 删除 ───
|
|
|
|
|
|
|
|
|
|
const handleDelete = (record) => {
|
|
|
|
|
Modal.confirm({
|
|
|
|
|
title: "确认删除",
|
|
|
|
|
content: `确认删除专家「${record.userName}」?此操作不可恢复。`,
|
|
|
|
|
okText: "确认删除",
|
|
|
|
|
okButtonProps: { danger: true },
|
|
|
|
|
cancelText: "取消",
|
|
|
|
|
onOk: async () => {
|
2026-07-07 17:45:27 +08:00
|
|
|
const res = await deleteExpert({ data: record.id });
|
2026-07-02 17:50:43 +08:00
|
|
|
if (res?.success !== false) {
|
|
|
|
|
message.success("删除成功");
|
|
|
|
|
getData();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ─── 表格列 ───
|
|
|
|
|
|
|
|
|
|
const columns = [
|
|
|
|
|
{
|
|
|
|
|
title: "序号",
|
|
|
|
|
width: 60,
|
|
|
|
|
fixed: "left",
|
|
|
|
|
render: (_, __, index) => index + 1,
|
|
|
|
|
},
|
|
|
|
|
{ title: "姓名", dataIndex: "userName", width: 100 },
|
|
|
|
|
{ title: "账号", dataIndex: "account", width: 140 },
|
|
|
|
|
{
|
|
|
|
|
title: "性别",
|
2026-07-07 15:19:16 +08:00
|
|
|
dataIndex: "genderCode",
|
2026-07-02 17:50:43 +08:00
|
|
|
width: 70,
|
2026-07-07 15:19:16 +08:00
|
|
|
render: (code) => GENDER_MAP[code] || "--",
|
2026-07-02 17:50:43 +08:00
|
|
|
},
|
|
|
|
|
{ title: "身份证号", dataIndex: "idCardNo", width: 180 },
|
2026-07-07 17:45:27 +08:00
|
|
|
|
2026-07-02 17:50:43 +08:00
|
|
|
{
|
|
|
|
|
title: "操作",
|
|
|
|
|
width: 180,
|
|
|
|
|
fixed: "right",
|
|
|
|
|
render: (_, record) => (
|
|
|
|
|
<TableAction>
|
2026-07-07 17:45:27 +08:00
|
|
|
<Button
|
|
|
|
|
type="link"
|
|
|
|
|
size="small"
|
|
|
|
|
onClick={() => openDetail(record.id)}
|
|
|
|
|
>
|
|
|
|
|
查看
|
|
|
|
|
</Button>
|
|
|
|
|
<Button type="link" size="small" onClick={() => openEdit(record.id)}>
|
|
|
|
|
编辑
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
type="link"
|
|
|
|
|
size="small"
|
|
|
|
|
danger
|
|
|
|
|
onClick={() => handleDelete(record)}
|
|
|
|
|
>
|
|
|
|
|
删除
|
|
|
|
|
</Button>
|
2026-07-02 17:50:43 +08:00
|
|
|
</TableAction>
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<PageLayout
|
|
|
|
|
title="专家管理"
|
|
|
|
|
extra={
|
2026-07-07 17:45:27 +08:00
|
|
|
<Button type="primary" onClick={openAdd}>
|
|
|
|
|
新增专家
|
|
|
|
|
</Button>
|
2026-07-02 17:50:43 +08:00
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<SearchForm
|
|
|
|
|
style={{ marginBottom: 24 }}
|
|
|
|
|
form={searchForm}
|
|
|
|
|
loading={qualExpertLoading}
|
|
|
|
|
formLine={[
|
|
|
|
|
<Form.Item key="userName" name="userName">
|
2026-07-07 17:45:27 +08:00
|
|
|
<ControlWrapper.Input
|
|
|
|
|
label="姓名"
|
|
|
|
|
placeholder="请输入"
|
|
|
|
|
allowClear
|
|
|
|
|
/>
|
2026-07-02 17:50:43 +08:00
|
|
|
</Form.Item>,
|
|
|
|
|
<Form.Item key="account" name="account">
|
2026-07-07 17:45:27 +08:00
|
|
|
<ControlWrapper.Input
|
|
|
|
|
label="账号"
|
|
|
|
|
placeholder="请输入"
|
|
|
|
|
allowClear
|
|
|
|
|
/>
|
2026-07-02 17:50:43 +08:00
|
|
|
</Form.Item>,
|
|
|
|
|
<Form.Item key="genderCode" name="genderCode">
|
|
|
|
|
<ControlWrapper.Select
|
|
|
|
|
label="性别"
|
|
|
|
|
placeholder="请选择"
|
|
|
|
|
allowClear
|
|
|
|
|
style={{ width: "100%" }}
|
|
|
|
|
>
|
|
|
|
|
{GENDER_OPTIONS.map((item) => (
|
2026-07-07 17:45:27 +08:00
|
|
|
<Select.Option key={item.value} value={item.value}>
|
|
|
|
|
{item.label}
|
|
|
|
|
</Select.Option>
|
2026-07-02 17:50:43 +08:00
|
|
|
))}
|
|
|
|
|
</ControlWrapper.Select>
|
|
|
|
|
</Form.Item>,
|
|
|
|
|
<Form.Item key="idCardNo" name="idCardNo">
|
2026-07-07 17:45:27 +08:00
|
|
|
<ControlWrapper.Input
|
|
|
|
|
label="身份证号"
|
|
|
|
|
placeholder="请输入"
|
|
|
|
|
allowClear
|
|
|
|
|
/>
|
2026-07-02 17:50:43 +08:00
|
|
|
</Form.Item>,
|
|
|
|
|
]}
|
|
|
|
|
onReset={handleReset}
|
|
|
|
|
onFinish={(values) => {
|
|
|
|
|
router.query = {
|
|
|
|
|
...router.query,
|
|
|
|
|
...values,
|
|
|
|
|
current: 1,
|
|
|
|
|
size: 10,
|
|
|
|
|
};
|
|
|
|
|
getData();
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<Table
|
|
|
|
|
rowKey="id"
|
|
|
|
|
columns={columns}
|
|
|
|
|
dataSource={qualExpertList}
|
|
|
|
|
scroll={{ y: props.scrollY }}
|
|
|
|
|
loading={qualExpertLoading}
|
|
|
|
|
pagination={{
|
|
|
|
|
total: qualExpertTotal,
|
|
|
|
|
showSizeChanger: true,
|
|
|
|
|
showQuickJumper: true,
|
|
|
|
|
showTotal: (total) => `共 ${total} 条`,
|
|
|
|
|
current: router.query.current || 1,
|
|
|
|
|
pageSize: router.query.size || 10,
|
|
|
|
|
onChange: (page, pageSize) => {
|
|
|
|
|
router.query = { ...router.query, current: page, size: pageSize };
|
|
|
|
|
getData();
|
|
|
|
|
},
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
{/* 查看详情弹窗 */}
|
|
|
|
|
<Modal
|
|
|
|
|
title="专家详情"
|
|
|
|
|
open={detailVisible}
|
|
|
|
|
onCancel={() => setDetailVisible(false)}
|
|
|
|
|
footer={<Button onClick={() => setDetailVisible(false)}>关闭</Button>}
|
|
|
|
|
width={560}
|
|
|
|
|
>
|
2026-07-07 17:45:27 +08:00
|
|
|
<Descriptions
|
|
|
|
|
column={2}
|
|
|
|
|
bordered
|
|
|
|
|
size="small"
|
|
|
|
|
loading={qualExpertDetailLoading}
|
|
|
|
|
>
|
|
|
|
|
<Descriptions.Item label="姓名">
|
|
|
|
|
{qualExpertDetail?.userName || "--"}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="账号">
|
|
|
|
|
{qualExpertDetail?.account || "--"}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="性别">
|
|
|
|
|
{GENDER_MAP[qualExpertDetail?.genderCode] || "--"}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="身份证号">
|
|
|
|
|
{qualExpertDetail?.idCardNo || "--"}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="证书" span={2}>
|
2026-07-08 18:14:50 +08:00
|
|
|
{(Array.isArray(qualExpertDetail?.certificate)
|
|
|
|
|
? qualExpertDetail?.certificate
|
|
|
|
|
: JSON.parse(qualExpertDetail?.certificate || "[]")
|
|
|
|
|
)?.map((item) => (
|
2026-07-07 17:45:27 +08:00
|
|
|
<a key={item.id} href={item.url} target="_blank">
|
|
|
|
|
{item.name}
|
|
|
|
|
</a>
|
|
|
|
|
))}
|
|
|
|
|
</Descriptions.Item>
|
2026-07-02 17:50:43 +08:00
|
|
|
</Descriptions>
|
|
|
|
|
</Modal>
|
|
|
|
|
|
|
|
|
|
{/* 新增/编辑弹窗 */}
|
|
|
|
|
<Modal
|
|
|
|
|
title={editingId ? "编辑专家" : "新增专家"}
|
|
|
|
|
open={editVisible}
|
|
|
|
|
onCancel={() => setEditVisible(false)}
|
|
|
|
|
onOk={handleEditSubmit}
|
|
|
|
|
confirmLoading={submiting}
|
|
|
|
|
width={520}
|
|
|
|
|
>
|
|
|
|
|
<Form form={editForm} layout="vertical">
|
2026-07-07 17:45:27 +08:00
|
|
|
<Form.Item
|
|
|
|
|
name="userName"
|
|
|
|
|
label="姓名"
|
|
|
|
|
rules={[{ required: true, message: "请输入姓名" }]}
|
|
|
|
|
>
|
2026-07-02 17:50:43 +08:00
|
|
|
<Input placeholder="请输入" allowClear />
|
|
|
|
|
</Form.Item>
|
2026-07-07 17:45:27 +08:00
|
|
|
<Form.Item
|
|
|
|
|
name="account"
|
|
|
|
|
label="账号"
|
|
|
|
|
rules={[{ required: true, message: "请输入账号" }]}
|
|
|
|
|
>
|
2026-07-02 17:50:43 +08:00
|
|
|
<Input placeholder="请输入" allowClear />
|
|
|
|
|
</Form.Item>
|
|
|
|
|
<Form.Item name="genderCode" label="性别">
|
|
|
|
|
<Select placeholder="请选择" allowClear style={{ width: "100%" }}>
|
|
|
|
|
{GENDER_OPTIONS.map((item) => (
|
2026-07-07 17:45:27 +08:00
|
|
|
<Select.Option key={item.value} value={item.value}>
|
|
|
|
|
{item.label}
|
|
|
|
|
</Select.Option>
|
2026-07-02 17:50:43 +08:00
|
|
|
))}
|
|
|
|
|
</Select>
|
|
|
|
|
</Form.Item>
|
2026-07-08 09:53:55 +08:00
|
|
|
<Form.Item name="idCardNo" label="身份证号" rules={[idCardRule()]}>
|
2026-07-02 17:50:43 +08:00
|
|
|
<Input placeholder="请输入" allowClear />
|
|
|
|
|
</Form.Item>
|
2026-07-07 17:45:27 +08:00
|
|
|
<AttachmentUpload name="certificate" maxCount={1} label="证书" />
|
2026-07-02 17:50:43 +08:00
|
|
|
</Form>
|
|
|
|
|
</Modal>
|
|
|
|
|
</PageLayout>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-07 17:45:27 +08:00
|
|
|
export default Connect(
|
|
|
|
|
[NS_QUAL_EXPERT],
|
|
|
|
|
true,
|
|
|
|
|
)(AntdTableFuncControl(ExperManage));
|