Files
fe_supplier_frontend/src/pages/noticeManage/noticeManage.tsx
2025-06-17 21:06:27 +08:00

667 lines
20 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 { useIntl } from 'umi';
import { Button, Table, Modal, message, Input, Select, Form, Tooltip, Switch, Tag, Tabs } from 'antd';
import {
PlusOutlined,
DeleteOutlined,
ExclamationCircleOutlined,
SearchOutlined,
VerticalAlignTopOutlined,
} from '@ant-design/icons';
import { getNoticeList, addNotice, updateNotice, deleteNotice, batchDeleteNotice, updateNoticeStatus, updateNoticeTopStatus } from '@/servers/api/notice';
import './noticeManage.less';
// 引入封装的WangEditor组件
import WangEditor from '@/components/WangEidtor/WangEidtor';
const { Option } = Select;
const { TextArea } = Input;
const { TabPane } = Tabs;
type NoticeType = API.NoticeRecord & {
key: string;
};
const NoticeManage: React.FC = () => {
const intl = useIntl();
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [form] = Form.useForm();
const [modalVisible, setModalVisible] = useState<boolean>(false);
const [isEdit, setIsEdit] = useState<boolean>(false);
const [currentId, setCurrentId] = useState<string>('');
const [modalForm] = Form.useForm();
// 富文本内容状态
const [htmlZh, setHtmlZh] = useState<string>('');
const [htmlEn, setHtmlEn] = useState<string>('');
const [noticeData, setNoticeData] = useState<NoticeType[]>([
{
key: '1',
id: '1',
title: '系统维护通知',
titleEn: 'System Maintenance Notice',
content: '系统将于2023年7月1日进行例行维护请提前做好准备。',
contentEn: 'The system will undergo routine maintenance on July 1, 2023. Please prepare in advance.',
createTime: '2023-06-25 10:30:00',
createBy: 'admin',
status: '1', // 已发布
isTop: '1', // 已置顶
settingEn: 1, // 设置英文
},
{
key: '2',
id: '2',
title: '新功能上线通知',
titleEn: 'New Feature Launch',
content: '系统新增了XXXX功能欢迎使用。',
contentEn: 'The system has added XXXX function, welcome to use.',
createTime: '2023-06-20 14:45:00',
createBy: 'admin',
status: '0', // 草稿
isTop: '0', // 未置顶
settingEn: 1,
},
{
key: '3',
id: '3',
title: '用户反馈调查',
titleEn: '',
content: '为了提升用户体验,我们正在收集用户反馈...',
contentEn: '',
createTime: '2023-06-18 09:15:00',
createBy: 'admin',
status: '1', // 已发布
isTop: '0', // 未置顶
settingEn: 0,
},
]);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: 3,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total: number) => `${total} 条记录`,
});
const [searchParams, setSearchParams] = useState<API.NoticeSearchParams>({});
const [activeTabKey, setActiveTabKey] = useState<string>('zh');
// 获取通知列表(模拟数据)
const fetchNoticeList = (current: number = 1, pageSize: number = 10) => {
setLoading(true);
// 实际项目中应调用API
getNoticeList({
...searchParams,
})
.then(res => {
// 此处为模拟数据实际项目中应使用API返回的数据
setNoticeData([
{
key: '1',
id: '1',
title: '系统维护通知',
titleEn: 'System Maintenance Notice',
content: '系统将于2023年7月1日进行例行维护请提前做好准备。',
contentEn: 'The system will undergo routine maintenance on July 1, 2023. Please prepare in advance.',
createTime: '2023-06-25 10:30:00',
createBy: 'admin',
status: '1', // 已发布
isTop: '1', // 已置顶
settingEn: 1,
},
{
key: '2',
id: '2',
title: '新功能上线通知',
titleEn: 'New Feature Launch',
content: '系统新增了XXXX功能欢迎使用。',
contentEn: 'The system has added XXXX function, welcome to use.',
createTime: '2023-06-20 14:45:00',
createBy: 'admin',
status: '0', // 草稿
isTop: '0', // 未置顶
settingEn: 1,
},
{
key: '3',
id: '3',
title: '用户反馈调查',
titleEn: '',
content: '为了提升用户体验,我们正在收集用户反馈...',
contentEn: '',
createTime: '2023-06-18 09:15:00',
createBy: 'admin',
status: '1', // 已发布
isTop: '0', // 未置顶
settingEn: 0,
},
]);
setPagination({
...pagination,
current,
pageSize,
total: 3, // 应该是res.total
});
})
.catch(error => {
console.error('获取通知列表失败:', error);
message.error('获取通知列表失败');
})
.finally(() => {
setLoading(false);
});
};
// 首次加载时获取数据
useEffect(() => {
fetchNoticeList();
}, []);
// 处理编辑
const handleEdit = (record: NoticeType) => {
// 检查是否为已发布状态
if (record.status === '1') {
message.warning('已发布的通知不能编辑');
return;
}
setIsEdit(true);
setCurrentId(record.id);
setModalVisible(true);
setActiveTabKey('zh');
// 填充表单数据
modalForm.setFieldsValue({
isTop: record.isTop === '1',
titleZh: record.title,
titleEn: record.titleEn,
contentZh: record.content,
contentEn: record.contentEn || '',
});
};
// 处理删除
const showDeleteConfirm = (record: NoticeType) => {
// 检查是否为已发布状态
if (record.status === '1') {
message.warning('已发布的通知不能删除');
return;
}
Modal.confirm({
title: '确定要删除该通知吗?',
icon: <ExclamationCircleOutlined />,
content: `标题: ${record.title}`,
okText: '确定',
okType: 'danger',
cancelText: '取消',
maskClosable: false,
onOk: async () => {
try {
// 调用删除API
await deleteNotice(record.id);
// 更新本地数据
const newData = noticeData.filter(item => item.id !== record.id);
setNoticeData(newData);
message.success('删除成功');
} catch (error) {
console.error('删除通知失败:', error);
message.error('删除失败');
}
},
});
};
// 处理发布/下架
const handlePublishStatus = async (record: NoticeType) => {
// 状态: 0-草稿1-已发布
const isPublished = record.status === '1';
const actionText = isPublished ? '下架' : '发布';
const newStatus = isPublished ? '0' : '1';
try {
// 调用API更新状态
await updateNoticeStatus(record.id, newStatus);
// 更新本地数据
const newData = noticeData.map(item =>
item.id === record.id ? { ...item, status: newStatus } : item
);
setNoticeData(newData);
message.success(`${actionText}成功`);
} catch (error) {
console.error(`${actionText}失败:`, error);
message.error(`${actionText}失败`);
}
};
// 处理置顶/取消置顶
const handleToggleTop = async (record: NoticeType) => {
const isTop = record.isTop === '1';
const actionText = isTop ? '取消置顶' : '置顶';
const newIsTop = isTop ? '0' : '1';
try {
// 调用API更新置顶状态
await updateNoticeTopStatus(record.id, newIsTop);
// 更新本地数据
const newData = noticeData.map(item =>
item.id === record.id ? { ...item, isTop: newIsTop } : item
);
setNoticeData(newData);
message.success(`${actionText}成功`);
} catch (error) {
console.error(`${actionText}失败:`, error);
message.error(`${actionText}失败`);
}
};
// 获取状态标签
const getStatusTag = (status: string) => {
switch (status) {
case '0':
return <Tag color="default">稿</Tag>;
case '1':
return <Tag color="green"></Tag>;
case '2':
return <Tag color="orange"></Tag>;
default:
return <Tag color="default"></Tag>;
}
};
// 处理表格分页变化
const handleTableChange = (newPagination: any) => {
fetchNoticeList(newPagination.current, newPagination.pageSize);
};
const columns = [
{
title: '序号',
dataIndex: 'id',
key: 'id',
align: 'center' as const,
width: 80,
},
{
title: '标题',
dataIndex: 'title',
key: 'title',
ellipsis: {
showTitle: false,
},
render: (title: string, record: NoticeType) => (
<Tooltip placement="topLeft" title={title}>
<span>
{record.isTop === '1' && <Tag color="red" style={{ marginRight: 8 }}></Tag>}
{title}
</span>
</Tooltip>
),
},
{
title: '日期',
dataIndex: 'createTime',
key: 'createTime',
align: 'center' as const,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
align: 'center' as const,
render: (status: string) => getStatusTag(status),
},
{
title: '发布人',
dataIndex: 'createBy',
key: 'createBy',
align: 'center' as const,
},
{
title: '操作',
key: 'action',
width: 300,
align: 'center' as const,
render: (_: unknown, record: NoticeType) => (
<>
{record.status === '1' ? (
<>
<Button type="link" onClick={() => handlePublishStatus(record)}>
</Button>
<Button
type="link"
onClick={() => handleToggleTop(record)}
>
{record.isTop === '1' ? '取消置顶' : '置顶'}
</Button>
</>
) : (
<>
<Button type="link" onClick={() => handleEdit(record)}>
</Button>
<Button
type="link"
onClick={() => handlePublishStatus(record)}
>
</Button>
<Button
type="link"
onClick={() => showDeleteConfirm(record)}
>
</Button>
<Button
type="link"
onClick={() => handleToggleTop(record)}
>
{record.isTop === '1' ? '取消置顶' : '置顶'}
</Button>
</>
)}
</>
),
},
];
// 行选择限制
const checkSelectable = (record: NoticeType) => {
return record.status !== '1'; // 已发布的不能选择
};
const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
setSelectedRowKeys(newSelectedRowKeys);
};
const rowSelection = {
selectedRowKeys,
onChange: onSelectChange,
getCheckboxProps: (record: NoticeType) => ({
disabled: record.status === '1', // 已发布的不能选择
}),
};
const hasSelected = selectedRowKeys.length > 0;
// 处理添加
const handleAdd = () => {
setIsEdit(false);
setCurrentId('');
modalForm.resetFields();
setModalVisible(true);
setActiveTabKey('zh');
};
// 处理批量删除
const handleBatchDelete = () => {
Modal.confirm({
title: '确定要删除选中的通知吗?',
icon: <ExclamationCircleOutlined />,
content: '删除后无法恢复',
okText: '确定',
okType: 'danger',
cancelText: '取消',
maskClosable: false,
onOk: async () => {
setLoading(true);
try {
// 获取可删除的ID非已发布状态
const deleteIds = selectedRowKeys.filter(key => {
const record = noticeData.find(item => item.key === key);
return record && record.status !== '1';
}) as string[];
if (deleteIds.length > 0) {
// 调用批量删除API
await batchDeleteNotice(deleteIds);
// 更新本地数据
const newData = noticeData.filter(item => !deleteIds.includes(item.key));
setNoticeData(newData);
setSelectedRowKeys([]);
message.success('删除成功');
} else {
message.warning('没有可删除的通知');
}
} catch (error) {
console.error('批量删除失败:', error);
message.error('批量删除失败');
} finally {
setLoading(false);
}
},
});
};
// 处理搜索
const handleSearch = (values: API.NoticeSearchParams) => {
setSearchParams(values);
fetchNoticeList(1, pagination.pageSize);
};
// 处理模态框提交
const handleModalSubmit = () => {
// 先触发所有字段的验证
modalForm.validateFields()
.then(async values => {
try {
const noticeParams: API.NoticeRequest = {
title: values.titleZh,
titleEn: values.titleEn || '',
content: values.contentZh,
contentEn: values.contentEn || '',
isTop: values.isTop ? '1' : '0',
settingEn: values.titleEn && values.contentEn ? 1 : 0,
};
if (isEdit) {
// 编辑模式调用更新API
await updateNotice(currentId, noticeParams);
message.success('更新成功');
} else {
// 新增模式调用添加API
await addNotice(noticeParams);
message.success('添加成功');
}
setModalVisible(false);
modalForm.resetFields();
// 刷新列表
fetchNoticeList(pagination.current, pagination.pageSize);
} catch (error) {
console.error(isEdit ? '更新通知失败:' : '添加通知失败:', error);
message.error(isEdit ? '更新失败' : '添加失败');
}
})
.catch(errorInfo => {
// 获取所有字段的错误信息
const errorFields = errorInfo.errorFields || [];
// 检查是否有中文标题或内容的错误
const hasZhError = errorFields.some((field: any) => {
if (!field.name) return false;
const fieldName = Array.isArray(field.name) ? field.name.join('.') : String(field.name);
return fieldName.includes('titleZh') || fieldName.includes('contentZh');
});
// 检查是否有英文标题或内容的错误
const hasEnError = errorFields.some((field: any) => {
if (!field.name) return false;
const fieldName = Array.isArray(field.name) ? field.name.join('.') : String(field.name);
return fieldName.includes('titleEn') || fieldName.includes('contentEn');
});
// 如果有中文字段错误切换到中文Tab
if (hasZhError) {
setActiveTabKey('zh');
}
// 如果只有英文字段错误切换到英文Tab
else if (hasEnError && !hasZhError) {
setActiveTabKey('en');
}
console.log('表单验证失败:', errorInfo);
});
};
// 处理Tab切换
const handleTabChange = (key: string) => {
setActiveTabKey(key);
};
// 处理模态框取消
const handleModalCancel = () => {
setModalVisible(false);
modalForm.resetFields();
};
return (
<div className="common-container">
<div className="filter-action-row">
<Form
form={form}
name="search"
onFinish={handleSearch}
layout="inline"
className="filter-form"
>
<Form.Item name="title" label="标题">
<Input placeholder="请输入标题关键词" allowClear />
</Form.Item>
<Form.Item name="status" label="状态">
<Select placeholder="请选择状态" allowClear>
<Option value="0">稿</Option>
<Option value="1"></Option>
<Option value="2"></Option>
</Select>
</Form.Item>
<Form.Item className="filter-btns">
<Button type="primary" htmlType="submit" icon={<SearchOutlined />}>
</Button>
<Button
type="primary"
danger
icon={<DeleteOutlined />}
onClick={() => {
form.resetFields();
setSearchParams({});
handleSearch({});
}}
>
</Button>
</Form.Item>
</Form>
<div className="right-buttons">
<Button
type="primary"
ghost
icon={<PlusOutlined />}
onClick={handleAdd}
>
</Button>
<Button
danger
icon={<DeleteOutlined />}
onClick={handleBatchDelete}
disabled={!hasSelected}
loading={loading}
>
</Button>
{hasSelected && (
<span className="selected-count">
{selectedRowKeys.length}
</span>
)}
</div>
</div>
<div className="content-area">
<Table
rowSelection={rowSelection}
columns={columns}
dataSource={noticeData}
pagination={pagination}
loading={loading}
onChange={handleTableChange}
/>
</div>
{/* 新增/编辑模态框 */}
<Modal
title={isEdit ? "编辑通知" : "新增通知"}
visible={modalVisible}
onOk={handleModalSubmit}
onCancel={handleModalCancel}
width={900}
maskClosable={false}
destroyOnClose
>
<Form
form={modalForm}
layout="vertical"
name="noticeForm"
preserve={false}
>
<Form.Item name="isTop" label="是否置顶" valuePropName="checked">
<Switch />
</Form.Item>
<Tabs activeKey={activeTabKey} onChange={handleTabChange}>
<TabPane tab="中文版" key="zh">
<Form.Item
name="titleZh"
label="标题(中文)"
rules={[{ required: true, message: '请输入中文标题' }]}
>
<Input placeholder="请输入中文标题" />
</Form.Item>
<Form.Item
name="contentZh"
label="内容(中文)"
rules={[{ required: true, message: '请输入中文内容' }]}
>
<WangEditor
language="zh-CN"
height="300px"
placeholder="请输入通知内容..."
/>
</Form.Item>
</TabPane>
<TabPane tab="英文版" key="en">
<Form.Item
name="titleEn"
label="标题(英文)"
rules={[{ required: true, message: 'Please enter title in English' }]}
>
<Input placeholder="Please enter title in English" />
</Form.Item>
<Form.Item
name="contentEn"
label="内容(英文)"
rules={[{ required: true, message: 'Please enter content in English' }]}
>
<WangEditor
language="en"
height="300px"
placeholder="Please enter notice content..."
/>
</Form.Item>
</TabPane>
</Tabs>
</Form>
</Modal>
</div>
);
};
export default NoticeManage;