safety-eval-service-frontend/src/pages/Container/EnterpriseInfo/ResignationApply/index.js

413 lines
12 KiB
JavaScript
Raw Normal View History

2026-06-23 18:07:30 +08:00
import { Connect } from "@cqsjjb/jjb-dva-runtime";
2026-07-07 09:07:19 +08:00
import {
Button,
DatePicker,
Descriptions,
Form,
Input,
message,
Modal,
Select,
Table,
Tag,
} from "antd";
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
2026-07-07 14:32:37 +08:00
import AttachmentUpload from "~/components/AttachmentUpload";
2026-07-07 09:07:19 +08:00
import { UploadOutlined } from "@ant-design/icons";
2026-07-07 15:58:05 +08:00
import PreviewUrlButton from "~/components/PreviewUrlButton";
2026-06-23 18:07:30 +08:00
import { useEffect, useState } from "react";
2026-06-29 16:45:58 +08:00
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
2026-07-07 09:07:19 +08:00
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 {
NS_STAFF_INFO,
NS_STAFF_RESIGNATION_APPLY,
} from "~/enumerate/namespace";
import {
RESIGN_AUDIT_STATUS_LABEL,
AUDIT_STATUS_COLOR,
} from "~/utils/enterpriseForm";
2026-06-29 16:45:58 +08:00
2026-07-07 09:07:19 +08:00
const { router } = tools;
2026-06-23 18:07:30 +08:00
function ResignationApplyPage(props) {
const [addModalOpen, setAddModalOpen] = useState(false);
const [viewModalOpen, setViewModalOpen] = useState(false);
const [currentId, setCurrentId] = useState("");
const [staffOptions, setStaffOptions] = useState([]);
const [searchForm] = Form.useForm();
2026-07-07 09:07:19 +08:00
const { staffResignationApply } = props;
const {
staffResignationApplyList: dataSource,
staffResignationApplyTotal: total,
staffResignationApplyLoading: loading,
} = staffResignationApply || {};
2026-06-23 18:07:30 +08:00
2026-07-07 09:07:19 +08:00
const handleSearch = () => {
props.staffResignationApplyList({ ...router.query });
};
2026-06-23 18:07:30 +08:00
useEffect(() => {
2026-07-07 09:07:19 +08:00
searchForm.setFieldsValue(router.query);
handleSearch();
2026-06-23 18:07:30 +08:00
}, []);
useEffect(() => {
let cancelled = false;
2026-07-07 09:07:19 +08:00
props
.staffInfoList?.({ current: 1, size: 500 })
.then((res) => {
if (!cancelled) {
setStaffOptions(
(res?.data || []).map((s) => ({
label: `${s.userName ?? s.staffName}${s.account}`,
value: s.id,
staffName: s.userName ?? s.staffName,
account: s.account,
})),
);
}
})
.catch(() => {});
2026-06-23 18:07:30 +08:00
return () => {
cancelled = true;
};
}, []);
return (
2026-06-30 18:30:30 +08:00
<PageLayout
title={
<div>
<span>离职申请管理</span>
<div className="pageLayout-extra">
机构管理员在人员信息管理页面进行相应的修改操作
</div>
</div>
}
2026-07-07 09:07:19 +08:00
extra={
<Button
type="primary"
style={{ marginTop: 16 }}
onClick={() => {
setCurrentId("");
setAddModalOpen(true);
}}
>
新增离职申请
</Button>
}
2026-06-30 18:30:30 +08:00
>
2026-07-07 09:07:19 +08:00
<SearchForm
style={{ marginBottom: 24 }}
2026-06-23 18:07:30 +08:00
form={searchForm}
2026-07-07 09:07:19 +08:00
loading={loading}
formLine={[
<Form.Item key="applicantName" name="applicantName">
<ControlWrapper.Input
label="姓名"
placeholder="请输入"
allowClear
/>
</Form.Item>,
<Form.Item key="auditStatusCode" name="auditStatusCode">
<ControlWrapper.Select
label="离职申请审核状态"
placeholder="请选择"
allowClear
style={{ width: "100%" }}
>
{Object.entries(RESIGN_AUDIT_STATUS_LABEL).map(
([value, label]) => (
<Select.Option key={value} value={Number(value)}>
{label}
</Select.Option>
),
)}
</ControlWrapper.Select>
</Form.Item>,
2026-06-23 18:07:30 +08:00
]}
2026-07-07 11:39:02 +08:00
onReset={(value) => {
router.query = { ...value, current: 1, size: 10 };
2026-07-07 09:07:19 +08:00
handleSearch();
}}
2026-07-07 11:39:02 +08:00
onFinish={(value) => {
router.query = { ...value, current: 1, size: 10 };
2026-07-07 09:07:19 +08:00
handleSearch();
}}
2026-06-23 18:07:30 +08:00
/>
<Table
2026-07-07 09:07:19 +08:00
rowKey="id"
2026-06-23 18:07:30 +08:00
columns={[
2026-07-07 09:07:19 +08:00
{ title: "账户", dataIndex: "account", width: 140 },
{ title: "姓名", dataIndex: "applicantName", width: 100 },
{ title: "部门", dataIndex: "deptName", width: 160 },
{ title: "申请时间", dataIndex: "applyTime", width: 160 },
2026-06-23 18:07:30 +08:00
{
title: "离职申请审核状态",
2026-07-07 09:07:19 +08:00
dataIndex: "auditStatusCode",
width: 140,
render: (_, record) => {
const code = record.auditStatusCode ?? record.auditStatus;
const label = RESIGN_AUDIT_STATUS_LABEL[code] ?? "-";
return <Tag color={AUDIT_STATUS_COLOR[code]}>{label}</Tag>;
},
2026-06-23 18:07:30 +08:00
},
{
title: "操作",
2026-07-07 09:07:19 +08:00
width: 80,
2026-06-23 18:07:30 +08:00
render: (_, record) => (
2026-07-07 09:07:19 +08:00
<TableAction>
<Button
type="link"
size="small"
onClick={() => {
setCurrentId(record.id);
setViewModalOpen(true);
}}
>
查看
</Button>
</TableAction>
2026-06-23 18:07:30 +08:00
),
},
]}
2026-07-07 09:07:19 +08:00
dataSource={dataSource}
scroll={{ y: props.scrollY }}
loading={loading}
pagination={{
total,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (t) => `${t}`,
current: router.query.current || 1,
pageSize: router.query.size || 10,
onChange: (page, pageSize) => {
router.query = { ...router.query, current: page, size: pageSize };
handleSearch();
},
}}
2026-06-23 18:07:30 +08:00
/>
2026-07-07 09:07:19 +08:00
<AddModal
open={addModalOpen}
staffOptions={staffOptions}
requestAdd={props.staffResignationApplyAdd}
onCancel={() => setAddModalOpen(false)}
onSuccess={handleSearch}
/>
<ViewModal
open={viewModalOpen}
currentId={currentId}
requestDetail={props.staffResignationApplyInfo}
onCancel={() => {
setViewModalOpen(false);
setCurrentId("");
}}
/>
2026-06-29 16:45:58 +08:00
</PageLayout>
2026-06-23 18:07:30 +08:00
);
}
function AddModal({ open, staffOptions, requestAdd, onCancel, onSuccess }) {
const [form] = Form.useForm();
const [submitting, setSubmitting] = useState(false);
const handleCancel = () => {
form.resetFields();
onCancel();
};
2026-07-07 09:07:19 +08:00
const handleSubmit = async () => {
2026-06-23 18:07:30 +08:00
try {
2026-07-07 09:07:19 +08:00
const values = await form.validateFields();
2026-06-23 18:07:30 +08:00
setSubmitting(true);
2026-07-07 09:07:19 +08:00
values.applyTime =
values.applyTime?.format?.("YYYY-MM-DD HH:mm:ss") || values.applyTime;
2026-07-07 09:07:19 +08:00
values.expectedResignDate =
values.expectedResignDate?.format?.("YYYY-MM-DD") ||
values.expectedResignDate;
2026-07-07 14:32:37 +08:00
values.reportFileUrl = values.reportFileUrl?.map?.((f) => f.url).filter(Boolean).join(",") || undefined;
2026-06-23 18:07:30 +08:00
const res = await requestAdd(values);
if (res?.success !== false) {
message.success("提交成功");
handleCancel();
onSuccess();
2026-07-07 09:07:19 +08:00
} else {
2026-06-23 18:07:30 +08:00
message.error(res?.message || "提交失败");
}
2026-07-07 09:07:19 +08:00
} catch {
// validation error
} finally {
2026-06-23 18:07:30 +08:00
setSubmitting(false);
}
};
return (
<Modal
open={open}
2026-07-06 18:06:49 +08:00
destroyOnHidden
2026-06-23 18:07:30 +08:00
title="添加离职申请"
width={720}
confirmLoading={submitting}
onCancel={handleCancel}
2026-07-07 09:07:19 +08:00
onOk={handleSubmit}
2026-06-23 18:07:30 +08:00
>
2026-07-07 09:07:19 +08:00
<Form form={form} layout="vertical">
2026-06-23 18:07:30 +08:00
<Form.Item
name="personnelId"
label="申请人"
rules={[{ required: true, message: "请选择申请人" }]}
>
<Select
showSearch
placeholder="请选择申请人"
options={staffOptions}
optionFilterProp="label"
/>
</Form.Item>
<Form.Item
name="applyTime"
label="申请时间"
rules={[{ required: true, message: "请选择申请时间" }]}
>
<DatePicker showTime style={{ width: "100%" }} />
</Form.Item>
<Form.Item
2026-07-07 09:07:19 +08:00
name="expectedResignDate"
2026-06-23 18:07:30 +08:00
label="预计离职日期"
rules={[{ required: true, message: "请选择预计离职日期" }]}
>
<DatePicker style={{ width: "100%" }} />
</Form.Item>
<Form.Item
2026-07-07 09:07:19 +08:00
name="resignReason"
2026-06-23 18:07:30 +08:00
label="离职原因"
rules={[{ required: true, message: "请输入离职原因" }]}
>
<Input.TextArea rows={3} placeholder="请输入离职原因" />
</Form.Item>
<Form.Item name="remark" label="备注">
<Input.TextArea rows={2} placeholder="请输入备注" />
</Form.Item>
2026-07-07 14:32:37 +08:00
<AttachmentUpload name="reportFileUrl" label="离职通知报告" maxCount={1} accept=".pdf" />
2026-06-23 18:07:30 +08:00
<Form.Item
2026-07-07 09:07:19 +08:00
name="reportFileUrl"
2026-06-23 18:07:30 +08:00
label="离职通知报告"
rules={[{ required: true, message: "请上传离职通知报告" }]}
>
<Upload
2026-07-07 09:07:19 +08:00
action={`${window.process.env.app.API_HOST}/safetyEval/file/upload`}
2026-06-23 18:07:30 +08:00
maxCount={1}
2026-07-07 09:07:19 +08:00
accept=".pdf"
2026-07-07 15:58:05 +08:00
showUploadList={{
showPreviewIcon: true,
showRemoveIcon: true,
showDownloadIcon: false,
}}
onPreview={() => {
const url = form.getFieldValue("reportFileUrl");
if (url) window.open(url);
}}
2026-07-07 09:07:19 +08:00
onChange={(info) => {
if (info.file.status === "uploading") {
setUploading(true);
} else {
setUploading(false);
if (info.file.status === "done") {
const data = info.file.response?.data;
if (data) {
2026-07-07 15:58:05 +08:00
const url = data.url || data;
form.setFieldsValue({ reportFileUrl: url });
} else {
message.error("上传失败,未获取到文件地址");
2026-07-07 09:07:19 +08:00
}
2026-07-07 15:58:05 +08:00
} else if (info.file.status === "error") {
message.error(`${info.file.name} 上传失败`);
2026-07-07 09:07:19 +08:00
}
}
}}
2026-07-07 15:58:05 +08:00
onRemove={() => {
form.setFieldsValue({ reportFileUrl: "" });
}}
2026-07-07 09:07:19 +08:00
>
<Button loading={uploading} icon={<UploadOutlined />}>
上传文件
</Button>
</Upload>
2026-06-23 18:07:30 +08:00
</Form.Item>
</Form>
</Modal>
);
}
2026-07-07 09:07:19 +08:00
function ViewModal({ open, currentId, requestDetail, onCancel }) {
2026-06-23 18:07:30 +08:00
const [info, setInfo] = useState({});
const [detailLoading, setDetailLoading] = useState(false);
useEffect(() => {
if (!open || !currentId) {
setInfo({});
return;
}
let cancelled = false;
setDetailLoading(true);
2026-07-07 09:07:19 +08:00
requestDetail({ id: currentId })
.then((res) => {
if (!cancelled) setInfo(res?.data || {});
})
.finally(() => {
if (!cancelled) setDetailLoading(false);
});
2026-06-23 18:07:30 +08:00
return () => {
cancelled = true;
};
}, [open, currentId]);
return (
<Modal
open={open}
2026-07-06 18:06:49 +08:00
destroyOnHidden
2026-06-23 18:07:30 +08:00
title="查看离职申请"
width={720}
loading={detailLoading}
cancelText="返回"
okButtonProps={{ style: { display: "none" } }}
onCancel={onCancel}
>
<Descriptions bordered column={1} labelStyle={{ width: 120 }}>
2026-07-07 09:07:19 +08:00
<Descriptions.Item label="申请人">
{info.applicantName}
</Descriptions.Item>
2026-06-23 18:07:30 +08:00
<Descriptions.Item label="申请时间">{info.applyTime}</Descriptions.Item>
2026-07-07 09:07:19 +08:00
<Descriptions.Item label="预计离职日期">
{info.expectedResignDate}
</Descriptions.Item>
<Descriptions.Item label="离职原因">
{info.resignReason}
</Descriptions.Item>
2026-06-23 18:07:30 +08:00
<Descriptions.Item label="备注">{info.remark}</Descriptions.Item>
<Descriptions.Item label="离职通知报告">
2026-07-07 15:27:25 +08:00
<a href={info.reportFileUrl} target="_blank" rel="noopener noreferrer">
查看
</a>
2026-07-07 15:58:05 +08:00
{info.reportFileUrl ? (
<PreviewUrlButton url={info.reportFileUrl}>
查看文件
</PreviewUrlButton>
) : (
"-"
)}
2026-06-23 18:07:30 +08:00
</Descriptions.Item>
</Descriptions>
</Modal>
);
}
2026-07-07 09:07:19 +08:00
export default Connect(
[NS_STAFF_RESIGNATION_APPLY, NS_STAFF_INFO],
true,
)(AntdTableFuncControl(ResignationApplyPage));