动态更新面包屑导航

This commit is contained in:
linxd
2025-07-03 14:40:22 +08:00
parent c8b28a4a29
commit 7114466b26
10 changed files with 586 additions and 511 deletions

123
README.md Normal file
View File

@ -0,0 +1,123 @@
# 动态面包屑导航实现说明
## 功能介绍
本项目实现了动态修改页面面包屑名称的功能,特别适用于新增和修改页面共用一个组件的场景。通过 dva 状态管理,可以在具体页面中动态设置面包屑导航的最后一级显示文本。
## 实现原理
1. 通过 `breadcrumb` 模型管理面包屑状态
2. 使用 `connect``breadcrumb` 状态连接到布局组件
3. 在页面组件中根据不同条件(如是否有 id 参数)来动态设置面包屑名称
## 代码结构
- `src/models/breadcrumb.ts` - 面包屑状态模型
- `src/layouts/BasicLayout.tsx` - 支持动态面包屑的布局组件
## 使用示例
### 实际项目中的使用方式 (supplierAnnualTemplateManageAdd.tsx)
```tsx
import React, { useEffect } from 'react';
import { useIntl, connect } from 'umi';
import type { Dispatch } from 'umi';
import type { BreadcrumbState } from '@/models/breadcrumb';
// 组件 props 接口定义
interface PageProps extends ConnectProps {
breadcrumb: BreadcrumbState; // dva model状态
dispatch: Dispatch; // dva dispatch方法
}
const YourPage: React.FC<PageProps> = ({ dispatch }) => {
const intl = useIntl();
const location = useLocation();
// 设置面包屑名称
useEffect(() => {
if (location.state?.editData?.id && dispatch) {
// 编辑模式,设置编辑相关的面包屑
dispatch({
type: 'breadcrumb/updateBreadcrumbName',
payload: intl.formatMessage({ id: "yourNamespace.add.edit" }),
});
} else {
// 新增模式,设置新增相关的面包屑
dispatch({
type: 'breadcrumb/updateBreadcrumbName',
payload: intl.formatMessage({ id: "yourNamespace.add.title" }),
});
}
// 组件卸载时重置面包屑
return () => {
dispatch({
type: 'breadcrumb/resetBreadcrumb',
});
};
}, [dispatch, intl, location]);
// 页面其他内容...
return (
<div>
{/* 页面内容... */}
</div>
);
};
// 将dva model中的状态映射到组件props
export default connect(({ breadcrumb }: { breadcrumb: BreadcrumbState }) => ({
breadcrumb,
}))(YourPage);
```
### 关键实现点
1. 在页面组件中通过 connect 连接到 dva 状态:
```tsx
// 将dva model中的状态映射到组件props
export default connect(({ breadcrumb }: { breadcrumb: BreadcrumbState }) => ({
breadcrumb,
}))(YourPage);
```
2. 在 useEffect 中动态设置面包屑名称:
```tsx
useEffect(() => {
// 根据条件设置不同的面包屑名称
if (location.state?.editData?.id) {
dispatch({
type: 'breadcrumb/updateBreadcrumbName',
payload: intl.formatMessage({ id: "yourNamespace.add.edit" }),
});
} else {
dispatch({
type: 'breadcrumb/updateBreadcrumbName',
payload: intl.formatMessage({ id: "yourNamespace.add.title" }),
});
}
// 组件卸载时重置面包屑
return () => {
dispatch({
type: 'breadcrumb/resetBreadcrumb',
});
};
}, [dispatch, intl, location]);
```
## 注意事项
1. **payload 必须是字符串**:传递给 `updateBreadcrumbName` action 的 payload 必须是字符串类型,不能是对象或 React 组件
- 正确:`payload: intl.formatMessage({ id: "menu.edit" })`
- 错误:`payload: { breadcrumbName: <FormattedMessage id="menu.edit" /> }`
2. **重置面包屑**:确保在组件卸载时调用 `resetBreadcrumb` 以重置面包屑状态
3. **国际化处理**:使用 `intl.formatMessage()` 方法处理国际化文本,不要在 payload 中直接使用 FormattedMessage 组件
4. **依赖项管理**:确保 useEffect 的依赖项包含 `dispatch`, `intl` 和用于条件判断的变量(如 `location`

View File

@ -2,11 +2,13 @@
import React from 'react'; import React from 'react';
import ProLayout, { PageContainer } from '@ant-design/pro-layout'; import ProLayout, { PageContainer } from '@ant-design/pro-layout';
import { Link, useLocation, useIntl, useHistory } from 'umi'; import { Link, useLocation, useIntl, useHistory } from 'umi';
import { connect } from 'dva';
import defaultSettings from '../../config/defaultSettings'; import defaultSettings from '../../config/defaultSettings';
import routes from '../../config/router.config'; // 引入你的自定义路由结构 import routes from '../../config/router.config'; // 引入你的自定义路由结构
import { ConfigProvider, Breadcrumb } from 'antd'; import { ConfigProvider, Breadcrumb } from 'antd';
import HeaderComponent from './Header'; import HeaderComponent from './Header';
import IconFont from '@/components/IconFont/IconFont'; import IconFont from '@/components/IconFont/IconFont';
import { BreadcrumbState } from '@/models/breadcrumb';
const MenuRender = (item: any, isSubMenu: boolean) => { const MenuRender = (item: any, isSubMenu: boolean) => {
@ -32,22 +34,29 @@ const MenuRender = (item: any, isSubMenu: boolean) => {
); );
}; };
const BreadcrumbRender = (breadcrumb: any, intl: any, history: any) => { const BreadcrumbRender = (routeBreadcrumb: any, intl: any, history: any, dynamicBreadcrumbName: string | null) => {
const breadcrumbRoutes = breadcrumb?.routes; const breadcrumbRoutes = routeBreadcrumb?.routes;
return ( return (
<Breadcrumb> <Breadcrumb>
<Breadcrumb.Item <Breadcrumb.Item
onClick={() => { onClick={() => {
history.push('/'); history.push('/');
}} }}
style={{ cursor: 'pointer' }}
> >
{intl.formatMessage({ id: 'menu.首页' })} <span style={{ cursor: 'pointer' }}>
{intl.formatMessage({ id: 'menu.首页' })}
</span>
</Breadcrumb.Item> </Breadcrumb.Item>
{breadcrumbRoutes?.map((item: any) => { {breadcrumbRoutes?.map((item: any, index: number) => {
// 判断是否是最后一个面包屑项且存在动态名称
const isLastItem = index === (breadcrumbRoutes.length - 1);
const displayName = (isLastItem && dynamicBreadcrumbName)
? dynamicBreadcrumbName
: intl.formatMessage({ id: `menu.${item.breadcrumbName}` || '' });
return ( return (
<Breadcrumb.Item key={item.path}> <Breadcrumb.Item key={item.path}>
{intl.formatMessage({ id: `menu.${item.breadcrumbName}` || '' })} {displayName}
</Breadcrumb.Item> </Breadcrumb.Item>
); );
})} })}
@ -55,7 +64,13 @@ const BreadcrumbRender = (breadcrumb: any, intl: any, history: any) => {
); );
}; };
const BasicLayout: React.FC = (props) => { interface BasicLayoutProps {
children: React.ReactNode;
breadcrumb: BreadcrumbState;
}
const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
const { children, breadcrumb } = props;
const location = useLocation(); const location = useLocation();
const intl = useIntl(); const intl = useIntl();
const history = useHistory(); const history = useHistory();
@ -81,14 +96,18 @@ const BasicLayout: React.FC = (props) => {
ghost={true} ghost={true}
header={{ header={{
title: false, title: false,
breadcrumbRender: ({ breadcrumb }) => BreadcrumbRender(breadcrumb, intl, history), breadcrumbRender: ({ breadcrumb: routeBreadcrumb }) =>
BreadcrumbRender(routeBreadcrumb, intl, history, breadcrumb.breadcrumbName),
}} }}
> >
{props.children} {children}
</PageContainer> </PageContainer>
</ProLayout> </ProLayout>
</ConfigProvider> </ConfigProvider>
); );
}; };
export default BasicLayout; export default connect(({ breadcrumb }: { breadcrumb: BreadcrumbState }) => ({
breadcrumb
}))(BasicLayout);

View File

@ -1,64 +0,0 @@
import React, { useEffect } from 'react';
// import Header from './Header';
import { Layout, Breadcrumb } from 'antd';
import { useLocation, useIntl, Link, connect } from 'umi';
import type { ConnectProps, Dispatch } from 'umi';
const { Header, Sider, Content } = Layout;
//导入logo图片
import HeaderComponent from './Header';
import SiderMenu from './SiderMenu';
import './layout.less';
import type { BreadcrumbModelState } from '@/models/breadcrumb';
interface LayoutIndexProps extends ConnectProps {
breadcrumb: BreadcrumbModelState;
dispatch: Dispatch;
}
const LayoutIndex: React.FC<LayoutIndexProps> = (props) => {
const { children, breadcrumb, dispatch } = props;
const location = useLocation();
const intl = useIntl();
// 当路由变化时更新面包屑
useEffect(() => {
console.log(location)
dispatch({
type: 'breadcrumb/updateBreadcrumbs',
payload: { pathname: location.pathname, intl },
});
}, [location.pathname, intl, dispatch]);
return (
<>
<Layout>
<Header className="header">
<HeaderComponent />
</Header>
<Layout>
<Sider width={200} theme="light">
<SiderMenu />
</Sider>
<Layout className="layout-content">
<Breadcrumb style={{ margin: '10px 0' }}>
{breadcrumb.breadcrumbs.map((breadcrumbItem, index) => (
<Breadcrumb.Item key={breadcrumbItem.path}>
{index < breadcrumb.breadcrumbs.length - 1 ? (
<Link to={breadcrumbItem.path}>{breadcrumbItem.breadcrumbName}</Link>
) : (
breadcrumbItem.breadcrumbName
)}
</Breadcrumb.Item>
))}
</Breadcrumb>
<Content>{children}</Content>
</Layout>
</Layout>
</Layout>
</>
);
};
export default connect(({ breadcrumb }: { breadcrumb: BreadcrumbModelState }) => ({
breadcrumb,
}))(LayoutIndex);

View File

@ -1,127 +0,0 @@
import React, { useEffect, useState } from 'react';
import { Menu, Tooltip } from 'antd';
import { useIntl, Link, useHistory } from 'umi';
import IconFont from '@/components/IconFont/IconFont';
import routerConfig from '../../config/router.config';
// 路由接口定义
interface IRouteItem {
path: string;
component?: string;
name?: string;
meta?: {
title?: string;
hide?: boolean;
icon?: string;
};
routes?: IRouteItem[];
children?: IRouteItem[];
redirect?: string;
}
interface IMenuItem {
label: string;
key: string;
path: string;
icon: string;
children?: IMenuItem[];
}
// 获取路由配置转换为菜单项
const generateMenuItems = (): IMenuItem[] => {
// 找到主布局下的路由
const mainLayoutRoute = routerConfig.find((route: IRouteItem) => route.path === '/');
if (!mainLayoutRoute || !mainLayoutRoute.routes) return [];
// 递归处理路由,生成菜单项
const processRoutes = (routes: IRouteItem[], parentPath: string = ''): IMenuItem[] => {
return routes
.filter((route) => !route.redirect && route.name && !route.meta?.hide)
.map((route) => {
// 构建完整路径
const routePath = route.path.startsWith('/')
? route.path
: `${parentPath}/${route.path}`.replace(/\/+/g, '/');
// 创建菜单项
const menuItem: IMenuItem = {
label: route.meta?.title ? `menu.${route.meta.title}` : `menu.${route.name || ''}`,
key: route.path || '',
path: routePath,
icon: route.meta?.icon || 'icon-liebiaomoshi',
};
// 如果有子路由,递归处理
if (route.routes && route.routes.length > 0) {
const children = processRoutes(route.routes, routePath);
if (children.length > 0) {
menuItem.children = children;
}
}
// 如果有children递归处理
if (route.children && route.children.length > 0) {
const children = processRoutes(route.children, routePath);
if (children.length > 0) {
menuItem.children = children;
}
}
return menuItem;
});
};
return processRoutes(mainLayoutRoute.routes);
};
const items: IMenuItem[] = generateMenuItems();
const SiderMenu: React.FC = (props: any) => {
//当前激活菜单
const [current, setCurrent] = useState('index');
const intl = useIntl();
const history = useHistory();
useEffect(() => {
// 获取当前激活菜单
const path = history.location.pathname;
setCurrent(path);
// if (path.split('/').length > 1) {
// setCurrent(path.split('/')[path.split('/').length - 1]);
// return;
// }
}, [history.location.pathname]);
// 递归渲染菜单项
const renderMenuItems = (menuItems: IMenuItem[]) => {
return menuItems.map((item: IMenuItem) =>
item.children && item.children.length > 0 ? (
<Menu.SubMenu
key={item.path}
title={
<Tooltip title={intl.formatMessage({ id: item.label })} placement="right">
<>{intl.formatMessage({ id: item.label })}</>
</Tooltip>
}
icon={<IconFont type={item.icon} />}
>
{renderMenuItems(item.children)}
</Menu.SubMenu>
) : (
<Menu.Item key={item.path} icon={<IconFont type={item.icon} />}>
<Tooltip title={intl.formatMessage({ id: item.label })} placement="right">
<Link to={item.path}>{intl.formatMessage({ id: item.label })}</Link>
</Tooltip>
</Menu.Item>
),
);
};
return (
<div className="header-menu">
<Menu selectedKeys={[current]} mode="inline">
{renderMenuItems(items)}
</Menu>
</div>
);
};
export default SiderMenu;

View File

@ -1,133 +1,57 @@
import { Effect, Reducer } from 'umi';
export interface BreadcrumbItem { type Effect = (action: { payload: any }, effects: { call: any; put: any; select: any }) => Generator<any, void, unknown>;
path: string; type Reducer<S> = (state: S, action: { payload: any }) => S;
breadcrumbName: string;
}
export interface BreadcrumbModelState { export interface BreadcrumbState {
breadcrumbs: BreadcrumbItem[]; breadcrumbName: string | null;
} }
export interface BreadcrumbModelType { export interface BreadcrumbModelType {
namespace: 'breadcrumb'; namespace: 'breadcrumb';
state: BreadcrumbModelState; state: BreadcrumbState;
effects: { effects: {
updateBreadcrumbs: Effect; updateBreadcrumbName: Effect;
resetBreadcrumb: Effect;
}; };
reducers: { reducers: {
saveBreadcrumbs: Reducer<BreadcrumbModelState>; setBreadcrumbName: Reducer<BreadcrumbState>;
resetState: Reducer<BreadcrumbState>;
}; };
} }
/**
* 生成面包屑数据
* @param pathname 当前路径
* @param intl 国际化对象
*/
const generateBreadcrumbs = (pathname: string, intl: any): BreadcrumbItem[] => {
// 如果是首页,直接返回首页
if (pathname === '/' || pathname === '/index') {
return [
{
path: '/',
breadcrumbName: intl.formatMessage({ id: 'menu.首页' }, { defaultMessage: '首页' }),
},
];
}
// 构建面包屑数据
const breadcrumbs = [
{
path: '/',
breadcrumbName: intl.formatMessage({ id: 'menu.首页' }, { defaultMessage: '首页' }),
},
];
// 按照斜杠分割路径
const pathSegments = pathname.split('/').filter(segment => segment);
let currentPath = '';
// 构建每一级的路径和对应的面包屑名称
for (let i = 0; i < pathSegments.length; i++) {
const segment = pathSegments[i];
currentPath += `/${segment}`;
// 尝试使用多语言配置
// 先尝试完整路径的多语言配置
let menuKey = `menu.${pathSegments.slice(0, i + 1).join('/')}`;
let menuName = intl.formatMessage({ id: menuKey }, { defaultMessage: '' });
// 如果完整路径没有对应的多语言,则尝试使用当前段的多语言
if (!menuName) {
menuKey = `menu.${segment}`;
menuName = intl.formatMessage({ id: menuKey }, { defaultMessage: '' });
}
// 如果仍然没有找到多语言配置,尝试使用特定页面的多语言配置
if (!menuName) {
// 处理特殊情况,如数据统计下的子页面
if (pathSegments[0] === 'dataStatistics') {
switch (segment) {
case 'supplierEvaluateStatistics':
menuName = intl.formatMessage({ id: 'menu.供应商评价情况统计' }, { defaultMessage: '供应商评价情况统计' });
break;
case 'supplierAnnualStatistics':
menuName = intl.formatMessage({ id: 'menu.供应商年审情况统计' }, { defaultMessage: '供应商年审情况统计' });
break;
case 'supplierQualificationWarningStatistics':
menuName = intl.formatMessage({ id: 'menu.供应商资质预警统计' }, { defaultMessage: '供应商资质预警统计' });
break;
case 'supplierExitStatistics':
menuName = intl.formatMessage({ id: 'menu.供应商清退情况统计' }, { defaultMessage: '供应商清退情况统计' });
break;
default:
menuName = segment;
}
} else if (pathSegments[0] === 'supplierEvaluate') {
// 处理供应商评价模块
menuName = intl.formatMessage({ id: `menu.${segment}` }, { defaultMessage: segment });
} else if (pathSegments[0] === 'supplierAnnual') {
// 处理供应商年审模块
menuName = intl.formatMessage({ id: `menu.${segment}` }, { defaultMessage: segment });
} else {
menuName = segment;
}
}
breadcrumbs.push({
path: currentPath,
breadcrumbName: menuName || segment,
});
}
return breadcrumbs;
};
const BreadcrumbModel: BreadcrumbModelType = { const BreadcrumbModel: BreadcrumbModelType = {
namespace: 'breadcrumb', namespace: 'breadcrumb',
state: { state: {
breadcrumbs: [], breadcrumbName: null,
}, },
effects: { effects: {
*updateBreadcrumbs({ payload }, { put }) { *updateBreadcrumbName({ payload }, { put }) {
const { pathname, intl } = payload;
console.log(pathname)
const breadcrumbs = generateBreadcrumbs(pathname, intl);
yield put({ yield put({
type: 'saveBreadcrumbs', type: 'setBreadcrumbName',
payload: breadcrumbs, payload,
});
},
*resetBreadcrumb(_, { put }) {
yield put({
type: 'resetState',
}); });
}, },
}, },
reducers: { reducers: {
saveBreadcrumbs(state, { payload }) { setBreadcrumbName(state: BreadcrumbState, { payload }: { payload: string }) {
return { return {
...state, ...state,
breadcrumbs: payload, breadcrumbName: payload,
};
},
resetState() {
return {
breadcrumbName: null,
}; };
}, },
}, },

View File

@ -1,5 +1,8 @@
import globalModal from './globalModal'; import globalModal from './globalModal';
import breadcrumb from './breadcrumb';
export default { export default {
globalModal, globalModal,
breadcrumb,
// ... 其他 models // ... 其他 models
}; };

View File

@ -8,6 +8,7 @@ import SupplierSelectStep from './components/SupplierSelectStep';
import EvaluatorSelectStep from './components/EvaluatorSelectStep'; import EvaluatorSelectStep from './components/EvaluatorSelectStep';
import styles from './supplierAnnualTaskManageAdd.less'; import styles from './supplierAnnualTaskManageAdd.less';
import type { SupplierTaskModelState } from '@/models/supplierAnnualTaskManage'; import type { SupplierTaskModelState } from '@/models/supplierAnnualTaskManage';
import type { BreadcrumbState } from '@/models/breadcrumb';
const { Step } = Steps; const { Step } = Steps;
@ -38,7 +39,7 @@ interface PageProps extends ConnectProps {
* 供应商任务管理添加/编辑组件 * 供应商任务管理添加/编辑组件
* 使用步骤式表单引导用户完成添加或编辑任务的流程 * 使用步骤式表单引导用户完成添加或编辑任务的流程
*/ */
const SupplierTaskManageAdd: React.FC<PageProps> = ({ supplierAnnualTaskManage, dispatch }) => { const SupplierTaskManageAdd: React.FC<PageProps> = ({ supplierAnnualTaskManage, dispatch, breadcrumb }) => {
const intl = useIntl(); const intl = useIntl();
// 获取dva model中的状态 // 获取dva model中的状态
const { currentStep, loading, detailLoading } = supplierAnnualTaskManage; const { currentStep, loading, detailLoading } = supplierAnnualTaskManage;
@ -85,14 +86,23 @@ const SupplierTaskManageAdd: React.FC<PageProps> = ({ supplierAnnualTaskManage,
* 组件卸载时重置状态 * 组件卸载时重置状态
*/ */
useEffect(() => { useEffect(() => {
if (isEditMode && dispatch) {
dispatch({
type: 'breadcrumb/updateBreadcrumbName',
payload: intl.formatMessage({ id: 'supplierAnnualTaskManage.edit.title' }),
});
}
return () => { return () => {
if (dispatch) { if (dispatch) {
dispatch({ dispatch({
type: 'supplierAnnualTaskManage/resetState', type: 'supplierAnnualTaskManage/resetState',
}); });
dispatch({
type: 'breadcrumb/resetBreadcrumb',
});
} }
}; };
}, [dispatch]); }, [dispatch, isEditMode]);
// 步骤配置,定义每个步骤的标题、描述和内容组件 // 步骤配置,定义每个步骤的标题、描述和内容组件
const steps = [ const steps = [
@ -274,7 +284,8 @@ const SupplierTaskManageAdd: React.FC<PageProps> = ({ supplierAnnualTaskManage,
// 将dva model中的状态映射到组件props // 将dva model中的状态映射到组件props
export default connect( export default connect(
({ supplierAnnualTaskManage }: { supplierAnnualTaskManage: SupplierTaskModelState }) => ({ ({ supplierAnnualTaskManage, breadcrumb }: { supplierAnnualTaskManage: SupplierTaskModelState, breadcrumb: BreadcrumbState }) => ({
supplierAnnualTaskManage, supplierAnnualTaskManage,
breadcrumb,
}), }),
)(SupplierTaskManageAdd); )(SupplierTaskManageAdd);

View File

@ -17,23 +17,17 @@ import {
Popconfirm, Popconfirm,
Modal, Modal,
} from 'antd'; } from 'antd';
import { history, useLocation, useIntl } from 'umi'; import { history, useLocation, useIntl, connect, FormattedMessage } from 'umi';
import { import type { ConnectProps, Dispatch } from 'umi';
ArrowLeftOutlined, import { ArrowLeftOutlined, SaveOutlined, PlusOutlined, DeleteOutlined } from '@ant-design/icons';
SaveOutlined,
PlusOutlined,
DeleteOutlined,
} from '@ant-design/icons';
import CategorySelector from '@/components/CategorySelector'; import CategorySelector from '@/components/CategorySelector';
import { import type { BreadcrumbState } from '@/models/breadcrumb';
AnnualTemplateStatus, import { AnnualTemplateStatus, AnnualTemplateStatusText } from '@/dicts/supplierAnnualDict';
AnnualTemplateStatusText,
} from '@/dicts/supplierAnnualDict';
import { import {
addAnnualTemplate, addAnnualTemplate,
updateAnnualTemplate, updateAnnualTemplate,
getAnnualTemplateDetail, getAnnualTemplateDetail,
getAllAnnualTemplates getAllAnnualTemplates,
} from '@/servers/api/supplierAnnual'; } from '@/servers/api/supplierAnnual';
import styles from './supplierAnnualTemplateManage.less'; import styles from './supplierAnnualTemplateManage.less';
@ -53,33 +47,30 @@ const CategoryLimitationType = {
UNIVERSAL: '0', // 不限 UNIVERSAL: '0', // 不限
LIMITED: '1', // 限制 LIMITED: '1', // 限制
}; };
// 品类限制类型文本
const CategoryLimitationTypeText = {
[CategoryLimitationType.UNIVERSAL]: '不限',
[CategoryLimitationType.LIMITED]: '限制',
};
// 是否星号项常量 // 是否星号项常量
const StarOptions = { const StarOptions = {
YES: '1', YES: '1',
NO: '0', NO: '0',
}; };
// 是否星号项文本 interface PageProps extends ConnectProps {
const StarOptionsText = { breadcrumb: BreadcrumbState; // dva model状态
[StarOptions.YES]: '是', dispatch: Dispatch; // dva dispatch方法
[StarOptions.NO]: '否', }
}; const SupplierAnnualTemplateManageAdd: React.FC<PageProps> = ({ breadcrumb, dispatch }) => {
const SupplierAnnualTemplateManageAdd: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [isEdit, setIsEdit] = useState<boolean>(false); const [isEdit, setIsEdit] = useState<boolean>(false);
const [templateDetail, setTemplateDetail] = useState<supplierAnnualTemplateManage.TemplateDetailData | null>(null); const [templateDetail, setTemplateDetail] =
const [templateList, setTemplateList] = useState<supplierAnnualTemplateManage.TemplateRecord[]>([]); useState<supplierAnnualTemplateManage.TemplateDetailData | null>(null);
const [indicatorList, setIndicatorList] = useState<supplierAnnualTemplateManage.IndicatorItem[]>([]); const [templateList, setTemplateList] = useState<supplierAnnualTemplateManage.TemplateRecord[]>(
[],
);
const [indicatorList, setIndicatorList] = useState<supplierAnnualTemplateManage.IndicatorItem[]>(
[],
);
// 获取路由传递的数据 // 获取路由传递的数据
const location = useLocation<LocationState>(); const location = useLocation<LocationState>();
@ -90,9 +81,22 @@ const SupplierAnnualTemplateManageAdd: React.FC = () => {
setLoading(true); setLoading(true);
const res = await getAllAnnualTemplates(); const res = await getAllAnnualTemplates();
if (res.success && res.data) { if (res.success && res.data) {
setTemplateList(res.data); // 如果是修改,需要过滤掉自己
if (location.state?.editData) {
setTemplateList(
res.data.filter(
(template: supplierAnnualTemplateManage.TemplateRecord) =>
template.id !== location.state.editData?.id,
),
);
} else {
setTemplateList(res.data);
}
} else { } else {
message.error(res.message || intl.formatMessage({ id: 'supplierAnnualTemplateManage.list.getListFailed' })); message.error(
res.message ||
intl.formatMessage({ id: 'supplierAnnualTemplateManage.list.getListFailed' }),
);
} }
setLoading(false); setLoading(false);
} catch (error) { } catch (error) {
@ -124,7 +128,10 @@ const SupplierAnnualTemplateManageAdd: React.FC = () => {
setIndicatorList(res.data.indicatorList); setIndicatorList(res.data.indicatorList);
} }
} else { } else {
message.error(res.message || intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.getDetailFailed' })); message.error(
res.message ||
intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.getDetailFailed' }),
);
} }
setLoading(false); setLoading(false);
} catch (error) { } catch (error) {
@ -133,7 +140,21 @@ const SupplierAnnualTemplateManageAdd: React.FC = () => {
setLoading(false); 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(() => { useEffect(() => {
// 获取所有模板列表 // 获取所有模板列表
@ -176,20 +197,27 @@ const SupplierAnnualTemplateManageAdd: React.FC = () => {
} }
if (res && res.success) { if (res && res.success) {
message.success(isEdit message.success(
? intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.submitSuccess' }) isEdit
: intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.saveSuccess' })); ? intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.submitSuccess' })
: intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.saveSuccess' }),
);
history.goBack(); history.goBack();
} else { } else {
message.error(res?.message || (isEdit message.error(
? intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.submitFailed' }) res?.message ||
: intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.saveFailed' }))); (isEdit
? intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.submitFailed' })
: intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.saveFailed' })),
);
} }
} catch (error) { } catch (error) {
console.error('提交失败:', error); console.error('提交失败:', error);
message.error(isEdit message.error(
? intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.submitFailed' }) isEdit
: intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.saveFailed' })); ? intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.submitFailed' })
: intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.saveFailed' }),
);
} finally { } finally {
setLoading(false); setLoading(false);
} }
@ -199,17 +227,21 @@ const SupplierAnnualTemplateManageAdd: React.FC = () => {
const handleSubmit = async (values: any) => { const handleSubmit = async (values: any) => {
// 检查指标列表 // 检查指标列表
if (!indicatorList || indicatorList.length === 0) { if (!indicatorList || indicatorList.length === 0) {
message.error(intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.atLeastOneIndicator' })); message.error(
intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.atLeastOneIndicator' }),
);
return; return;
} }
// 检查指标名称不能为空 // 检查指标名称不能为空
const emptyNameIndex = indicatorList.findIndex(item => !item.itemName); const emptyNameIndex = indicatorList.findIndex((item) => !item.itemName);
if (emptyNameIndex !== -1) { if (emptyNameIndex !== -1) {
message.error(intl.formatMessage( message.error(
{ id: 'supplierAnnualTemplateManage.add.indicatorNameRequired' }, intl.formatMessage(
{ index: emptyNameIndex + 1 } { id: 'supplierAnnualTemplateManage.add.indicatorNameRequired' },
)); { index: emptyNameIndex + 1 },
),
);
return; return;
} }
@ -235,7 +267,7 @@ const SupplierAnnualTemplateManageAdd: React.FC = () => {
cancelText: intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.cancel' }), cancelText: intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.cancel' }),
onOk: async () => { onOk: async () => {
await submitFormData(submitData); await submitFormData(submitData);
} },
}); });
}; };
@ -255,20 +287,27 @@ const SupplierAnnualTemplateManageAdd: React.FC = () => {
// 复制指标列表 // 复制指标列表
if (res.data.indicatorList && res.data.indicatorList.length > 0) { if (res.data.indicatorList && res.data.indicatorList.length > 0) {
const copiedList = JSON.parse(JSON.stringify(res.data.indicatorList)).map((item: any) => { const copiedList = JSON.parse(JSON.stringify(res.data.indicatorList)).map(
// 删除id防止ID冲突 (item: any) => {
delete item.id; // 删除id防止ID冲突
return item; delete item.id;
}); return item;
},
);
setIndicatorList(copiedList); setIndicatorList(copiedList);
} }
} else { } else {
message.error(res.message || intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.getDetailFailed' })); message.error(
res.message ||
intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.getDetailFailed' }),
);
} }
setLoading(false); setLoading(false);
} catch (error) { } catch (error) {
console.error('获取模板详情失败:', error); console.error('获取模板详情失败:', error);
message.error(intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.getDetailFailed' })); message.error(
intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.getDetailFailed' }),
);
setLoading(false); setLoading(false);
} }
} }
@ -320,7 +359,9 @@ const SupplierAnnualTemplateManageAdd: React.FC = () => {
<Input <Input
value={text} value={text}
onChange={(e) => handleIndicatorChange(index, 'itemName', e.target.value)} onChange={(e) => handleIndicatorChange(index, 'itemName', e.target.value)}
placeholder={`${intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.pleaseInput' })}${intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.checkItem' })}`} placeholder={`${intl.formatMessage({
id: 'supplierAnnualTemplateManage.common.pleaseInput',
})}${intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.checkItem' })}`}
/> />
), ),
}, },
@ -335,8 +376,12 @@ const SupplierAnnualTemplateManageAdd: React.FC = () => {
onChange={(value) => handleIndicatorChange(index, 'isStar', value)} onChange={(value) => handleIndicatorChange(index, 'isStar', value)}
className={styles.starSelector} className={styles.starSelector}
> >
<Option value={StarOptions.YES}>{intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.yes' })}</Option> <Option value={StarOptions.YES}>
<Option value={StarOptions.NO}>{intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.no' })}</Option> {intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.yes' })}
</Option>
<Option value={StarOptions.NO}>
{intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.no' })}
</Option>
</Select> </Select>
), ),
}, },
@ -355,12 +400,19 @@ const SupplierAnnualTemplateManageAdd: React.FC = () => {
/> />
)} )}
<Popconfirm <Popconfirm
title={intl.formatMessage({ id: 'supplierAnnualTemplateManage.modal.deleteConfirmContent' })} title={intl.formatMessage({
id: 'supplierAnnualTemplateManage.modal.deleteConfirmContent',
})}
onConfirm={() => handleDeleteIndicator(index)} onConfirm={() => handleDeleteIndicator(index)}
okText={intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.confirm' })} okText={intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.confirm' })}
cancelText={intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.cancel' })} cancelText={intl.formatMessage({ id: 'supplierAnnualTemplateManage.common.cancel' })}
> >
<Button type="link" danger icon={<DeleteOutlined />} title={intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.deleteIndicator' })} /> <Button
type="link"
danger
icon={<DeleteOutlined />}
title={intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.deleteIndicator' })}
/>
</Popconfirm> </Popconfirm>
</Space> </Space>
), ),
@ -369,143 +421,223 @@ const SupplierAnnualTemplateManageAdd: React.FC = () => {
return ( return (
<div className="common-container"> <div className="common-container">
<div className={styles.pageHeader}> <div className={styles.pageHeader}>
<Title level={4} style={{ margin: 0 }}> <Title level={4} style={{ margin: 0 }}>
{isEdit {isEdit
? intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.edit' }) ? intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.edit' })
: intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.title' }) : intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.title' })}
} </Title>
</Title> <Button type="link" icon={<ArrowLeftOutlined />} onClick={handleBack}>
<Button type="link" icon={<ArrowLeftOutlined />} onClick={handleBack}> {intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.back' })}
{intl.formatMessage({ id: 'supplierAnnualTemplateManage.add.back' })} </Button>
</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}
className={styles.innerCard}
>
<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}
>
{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> </div>
</Form>
<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} className={styles.innerCard}>
<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}
>
{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> </div>
); );
}; };
export default SupplierAnnualTemplateManageAdd; // export default SupplierAnnualTemplateManageAdd;
// 将dva model中的状态映射到组件props
export default connect(({ breadcrumb }: { breadcrumb: BreadcrumbState }) => ({
breadcrumb,
}))(SupplierAnnualTemplateManageAdd);

View File

@ -10,6 +10,7 @@ import DivisionStep from './components/DivisionStep';
import styles from './supplierTaskManageAdd.less'; import styles from './supplierTaskManageAdd.less';
import { TaskNotifyLowerUnits } from '@/dicts/supplierTaskDict'; import { TaskNotifyLowerUnits } from '@/dicts/supplierTaskDict';
import type { SupplierTaskModelState } from '@/models/supplierTaskManage'; import type { SupplierTaskModelState } from '@/models/supplierTaskManage';
import type { BreadcrumbState } from '@/models/breadcrumb';
const { Step } = Steps; const { Step } = Steps;
@ -40,7 +41,11 @@ interface PageProps extends ConnectProps {
* 供应商任务管理添加/编辑组件 * 供应商任务管理添加/编辑组件
* 使用步骤式表单引导用户完成添加或编辑任务的流程 * 使用步骤式表单引导用户完成添加或编辑任务的流程
*/ */
const SupplierTaskManageAdd: React.FC<PageProps> = ({ supplierTaskManage, dispatch }) => { const SupplierTaskManageAdd: React.FC<PageProps> = ({
supplierTaskManage,
dispatch,
breadcrumb,
}) => {
const intl = useIntl(); const intl = useIntl();
// 获取dva model中的状态 // 获取dva model中的状态
const { currentStep, loading, detailLoading } = supplierTaskManage; const { currentStep, loading, detailLoading } = supplierTaskManage;
@ -70,6 +75,11 @@ const SupplierTaskManageAdd: React.FC<PageProps> = ({ supplierTaskManage, dispat
type: 'supplierTaskManage/saveMode', type: 'supplierTaskManage/saveMode',
payload: 'edit', payload: 'edit',
}); });
// 更新面包屑
dispatch({
type: 'breadcrumb/updateBreadcrumbName',
payload: intl.formatMessage({ id: 'supplierTaskManage.title.edit' }),
});
// 编辑模式,获取任务详情 // 编辑模式,获取任务详情
dispatch({ dispatch({
type: 'supplierTaskManage/fetchTaskDetail', type: 'supplierTaskManage/fetchTaskDetail',
@ -106,6 +116,9 @@ const SupplierTaskManageAdd: React.FC<PageProps> = ({ supplierTaskManage, dispat
dispatch({ dispatch({
type: 'supplierTaskManage/resetState', type: 'supplierTaskManage/resetState',
}); });
dispatch({
type: 'breadcrumb/resetBreadcrumb',
});
} }
}; };
}, [dispatch]); }, [dispatch]);
@ -124,7 +137,9 @@ const SupplierTaskManageAdd: React.FC<PageProps> = ({ supplierTaskManage, dispat
}, },
{ {
title: intl.formatMessage({ id: 'supplierTaskManage.step.selectEvaluator.title' }), title: intl.formatMessage({ id: 'supplierTaskManage.step.selectEvaluator.title' }),
description: intl.formatMessage({ id: 'supplierTaskManage.step.selectEvaluator.description' }), description: intl.formatMessage({
id: 'supplierTaskManage.step.selectEvaluator.description',
}),
content: <EvaluatorSelectStep ref={evaluatorFormRef} />, content: <EvaluatorSelectStep ref={evaluatorFormRef} />,
}, },
{ {
@ -177,7 +192,9 @@ const SupplierTaskManageAdd: React.FC<PageProps> = ({ supplierTaskManage, dispat
if (typeof errorInfo === 'string') { if (typeof errorInfo === 'string') {
message.error(errorInfo); message.error(errorInfo);
} else if (errorInfo && errorInfo.errorFields) { } else if (errorInfo && errorInfo.errorFields) {
message.error(intl.formatMessage({ id: 'supplierTaskManage.message.formValidationFailed' })); message.error(
intl.formatMessage({ id: 'supplierTaskManage.message.formValidationFailed' }),
);
} }
} }
}; };
@ -357,7 +374,14 @@ const SupplierTaskManageAdd: React.FC<PageProps> = ({ supplierTaskManage, dispat
// 将dva model中的状态映射到组件props // 将dva model中的状态映射到组件props
export default connect( export default connect(
({ supplierTaskManage }: { supplierTaskManage: SupplierTaskModelState }) => ({ ({
supplierTaskManage, supplierTaskManage,
breadcrumb,
}: {
supplierTaskManage: SupplierTaskModelState;
breadcrumb: BreadcrumbState;
}) => ({
supplierTaskManage,
breadcrumb,
}), }),
)(SupplierTaskManageAdd); )(SupplierTaskManageAdd);

View File

@ -16,7 +16,9 @@ import {
Spin, Spin,
Modal, Modal,
} from 'antd'; } from 'antd';
import { history, useLocation, useIntl } from 'umi'; 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 { ArrowLeftOutlined, SaveOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
import EvaluateTemplateTable from '@/components/EvaluateTemplateTable'; import EvaluateTemplateTable from '@/components/EvaluateTemplateTable';
import CategorySelector from '@/components/CategorySelector'; import CategorySelector from '@/components/CategorySelector';
@ -50,6 +52,11 @@ interface FormValues {
[key: string]: any; [key: string]: any;
} }
interface PageProps extends ConnectProps {
breadcrumb: BreadcrumbState; // dva model状态
dispatch: Dispatch; // dva dispatch方法
}
interface LocationState { interface LocationState {
isEdit?: boolean; isEdit?: boolean;
editData?: SupplierTemplateManage.TemplateItem; editData?: SupplierTemplateManage.TemplateItem;
@ -57,7 +64,7 @@ interface LocationState {
const { Title } = Typography; const { Title } = Typography;
const SupplierTemplateManageAdd: React.FC = () => { const SupplierTemplateManageAdd: React.FC<PageProps> = ({ breadcrumb, dispatch }) => {
const intl = useIntl(); const intl = useIntl();
const [form] = Form.useForm<FormValues>(); const [form] = Form.useForm<FormValues>();
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
@ -79,7 +86,12 @@ const SupplierTemplateManageAdd: React.FC = () => {
try { try {
const res = await getAllTemplates(); const res = await getAllTemplates();
if (res.success && res.data) { if (res.success && res.data) {
setTemplateList(res.data); // 如果是修改,需要过滤掉自己
if (location.state?.editData) {
setTemplateList(res.data.filter((template: SupplierTemplateManage.TemplateItem) => template.id !== location.state.editData?.id));
} else {
setTemplateList(res.data);
}
} else { } else {
message.error(intl.formatMessage({ id: 'supplierTemplateManage.message.getTemplateListFailed' }) || res.message); message.error(intl.formatMessage({ id: 'supplierTemplateManage.message.getTemplateListFailed' }) || res.message);
} }
@ -122,6 +134,19 @@ const SupplierTemplateManageAdd: React.FC = () => {
setLoading(false); setLoading(false);
} }
}; };
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(() => { useEffect(() => {
// 获取所有模板列表 // 获取所有模板列表
@ -450,4 +475,9 @@ const SupplierTemplateManageAdd: React.FC = () => {
); );
}; };
export default SupplierTemplateManageAdd;
// export default SupplierTemplateManageAdd;
// 将dva model中的状态映射到组件props
export default connect(({ breadcrumb }: { breadcrumb: BreadcrumbState }) => ({
breadcrumb,
}))(SupplierTemplateManageAdd);