登录与品类
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
export default {
|
export default {
|
||||||
dev: {
|
dev: {
|
||||||
'/api/v1': {
|
'/api/v1': {
|
||||||
|
// target: 'http://10.0.0.10:18030',//
|
||||||
target: 'http://10.0.0.14:18030',// 李
|
target: 'http://10.0.0.14:18030',// 李
|
||||||
// target: 'http://10.0.0.46:18030',// 袁
|
// target: 'http://10.0.0.46:18030',// 袁
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
@ -11,13 +12,12 @@ export default {
|
|||||||
// target: 'http://10.0.0.10:18012',// 茂
|
// target: 'http://10.0.0.10:18012',// 茂
|
||||||
// target: 'http://10.0.0.125:18012',// 测试
|
// target: 'http://10.0.0.125:18012',// 测试
|
||||||
target: 'http://10.0.0.14:18012',// 李
|
target: 'http://10.0.0.14:18012',// 李
|
||||||
// target: 'http://10.0.0.14:18030',// 李
|
|
||||||
// target: 'http://10.0.0.46:18030',// 袁
|
// target: 'http://10.0.0.46:18030',// 袁
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
pathRewrite: { '^/api': '' },
|
pathRewrite: { '^/api': '' },
|
||||||
},
|
},
|
||||||
'/upload': {
|
'/upload': {
|
||||||
target: 'http://10.0.0.14:18013',//
|
target: 'http://10.0.0.14:18012',//
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
pathRewrite: { '^/upload': '' },
|
pathRewrite: { '^/upload': '' },
|
||||||
},
|
},
|
||||||
|
@ -165,7 +165,7 @@ const CategorySelector: React.FC<CategorySelectorProps> = ({
|
|||||||
treeNodeFilterProp="title"
|
treeNodeFilterProp="title"
|
||||||
multiple={multiple}
|
multiple={multiple}
|
||||||
treeDefaultExpandAll={treeData.length < 30} // 当节点数较少时默认展开所有节点
|
treeDefaultExpandAll={treeData.length < 30} // 当节点数较少时默认展开所有节点
|
||||||
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
|
dropdownStyle={{ maxHeight: 400, overflow: 'auto', whiteSpace: 'nowrap', }}
|
||||||
treeCheckable={multiple} // 多选模式下显示复选框
|
treeCheckable={multiple} // 多选模式下显示复选框
|
||||||
showCheckedStrategy={SHOW_CHILD} // 显示策略:显示子节点
|
showCheckedStrategy={SHOW_CHILD} // 显示策略:显示子节点
|
||||||
filterTreeNode={(inputValue, node) => {
|
filterTreeNode={(inputValue, node) => {
|
||||||
|
@ -22,6 +22,7 @@ interface viewDataData {
|
|||||||
interface coscoSupplierUserCategory {
|
interface coscoSupplierUserCategory {
|
||||||
categoryName: string;
|
categoryName: string;
|
||||||
categoryId: string;
|
categoryId: string;
|
||||||
|
categoryPathName?: string;
|
||||||
supplierUserId: string;
|
supplierUserId: string;
|
||||||
}
|
}
|
||||||
interface SupplierUserCategory {
|
interface SupplierUserCategory {
|
||||||
@ -32,19 +33,27 @@ interface SupplierUserCategory {
|
|||||||
interface CategoryNode {
|
interface CategoryNode {
|
||||||
key: string;
|
key: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
pathName: string;
|
||||||
|
path: string;
|
||||||
children?: CategoryNode[];
|
children?: CategoryNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenTree(
|
function getKeyTitlePathMap(
|
||||||
tree: CategoryNode[],
|
tree: CategoryNode[],
|
||||||
map: Record<string, string> = {}
|
keyTitleMap: Record<string, string> = {},
|
||||||
): Record<string, string> {
|
keyPathMap: Record<string, string> = {},
|
||||||
|
keyIdMap: Record<string, string> = {}
|
||||||
|
) {
|
||||||
tree.forEach((node) => {
|
tree.forEach((node) => {
|
||||||
map[node.key] = node.title;
|
keyTitleMap[node.key] = node.title;
|
||||||
if (node.children) flattenTree(node.children, map);
|
keyPathMap[node.key] = node.pathName;
|
||||||
|
keyIdMap[node.key] = node.path;
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
getKeyTitlePathMap(node.children, keyTitleMap, keyPathMap, keyIdMap);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return map;
|
return { keyTitleMap, keyPathMap, keyIdMap };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const InvoiceFormModal: React.FC<props> = ({
|
const InvoiceFormModal: React.FC<props> = ({
|
||||||
@ -141,12 +150,15 @@ const InvoiceFormModal: React.FC<props> = ({
|
|||||||
const leafKeys = findLeafKeys(convertTreeData(categoriesTreeData));
|
const leafKeys = findLeafKeys(convertTreeData(categoriesTreeData));
|
||||||
const onlyLeafChecked = keys.filter(key => leafKeys.includes(String(key)));
|
const onlyLeafChecked = keys.filter(key => leafKeys.includes(String(key)));
|
||||||
|
|
||||||
// 平铺tree获取id=>title映射
|
// 获取映射
|
||||||
const keyTitleMap = flattenTree(convertTreeData(categoriesTreeData));
|
const { keyTitleMap, keyPathMap, keyIdMap } = getKeyTitlePathMap(convertTreeData(categoriesTreeData));
|
||||||
|
|
||||||
// 拼 categoryItem 数组
|
// 拼 categoryItem 数组
|
||||||
const coscoSupplierUserCategoryList = onlyLeafChecked.map((id) => ({
|
const coscoSupplierUserCategoryList = onlyLeafChecked.map((id) => ({
|
||||||
categoryId: id,
|
categoryId: id,
|
||||||
categoryName: keyTitleMap[id] || '',
|
categoryName: keyTitleMap[id] || '',
|
||||||
|
categoryPathName: keyPathMap[id] || '',
|
||||||
|
categoryPathId: keyIdMap[id] || '',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setCheckedKeys(keys); // UI 显示用,还是全量
|
setCheckedKeys(keys); // UI 显示用,还是全量
|
||||||
@ -208,7 +220,7 @@ const InvoiceFormModal: React.FC<props> = ({
|
|||||||
<Descriptions.Item label="联系人邮箱">{viewData?.contactsEmail}</Descriptions.Item>
|
<Descriptions.Item label="联系人邮箱">{viewData?.contactsEmail}</Descriptions.Item>
|
||||||
<Descriptions.Item label="负责品类">
|
<Descriptions.Item label="负责品类">
|
||||||
{viewData?.coscoSupplierUserCategoryList && viewData.coscoSupplierUserCategoryList.map((item) => {
|
{viewData?.coscoSupplierUserCategoryList && viewData.coscoSupplierUserCategoryList.map((item) => {
|
||||||
return <div>{item.categoryName}</div>
|
return <div>{item.categoryPathName}</div>
|
||||||
})}
|
})}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
|
@ -137,18 +137,18 @@ const OtherAttachmentsTab: React.FC<Props> = (props) => {
|
|||||||
key: 'coscoSupplierUserCategoryList',
|
key: 'coscoSupplierUserCategoryList',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
width: 160,
|
width: 160,
|
||||||
render: (value: { categoryName: string }[] = []) => {
|
render: (value: { categoryName: string; categoryPathName: string }[] = []) => {
|
||||||
if (!value || value.length === 0) return '-';
|
if (!value || value.length === 0) return '-';
|
||||||
if (value.length === 1) {
|
|
||||||
return <span>{value[0].categoryName}</span>;
|
|
||||||
}
|
|
||||||
// 多于1条
|
// 多于1条
|
||||||
const allNames = value.map(item => item.categoryName).join('、');
|
const allNames = value.map(item => item.categoryPathName).join('\n');
|
||||||
return (
|
return (
|
||||||
<Tooltip title={allNames} overlayStyle={{ zIndex: 1200 }}>
|
<Tooltip title={<pre style={{ margin: 0, fontSize: 12, fontFamily: '微软雅黑', whiteSpace: 'pre-wrap' }}>{allNames}</pre>} overlayStyle={{ zIndex: 1200 }}>
|
||||||
<span>
|
<span>
|
||||||
{value[0].categoryName}
|
{value[0].categoryName}
|
||||||
<span>等</span>
|
{value.length !== 1 && (
|
||||||
|
<span>等</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Table, Tooltip } from 'antd';
|
import { Table, Tooltip } from 'antd';
|
||||||
import type { ColumnsType, TablePaginationConfig } from 'antd/es/table';
|
import type { ColumnsType, TablePaginationConfig } from 'antd/es/table';
|
||||||
import { getCoscoSupplierUserPage } from '../services';
|
import { getCoscoSupplierUserPage } from '../services';
|
||||||
import { useIntl } from 'umi';
|
import { useIntl } from 'umi';
|
||||||
|
|
||||||
interface getCoscoSupplierUser {
|
interface getCoscoSupplierUser {
|
||||||
@ -21,7 +21,7 @@ interface getCoscoSupplierUser {
|
|||||||
interface Props {
|
interface Props {
|
||||||
id?: string;
|
id?: string;
|
||||||
}
|
}
|
||||||
const OtherAttachmentsTab: React.FC<Props> = ({id}) => {
|
const OtherAttachmentsTab: React.FC<Props> = ({ id }) => {
|
||||||
//语言切换
|
//语言切换
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
//列表渲染数据
|
//列表渲染数据
|
||||||
@ -81,24 +81,24 @@ const OtherAttachmentsTab: React.FC<Props> = ({id}) => {
|
|||||||
key: 'coscoSupplierUserCategoryList',
|
key: 'coscoSupplierUserCategoryList',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
width: 160,
|
width: 160,
|
||||||
render: (value: { categoryName: string }[] = []) => {
|
render: (value: { categoryName: string; categoryPathName: string }[] = []) => {
|
||||||
if (!value || value.length === 0) return '-';
|
if (!value || value.length === 0) return '-';
|
||||||
if (value.length === 1) {
|
|
||||||
return <span>{value[0].categoryName}</span>;
|
|
||||||
}
|
|
||||||
// 多于1条
|
// 多于1条
|
||||||
const allNames = value.map(item => item.categoryName).join('、');
|
const allNames = value.map(item => item.categoryPathName).join('\n');
|
||||||
return (
|
return (
|
||||||
<Tooltip title={allNames} overlayStyle={{ zIndex: 1200 }}>
|
<Tooltip title={<pre style={{ margin: 0, fontSize: 12, fontFamily: '微软雅黑', whiteSpace: 'pre-wrap' }}>{allNames}</pre>} overlayStyle={{ zIndex: 1200 }}>
|
||||||
<span>
|
<span>
|
||||||
{value[0].categoryName}
|
{value[0].categoryName}
|
||||||
<span>等</span>
|
{value.length !== 1 && (
|
||||||
|
<span>等</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: '0 30px 0 0' }}>
|
<div style={{ padding: '0 30px 0 0' }}>
|
||||||
@ -114,7 +114,7 @@ const OtherAttachmentsTab: React.FC<Props> = ({id}) => {
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
onChange={(pagination) => getList(pagination.current!, pagination.pageSize!)}
|
onChange={(pagination) => getList(pagination.current!, pagination.pageSize!)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
// src/layouts/BasicLayout.tsx
|
// src/layouts/BasicLayout.tsx
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useState, useEffect } 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 } from 'umi';
|
||||||
import { connect } from 'dva';
|
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 type { BreadcrumbState } from '@/models/breadcrumb';
|
import type { BreadcrumbState } from '@/models/breadcrumb';
|
||||||
import { SupplierDetailModalProvider } from '@/components/SupplierDetailModalContext/SupplierDetailModalContext';
|
import type { TabModelState } from '@/models/tab';
|
||||||
|
|
||||||
const MenuRender = (item: any, isSubMenu: boolean) => {
|
const MenuRender = (item: any, isSubMenu: boolean) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@ -19,14 +19,14 @@ const MenuRender = (item: any, isSubMenu: boolean) => {
|
|||||||
<span className="ant-pro-menu-item">
|
<span className="ant-pro-menu-item">
|
||||||
<IconFont type={item.icon as string} />
|
<IconFont type={item.icon as string} />
|
||||||
<span className="ant-pro-menu-item-title">
|
<span className="ant-pro-menu-item-title">
|
||||||
{intl.formatMessage({ id: `menu.${item.name}` || '' })}
|
{intl.formatMessage({ id: `${item.name}` || '' })}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<Link className="ant-pro-menu-item" key={item.path} to={item.path || '/'} innerRef={null}>
|
<Link className="ant-pro-menu-item" key={item.path} to={item.path || '/'} innerRef={null}>
|
||||||
<IconFont type={item.icon as string} />
|
<IconFont type={item.icon as string} />
|
||||||
<span className="ant-pro-menu-item-title">
|
<span className="ant-pro-menu-item-title">
|
||||||
{intl.formatMessage({ id: `menu.${item.name}` || '' })}
|
{intl.formatMessage({ id: `${item.name}` || '' })}
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
@ -34,28 +34,6 @@ const MenuRender = (item: any, isSubMenu: boolean) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 递归交集过滤函数
|
|
||||||
function filterMenuByRouter(routes: any, menuRoutes: any) {
|
|
||||||
return routes.reduce((result: any, route: any) => {
|
|
||||||
// 只看有 name 的节点
|
|
||||||
const menu = menuRoutes.find((m) => m.name === route.name);
|
|
||||||
if (menu) {
|
|
||||||
// 匹配到后,递归处理 children
|
|
||||||
let children = [];
|
|
||||||
if (route.children && menu.children) {
|
|
||||||
children = filterMenuByRouter(route.children, menu.children);
|
|
||||||
}
|
|
||||||
// 构建新节点,保留原 router.config.ts 路径等信息(比如 path, icon, ...其它字段)
|
|
||||||
result.push({
|
|
||||||
...route,
|
|
||||||
// ...menu, // 可按需决定是否 menu 字段覆盖 route 字段(比如 path)
|
|
||||||
// children: children.length > 0 ? children : undefined
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
const BreadcrumbRender = (
|
const BreadcrumbRender = (
|
||||||
routeBreadcrumb: any,
|
routeBreadcrumb: any,
|
||||||
intl: any,
|
intl: any,
|
||||||
@ -89,64 +67,123 @@ const BreadcrumbRender = (
|
|||||||
interface BasicLayoutProps {
|
interface BasicLayoutProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
breadcrumb: BreadcrumbState;
|
breadcrumb: BreadcrumbState;
|
||||||
|
tab: TabModelState;
|
||||||
|
dispatch: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function convertMenuData(menus: any[]): any[] {
|
||||||
|
return menus.map(item => {
|
||||||
|
// 保留 icon 字段,如果没有就给默认
|
||||||
|
const icon = item.icon || undefined;
|
||||||
|
// 递归 children->routes
|
||||||
|
let routes;
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
routes = convertMenuData(item.children);
|
||||||
|
}
|
||||||
|
// 国际化优先找 menu.name,否则直接显示 name
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
icon,
|
||||||
|
routes, // prolayout 只认 routes
|
||||||
|
name: item.name, // 不要转 menu.xxx,MenuRender 时才转
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
|
const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
|
||||||
const { children, breadcrumb } = props;
|
const { children, tab, dispatch } = props;
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const history = useHistory();
|
|
||||||
|
const handleTabChange = (key: string) => {
|
||||||
|
dispatch({
|
||||||
|
type: 'tab/switchTab',
|
||||||
|
payload: { key },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTabEdit = (targetKey: any, action: string) => {
|
||||||
|
if (action === 'remove') {
|
||||||
|
dispatch({
|
||||||
|
type: 'tab/closeTab',
|
||||||
|
payload: { key: targetKey },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const [menuRoutes, setMenuRoutes] = useState<any[]>([]);
|
const [menuRoutes, setMenuRoutes] = useState<any[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const menuStr = sessionStorage.getItem('menuList');
|
const menuStr = sessionStorage.getItem('menuList');
|
||||||
if (menuStr) {
|
if (menuStr) {
|
||||||
const menusFromApi = JSON.parse(menuStr);
|
const menus = JSON.parse(menuStr);
|
||||||
// routes是本地静态路由,menusFromApi是接口菜单
|
setMenuRoutes(convertMenuData(menus));
|
||||||
const finalMenus = filterMenuByRouter(routes, menusFromApi);
|
|
||||||
console.log(finalMenus);
|
|
||||||
|
|
||||||
setMenuRoutes(finalMenus);
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
if (menuRoutes.length === 0) return null;
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfigProvider>
|
<ConfigProvider>
|
||||||
<SupplierDetailModalProvider>
|
<ProLayout
|
||||||
<ProLayout
|
{...defaultSettings}
|
||||||
{...defaultSettings}
|
route={{ path: '/', routes: menuRoutes }}
|
||||||
route={{ routes }}
|
breadcrumbRender={() => undefined}
|
||||||
// route={{ routes: menuRoutes }}
|
subMenuItemRender={(menuItemProps, defaultDom) => {
|
||||||
subMenuItemRender={(menuItemProps, defaultDom) => {
|
return MenuRender(menuItemProps, true);
|
||||||
return MenuRender(menuItemProps, true);
|
}}
|
||||||
|
menuItemRender={(item, dom) => {
|
||||||
|
return MenuRender(item, false);
|
||||||
|
}}
|
||||||
|
location={location}
|
||||||
|
fixSiderbar
|
||||||
|
layout="mix"
|
||||||
|
headerRender={() => {
|
||||||
|
return <HeaderComponent />;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PageContainer
|
||||||
|
ghost={true}
|
||||||
|
header={{
|
||||||
|
title: false,
|
||||||
|
breadcrumbRender: () => null,
|
||||||
|
// breadcrumbRender: ({ breadcrumb: routeBreadcrumb }) =>
|
||||||
|
// BreadcrumbRender(routeBreadcrumb, intl, history, breadcrumb.breadcrumbName),
|
||||||
}}
|
}}
|
||||||
menuItemRender={(item, dom) => {
|
// 将tab.tabList转换为需要的格式,添加国际化处理
|
||||||
return MenuRender(item, false);
|
tabList={tab.tabList.map((item) => ({
|
||||||
}}
|
...item,
|
||||||
location={location}
|
tab:
|
||||||
fixSiderbar
|
typeof item.tab === 'string'
|
||||||
layout="mix"
|
? intl.formatMessage({ id: `menu.${item.tab}` })
|
||||||
headerRender={() => {
|
: item.tab,
|
||||||
return <HeaderComponent />;
|
}))}
|
||||||
|
tabProps={{
|
||||||
|
type: 'editable-card',
|
||||||
|
hideAdd: true,
|
||||||
|
activeKey: tab.activeKey,
|
||||||
|
onChange: handleTabChange,
|
||||||
|
onEdit: handleTabEdit,
|
||||||
|
size: 'small',
|
||||||
|
tabBarGutter: 6,
|
||||||
|
renderTabBar: (propsTab, DefaultTabBar) => (
|
||||||
|
<DefaultTabBar {...propsTab} className="custom-tab-bar" />
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PageContainer
|
{children}
|
||||||
ghost={true}
|
</PageContainer>
|
||||||
header={{
|
</ProLayout>
|
||||||
title: false,
|
|
||||||
breadcrumbRender: ({ breadcrumb: routeBreadcrumb }) =>
|
|
||||||
BreadcrumbRender(routeBreadcrumb, intl, history, breadcrumb.breadcrumbName),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</PageContainer>
|
|
||||||
</ProLayout>
|
|
||||||
</SupplierDetailModalProvider>
|
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(({ breadcrumb }: { breadcrumb: BreadcrumbState }) => ({
|
export default connect(
|
||||||
breadcrumb,
|
({ breadcrumb, tab }: { breadcrumb: BreadcrumbState; tab: TabModelState }) => ({
|
||||||
}))(BasicLayout);
|
breadcrumb,
|
||||||
|
tab,
|
||||||
|
}),
|
||||||
|
)(BasicLayout);
|
||||||
|
@ -1,44 +1,17 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React from 'react';
|
||||||
//导入logo图片
|
//导入logo图片
|
||||||
import LogoImg from '@/assets/img/logo.png';
|
import LogoImg from '@/assets/img/logo.png';
|
||||||
//导入菜单组件
|
//导入菜单组件
|
||||||
import Language from './Language';
|
import Language from './Language';
|
||||||
import User from './User';
|
import User from './User';
|
||||||
import { history } from 'umi';
|
|
||||||
import './layout.less';
|
import './layout.less';
|
||||||
import { Logout } from '@/servers/api/login';
|
|
||||||
|
|
||||||
const HeaderComponent: React.FC = () => {
|
const HeaderComponent: React.FC = () => {
|
||||||
// 用 state 保存 userId
|
|
||||||
const [userId, setUserId] = useState(() => sessionStorage.getItem('userId'));
|
|
||||||
|
|
||||||
const handLogout = () => {
|
|
||||||
Logout()
|
|
||||||
sessionStorage.clear();
|
|
||||||
history.replace('/login');
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// 定义一个方法用于手动刷新 userId
|
|
||||||
const refreshUserId = () => setUserId(sessionStorage.getItem('userId'));
|
|
||||||
// 登录后你可以手动调用 refreshUserId
|
|
||||||
// 或者监听页面 storage 事件(多窗口/多tab同步)
|
|
||||||
window.addEventListener('storage', refreshUserId);
|
|
||||||
// 页面内操作,比如登录成功后,也可以在登录回调里调用 setUserId
|
|
||||||
return () => window.removeEventListener('storage', refreshUserId);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="headerComponent">
|
<div className="headerComponent">
|
||||||
<img className="logo" src={LogoImg} alt="logo" />
|
<img className="logo" src={LogoImg} alt="logo" />
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<Language />
|
<Language />
|
||||||
{!userId ? (
|
<User />
|
||||||
<User />
|
|
||||||
) : (
|
|
||||||
<div style={{cursor: 'pointer'}} onClick={handLogout}>退出</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,13 +1,94 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Link, useIntl } from 'umi';
|
import { Link, useIntl, connect, history } from 'umi';
|
||||||
const User: React.FC = (props) => {
|
import { Dropdown, Button, Modal } from 'antd';
|
||||||
const intl = useIntl();
|
import { DownOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||||
|
import type { ConnectProps, Dispatch } from 'umi';
|
||||||
|
import type { UserModelState } from '@/models/user';
|
||||||
|
import { message } from 'antd';
|
||||||
|
|
||||||
|
interface PageProps extends ConnectProps {
|
||||||
|
user: UserModelState; // dva model状态
|
||||||
|
dispatch: Dispatch; // dva dispatch方法
|
||||||
|
}
|
||||||
|
const User: React.FC<PageProps> = ({ user, dispatch }) => {
|
||||||
|
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
useEffect(() => {
|
||||||
|
if (!user.userInfo) {
|
||||||
|
const userinfoStr = sessionStorage.getItem('Userinfo');
|
||||||
|
if (userinfoStr) {
|
||||||
|
try {
|
||||||
|
const userInfo = JSON.parse(userinfoStr);
|
||||||
|
dispatch({
|
||||||
|
type: 'user/saveUserInfo',
|
||||||
|
payload: userInfo,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// 解析失败时可以报错提示
|
||||||
|
console.error('Userinfo 解析失败', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (user.token) {
|
||||||
|
dispatch({
|
||||||
|
type: 'user/fetchUserInfo',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [user.token]);
|
||||||
|
const handleMenuClick = (e: any) => {
|
||||||
|
if (e.key === 'logout') {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确定退出登录吗?',
|
||||||
|
icon: <ExclamationCircleOutlined />,
|
||||||
|
content: '退出登录后,您将需要重新登录',
|
||||||
|
onOk() {
|
||||||
|
dispatch({
|
||||||
|
type: 'user/logout',
|
||||||
|
}).then(() => {
|
||||||
|
sessionStorage.clear();
|
||||||
|
message.success('退出登录成功');
|
||||||
|
history.push('/login');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else if(e.key === 'profile') {
|
||||||
|
console.log(111);
|
||||||
|
|
||||||
|
// history.push('/profile');
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="user">
|
<div className="user">
|
||||||
<Link to={'/login'}>{intl.formatMessage({ id: '登录/注册' })}</Link>
|
{user.userInfo?.fullName ? (
|
||||||
|
<Dropdown
|
||||||
|
trigger={['hover']}
|
||||||
|
menu={{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: 'profile',
|
||||||
|
label: '个人中心',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'logout',
|
||||||
|
label: '退出登录',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onClick: handleMenuClick,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button type="link">
|
||||||
|
{`${user.userInfo?.fullName}`}
|
||||||
|
<DownOutlined />
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
) : (
|
||||||
|
<Link to={'/login'}>{intl.formatMessage({ id: '登录' })}</Link>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default User;
|
|
||||||
|
export default connect(({ user }: { user: UserModelState }) => ({ user }))(User);
|
||||||
|
@ -20,8 +20,32 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.layout-content {
|
.layout-content {
|
||||||
background: rgb(245,246,250);
|
background: rgb(245, 246, 250);
|
||||||
padding: 0 15px;
|
padding: 0 15px;
|
||||||
height: calc(100vh - 64px);
|
height: calc(100vh - 64px);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
.ant-page-header.has-footer{
|
||||||
|
padding-top: 0 !important;
|
||||||
|
}
|
||||||
|
.custom-tab-bar {
|
||||||
|
margin-bottom: 10px !important;
|
||||||
|
.ant-tabs-tab {
|
||||||
|
border-radius: 5px !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
padding: 6px 10px !important;
|
||||||
|
.ant-tabs-tab-remove{
|
||||||
|
margin-left: 0 !important;
|
||||||
|
font-size: 10px !important;
|
||||||
|
}
|
||||||
|
&.ant-tabs-tab-active{
|
||||||
|
background-color: @main-color !important;
|
||||||
|
.ant-tabs-tab-btn{
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.ant-tabs-tab-remove{
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,7 +3,7 @@ export default {
|
|||||||
"login.title": "E-Bidding Platform",
|
"login.title": "E-Bidding Platform",
|
||||||
"login.tab.supplier": "Supplier",
|
"login.tab.supplier": "Supplier",
|
||||||
"login.tab.expert": "Expert",
|
"login.tab.expert": "Expert",
|
||||||
"login.tab.agent": "Bidding Agent",
|
"login.tab.agent": "Group user",
|
||||||
"login.username.placeholder": "Please enter username",
|
"login.username.placeholder": "Please enter username",
|
||||||
"login.password.placeholder": "Please enter password",
|
"login.password.placeholder": "Please enter password",
|
||||||
"login.remember": "Remember me",
|
"login.remember": "Remember me",
|
||||||
|
@ -3,7 +3,7 @@ export default {
|
|||||||
"login.title": "中远海运",
|
"login.title": "中远海运",
|
||||||
"login.tab.supplier": "供应商",
|
"login.tab.supplier": "供应商",
|
||||||
"login.tab.expert": "专家",
|
"login.tab.expert": "专家",
|
||||||
"login.tab.agent": "招标代理",
|
"login.tab.agent": "集团用户",
|
||||||
"login.username.placeholder": "请输入用户名",
|
"login.username.placeholder": "请输入用户名",
|
||||||
"login.password.placeholder": "请输入密码",
|
"login.password.placeholder": "请输入密码",
|
||||||
"login.remember": "记住密码",
|
"login.remember": "记住密码",
|
||||||
|
192
src/models/tab.ts
Normal file
192
src/models/tab.ts
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import { Effect, Reducer, Subscription } from 'umi';
|
||||||
|
import { history } from 'umi';
|
||||||
|
import routes from '../../config/router.config';
|
||||||
|
|
||||||
|
export interface TabItem {
|
||||||
|
tab: string;
|
||||||
|
key: string;
|
||||||
|
path: string;
|
||||||
|
closable: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TabModelState {
|
||||||
|
tabList: TabItem[];
|
||||||
|
activeKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TabModelType {
|
||||||
|
namespace: 'tab';
|
||||||
|
state: TabModelState;
|
||||||
|
effects: {
|
||||||
|
addTab: Effect;
|
||||||
|
closeTab: Effect;
|
||||||
|
switchTab: Effect;
|
||||||
|
};
|
||||||
|
reducers: {
|
||||||
|
updateState: Reducer<TabModelState>;
|
||||||
|
};
|
||||||
|
subscriptions: {
|
||||||
|
setup: Subscription;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归查找路由
|
||||||
|
const findRouteByPath = (path: string, routeData: any[]): any => {
|
||||||
|
let result = null;
|
||||||
|
for (const route of routeData) {
|
||||||
|
if (route.path === path) {
|
||||||
|
result = route;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (route.routes) {
|
||||||
|
const subResult = findRouteByPath(path, route.routes);
|
||||||
|
if (subResult) {
|
||||||
|
result = subResult;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TabModel: TabModelType = {
|
||||||
|
namespace: 'tab',
|
||||||
|
|
||||||
|
state: {
|
||||||
|
tabList: [],
|
||||||
|
activeKey: '/',
|
||||||
|
},
|
||||||
|
|
||||||
|
effects: {
|
||||||
|
*addTab({ payload }, { call, put, select }) {
|
||||||
|
const { tabList } = yield select((state: any) => state.tab);
|
||||||
|
const { path, tab, key } = payload;
|
||||||
|
|
||||||
|
// 检查tab是否已存在
|
||||||
|
const isExist = tabList.find((item: TabItem) => item.key === key);
|
||||||
|
|
||||||
|
if (!isExist) {
|
||||||
|
// 添加新tab
|
||||||
|
yield put({
|
||||||
|
type: 'updateState',
|
||||||
|
payload: {
|
||||||
|
tabList: [...tabList, { tab, key, path, closable: true }],
|
||||||
|
activeKey: key,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 切换到已有tab
|
||||||
|
yield put({
|
||||||
|
type: 'updateState',
|
||||||
|
payload: {
|
||||||
|
activeKey: key,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
*closeTab({ payload }, { call, put, select }) {
|
||||||
|
const { key } = payload;
|
||||||
|
const { tabList, activeKey } = yield select((state: any) => state.tab);
|
||||||
|
|
||||||
|
// 过滤掉要关闭的tab
|
||||||
|
const newTabList = tabList.filter((item: TabItem) => item.key !== key);
|
||||||
|
|
||||||
|
// 如果关闭的是当前激活的tab,则切换到前一个tab
|
||||||
|
let newActiveKey = activeKey;
|
||||||
|
if (key === activeKey) {
|
||||||
|
const index = tabList.findIndex((item: TabItem) => item.key === key);
|
||||||
|
|
||||||
|
// 判断关闭后是否还有tab
|
||||||
|
if (newTabList.length === 0) {
|
||||||
|
// 没有剩余tab时,导航到首页
|
||||||
|
history.push('/');
|
||||||
|
newActiveKey = '/';
|
||||||
|
} else {
|
||||||
|
// 有剩余tab时,选择适当的tab作为活动tab
|
||||||
|
newActiveKey = index === 0 ? newTabList[0].key : tabList[index - 1].key;
|
||||||
|
|
||||||
|
// 切换路由
|
||||||
|
const targetTab = newTabList.find((item: TabItem) => item.key === newActiveKey);
|
||||||
|
if (targetTab) {
|
||||||
|
history.push(targetTab.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield put({
|
||||||
|
type: 'updateState',
|
||||||
|
payload: {
|
||||||
|
tabList: newTabList,
|
||||||
|
activeKey: newActiveKey,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
*switchTab({ payload }, { call, put, select }) {
|
||||||
|
const { key } = payload;
|
||||||
|
const { tabList } = yield select((state: any) => state.tab);
|
||||||
|
|
||||||
|
// 找到目标tab并跳转
|
||||||
|
const targetTab = tabList.find((item: TabItem) => item.key === key);
|
||||||
|
if (targetTab) {
|
||||||
|
history.push(targetTab.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield put({
|
||||||
|
type: 'updateState',
|
||||||
|
payload: {
|
||||||
|
activeKey: key,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
reducers: {
|
||||||
|
updateState(state, { payload }) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...payload,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
subscriptions: {
|
||||||
|
setup({ dispatch }) {
|
||||||
|
return history.listen((location) => {
|
||||||
|
const { pathname } = location;
|
||||||
|
// 需要排除的路由
|
||||||
|
const excludeRoutes = ['/login', '/register'];
|
||||||
|
|
||||||
|
// 首页特殊处理
|
||||||
|
if (pathname === '/') {
|
||||||
|
dispatch({
|
||||||
|
type: 'updateState',
|
||||||
|
payload: {
|
||||||
|
activeKey: '/',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找当前路由对应的菜单项
|
||||||
|
const currentRoute = findRouteByPath(pathname, routes);
|
||||||
|
|
||||||
|
if (currentRoute && !excludeRoutes.includes(pathname)) {
|
||||||
|
// 如果找到对应路由,添加或激活对应的tab
|
||||||
|
dispatch({
|
||||||
|
type: 'addTab',
|
||||||
|
payload: {
|
||||||
|
path: pathname,
|
||||||
|
key: pathname,
|
||||||
|
// 直接使用菜单名称作为国际化ID
|
||||||
|
tab: currentRoute.name || '未命名页面',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TabModel;
|
@ -1,35 +1,39 @@
|
|||||||
import type { Effect, Reducer } from 'umi';
|
import type { Effect, Reducer } from 'umi';
|
||||||
|
|
||||||
import { queryCurrent, query as queryUsers } from '@/servers/user';
|
import { getUserinfo, Logout } from '@/servers/api/login';
|
||||||
|
|
||||||
export type CurrentUser = {
|
|
||||||
avatar?: string;
|
export type User = {
|
||||||
name?: string;
|
userId: string;
|
||||||
title?: string;
|
userName: string;
|
||||||
group?: string;
|
userType: string;
|
||||||
signature?: string;
|
};
|
||||||
tags?: {
|
|
||||||
key: string;
|
export type UserInfo = {
|
||||||
label: string;
|
userId: string;
|
||||||
}[];
|
fullName: string;
|
||||||
userid?: string;
|
loginName: string;
|
||||||
unreadCount?: number;
|
userType: string;
|
||||||
|
authorityList: { roleId: string, roleName: string, roleCode: string, roleScope: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserModelState = {
|
export type UserModelState = {
|
||||||
currentUser?: CurrentUser;
|
user?: User;
|
||||||
|
token?: string;
|
||||||
|
userInfo?: UserInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserModelType = {
|
export type UserModelType = {
|
||||||
namespace: 'user';
|
namespace: 'user';
|
||||||
state: UserModelState;
|
state: UserModelState;
|
||||||
effects: {
|
effects: {
|
||||||
fetch: Effect;
|
fetchUserInfo: Effect;
|
||||||
fetchCurrent: Effect;
|
logout: Effect;
|
||||||
};
|
};
|
||||||
reducers: {
|
reducers: {
|
||||||
saveCurrentUser: Reducer<UserModelState>;
|
saveLoginUser: Reducer<UserModelState>;
|
||||||
changeNotifyCount: Reducer<UserModelState>;
|
saveUserInfo: Reducer<UserModelState>;
|
||||||
|
clearUserInfo: Reducer;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -37,49 +41,53 @@ const UserModel: UserModelType = {
|
|||||||
namespace: 'user',
|
namespace: 'user',
|
||||||
|
|
||||||
state: {
|
state: {
|
||||||
currentUser: {},
|
user: undefined, // 登录返回的用户信息
|
||||||
|
token: sessionStorage.getItem('token') || '', // 登录返回的token
|
||||||
|
userInfo: undefined, // 请求userInfo返回的用户信息
|
||||||
},
|
},
|
||||||
|
|
||||||
effects: {
|
effects: {
|
||||||
*fetch(_, { call, put }) {
|
*fetchUserInfo(_, { call, put }): Generator<any, void, any> {
|
||||||
const response = yield call(queryUsers);
|
const userinfo = JSON.parse(sessionStorage.getItem('Userinfo') ?? '{}');
|
||||||
|
console.log(userinfo,'userinfo');
|
||||||
|
|
||||||
yield put({
|
yield put({
|
||||||
type: 'save',
|
type: 'saveUserInfo',
|
||||||
payload: response,
|
payload: userinfo,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
*fetchCurrent(_, { call, put }) {
|
*logout(_, { call, put }): Generator<any, void, any> {
|
||||||
const response = yield call(queryCurrent);
|
yield call(Logout);
|
||||||
yield put({
|
yield put({
|
||||||
type: 'saveCurrentUser',
|
type: 'clearUserInfo'
|
||||||
payload: response,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
reducers: {
|
reducers: {
|
||||||
saveCurrentUser(state, action) {
|
clearUserInfo(state) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentUser: action.payload || {},
|
userInfo: undefined,
|
||||||
|
token: '',
|
||||||
|
user: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
changeNotifyCount(
|
saveLoginUser(state, action) {
|
||||||
state = {
|
|
||||||
currentUser: {},
|
|
||||||
},
|
|
||||||
action,
|
|
||||||
) {
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentUser: {
|
user: action.payload.user || {},
|
||||||
...state.currentUser,
|
token: action.payload.token || '',
|
||||||
notifyCount: action.payload.totalCount,
|
|
||||||
unreadCount: action.payload.unreadCount,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
saveUserInfo(state, action) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
userInfo: action.payload || {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default UserModel;
|
export default UserModel;
|
||||||
|
@ -130,9 +130,24 @@ const SupplierAnnualStatistics: React.FC = () => {
|
|||||||
{
|
{
|
||||||
title: intl.formatMessage({ id: 'dataStatistics.common.category' }),
|
title: intl.formatMessage({ id: 'dataStatistics.common.category' }),
|
||||||
dataIndex: 'categoryNameList',
|
dataIndex: 'categoryNameList',
|
||||||
key: 'categoryNameList',
|
|
||||||
width: 120,
|
width: 120,
|
||||||
render: (text: string[]) => text.join(','),
|
ellipsis: true,
|
||||||
|
render: (value: { categoryName: string; categoryPathName: string }[] = []) => {
|
||||||
|
if (!value || value.length === 0) return '-';
|
||||||
|
|
||||||
|
// 多于1条
|
||||||
|
const allNames = value.map(item => item.categoryPathName).join('\n');
|
||||||
|
return (
|
||||||
|
<Tooltip title={<pre style={{ margin: 0, fontSize: 12, fontFamily: '微软雅黑', whiteSpace: 'pre-wrap' }}>{allNames}</pre>} overlayStyle={{ zIndex: 1200 }}>
|
||||||
|
<span>
|
||||||
|
{value[0].categoryName}
|
||||||
|
{value.length !== 1 && (
|
||||||
|
<span>等</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: intl.formatMessage({ id: 'dataStatistics.common.accessUnit' }),
|
title: intl.formatMessage({ id: 'dataStatistics.common.accessUnit' }),
|
||||||
|
@ -3,7 +3,7 @@ import { Form, Input, Button, Checkbox, Tabs, message } from 'antd';
|
|||||||
import { UserOutlined, LockOutlined, EyeInvisibleOutlined, EyeTwoTone } from '@ant-design/icons';
|
import { UserOutlined, LockOutlined, EyeInvisibleOutlined, EyeTwoTone } from '@ant-design/icons';
|
||||||
import { history, useIntl } from 'umi';
|
import { history, useIntl } from 'umi';
|
||||||
import './login.less';
|
import './login.less';
|
||||||
import { getCaptcha, supplierLogin, expertLogin, accountLogin, getUserinfo, refreshDictCache } from '@/servers/api/login';
|
import { getCaptcha, supplierLogin, expertLogin, accountLogin, getUserinfo, refreshDictCache, findMenuList } from '@/servers/api/login';
|
||||||
|
|
||||||
import { encryptWithRsa } from '@/utils/encryptWithRsa'
|
import { encryptWithRsa } from '@/utils/encryptWithRsa'
|
||||||
|
|
||||||
@ -26,18 +26,20 @@ const LoginPage: React.FC = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchCaptcha();
|
fetchCaptcha();
|
||||||
if(!sessionStorage.getItem('dict')) {
|
if (!sessionStorage.getItem('dict')) {
|
||||||
refreshDictCache().then((res) => {
|
refreshDictCache().then((res) => {
|
||||||
if(res.code == 200) {
|
if (res.code == 200) {
|
||||||
sessionStorage.setItem('dict', JSON.stringify(res.data))
|
sessionStorage.setItem('dict', JSON.stringify(res.data))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form.setFieldsValue( { password: 'cosco2025', identifying: '1' } )
|
||||||
|
|
||||||
|
|
||||||
}, [activeKey]);
|
}, [activeKey]);
|
||||||
|
|
||||||
// 组件挂载时,检查是否有记住的用户名
|
// 组件挂载时,检查是否有记住的用户名
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// const savedUser = localStorage.getItem('remember_user');
|
// const savedUser = localStorage.getItem('remember_user');
|
||||||
// if (savedUser) {
|
// if (savedUser) {
|
||||||
@ -88,25 +90,14 @@ const LoginPage: React.FC = () => {
|
|||||||
// })
|
// })
|
||||||
}
|
}
|
||||||
sessionStorage.setItem('currentUser', JSON.stringify(loginRes.data));
|
sessionStorage.setItem('currentUser', JSON.stringify(loginRes.data));
|
||||||
|
await getUserinfo().then(async (res) => {
|
||||||
|
const roleIdList = res.authorityList.map((item: any) => item.roleId);
|
||||||
getUserinfo().then(async (res) => {
|
|
||||||
// if(res.code == 200) {
|
|
||||||
const roleIdList = res.authorityList.map((item: any) => {
|
|
||||||
return item.roleId
|
|
||||||
})
|
|
||||||
console.log(roleIdList, 'roleIdList');
|
|
||||||
sessionStorage.setItem('Userinfo', JSON.stringify(res));
|
sessionStorage.setItem('Userinfo', JSON.stringify(res));
|
||||||
// const menuList = await findMenuList({ roleIdList });
|
const menuList: any = await findMenuList({ roleIdList });
|
||||||
// sessionStorage.setItem('menuList', JSON.stringify(menuList.data));
|
sessionStorage.setItem('menuList', JSON.stringify(menuList.data));
|
||||||
// }
|
message.success('登录成功');
|
||||||
})
|
history.push('/index');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
message.success('登录成功');
|
|
||||||
history.push('/index');
|
|
||||||
} else {
|
} else {
|
||||||
message.error(loginRes.message || '登录失败');
|
message.error(loginRes.message || '登录失败');
|
||||||
}
|
}
|
||||||
@ -162,7 +153,7 @@ const LoginPage: React.FC = () => {
|
|||||||
|
|
||||||
// 忘记密码点击
|
// 忘记密码点击
|
||||||
const onForgot = () => {
|
const onForgot = () => {
|
||||||
history.push('/forgot?type='+activeKey);
|
history.push('/forgot?type=' + activeKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -179,7 +170,7 @@ const LoginPage: React.FC = () => {
|
|||||||
<div className="login-tab-container">
|
<div className="login-tab-container">
|
||||||
<Tabs activeKey={activeKey} onChange={handleTabChange} className='login-tabs'>
|
<Tabs activeKey={activeKey} onChange={handleTabChange} className='login-tabs'>
|
||||||
<TabPane tab={intl.formatMessage({ id: 'login.tab.supplier' })} key="supplierLogin" />
|
<TabPane tab={intl.formatMessage({ id: 'login.tab.supplier' })} key="supplierLogin" />
|
||||||
<TabPane tab={intl.formatMessage({ id: 'login.tab.expert' })} key="expertLogin" />
|
{/* <TabPane tab={intl.formatMessage({ id: 'login.tab.expert' })} key="expertLogin" /> */}
|
||||||
<TabPane tab={intl.formatMessage({ id: 'login.tab.agent' })} key="accountLogin" />
|
<TabPane tab={intl.formatMessage({ id: 'login.tab.agent' })} key="accountLogin" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
@ -239,7 +230,7 @@ const LoginPage: React.FC = () => {
|
|||||||
<Form.Item name="remember" valuePropName="checked" noStyle>
|
<Form.Item name="remember" valuePropName="checked" noStyle>
|
||||||
<Checkbox>{intl.formatMessage({ id: 'login.remember' })}</Checkbox>
|
<Checkbox>{intl.formatMessage({ id: 'login.remember' })}</Checkbox>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Button type="link" onClick={()=>onForgot()} className="login-form-forgot" href="">
|
<Button type="link" onClick={() => onForgot()} className="login-form-forgot" href="">
|
||||||
{intl.formatMessage({ id: 'login.forgot' })}
|
{intl.formatMessage({ id: 'login.forgot' })}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,22 +5,31 @@ import SupplierSelector from './SupplierSelector';
|
|||||||
import AccessDepartmentSelect from '@/components/AccessDepartmentSelect';
|
import AccessDepartmentSelect from '@/components/AccessDepartmentSelect';
|
||||||
// 请求
|
// 请求
|
||||||
import { categoryTree, add } from '../services';
|
import { categoryTree, add } from '../services';
|
||||||
|
|
||||||
interface CategoryNode {
|
interface CategoryNode {
|
||||||
key: string;
|
key: string;
|
||||||
|
pathName: string;
|
||||||
|
path: string;
|
||||||
title: string;
|
title: string;
|
||||||
children?: CategoryNode[];
|
children?: CategoryNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenTree(
|
function getKeyTitlePathMap(
|
||||||
tree: CategoryNode[],
|
tree: CategoryNode[],
|
||||||
map: Record<string, string> = {}
|
keyTitleMap: Record<string, string> = {},
|
||||||
): Record<string, string> {
|
keyPathMap: Record<string, string> = {},
|
||||||
|
keyIdMap: Record<string, string> = {}
|
||||||
|
) {
|
||||||
tree.forEach((node) => {
|
tree.forEach((node) => {
|
||||||
map[node.key] = node.title;
|
keyTitleMap[node.key] = node.title;
|
||||||
if (node.children) flattenTree(node.children, map);
|
keyPathMap[node.key] = node.pathName;
|
||||||
|
keyIdMap[node.key] = node.path;
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
getKeyTitlePathMap(node.children, keyTitleMap, keyPathMap, keyIdMap);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return map;
|
return { keyTitleMap, keyPathMap, keyIdMap };
|
||||||
}
|
}
|
||||||
// 主体
|
// 主体
|
||||||
const CreateModal: React.FC<{ visible: boolean; onCancel: () => void; }> = ({ visible, onCancel }) => {
|
const CreateModal: React.FC<{ visible: boolean; onCancel: () => void; }> = ({ visible, onCancel }) => {
|
||||||
|
|
||||||
@ -76,14 +85,16 @@ const CreateModal: React.FC<{ visible: boolean; onCancel: () => void; }> = ({ vi
|
|||||||
const leafKeys = findLeafKeys(convertTreeData(categoriesTreeData));
|
const leafKeys = findLeafKeys(convertTreeData(categoriesTreeData));
|
||||||
const onlyLeafChecked = keys.filter(key => leafKeys.includes(String(key)));
|
const onlyLeafChecked = keys.filter(key => leafKeys.includes(String(key)));
|
||||||
|
|
||||||
// 平铺tree获取id=>title映射
|
// 获取映射
|
||||||
const keyTitleMap = flattenTree(convertTreeData(categoriesTreeData));
|
const { keyTitleMap, keyPathMap, keyIdMap } = getKeyTitlePathMap(convertTreeData(categoriesTreeData));
|
||||||
// 拼 categoryItem 数组
|
|
||||||
const coscoAccessCategoryList = onlyLeafChecked.map((id) => ({
|
// 拼 categoryItem 数组
|
||||||
categoryId: id,
|
const coscoAccessCategoryList = onlyLeafChecked.map((id) => ({
|
||||||
categoryName: keyTitleMap[id] || '',
|
categoryId: id,
|
||||||
}));
|
categoryName: keyTitleMap[id] || '',
|
||||||
|
categoryPathName: keyPathMap[id] || '',
|
||||||
|
categoryPathId: keyIdMap[id] || '',
|
||||||
|
}));
|
||||||
|
|
||||||
setCheckedKeys(keys); // UI 显示用,还是全量
|
setCheckedKeys(keys); // UI 显示用,还是全量
|
||||||
form.setFieldsValue({ categoryIds: onlyLeafChecked, coscoAccessCategoryList }); // 只存叶子到表单
|
form.setFieldsValue({ categoryIds: onlyLeafChecked, coscoAccessCategoryList }); // 只存叶子到表单
|
||||||
|
@ -67,7 +67,7 @@ const ViewModal: React.FC<{
|
|||||||
<Descriptions.Item label="申请准入品类">
|
<Descriptions.Item label="申请准入品类">
|
||||||
{data.coscoAccessCategoryList.map((item) => {
|
{data.coscoAccessCategoryList.map((item) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ margin: '5px' }}>{item.categoryName}</div>
|
<div style={{ margin: '5px' }}>{item.categoryPathName}</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
|
@ -67,7 +67,7 @@ const ViewModal: React.FC<{
|
|||||||
<Descriptions.Item label="申请准入品类">
|
<Descriptions.Item label="申请准入品类">
|
||||||
{data.coscoAccessCategoryList.map((item) => {
|
{data.coscoAccessCategoryList.map((item) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ margin: '5px' }}>{item.categoryName}</div>
|
<div style={{ margin: '5px' }}>{item.categoryPathName}</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
|
@ -86,7 +86,7 @@ const ViewModal: React.FC<{
|
|||||||
<Descriptions.Item label="申请准入品类">
|
<Descriptions.Item label="申请准入品类">
|
||||||
{data.coscoAccessCategoryList.map((item) => {
|
{data.coscoAccessCategoryList.map((item) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ margin: '5px' }}>{item.categoryName}</div>
|
<div style={{ margin: '5px' }}>{item.categoryPathName}</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
@ -101,7 +101,7 @@ const ViewModal: React.FC<{
|
|||||||
<Descriptions.Item label="申请准入品类">
|
<Descriptions.Item label="申请准入品类">
|
||||||
{data.coscoAccessCategoryList.map((item) => {
|
{data.coscoAccessCategoryList.map((item) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ margin: '5px' }}>{item.categoryName}</div>
|
<div style={{ margin: '5px' }}>{item.categoryPathName}</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
@ -117,7 +117,7 @@ const ViewModal: React.FC<{
|
|||||||
<Descriptions.Item label="申请准入品类">
|
<Descriptions.Item label="申请准入品类">
|
||||||
{data.coscoAccessCategoryList.map((item) => {
|
{data.coscoAccessCategoryList.map((item) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ margin: '5px' }}>{item.categoryName}</div>
|
<div style={{ margin: '5px' }}>{item.categoryPathName}</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
|
@ -43,7 +43,7 @@ const SupplierEntryReview: React.FC = () => {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [modalInfo, setModalInfo] = useState<ModalInfo>({ visible: false, record: null });
|
const [modalInfo, setModalInfo] = useState<ModalInfo>({ visible: false, record: null });
|
||||||
//
|
//
|
||||||
const [enterpriseType, setEnterpriseType] = useState<Dict[]>();
|
const [enterpriseType, setEnterpriseType] = useState<Dict[]>();
|
||||||
// 查询数据
|
// 查询数据
|
||||||
const fetchData = async (params = {}) => {
|
const fetchData = async (params = {}) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@ -75,11 +75,11 @@ const SupplierEntryReview: React.FC = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchData({ pageNo: 1 });
|
fetchData({ pageNo: 1 });
|
||||||
getDictList('approve_type').then((res) => {
|
getDictList('approve_type').then((res) => {
|
||||||
if (res.code == 200) {
|
if (res.code == 200) {
|
||||||
setEnterpriseType(res.data)
|
setEnterpriseType(res.data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 表格分页切换
|
// 表格分页切换
|
||||||
@ -146,18 +146,18 @@ const SupplierEntryReview: React.FC = () => {
|
|||||||
dataIndex: 'categoryNameList',
|
dataIndex: 'categoryNameList',
|
||||||
width: 120,
|
width: 120,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
render: (value: { item: string }[] = []) => {
|
render: (value: { categoryName: string; categoryPathName: string }[] = []) => {
|
||||||
if (!value || value.length === 0) return '-';
|
if (!value || value.length === 0) return '-';
|
||||||
if (value.length === 1) {
|
|
||||||
return <span>{value[0]}</span>;
|
|
||||||
}
|
|
||||||
// 多于1条
|
// 多于1条
|
||||||
const allNames = value.map(item => item).join('、');
|
const allNames = value.map(item => item.categoryPathName).join('\n');
|
||||||
return (
|
return (
|
||||||
<Tooltip title={allNames} overlayStyle={{ zIndex: 1200 }}>
|
<Tooltip title={<pre style={{ margin: 0, fontSize: 12, fontFamily: '微软雅黑', whiteSpace: 'pre-wrap' }}>{allNames}</pre>} overlayStyle={{ zIndex: 1200 }}>
|
||||||
<span>
|
<span>
|
||||||
{value[0]}
|
{value[0].categoryName}
|
||||||
<span>等</span>
|
{value.length !== 1 && (
|
||||||
|
<span>等</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
@ -213,7 +213,7 @@ const SupplierEntryReview: React.FC = () => {
|
|||||||
onFinish={handleSearch}
|
onFinish={handleSearch}
|
||||||
>
|
>
|
||||||
<Form.Item name="accessType" label="准入方式">
|
<Form.Item name="accessType" label="准入方式">
|
||||||
<AdmissionTypeSelect/>
|
<AdmissionTypeSelect />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="deptId" label="准入部门">
|
<Form.Item name="deptId" label="准入部门">
|
||||||
<AccessDepartmentSelect orgCategory='' />
|
<AccessDepartmentSelect orgCategory='' />
|
||||||
|
@ -28,20 +28,28 @@ interface ReviewerSelectorData {
|
|||||||
|
|
||||||
interface CategoryNode {
|
interface CategoryNode {
|
||||||
key: string;
|
key: string;
|
||||||
|
pathName: string;
|
||||||
|
path: string;
|
||||||
title: string;
|
title: string;
|
||||||
children?: CategoryNode[];
|
children?: CategoryNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenTree(
|
function getKeyTitlePathMap(
|
||||||
tree: CategoryNode[],
|
tree: CategoryNode[],
|
||||||
map: Record<string, string> = {}
|
keyTitleMap: Record<string, string> = {},
|
||||||
): Record<string, string> {
|
keyPathMap: Record<string, string> = {},
|
||||||
|
keyIdMap: Record<string, string> = {}
|
||||||
|
) {
|
||||||
tree.forEach((node) => {
|
tree.forEach((node) => {
|
||||||
map[node.key] = node.title;
|
keyTitleMap[node.key] = node.title;
|
||||||
if (node.children) flattenTree(node.children, map);
|
keyPathMap[node.key] = node.pathName;
|
||||||
|
keyIdMap[node.key] = node.path;
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
getKeyTitlePathMap(node.children, keyTitleMap, keyPathMap, keyIdMap);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return map;
|
return { keyTitleMap, keyPathMap, keyIdMap };
|
||||||
}
|
}
|
||||||
// 主体
|
// 主体
|
||||||
const CreateModal: React.FC<{ visible: boolean; onCancel: () => void; }> = ({ visible, onCancel }) => {
|
const CreateModal: React.FC<{ visible: boolean; onCancel: () => void; }> = ({ visible, onCancel }) => {
|
||||||
|
|
||||||
@ -106,12 +114,15 @@ const CreateModal: React.FC<{ visible: boolean; onCancel: () => void; }> = ({ vi
|
|||||||
const leafKeys = findLeafKeys(convertTreeData(categoriesTreeData));
|
const leafKeys = findLeafKeys(convertTreeData(categoriesTreeData));
|
||||||
const onlyLeafChecked = keys.filter(key => leafKeys.includes(String(key)));
|
const onlyLeafChecked = keys.filter(key => leafKeys.includes(String(key)));
|
||||||
|
|
||||||
// 平铺tree获取id=>title映射
|
// 获取映射
|
||||||
const keyTitleMap = flattenTree(convertTreeData(categoriesTreeData));
|
const { keyTitleMap, keyPathMap, keyIdMap } = getKeyTitlePathMap(convertTreeData(categoriesTreeData));
|
||||||
|
|
||||||
// 拼 categoryItem 数组
|
// 拼 categoryItem 数组
|
||||||
const coscoAccessCategoryList = onlyLeafChecked.map((id) => ({
|
const coscoAccessCategoryList = onlyLeafChecked.map((id) => ({
|
||||||
categoryId: id,
|
categoryId: id,
|
||||||
categoryName: keyTitleMap[id] || '',
|
categoryName: keyTitleMap[id] || '',
|
||||||
|
categoryPathName: keyPathMap[id] || '',
|
||||||
|
categoryPathId: keyIdMap[id] || '',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setCheckedKeys(keys); // UI 显示用,还是全量
|
setCheckedKeys(keys); // UI 显示用,还是全量
|
||||||
|
@ -89,7 +89,7 @@ const ViewModal: React.FC<{
|
|||||||
<Descriptions.Item label="申请准入品类">
|
<Descriptions.Item label="申请准入品类">
|
||||||
{data.coscoAccessCategoryList.map((item) => {
|
{data.coscoAccessCategoryList.map((item) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ margin: '5px' }}>{item.categoryName}</div>
|
<div style={{ margin: '5px' }}>{item.categoryPathName}</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
@ -104,7 +104,7 @@ const ViewModal: React.FC<{
|
|||||||
<Descriptions.Item label="申请准入品类">
|
<Descriptions.Item label="申请准入品类">
|
||||||
{data.coscoAccessCategoryList.map((item) => {
|
{data.coscoAccessCategoryList.map((item) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ margin: '5px' }}>{item.categoryName}</div>
|
<div style={{ margin: '5px' }}>{item.categoryPathName}</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
@ -120,7 +120,7 @@ const ViewModal: React.FC<{
|
|||||||
<Descriptions.Item label="申请准入品类">
|
<Descriptions.Item label="申请准入品类">
|
||||||
{data.coscoAccessCategoryList.map((item) => {
|
{data.coscoAccessCategoryList.map((item) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ margin: '5px' }}>{item.categoryName}</div>
|
<div style={{ margin: '5px' }}>{item.categoryPathName}</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
|
@ -90,7 +90,7 @@ const ViewModal: React.FC<{
|
|||||||
<Descriptions.Item label="申请准入品类">
|
<Descriptions.Item label="申请准入品类">
|
||||||
{data.coscoAccessCategoryList.map((item) => {
|
{data.coscoAccessCategoryList.map((item) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ margin: '5px' }}>{item.categoryName}</div>
|
<div style={{ margin: '5px' }}>{item.categoryPathName}</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
@ -105,7 +105,7 @@ const ViewModal: React.FC<{
|
|||||||
<Descriptions.Item label="申请准入品类">
|
<Descriptions.Item label="申请准入品类">
|
||||||
{data.coscoAccessCategoryList.map((item) => {
|
{data.coscoAccessCategoryList.map((item) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ margin: '5px' }}>{item.categoryName}</div>
|
<div style={{ margin: '5px' }}>{item.categoryPathName}</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
@ -120,7 +120,7 @@ const ViewModal: React.FC<{
|
|||||||
<Descriptions.Item label="申请准入品类">
|
<Descriptions.Item label="申请准入品类">
|
||||||
{data.coscoAccessCategoryList.map((item) => {
|
{data.coscoAccessCategoryList.map((item) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ margin: '5px' }}>{item.categoryName}</div>
|
<div style={{ margin: '5px' }}>{item.categoryPathName}</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
|
@ -117,18 +117,18 @@ const CooperateEnterprise: React.FC = () => {
|
|||||||
dataIndex: 'categoryNameList',
|
dataIndex: 'categoryNameList',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 160,
|
width: 160,
|
||||||
render: (value: { item: string }[] = []) => {
|
render: (value: { categoryName: string; categoryPathName: string }[] = []) => {
|
||||||
if (!value || value.length === 0) return '-';
|
if (!value || value.length === 0) return '-';
|
||||||
if (value.length === 1) {
|
|
||||||
return <span>{value[0]}</span>;
|
|
||||||
}
|
|
||||||
// 多于1条
|
// 多于1条
|
||||||
const allNames = value.map(item => item).join('、');
|
const allNames = value.map(item => item.categoryPathName).join('\n');
|
||||||
return (
|
return (
|
||||||
<Tooltip title={allNames} overlayStyle={{ zIndex: 1200 }}>
|
<Tooltip title={<pre style={{ margin: 0, fontSize: 12, fontFamily: '微软雅黑', whiteSpace: 'pre-wrap' }}>{allNames}</pre>} overlayStyle={{ zIndex: 1200 }}>
|
||||||
<span>
|
<span>
|
||||||
{value[0]}
|
{value[0].categoryName}
|
||||||
<span>等</span>
|
{value.length !== 1 && (
|
||||||
|
<span>等</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useIntl } from 'umi';
|
import { useIntl } from 'umi';
|
||||||
import { Form, Button, Table, DatePicker } from 'antd';
|
import { Form, Button, Table, DatePicker, Tooltip } from 'antd';
|
||||||
import type { ColumnsType, TablePaginationConfig } from 'antd/es/table';
|
import type { ColumnsType, TablePaginationConfig } from 'antd/es/table';
|
||||||
import { SearchOutlined, DeleteOutlined } from '@ant-design/icons';
|
import { SearchOutlined, DeleteOutlined } from '@ant-design/icons';
|
||||||
//接口
|
//接口
|
||||||
@ -20,6 +20,7 @@ interface Data {
|
|||||||
createTime: string;
|
createTime: string;
|
||||||
exitTime: string;
|
exitTime: string;
|
||||||
exitReason: string;
|
exitReason: string;
|
||||||
|
categoryPathName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CooperateEnterprise: React.FC = () => {
|
const CooperateEnterprise: React.FC = () => {
|
||||||
@ -91,11 +92,23 @@ const CooperateEnterprise: React.FC = () => {
|
|||||||
key: 'deptName',
|
key: 'deptName',
|
||||||
ellipsis: true
|
ellipsis: true
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: '准入品类',
|
title: '准入品类',
|
||||||
dataIndex: 'categoryName',
|
dataIndex: 'categoryName',
|
||||||
key: 'categoryName',
|
key: 'categoryName',
|
||||||
ellipsis: true
|
ellipsis: true,
|
||||||
|
width: 120,
|
||||||
|
align: 'center',
|
||||||
|
render: (_:any, item:{ categoryName:string; categoryPathName:string; }) => {
|
||||||
|
return (
|
||||||
|
<Tooltip title={<pre style={{ margin: 0, fontSize: 12, fontFamily: '微软雅黑', whiteSpace: 'pre-wrap' }}>{item.categoryPathName}</pre>} overlayStyle={{ zIndex: 1200 }}>
|
||||||
|
<span>
|
||||||
|
{item.categoryName}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '准入时间',
|
title: '准入时间',
|
||||||
|
@ -35,13 +35,15 @@ function collectSelectedNodesWithParents(
|
|||||||
): { id: string; categoryName: string; lockType: number; categoryType: string }[] {
|
): { id: string; categoryName: string; lockType: number; categoryType: string }[] {
|
||||||
const allKeysSet = collectCheckedWithParents(nodes, checkedKeys);
|
const allKeysSet = collectCheckedWithParents(nodes, checkedKeys);
|
||||||
|
|
||||||
const result: { id: string; categoryName: string; lockType: number; categoryType: string }[] = [];
|
const result: { id: string; categoryName: string; lockType: number; categoryType: string; categoryPathName: string; categoryPathId: string }[] = [];
|
||||||
function dfs(list: TreeNodeType[]) {
|
function dfs(list: TreeNodeType[]) {
|
||||||
list.forEach(node => {
|
list.forEach(node => {
|
||||||
if (allKeysSet.has(node.key)) {
|
if (allKeysSet.has(node.key)) {
|
||||||
result.push({
|
result.push({
|
||||||
id: node.key,
|
id: node.key,
|
||||||
categoryName: node.categoryName,
|
categoryName: node.categoryName,
|
||||||
|
categoryPathName: node.pathName,
|
||||||
|
categoryPathId: node.path,
|
||||||
lockType: 0,
|
lockType: 0,
|
||||||
categoryType: node.children?.length === 0 ? '1' : '0',
|
categoryType: node.children?.length === 0 ? '1' : '0',
|
||||||
});
|
});
|
||||||
|
@ -142,13 +142,29 @@ const mySupplierInquiry: React.FC<mySupplierInquiryProps> = ({ dispatch }) => {
|
|||||||
width: 120,
|
width: 120,
|
||||||
render: (code: string) => enterpriseTypeMap[code] || code
|
render: (code: string) => enterpriseTypeMap[code] || code
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: '准入品类',
|
title: '准入品类',
|
||||||
dataIndex: 'categoryName',
|
dataIndex: 'categoryNameList',
|
||||||
key: 'categoryName',
|
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 120,
|
width: 120,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
render: (value: { categoryName: string; categoryPathName: string }[] = []) => {
|
||||||
|
if (!value || value.length === 0) return '-';
|
||||||
|
|
||||||
|
// 多于1条
|
||||||
|
const allNames = value.map(item => item.categoryPathName).join('\n');
|
||||||
|
return (
|
||||||
|
<Tooltip title={<pre style={{ margin: 0, fontSize: 12, fontFamily: '微软雅黑', whiteSpace: 'pre-wrap' }}>{allNames}</pre>} overlayStyle={{ zIndex: 1200 }}>
|
||||||
|
<span>
|
||||||
|
{value[0].categoryName}
|
||||||
|
{value.length !== 1 && (
|
||||||
|
<span>等</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '准入时间',
|
title: '准入时间',
|
||||||
|
@ -17,6 +17,8 @@ interface Supplier {
|
|||||||
lastEvalDate: string; // 评价时间
|
lastEvalDate: string; // 评价时间
|
||||||
supplierId: string;
|
supplierId: string;
|
||||||
categoryId: string;
|
categoryId: string;
|
||||||
|
categoryPathId: string;
|
||||||
|
categoryPathName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateBlacklistModalProps {
|
interface CreateBlacklistModalProps {
|
||||||
@ -73,9 +75,14 @@ const CreateBlacklistModal: React.FC<CreateBlacklistModalProps> = ({
|
|||||||
} = {
|
} = {
|
||||||
coscoSupplierexit: {...values},
|
coscoSupplierexit: {...values},
|
||||||
supplierIdList: suppliers.map((item) => item.supplierId),
|
supplierIdList: suppliers.map((item) => item.supplierId),
|
||||||
coscoSupplierexitSupplierCategoryList: suppliers.map((item) => ({ supplierId: item.supplierId, categoryId: item.categoryId, categoryName: item.categoryName }))
|
coscoSupplierexitSupplierCategoryList: suppliers.map((item) => ({
|
||||||
|
supplierId: item.supplierId,
|
||||||
|
categoryId: item.categoryId,
|
||||||
|
categoryName: item.categoryName,
|
||||||
|
categoryPathName: item.categoryPathName,
|
||||||
|
categoryPathId: item.categoryPathId,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await add(payload);
|
const res = await add(payload);
|
||||||
if (res?.success) {
|
if (res?.success) {
|
||||||
message.success("提交成功");
|
message.success("提交成功");
|
||||||
|
@ -21,6 +21,8 @@ interface Supplier {
|
|||||||
lastEvalDate: string; // 评价时间
|
lastEvalDate: string; // 评价时间
|
||||||
supplierId: string;
|
supplierId: string;
|
||||||
categoryId: string;
|
categoryId: string;
|
||||||
|
categoryPathName: string;
|
||||||
|
categoryPathId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SupplierSelectModalProps {
|
interface SupplierSelectModalProps {
|
||||||
|
Reference in New Issue
Block a user