Files
fe_supplier_frontend/src/pages/supplierEvaluateManage/supplierTemplateManage/supplierTemplateManageAdd.tsx
2025-08-11 11:25:19 +08:00

695 lines
25 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react';
import {
Card,
Form,
Input,
Select,
Radio,
Button,
message,
Row,
Col,
Divider,
Space,
Switch,
Typography,
Spin,
Modal,
Checkbox,
} from 'antd';
import { history, useLocation, useIntl, connect } from 'umi';
import type { Dispatch, ConnectProps } from 'umi';
import type { BreadcrumbState } from '@/models/breadcrumb';
import { ArrowLeftOutlined, SaveOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
import EvaluateTemplateTable from '@/components/EvaluateTemplateTable';
import CategorySelector from '@/components/TreeCategorySelector';
import {
CategoryLimitationType,
CategoryLimitationTypeText,
TemplateStatus,
TemplateStatusText,
IndicatorAddOption,
IndicatorAddOptionText,
Types,
} from '@/dicts/supplierTemplateDict';
import { useUser } from '@/hooks/useUser';
import {
getTemplateDetail,
getAllTemplates,
updateTemplate,
addTemplate,
} from '@/servers/api/supplierEvaluate';
import styles from './supplierTemplateManage.less';
import { getDictList } from '@/servers/api/dicts';
import type { DictItem } from '@/servers/api/dicts';
const { Option } = Select;
const { confirm } = Modal;
// 定义指标相关的接口
interface FormValues {
templateName: string;
categoryLimitation: string;
categoryId?: string;
status: string;
copyTemplateId?: string;
indicatorStMore: string;
indicatorNdMore: string;
[key: string]: any;
}
interface PageProps extends ConnectProps {
breadcrumb: BreadcrumbState; // dva model状态
dispatch: Dispatch; // dva dispatch方法
}
interface LocationState {
isEdit?: boolean;
editData?: SupplierTemplateManage.TemplateItem;
}
const { Title } = Typography;
const SupplierTemplateManageAdd: React.FC<PageProps> = ({ breadcrumb, dispatch }) => {
const { getUserRole, setUserInfo } = useUser();
const intl = useIntl();
const [form] = Form.useForm<FormValues>();
const [loading, setLoading] = useState<boolean>(false);
const [templateData, setTemplateData] = useState<SupplierTemplateManage.IndicatorSt[]>([]);
const [isEdit, setIsEdit] = useState<boolean>(false);
const [templateDetail, setTemplateDetail] =
useState<SupplierTemplateManage.TemplateDetailResponse['data']>();
const [templateList, setTemplateList] = useState<SupplierTemplateManage.TemplateItem[]>([]);
// 添加控制开关的状态 一级指标是否可增加(0.可增加、1.不可增加)
//二级指标是否可增加(0.可增加、1.不可增加)
const [indicatorTypes, setIndicatorTypes] = useState<{ label: string; value: string }[]>([]);
// 获取路由传递的数据
const location = useLocation<LocationState>();
// 获取所有模板列表
const fetchTemplateList = async () => {
try {
const res = await getAllTemplates({ status: '1', type: 'currentUnit' });
if (res.success && res.data) {
// 如果是修改,需要过滤掉自己
if (location.state?.editData) {
setTemplateList(
res.data.filter(
(template: SupplierTemplateManage.TemplateItem) =>
template.id !== location.state.editData?.id,
),
);
} else {
setTemplateList(res.data);
}
} else {
message.error(
intl.formatMessage({ id: 'supplierTemplateManage.message.getTemplateListFailed' }) ||
res.message,
);
}
} catch (error) {
console.error('获取模板列表失败:', error);
message.error(
intl.formatMessage({ id: 'supplierTemplateManage.message.getTemplateListFailed' }),
);
}
};
// 获取模板详情
const fetchTemplateDetail = async (templateId: string) => {
try {
setLoading(true);
const res = await getTemplateDetail(templateId);
if (res.success && res.data) {
setTemplateDetail(res.data);
// 设置表单数据
form.setFieldsValue({
templateName: res.data.templateName,
categoryLimitation: res.data.categoryLimitation,
categoryId: res.data.categoryId,
status: res.data.status,
copyTemplateId: res.data.copyTemplateId,
//一级指标是否可追加
indicatorStMore: res.data.indicatorStMore || IndicatorAddOption.CAN_ADD,
//二级指标是否可追加
indicatorNdMore: res.data.indicatorNdMore || IndicatorAddOption.CAN_ADD,
//禁用指标类型 string(单选) 字典表 Indicator_type
indicatorTypeMore: res.data.indicatorTypeMore || '',
});
// 直接设置指标数据,无需转换
if (res.data.indicatorStList && res.data.indicatorStList.length > 0) {
setTemplateData(res.data.indicatorStList as SupplierTemplateManage.IndicatorSt[]);
}
} else {
message.error(
intl.formatMessage({ id: 'supplierTemplateManage.message.getDetailFailed' }) ||
res.message,
);
}
} catch (error) {
console.error('获取模板详情失败:', error);
message.error(intl.formatMessage({ id: 'supplierTemplateManage.message.getDetailFailed' }));
} finally {
setLoading(false);
}
};
// 获取指标类型字典
const fetchIndicatorTypes = async () => {
try {
const res = await getDictList('Indicator_type');
if (res.success && res.data) {
setIndicatorTypes(
res.data.map((item: DictItem) => ({
label: item.dicName,
value: item.code,
})),
);
} else {
message.error(
intl.formatMessage({ id: 'supplierTemplateManage.evaluateTable.message.getTypeFailed' }),
);
}
} catch (error) {
console.error('获取指标类型失败:', error);
message.error(
intl.formatMessage({ id: 'supplierTemplateManage.evaluateTable.message.getTypeFailed' }),
);
} finally {
}
};
useEffect(() => {
if (location.state?.editData?.id && dispatch) {
dispatch({
type: 'breadcrumb/updateBreadcrumbName',
payload: intl.formatMessage({ id: 'supplierTemplateManage.edit.title' }),
});
}
return () => {
dispatch({
type: 'breadcrumb/resetBreadcrumb',
});
};
}, [dispatch, intl, location]);
// 初始化编辑数据
useEffect(() => {
// 获取所有模板列表
fetchTemplateList();
// 获取指标类型
fetchIndicatorTypes();
// 如果是编辑模式,加载编辑数据
if (location.state?.isEdit && location.state?.editData) {
setIsEdit(true);
// 获取模板详情
if (location.state.editData.id) {
fetchTemplateDetail(location.state.editData.id);
}
}
}, []);
// 处理返回
const handleBack = () => {
history.goBack();
};
// 提交时校验 全部一级指标分值相加必须等于100分 并且 二级指标分值相加必须等于一级指标分值
const validateScore = () => {
// 校验一级指标总分必须等于100
const totalScore = templateData.reduce(
(acc, stItem) => acc + parseFloat(stItem.score || '0'),
0,
);
if (totalScore !== 100) {
message.error('一级指标分值相加必须等于100分');
return false;
}
// 校验每个一级指标下的二级指标之和是否等于该一级指标分值
for (const stItem of templateData) {
const firstLevelScore = parseFloat(stItem.score || '0');
const secondLevelTotal =
stItem.indicatorNdList?.reduce((acc, ndItem) => acc + parseFloat(ndItem.score || '0'), 0) ||
0;
if (secondLevelTotal !== firstLevelScore) {
message.error(`二级指标分值之和必须等于其一级指标的分值`);
return false;
}
}
return true;
};
// 提交数据的函数
const handleDataSubmit = async (values: FormValues) => {
// 准备提交数据
const selectedTemplate = templateList.find((template) => template.id === values.copyTemplateId);
const dataToSubmit: SupplierTemplateManage.TemplateUpdateRequest = {
...values,
templateType: selectedTemplate?.templateType || '',
indicatorStList: templateData,
status: parseInt(values.status, 10),
} as unknown as SupplierTemplateManage.TemplateUpdateRequest;
// 如果是编辑模式添加ID
if (isEdit && templateDetail) {
dataToSubmit.id = templateDetail.id;
}
setLoading(true);
try {
// 调用API接口
let res;
if (isEdit) {
res = await updateTemplate(dataToSubmit);
} else {
res = await addTemplate(
dataToSubmit as unknown as SupplierTemplateManage.TemplateAddRequest,
);
}
if (res.success) {
message.success(
isEdit
? intl.formatMessage({ id: 'supplierTemplateManage.message.updateSuccess' })
: intl.formatMessage({ id: 'supplierTemplateManage.message.saveSuccess' }),
);
history.goBack();
} else {
message.error(
res.message ||
(isEdit
? intl.formatMessage({ id: 'supplierTemplateManage.message.updateFailed' })
: intl.formatMessage({ id: 'supplierTemplateManage.message.saveFailed' })),
);
}
} catch (error) {
console.error('提交失败:', error);
message.error(intl.formatMessage({ id: 'supplierTemplateManage.message.submitFailed' }));
} finally {
setLoading(false);
}
};
// 处理表单提交
const handleSubmit = (values: FormValues) => {
// 验证指标数据
if (!templateData || templateData.length === 0) {
message.error(intl.formatMessage({ id: 'supplierTemplateManage.message.addIndicator' }));
return;
}
// 校验分数
if (!validateScore()) {
return;
}
// 显示确认对话框
confirm({
title: isEdit
? intl.formatMessage({ id: 'supplierTemplateManage.confirm.update.title' })
: intl.formatMessage({ id: 'supplierTemplateManage.confirm.save.title' }),
icon: <ExclamationCircleOutlined />,
content: isEdit
? intl.formatMessage({ id: 'supplierTemplateManage.confirm.update.content' })
: intl.formatMessage({ id: 'supplierTemplateManage.confirm.save.content' }),
okText: intl.formatMessage({ id: 'supplierTemplateManage.confirm.ok' }),
cancelText: intl.formatMessage({ id: 'supplierTemplateManage.confirm.cancel' }),
onOk() {
// 用户点击确认,执行提交操作
handleDataSubmit(values);
},
});
};
// 处理指标数据变更
const handleTemplateDataChange = (data: SupplierTemplateManage.IndicatorSt[]) => {
setTemplateData(data);
};
const searchTemplateInfoCopyToTemplateData = async (templateId: string) => {
// 要求,如果选择模板时,需读取模板中的 indicatorStMore(一级指标是否可编辑) 和 indicatorNdMore(二级指标是否可编辑) 的值
// 来动态设置 EvaluateTemplateTable 组件的value(templateData) 中的disable属性
// 注意 当一级指标禁用时,需要判断指标类型是通用类型的才禁用,其他类型的指标不进行禁用
if (templateId) {
try {
setLoading(true);
const res = await getTemplateDetail(templateId);
if (res.success && res.data) {
// 一级指标是否可编辑
// const indicatorStMoreStatus = res.data.indicatorStMore === IndicatorAddOption.CANNOT_ADD;
// 二级指标是否可编辑
// const indicatorNdMoreStatus = res.data.indicatorNdMore === IndicatorAddOption.CANNOT_ADD;
// 只复制指标数据,不复制基础信息
if (res.data.indicatorStList && res.data.indicatorStList.length > 0) {
// 复制模板数据但清除ID以创建新记录
// const copiedIndicatorStList = JSON.parse(JSON.stringify(res.data.indicatorStList)).map(
// (stItem: SupplierTemplateManage.IndicatorSt) => {
// // 通用类型指标禁用,其他类型指标不禁用 (如果当前是true 表示已禁用, 需要同时满足当前指标类型是通用类型)
// stItem.disabled = indicatorStMoreStatus && stItem.indicatorType === 'generalType';
// // 删除ID而不是设为undefined
// // delete stItem.id;
// stItem.indicatorNdList = stItem.indicatorNdList.map(
// (ndItem: SupplierTemplateManage.IndicatorNd) => {
// ndItem.disabled = indicatorNdMoreStatus;
// // delete ndItem.id;
// return ndItem;
// },
// );
// return stItem;
// },
// );
// setTemplateData(copiedIndicatorStList);
setTemplateData(res.data.indicatorStList);
// 赋值表单,因为需要传递到组件中判断状态
form.setFieldsValue({
indicatorStMore: res.data.indicatorStMore,
indicatorTypeMore: res.data.indicatorTypeMore,
});
}
} else {
message.error(
intl.formatMessage({ id: 'supplierTemplateManage.message.getDetailFailed' }) ||
res.message,
);
}
} catch (error) {
console.error('获取模板详情失败:', error);
message.error(intl.formatMessage({ id: 'supplierTemplateManage.message.getDetailFailed' }));
} finally {
setLoading(false);
}
}
};
// 处理模板选择
const handleTemplateSelect = async (templateId: string) => {
if (isEdit) {
// 如果是编辑模式,需要增加弹出提示
// 提示内容,切换模板会覆盖当前数据,是否继续
confirm({
title: intl.formatMessage({ id: 'supplierTemplateManage.confirm.switchTemplate.title' }),
content: intl.formatMessage({
id: 'supplierTemplateManage.confirm.switchTemplate.content',
}),
okText: intl.formatMessage({ id: 'supplierTemplateManage.confirm.switchTemplate.ok' }),
cancelText: intl.formatMessage({
id: 'supplierTemplateManage.confirm.switchTemplate.cancel',
}),
onOk: () => {
searchTemplateInfoCopyToTemplateData(templateId);
},
onCancel: () => {
// 取消选择 ,将select还原回去
form.setFieldsValue({
copyTemplateId: templateDetail?.copyTemplateId,
});
},
});
} else {
searchTemplateInfoCopyToTemplateData(templateId);
}
};
return (
<div className="common-container">
<div className={styles.pageHeader}>
<Title level={4} style={{ margin: 0 }}>
{isEdit
? intl.formatMessage({ id: 'supplierTemplateManage.edit.title' })
: intl.formatMessage({ id: 'supplierTemplateManage.add.title' })}
</Title>
<Button type="link" icon={<ArrowLeftOutlined />} onClick={handleBack}>
{intl.formatMessage({ id: 'supplierTemplateManage.button.back' })}
</Button>
</div>
<Form
form={form}
onFinish={handleSubmit}
initialValues={{
categoryLimitation: CategoryLimitationType.UNIVERSAL,
status: TemplateStatus.DRAFT,
indicatorStMore: IndicatorAddOption.CAN_ADD,
indicatorNdMore: IndicatorAddOption.CAN_ADD,
}}
labelCol={{ span: 7 }}
wrapperCol={{ span: 17 }}
>
<Spin spinning={loading}>
<Card
title={intl.formatMessage({ id: 'supplierTemplateManage.card.basicInfo' })}
bordered={false}
// className={styles.innerCard}
>
<Row gutter={24}>
<Col span={8}>
<Form.Item
label={intl.formatMessage({ id: 'supplierTemplateManage.form.templateName' })}
name="templateName"
rules={[
{
required: true,
message: intl.formatMessage({
id: 'supplierTemplateManage.rule.templateName',
}),
},
]}
>
<Input
placeholder={intl.formatMessage({
id: 'supplierTemplateManage.placeholder.templateName',
})}
maxLength={50}
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
label={intl.formatMessage({
id: 'supplierTemplateManage.form.categoryLimitation',
})}
name="categoryLimitation"
rules={[
{
required: true,
message: intl.formatMessage({
id: 'supplierTemplateManage.rule.categoryLimitation',
}),
},
]}
>
<Radio.Group>
<Radio value={CategoryLimitationType.UNIVERSAL}>
{CategoryLimitationTypeText[CategoryLimitationType.UNIVERSAL]}
</Radio>
<Radio value={CategoryLimitationType.LIMITED}>
{CategoryLimitationTypeText[CategoryLimitationType.LIMITED]}
</Radio>
</Radio.Group>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
noStyle
shouldUpdate={(prevValues: FormValues, currentValues: FormValues) =>
prevValues.categoryLimitation !== currentValues.categoryLimitation
}
>
{({ getFieldValue }) => {
const categoryLimitation = getFieldValue('categoryLimitation');
return categoryLimitation === CategoryLimitationType.LIMITED ? (
<Form.Item
label={intl.formatMessage({
id: 'supplierTemplateManage.form.selectCategory',
})}
name="categoryId"
rules={[
{
required: true,
message: intl.formatMessage({
id: 'supplierTemplateManage.rule.category',
}),
},
]}
>
<CategorySelector value={categoryLimitation} multiple={false} />
</Form.Item>
) : null;
}}
</Form.Item>
</Col>
</Row>
<Row gutter={24}>
<Col span={8}>
<Form.Item
label={intl.formatMessage({ id: 'supplierTemplateManage.form.selectTemplate' })}
name="copyTemplateId"
>
<Select
placeholder={intl.formatMessage({
id: 'supplierTemplateManage.form.selectTemplate',
})}
loading={templateList.length === 0}
showSearch={true}
allowClear={true}
onClear={() => {
setTemplateData([]);
form.setFieldsValue({
copyTemplateId: '',
});
}}
filterOption={(input, option) => {
return (option?.children ?? '').toLowerCase().includes(input.toLowerCase());
}}
onSelect={handleTemplateSelect}
>
{templateList.map((template) =>
template.id ? (
<Option key={template.id} value={template.id}>
{template.templateName}
</Option>
) : null,
)}
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
label={intl.formatMessage({ id: 'supplierTemplateManage.form.status' })}
name="status"
rules={[{ required: true }]}
>
<Radio.Group>
<Radio value={TemplateStatus.DRAFT}>
{TemplateStatusText[TemplateStatus.DRAFT]}
</Radio>
<Radio value={TemplateStatus.ENABLED}>
{TemplateStatusText[TemplateStatus.ENABLED]}
</Radio>
<Radio value={TemplateStatus.DISABLED}>
{TemplateStatusText[TemplateStatus.DISABLED]}
</Radio>
</Radio.Group>
</Form.Item>
</Col>
</Row>
<Row gutter={24}>
<Col span={8}>
<Form.Item
label="是否可追加一级指标"
name="indicatorStMore"
hidden={getUserRole() !== 'admin'}
>
<Radio.Group
onChange={(e) => {
if (e.target.value === IndicatorAddOption.CAN_ADD) {
form.setFieldsValue({
indicatorTypeMore: '',
});
}
}}
>
<Radio value={IndicatorAddOption.CAN_ADD}>
{IndicatorAddOptionText[IndicatorAddOption.CAN_ADD]}
</Radio>
<Radio value={IndicatorAddOption.CANNOT_ADD}>
{IndicatorAddOptionText[IndicatorAddOption.CANNOT_ADD]}
</Radio>
</Radio.Group>
</Form.Item>
</Col>
<Form.Item // 这里必须放在Row下
shouldUpdate={(prev, curr) => prev.indicatorStMore !== curr.indicatorStMore}
noStyle
>
{({ getFieldValue }) =>
getFieldValue('indicatorStMore') === IndicatorAddOption.CANNOT_ADD ? (
<Col span={8}>
<Form.Item
label="禁用指标类型"
name="indicatorTypeMore"
hidden={getUserRole() !== 'admin'}
>
<Select
placeholder="请选择禁用指标类型"
options={indicatorTypes}
allowClear
/>
</Form.Item>
</Col>
) : null
}
</Form.Item>
{/* <Col span={8}>
<Form.Item
label={intl.formatMessage({ id: 'supplierTemplateManage.form.indicatorNdMore' })}
name="indicatorNdMore"
valuePropName="checked"
getValueProps={(value) => ({ checked: value === IndicatorAddOption.CAN_ADD })}
>
<Switch
checked={indicatorNdMore === IndicatorAddOption.CAN_ADD}
onChange={(checked) => handleSwitchChange('indicatorNdMore', checked)}
/>
</Form.Item>
</Col> */}
</Row>
</Card>
<Divider />
<Card
title={intl.formatMessage({ id: 'supplierTemplateManage.card.indicatorInfo' })}
bordered={false}
className={styles.innerCard}
>
<Form.Item
shouldUpdate={(prev, curr) =>
prev.indicatorStMore !== curr.indicatorStMore ||
prev.indicatorTypeMore !== curr.indicatorTypeMore
}
noStyle
>
{({ getFieldValue }) => (
<EvaluateTemplateTable
disableType={getFieldValue('indicatorTypeMore')}
firstIsAdd={getFieldValue('indicatorStMore') === IndicatorAddOption.CAN_ADD}
onChange={handleTemplateDataChange}
value={templateData}
/>
)}
</Form.Item>
</Card>
</Spin>
<div className={styles.formActions}>
<Space>
<Button onClick={handleBack}>
{intl.formatMessage({ id: 'supplierTemplateManage.button.cancel' })}
</Button>
<Button type="primary" htmlType="submit" loading={loading} icon={<SaveOutlined />}>
{isEdit
? intl.formatMessage({ id: 'supplierTemplateManage.button.update' })
: intl.formatMessage({ id: 'supplierTemplateManage.button.save' })}
</Button>
</Space>
</div>
</Form>
</div>
);
};
// export default SupplierTemplateManageAdd;
// 将dva model中的状态映射到组件props
export default connect(({ breadcrumb }: { breadcrumb: BreadcrumbState }) => ({
breadcrumb,
}))(SupplierTemplateManageAdd);