Files
fe_supplier_frontend/src/pages/supplierAnnualManage/supplierAnnualTemplateManage/supplierAnnualTemplateManageAdd.tsx
2025-07-16 15:58:52 +08:00

650 lines
22 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,
Typography,
Spin,
Table,
Popconfirm,
Modal,
} from 'antd';
import { history, useLocation, useIntl, connect, FormattedMessage } from 'umi';
import type { ConnectProps, Dispatch } from 'umi';
import { ArrowLeftOutlined, SaveOutlined, PlusOutlined, DeleteOutlined } from '@ant-design/icons';
import CategorySelector from '@/components/CategorySelector';
import type { BreadcrumbState } from '@/models/breadcrumb';
import { AnnualTemplateStatus, AnnualTemplateStatusText } from '@/dicts/supplierAnnualDict';
import {
addAnnualTemplate,
updateAnnualTemplate,
getAnnualTemplateDetail,
getAllAnnualTemplates,
} from '@/servers/api/supplierAnnual';
import styles from './supplierAnnualTemplateManage.less';
const { Option } = Select;
interface LocationState {
isEdit?: boolean;
editData?: supplierAnnualTemplateManage.TemplateRecord;
}
// 模板记录类型 - 使用实际API接口类型
const { Title } = Typography;
// 品类限制类型常量
const CategoryLimitationType = {
UNIVERSAL: '0', // 不限
LIMITED: '1', // 限制
};
// 是否星号项常量
const StarOptions = {
YES: '1',
NO: '0',
};
interface PageProps extends ConnectProps {
breadcrumb: BreadcrumbState; // dva model状态
dispatch: Dispatch; // dva dispatch方法
}
const SupplierAnnualTemplateManageAdd: React.FC<PageProps> = ({ breadcrumb, dispatch }) => {
const intl = useIntl();
const [form] = Form.useForm();
const [loading, setLoading] = useState<boolean>(false);
const [isEdit, setIsEdit] = useState<boolean>(false);
const [templateDetail, setTemplateDetail] =
useState<supplierAnnualTemplateManage.TemplateDetailData | null>(null);
const [templateList, setTemplateList] = useState<supplierAnnualTemplateManage.TemplateRecord[]>(
[],
);
const [indicatorList, setIndicatorList] = useState<supplierAnnualTemplateManage.IndicatorItem[]>(
[],
);
// 获取路由传递的数据
const location = useLocation<LocationState>();
// 获取所有模板列表
const fetchTemplateList = async () => {
try {
setLoading(true);
const res = await getAllAnnualTemplates({ status: '1' });
if (res.success && res.data) {
// 如果是修改,需要过滤掉自己
if (location.state?.editData) {
setTemplateList(
res.data.filter(
(template: supplierAnnualTemplateManage.TemplateRecord) =>
template.id !== location.state.editData?.id,
),
);
} else {
setTemplateList(res.data);
}
} else {
message.error(
res.message ||
intl.formatMessage({ id: 'supplierAnnualTemplateManage.list.getListFailed' }),
);
}
setLoading(false);
} catch (error) {
console.error('获取模板列表失败:', error);
message.error(intl.formatMessage({ id: 'supplierAnnualTemplateManage.list.getListFailed' }));
setLoading(false);
}
};
// 获取模板详情
const fetchTemplateDetail = async (templateId: string) => {
try {
setLoading(true);
const res = await getAnnualTemplateDetail(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.templateType,
});
// 设置指标数据
if (res.data.indicatorList && res.data.indicatorList.length > 0) {
setIndicatorList(res.data.indicatorList);
}
} else {
message.error(
res.message ||
intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.getDetailFailed' }),
);
}
setLoading(false);
} catch (error) {
console.error('获取模板详情失败:', error);
message.error(intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.getDetailFailed' }));
setLoading(false);
}
};
useEffect(() => {
if (location.state?.editData?.id && dispatch) {
dispatch({
type: 'breadcrumb/updateBreadcrumbName',
payload: intl.formatMessage({ id: "supplierAnnualTemplateManage.add.edit" }),
});
}
// 组件卸载时重置面包屑
return () => {
dispatch({
type: 'breadcrumb/resetBreadcrumb',
});
};
}, [dispatch, intl, location]);
// 初始化编辑数据
useEffect(() => {
// 获取所有模板列表
fetchTemplateList();
// 如果是编辑模式,加载编辑数据
if (location.state?.isEdit && location.state?.editData) {
setIsEdit(true);
// 获取模板详情
if (location.state.editData.id) {
fetchTemplateDetail(location.state.editData.id);
}
} else {
// 非编辑模式,默认添加一条空指标
const newIndicator: supplierAnnualTemplateManage.IndicatorItem = {
orderBy: '1',
itemName: '',
isStar: StarOptions.NO,
};
setIndicatorList([newIndicator]);
}
}, []);
// 处理返回
const handleBack = () => {
history.goBack();
};
// 实际提交数据的函数
const submitFormData = async (submitData: supplierAnnualTemplateManage.AddTemplateRequest) => {
setLoading(true);
try {
// 调用API接口
let res;
if (isEdit) {
res = await updateAnnualTemplate(submitData);
} else {
res = await addAnnualTemplate(submitData);
}
if (res && res.success) {
message.success(
isEdit
? intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.submitSuccess' })
: intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.saveSuccess' }),
);
history.goBack();
} else {
message.error(
res?.message ||
(isEdit
? intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.submitFailed' })
: intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.saveFailed' })),
);
}
} catch (error) {
console.error('提交失败:', error);
message.error(
isEdit
? intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.submitFailed' })
: intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.saveFailed' }),
);
} finally {
setLoading(false);
}
};
// 处理表单提交前的验证
const handleSubmit = async (values: any) => {
// 检查指标列表
if (!indicatorList || indicatorList.length === 0) {
message.error(
intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.atLeastOneIndicator' }),
);
return;
}
// 检查指标名称不能为空
const emptyNameIndex = indicatorList.findIndex((item) => !item.itemName);
if (emptyNameIndex !== -1) {
message.error(
intl.formatMessage(
{ id: 'supplierAnnualTemplateManage.add.indicatorNameRequired' },
{ index: emptyNameIndex + 1 },
),
);
return;
}
// 准备提交数据
const submitData: supplierAnnualTemplateManage.AddTemplateRequest = {
...values,
indicatorList,
templateType: values.copyTemplateId || '1', // 默认类型
};
// 如果是编辑模式添加ID
if (isEdit && templateDetail) {
submitData.id = templateDetail.id;
}
// 显示确认弹框
Modal.confirm({
title: intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.confirm' }),
content: isEdit
? intl.formatMessage({ id: 'supplierAnnualTemplateManage.modal.enableConfirmContent' })
: intl.formatMessage({ id: 'supplierAnnualTemplateManage.modal.disableConfirmContent' }),
okText: intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.confirm' }),
cancelText: intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.cancel' }),
onOk: async () => {
await submitFormData(submitData);
},
});
};
// 处理模板选择
const handleTemplateSelect = async (templateId: string) => {
// 如果是新建模式,并且选择了模板,获取模板详情作为基础数据
if (!isEdit && templateId) {
try {
setLoading(true);
const res = await getAnnualTemplateDetail(templateId);
if (res.success && res.data) {
// 设置品类限制和品类信息
form.setFieldsValue({
categoryLimitation: res.data.categoryLimitation,
categoryId: res.data.categoryId,
});
// 复制指标列表
if (res.data.indicatorList && res.data.indicatorList.length > 0) {
const copiedList = JSON.parse(JSON.stringify(res.data.indicatorList)).map(
(item: any) => {
// 删除id防止ID冲突
delete item.id;
return item;
},
);
setIndicatorList(copiedList);
}
} else {
message.error(
res.message ||
intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.getDetailFailed' }),
);
}
setLoading(false);
} catch (error) {
console.error('获取模板详情失败:', error);
message.error(
intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.getDetailFailed' }),
);
setLoading(false);
}
}
};
// 处理添加指标
const handleAddIndicator = () => {
const newIndicator: supplierAnnualTemplateManage.IndicatorItem = {
orderBy: String(indicatorList.length + 1),
itemName: '',
isStar: StarOptions.NO,
};
setIndicatorList([...indicatorList, newIndicator]);
};
// 处理删除指标
const handleDeleteIndicator = (index: number) => {
const newList = [...indicatorList];
newList.splice(index, 1);
// 更新序号
const updatedList = newList.map((item, idx) => ({
...item,
orderBy: String(idx + 1),
}));
setIndicatorList(updatedList);
};
// 处理指标项修改
const handleIndicatorChange = (index: number, key: string, value: string) => {
const newList = [...indicatorList];
newList[index][key] = value;
setIndicatorList(newList);
};
// 指标表格列定义
const columns = [
{
title: intl.formatMessage({ id: 'supplierAnnualTemplateManage.list.serialNumber' }),
dataIndex: 'orderBy',
key: 'orderBy',
width: 80,
render: (_: string, __: any, index: number) => index + 1,
},
{
title: intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.checkItem' }),
dataIndex: 'itemName',
key: 'itemName',
render: (text: string, record: supplierAnnualTemplateManage.IndicatorItem, index: number) => (
<Input
value={text}
onChange={(e) => handleIndicatorChange(index, 'itemName', e.target.value)}
placeholder={`${intl.formatMessage({
id: 'supplierAnnualTemplateManage.common.pleaseInput',
})}${intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.checkItem' })}`}
/>
),
},
{
title: intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.starItem' }),
dataIndex: 'isStar',
key: 'isStar',
width: 120,
render: (text: string, record: supplierAnnualTemplateManage.IndicatorItem, index: number) => (
<Select
value={text}
onChange={(value) => handleIndicatorChange(index, 'isStar', value)}
className={styles.starSelector}
>
<Option value={StarOptions.YES}>
{intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.yes' })}
</Option>
<Option value={StarOptions.NO}>
{intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.no' })}
</Option>
</Select>
),
},
{
title: intl.formatMessage({ id: 'supplierAnnualTemplateManage.list.operation' }),
key: 'action',
width: 120,
render: (_: any, __: supplierAnnualTemplateManage.IndicatorItem, index: number) => (
<Space>
{index === indicatorList.length - 1 && (
<Button
type="link"
icon={<PlusOutlined />}
onClick={handleAddIndicator}
title={intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.addIndicator' })}
/>
)}
<Popconfirm
title={intl.formatMessage({
id: 'supplierAnnualTemplateManage.modal.deleteConfirmContent',
})}
onConfirm={() => handleDeleteIndicator(index)}
okText={intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.confirm' })}
cancelText={intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.cancel' })}
>
<Button
type="link"
danger
icon={<DeleteOutlined />}
title={intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.deleteIndicator' })}
/>
</Popconfirm>
</Space>
),
},
];
return (
<div className="common-container">
<div className={styles.pageHeader}>
<Title level={4} style={{ margin: 0 }}>
{isEdit
? intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.edit' })
: intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.title' })}
</Title>
<Button type="link" icon={<ArrowLeftOutlined />} onClick={handleBack}>
{intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.back' })}
</Button>
</div>
<Form
form={form}
onFinish={handleSubmit}
initialValues={{
categoryLimitation: CategoryLimitationType.UNIVERSAL,
status: AnnualTemplateStatus.DRAFT,
}}
labelCol={{ span: 7 }}
wrapperCol={{ span: 17 }}
>
<Spin spinning={loading}>
<Card
title={intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.basicInfo' })}
bordered={false}
>
<Row gutter={24}>
<Col span={8}>
<Form.Item
label={intl.formatMessage({
id: 'supplierAnnualTemplateManage.list.templateName',
})}
name="templateName"
rules={[
{
required: true,
message: `${intl.formatMessage({
id: 'supplierAnnualTemplateManage.common.pleaseInput',
})}${intl.formatMessage({
id: 'supplierAnnualTemplateManage.list.templateName',
})}`,
},
]}
>
<Input
placeholder={`${intl.formatMessage({
id: 'supplierAnnualTemplateManage.common.pleaseInput',
})}${intl.formatMessage({
id: 'supplierAnnualTemplateManage.list.templateName',
})}`}
maxLength={50}
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
label={intl.formatMessage({
id: 'supplierAnnualTemplateManage.add.categoryLimitation',
})}
name="categoryLimitation"
rules={[
{
required: true,
message: `${intl.formatMessage({
id: 'supplierAnnualTemplateManage.common.pleaseSelect',
})}${intl.formatMessage({
id: 'supplierAnnualTemplateManage.add.categoryLimitation',
})}`,
},
]}
>
<Radio.Group>
<Radio value={CategoryLimitationType.UNIVERSAL}>
{intl.formatMessage({
id: 'supplierAnnualTemplateManage.add.categoryLimitationUniversal',
})}
</Radio>
<Radio value={CategoryLimitationType.LIMITED}>
{intl.formatMessage({
id: 'supplierAnnualTemplateManage.add.categoryLimitationLimited',
})}
</Radio>
</Radio.Group>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
noStyle
shouldUpdate={(prevValues, currentValues) =>
prevValues.categoryLimitation !== currentValues.categoryLimitation
}
>
{({ getFieldValue }) => {
const categoryLimitation = getFieldValue('categoryLimitation');
return categoryLimitation === CategoryLimitationType.LIMITED ? (
<Form.Item
label={intl.formatMessage({
id: 'supplierAnnualTemplateManage.add.selectCategory',
})}
name="categoryId"
rules={[
{
required: true,
message: `${intl.formatMessage({
id: 'supplierAnnualTemplateManage.common.pleaseSelect',
})}${intl.formatMessage({
id: 'supplierAnnualTemplateManage.list.category',
})}`,
},
]}
>
<CategorySelector multiple={false} />
</Form.Item>
) : null;
}}
</Form.Item>
</Col>
</Row>
<Row gutter={24}>
<Col span={8}>
<Form.Item
label={intl.formatMessage({
id: 'supplierAnnualTemplateManage.add.selectTemplate',
})}
name="copyTemplateId"
>
<Select
placeholder={intl.formatMessage({
id: 'supplierAnnualTemplateManage.common.pleaseSelect',
})}
loading={templateList.length === 0}
onSelect={handleTemplateSelect}
allowClear={true}
onClear={() => {
setIndicatorList([]);
form.setFieldsValue({
copyTemplateId: '',
});
}}
>
{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: 'supplierAnnualTemplateManage.list.status' })}
name="status"
rules={[
{
required: true,
message: `${intl.formatMessage({
id: 'supplierAnnualTemplateManage.common.pleaseSelect',
})}${intl.formatMessage({ id: 'supplierAnnualTemplateManage.list.status' })}`,
},
]}
>
<Radio.Group>
<Radio value={AnnualTemplateStatus.DRAFT}>
{AnnualTemplateStatusText[AnnualTemplateStatus.DRAFT]}
</Radio>
<Radio value={AnnualTemplateStatus.ENABLED}>
{AnnualTemplateStatusText[AnnualTemplateStatus.ENABLED]}
</Radio>
<Radio value={AnnualTemplateStatus.DISABLED}>
{AnnualTemplateStatusText[AnnualTemplateStatus.DISABLED]}
</Radio>
</Radio.Group>
</Form.Item>
</Col>
</Row>
</Card>
<Divider />
<Card
title={intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.indicatorInfo' })}
bordered={false}
className={styles.innerCard}
>
<Table
columns={columns}
dataSource={indicatorList}
bordered
rowKey="orderBy"
size="middle"
pagination={false}
locale={{
emptyText: intl.formatMessage({
id: 'supplierAnnualTemplateManage.add.noIndicatorData',
}),
}}
className={styles.indicatorTable}
/>
</Card>
</Spin>
<div className={styles.formActions}>
<Space>
<Button onClick={handleBack}>
{intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.cancel' })}
</Button>
<Button type="primary" htmlType="submit" loading={loading} icon={<SaveOutlined />}>
{isEdit
? intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.update' })
: intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.save' })}
</Button>
</Space>
</div>
</Form>
</div>
);
};
// export default SupplierAnnualTemplateManageAdd;
// 将dva model中的状态映射到组件props
export default connect(({ breadcrumb }: { breadcrumb: BreadcrumbState }) => ({
breadcrumb,
}))(SupplierAnnualTemplateManageAdd);