提交 59e0b9ae authored 作者: maimai's avatar maimai

fix: layout

上级
流水线 #583 已失败 于阶段
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
module.exports = {
extends: [require.resolve('eslint-config-ali/typescript/react')],
rules: {},
};
**/*.md
**/*.svg
**/*.ejs
**/*.html
package.json
.umi
.umi-production
.umi-test
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 80,
"overrides": [
{
"files": ".prettierrc",
"options": { "parser": "json" }
}
]
}
import { defineConfig } from 'umi';
export default defineConfig({
nodeModulesTransform: {
type: 'none',
},
routes: [
{
path: '/login',
component: '@/pages/login',
},
{
path: '/',
component: '@/layouts/index',
routes: [
{ path: '/dashBoard', title: '看板', routes: [
{ path: '/dashBoard/batch', title: '库存统计', component: '@/pages/dashBoard/batch' },
{ path: '/dashBoard/serialNumber', title: '分装流水号明细', component: '@/pages/dashBoard/serialNumber' },
{ path: '/dashBoard/waterBills', title: '库存流水', component: '@/pages/dashBoard/waterBills' },
{ path: '/dashBoard/weighRecord', title: '交货称重记录', component: '@/pages/dashBoard/weightRecord' },
] },
{ path: '/delivery', title: '交货管理', routes: [
{ path: '/delivery/index', title: '化合物交货', component: '@/pages/delivery/index' },
{ path: '/delivery/receive', title: '化合物收货', component: '@/pages/delivery/receive' },
{ path: '/delivery/weigh', title: '入库前称重', component: '@/pages/delivery/weigh' },
{ path: '/delivery/inbound', title: '化合物入库', component: '@/pages/delivery/inbound' },
{ path: '/delivery/split', title: '库内分装', component: '@/pages/delivery/split' },
{ path: '/delivery/outbound', title: '化合物出库', component: '@/pages/delivery/outbound' },
] },
{ path: '/managerCenter', title: '管理中心', routes: [
{ path: '/managerCenter/room', title: '房间管理', component: '@/pages/managerCenter/room' },
{ path: '/managerCenter/device', title: '设备管理', component: '@/pages/managerCenter/device' },
{ path: '/managerCenter/goods', title: '货位管理', component: '@/pages/managerCenter/goods' },
{ path: '/managerCenter/printTemplate', title: '打印模板', component: '@/pages/managerCenter/printTemplate' },
{ path: '/managerCenter/consignmentProcess', title: '交货流程设置', component: '@/pages/managerCenter/consignmentProcess' },
{ path: '/managerCenter/consignmentProcess/add', title: '新增交货流程', component: '@/pages/managerCenter/consignmentProcess/Create', hideInMenu: true },
{ path: '/managerCenter/hardware', title: '硬件管理', component: '@/pages/managerCenter/hardware' },
] },
{ path: '/log', title: '业务日志', layout: 'log', routes: [
{
path: '/log/index',
title: '化合物修改日志',
layout: 'log',
component: '@/pages/log/compound'
}
] },
{ path: '/permissions', title: '账号权限', layout: 'system', routes: [
{
path: '/permissions/account',
title: '账号管理',
layout: 'system',
component: '@/pages/system/account'
},
{
path: '/permissions/project',
title: '项目管理',
layout: 'system',
component: '@/pages/system/project'
},
{
path: '/permissions/role',
title: '角色管理',
layout: 'system',
component: '@/pages/system/role'
},
{
path: '/permissions/role/add',
title: '新增权限',
layout: 'system',
hideInMenu: true,
component: '@/pages/system/role/Add'
}
] },
{ path: '/personal', title: '个人中心', layout: 'personal', component: '@/pages/personal' },
],
},
],
proxy: {
'/api': {
target: 'https://inventory.scionetest.ilabservice.cloud/',
changeOrigin: true,
pathRewrite: { '^/': '' },
},
'/api/v1/bi': {
target: 'http://106.15.63.121:31750/',
changeOrigin: true,
pathRewrite: { '^/': '' },
},
},
fastRefresh: {},
theme: {
'primary-color': '#00A4F5',
'info-color': '#00A4F5',
'processing-color': '#00A4F5',
'link-color': '#00A4F5',
'success-color': '#46cf84',
'error-color': '#fa4646',
'highlight-color': '#fa4646',
'normal-color': '#e0e0e0',
'heading-color': '#000000',
'text-color': '#4a4a4a',
'text-color-secondary': '#7c7c7c',
'disabled-color': '#c7c7c7',
'border-radius-base': '6px',
'border-color-base': '#e0e0e0',
'font-family':
'"SourceHanSans TW", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
},
headScripts: ['http://192.168.99.30:8000/CLodopfuncs.js'],
dva: {
immer: true,
hmr: false,
},
});
# umi project
## Getting Started
Install dependencies,
```bash
$ yarn
```
Start the dev server,
```bash
$ yarn start
```
{
"private": true,
"scripts": {
"start": "umi dev",
"build": "umi build",
"postinstall": "umi generate tmp",
"prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
"test": "umi-test",
"test:coverage": "umi-test --coverage"
},
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,jsx,less,md,json}": [
"prettier --write"
],
"*.ts?(x)": [
"prettier --parser=typescript --write"
]
},
"dependencies": {
"@ant-design/pro-layout": "^6.38.8",
"@umijs/preset-react": "1.x",
"axios": "^0.27.2",
"crypto-js": "^4.1.1",
"ilab-lib": "1.0.0",
"js-cookie": "^3.0.1",
"lodash": "^4.17.21",
"umi": "^3.3.10"
},
"devDependencies": {
"@types/lodash": "^4.14.182",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@typescript-eslint/eslint-plugin": "^5.28.0",
"@typescript-eslint/parser": "^5.28.0",
"@umijs/test": "^3.3.10",
"eslint": "^8.17.0",
"eslint-config-ali": "^14.0.1",
"eslint-import-resolver-typescript": "^2.7.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-react": "^7.30.0",
"eslint-plugin-react-hooks": "^4.6.0",
"lint-staged": "^10.0.7",
"prettier": "^2.2.0",
"react": "17.x",
"react-dom": "17.x",
"typescript": "^4.1.2",
"yorkie": "^2.0.0"
}
}
import React, { useState } from 'react';
import { Modal } from 'ilab-lib';
import { Form, InputNumber, Select } from 'antd';
import Tip from '@/components/Tip';
import { printOrder } from '@/utils/printTemplate';
import { getPrintLists } from '@/services/print'; // getPrinterModules
interface IPrintProps {
title?: React.ReactNode;
type?: number; // 1:交货 2:称重 3:分装
children: React.ReactNode;
printType?: number; // 1:普通 2:高频 3:超高频
printData?: any;
}
const Index = (props: IPrintProps) => {
const [form] = Form.useForm();
const { title = '打印标签', type = 1, printType = 1, printData, children } = props;
const [list, setList] = useState([]);
// const [templateList, setTemplateList] = useState([]);
const handleGetFormat = (item: any) => {
switch (item.printMode) {
case 1:
return `${item.aoPrinterDrive},${item.aoPrinterNumber};${item.aoPrinterTag}`;
case 2:
return `${item.bridgeNumber};${item.bridgeTag},${item.normallyPrinterName}`;
case 3:
return `${item.bridgeNumber};${item.bridgeTag},${item.normallyPrinterName}`;
default:
return '';
}
};
return (
<Modal
okText="确定"
cancelText="取消"
destroyOnClose
title={title}
callback={() => {
form.resetFields();
// getPrinterModules().then((res: any) => {
// setTemplateList(res?.data?.records || []);
// });
getPrintLists().then((res) => {
setList((res.data || [])?.filter((itm: any) => printType === itm.printerType));
});
}}
render={
<Form
name="printForm"
form={form}
style={{ width: 360 }}
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
autoComplete="off"
>
{
[2, 3].includes(type)
&& (
<>
<Form.Item
label="打印数量"
name="tagNum"
rules={[{ required: true, message: '请输入标签数量' }]}
>
<InputNumber style={{ width: '100%' }} placeholder="请输入" precision={0} />
</Form.Item>
<Tip style={{ margin: '0 0 24px 12%', width: 318 }} content="请输入要分装的瓶数,会按照当前数据数量生成对应数量的分装流水号" />
</>
)}
<Form.Item
label="打印机"
name="printName"
rules={[{ required: true, message: '请选择打印机名称' }]}
>
<Select style={{ width: '240px' }} placeholder="请选择">
{list?.map((item: any) => (
<Select.Option value={item.id} key={item.id}>
{item.printerName}
</Select.Option>
))}
</Select>
</Form.Item>
{
[2, 3].includes(type)
&& (
<Tip style={{ margin: '0 0 0 12%', width: 318 }} content="同步会生成预生成状态的序列号" />
)
}
</Form>
}
onOk={async () => {
try {
await form.validateFields();
const values = form.getFieldsValue();
const item: any = list?.filter((it: any) => it.id === values.printName)[0];
const printDirect = handleGetFormat(item);
let currentPrintData: any = [];
currentPrintData = [...printData];
printOrder(type, currentPrintData, {
printDirect,
printMode: item.printMode,
printerType: item.printerType,
printName: item.normallyPrinterName,
});
} catch (err) {
console.log(err);
}
}}
>
{children}
</Modal>
);
};
export default Index;
.noDataWord{
text-align: center;
color:#8791A3;
position: relative;
}
.noDataPngContainer {
text-align: center;
}
\ No newline at end of file
/**
* 无数据显示
* 显示文案图片内容等
*/
import React from 'react';
import './index.less';
interface NoDataPNGProps {
img: string;
name?: any;
width?: string;
marginTop?: string;
}
export default function NoDataPNG(props: NoDataPNGProps): JSX.Element {
const { img, name, width, marginTop } = props;
return (
<div className="noDataPngContainer">
<img
src={require(`@/assets/${img}`)}
style={{ width: width || '100%', maxWidth: '200px' }}
/>
<div className="noDataWord" style={{ top: marginTop || '-20px' }}>
{name || '暂无数据'}
</div>
</div>
);
}
.tip {
display: flex;
align-items: flex-start;
color: #5e6c84;
line-height: 16px;
background-color: #f5f6fa;
padding: 10px 14px;
border-radius: 6px;
span {
font-size: 16px;
margin-right: 8px;
color: #8791a3;
}
div {
font-size: 14px;
font-weight: 350;
}
}
import React from 'react';
import { Icon } from 'ilab-lib';
import classnames from 'classnames';
import './index.less';
interface IProps {
icon?: string;
content?: React.ReactNode;
style?: React.CSSProperties;
className?: string;
}
const Tip = (props: IProps) => {
const { icon = 'icon-chakanxiangqing', style, className, content } = props;
return (
<div style={style} className={classnames('tip', className)}>
<Icon type={icon} />
<div>
{content}
</div>
</div>
);
};
export default Tip;
export const DEFAULT_TIP_MESSAGE = '请求失败,请刷新重试';
export enum DateType {
// 日期
Date = 'YYYY-MM-DD',
// 日期时间(分)
DateTimeMin = 'YYYY-MM-DD HH:mm',
// 日期时间
DateTime = 'YYYY-MM-DD HH:mm:ss'
}
export const dirTypeList = {
1: '单行文本',
2: '多行文本',
3: '单选下拉列表',
4: '多选下拉列表',
5: '单选',
6: '多选',
7: '日期',
8: '数字',
9: '图片',
};
export const approvalType = new Map([
['ALL', '会签'],
['ANY', '或签'],
]);
@import '~antd/dist/antd.css';
.ant-table-thead > tr > th {
background: #ebf0f5 !important;
font-weight: 600;
color: #344563;
}
.ant-table-content {
border-top-left-radius: 6px;
border-top-right-radius: 6px;
}
.ant-table .ant-table-container table > thead > tr:first-child th:first-child {
border-top-left-radius: 6px;
}
.ant-table .ant-table-container table > thead > tr:first-child th:last-child {
border-top-right-radius: 6px;
}
.ant-btn:not(.ant-btn-text):not(.ant-btn-link) {
border-radius: 6px;
border-color: #d9d9d9 !important;
}
.ant-switch {
border-radius: 6px;
}
.ant-switch-handle:before {
border-radius: 5px;
}
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px #fff inset !important;
}
// ============= 自定义样式修改 ================
// 弹窗图片提示通用样式
.model-info-box {
height: 200px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #344563;
img {
width: 152px;
height: 101px;
}
h2 {
margin: 20px 10px 12px;
font-size: 16px;
color: #344563;
line-height: 11px;
}
div {
font-size: 14px;
margin-bottom: 10px;
}
}
.iTable {
margin-top: 16px;
}
.labelTitle {
color: #344563;
font-size: 20px;
font-weight: 500;
}
.menuListBox {
width: 180px;
.ant-dropdown-menu-item {
padding: 15px 20px;
color: #5e6c84;
font-size: 14px;
&:not(:last-child) {
border-bottom: 1px solid #e8eaed;
}
.anticon {
margin-right: 12px;
}
.top_menu_icon {
font-size: 24px;
}
&-active {
background-color: #eef9ff;
color: #00a4f5;
.top_menu_icon {
color: #00a4f5;
}
}
}
}
.ant-layout-sider-trigger {
width: 20px !important;
position: absolute;
right: -20px;
top: 50%;
background-color: #fff;
color: #a0a5ba;
border: 1px solid #eceef4;
border-radius: 0px 6px 6px 0px;
}
export interface ResponseData {
pageNum: number;
pages: number;
pageSize: number;
total: number;
records: any[];
}
type Data = ResponseData | string;
export interface BaseResponse {
debugMessage: string | null;
success: boolean;
code: string;
message: string;
data?: Data;
}
.ilab-layout {
width: 100%;
height: 100vh;
display: flex;
overflow-x: hidden;
flex-direction: column;
background-color: #f5f6fa;
&_head {
display: flex;
align-items: center;
width: 100%;
height: 72px;
padding: 0 16px 0 32px;
background-color: #fff;
z-index: 1;
box-shadow: 0px 2px 8px 1px rgba(0, 0, 0, 0.04);
> div + div {
margin-left: 32px;
}
.action {
font-size: 24px;
}
.logo {
display: flex;
align-items: center;
width: 370px;
height: 50px;
font-size: 20px;
color: #1468ac;
font-weight: 500;
cursor: pointer;
.img {
width: 76px;
height: 46px;
background: url('../assets/logos.png') no-repeat;
background-size: 100% 100%;
margin-right: 24px;
}
.title {
width: 257px;
height: 27px;
background: url('../assets/title.png') no-repeat;
background-size: 100%;
background-position: center center;
}
}
.menu {
display: flex;
&-li {
font-size: 16px;
color: #5e6c84;
padding: 10px 20px;
cursor: pointer;
transition: all 0.2s;
&:hover {
color: #00a4f5;
}
&.active {
color: #00a4f5;
background-color: #eef9ff;
box-shadow: inset 0px 1px 2px 0px rgba(6, 42, 76, 0.06);
border-radius: 6px;
}
}
}
.user {
display: flex;
align-items: center;
flex: 1;
justify-content: flex-end;
cursor: pointer;
.box {
line-height: 72px;
padding: 0 20px;
&:hover {
background-color: #eef9ff;
}
}
.message {
font-size: 24px;
margin-right: 24px;
&:hover {
color: #00a4f5;
}
}
.drop {
font-size: 14px;
}
}
}
.ilab-layout_breadcrumb {
margin-bottom: 16px;
}
:global {
.ant-breadcrumb-link {
border: 1px solid;
padding: 4px 10px 4px 10px;
display: inline-flex;
border-color: #dee1e6 transparent #dee1e6 #dee1e6;
position: relative;
background-color: #fff;
align-items: center;
}
.ant-breadcrumb > span {
color: #8791a3;
}
.ant-breadcrumb > span:last-child,
.ant-breadcrumb li:last-child {
position: relative;
color: #344563;
&::before {
content: ' ';
width: 0;
height: 0;
border-width: 16.5px 0 16.5px 11px;
border-style: solid;
border-color: transparent #fff transparent #dee1e6;
position: absolute;
right: -11.495px;
top: -0.5px;
z-index: 99;
}
&::after {
content: ' ';
width: 0;
height: 0;
border-width: 15px 0 15px 10px;
border-style: solid;
border-color: transparent #fff transparent #fff;
position: absolute;
left: 99.9%;
top: 1px;
z-index: 99;
}
}
.ant-breadcrumb {
> span:nth-child(1) .ant-breadcrumb-link:first-child {
padding-left: 10px;
border-radius: 6px 0 0 6px;
}
}
.ant-breadcrumb-separator {
display: none;
}
}
// 内容区
&_content {
display: flex;
flex: 1;
overflow-x: hidden;
overflow-y: auto;
background-color: #f5f6fa;
:global {
.ant-layout-sider-collapsed {
.title {
display: none;
}
}
}
aside {
width: 216px;
background-color: #fff;
transition: all 0.2s;
.menus {
display: flex;
flex-direction: column;
font-size: 16px;
list-style: none;
margin: 0;
padding: 24px 16px;
li {
width: 100%;
height: 38px;
line-height: 38px;
overflow: hidden;
padding: 0 12px;
color: #8791a3;
cursor: pointer;
margin-bottom: 5px;
transition: border-color 0.3s, background 0.3s,
padding 0.1s cubic-bezier(0.215, 0.61, 0.355, 1);
:global {
.anticon {
font-size: 20px;
margin-right: 16px;
}
}
&:hover,
&.active {
color: #00a4f5;
background-color: #eef9ff;
box-shadow: inset 0 1px 2px 0 rgb(6 42 76 / 6%);
border-radius: 6px;
}
}
}
}
main {
flex: 1;
padding: 16px 24px;
}
> * {
min-height: 100%;
}
.operation {
width: 48px;
background-color: #fff;
list-style: none;
display: flex;
flex-direction: column;
align-items: center;
padding: 0;
li {
position: relative;
width: 48px;
text-align: center;
font-size: 20px;
padding: 12px 0;
&::after {
position: absolute;
bottom: 0;
left: 0;
content: '';
width: 47px;
height: 1px;
background: linear-gradient(
27deg,
rgba(255, 255, 255, 0) 0%,
#e7e7e7 49%,
rgba(254, 254, 254, 0) 100%
);
border-radius: 1px;
}
}
}
}
}
import classnames from 'classnames';
import { Layout, Breadcrumb, Dropdown, Menu, Avatar, Space } from 'antd';
import { Icon } from 'ilab-lib';
import React, { useState, useEffect } from 'react';
import { IRoute, IRouteComponentProps } from 'umi';
import { deepQuery } from '@/utils';
import { doLogOut } from '@/services/user';
import Cookies from 'js-cookie';
import { CaretDownOutlined } from '@ant-design/icons';
import styles from './index.less';
const { Sider } = Layout;
const LayoutCom: React.FC<IRouteComponentProps> = (props) => {
const { children, history, routes = [] } = props;
const { pathname } = history.location;
const [currentRoute, setCurrentRoute] = useState<any>();
const menuList = routes.length > 0 ? (routes[1].routes || []) : [];
useEffect(() => {
const _current = deepQuery(
props.route.routes || [],
'routes',
'path',
props.location.pathname,
);
setCurrentRoute(_current);
}, [pathname]);
const getMenuMap = (arr: IRoute[]) => {
let obj = {};
(arr || []).forEach((item) => {
// @ts-ignore
obj[item.path] = item.title;
if (item.routes) {
obj = { ...obj, ...getMenuMap(item.routes || []) };
}
});
return obj;
};
const menu_obj: any = getMenuMap(menuList);
const Menu_Map = new Map(Object.keys(menu_obj || {}).map((item: any) => [item, menu_obj[item]]));
const toFlatArray: any = (tree: any[], parentId?: string) => {
return tree.reduce((t, _) => {
const child = _.routes;
return [
...t,
parentId ? { ..._, parentId } : _,
...(child && child.length ? toFlatArray(child, _.path) : [])];
}, []);
};
const getTreeIds = (tree: any[], nodeId: string, config?: any) => {
const { id = 'path' } = config || {};
const getIds = (flatArray: any[]) => {
let ids = [nodeId];
let child = flatArray.find((_) => _[id] === nodeId);
while (child && child.parentId) {
ids = [child.parentId, ...ids];
// eslint-disable-next-line no-loop-func
child = flatArray.find((_) => _[id] === child.parentId);
}
return ids;
};
return getIds(toFlatArray(tree));
};
const breadListIds = getTreeIds(menuList, pathname); // 面包屑path 列表
const fatherMenu = (tree: any[], key: string) => {
for (let i = 0; i < tree.length; i++) {
if (key.includes(tree[i].path)) {
return tree[i].routes?.filter((item: { hideInMenu: any }) => !item.hideInMenu);
}
}
};
const items = [
{ label: '个人中心', key: 'personal', icon: <Icon type="icon-gerenzhongxin_N" className="top_menu_icon" /> },
{ label: '系统日志', key: 'log', icon: <Icon type="icon-xitongrizhi1" className="top_menu_icon" /> },
{ label: '系统设置', key: 'permissions', icon: <Icon type="icon-xitongrizhi1" className="top_menu_icon" /> },
{ label: '退出登录', key: 'logout', icon: <Icon type="icon-anquantuichu_N" className="top_menu_icon" /> },
];
const AllMenu = menuList?.filter((item) => !item.hideInMenu);
const renderMenu = AllMenu.filter((item) => item.layout === currentRoute?.layout) || [];
return (
<div className={styles['ilab-layout']}>
<div className={styles['ilab-layout_head']}>
<div className={styles.logo} onClick={() => history.push('/dashBoard/batch')}>
<div className={styles.img} />
<div className={styles.title} />
</div>
<div className={styles.menu}>
{renderMenu.map((item: any) => (
<div
key={item.path}
className={classnames(
styles['menu-li'],
pathname.includes(item.path) && styles.active,
)}
onClick={() => {
const choose = renderMenu.filter((it) => it.path === item.path)[0];
const path = choose?.routes?.length ? choose?.routes[0].path : choose.path;
path && history.push(path);
}}
>
{item.title}
</div>
))}
</div>
<div className={styles.user}>
<Dropdown
overlay={<Menu
items={items}
className="menuListBox"
selectedKeys={[]}
onClick={({ key }) => {
if (key !== 'logout') {
const choose = AllMenu.filter((it) => it.path === `/${key}`)[0];
const path = choose?.routes?.length ? choose?.routes[0].path : choose.path;
path && history.push(path);
} else {
doLogOut().then(() => {
localStorage.removeItem('token');
localStorage.removeItem('isAdmin');
localStorage.removeItem('bizType');
Cookies.remove('token');
history.push('/login');
});
}
}}
/>}
placement="bottomRight"
className={styles.drop}
>
<Space className={styles.box}>
<Avatar size={40} style={{ backgroundColor: '#87d068' }} src="https://prod-saas-5.oss-cn-shanghai.aliyuncs.com/room/05f1fe75836a4fbf83a1ea3d541c3bc7/logo.png" />
麦麦
<CaretDownOutlined />
</Space>
</Dropdown>
</div>
</div>
<div id="main" className={styles['ilab-layout_content']}>
{
(fatherMenu(menuList, pathname) || []).length > 0 ? (
<Sider collapsible width={200} theme="light">
<ul className={styles.menus}>
{
(fatherMenu(menuList, pathname) || []).map((item: any) => (
<li
key={item.path}
onClick={() => history.push(item.path)}
className={pathname.includes(item.path) ? styles.active : ''}
>
<Icon type="icon-kucun-erji-baofeijilu" />
<span className="title">{item.title}</span>
</li>
))
}
</ul>
</Sider>
) : null
}
<main>
{currentRoute && (
<Breadcrumb className={styles['ilab-layout_breadcrumb']}>
{
breadListIds.map((item: string) => (
<Breadcrumb.Item key={item} >
{Menu_Map.get(item) || currentRoute.title}
</Breadcrumb.Item>))
}
</Breadcrumb>
)}
<div>{children}</div>
</main>
</div>
</div>
);
};
export default LayoutCom;
export interface UserModelState {
user: any;
userList: any[];
}
const UserModel = {
namespace: 'user',
state: {
user: {},
userList: [],
},
reducers: {
// 修改用户信息
setUserInfo(state: any, action: { payload: any }) {
if (!action.payload) {
localStorage.setItem('USER_INFO', action.payload ? JSON.stringify(action.payload) : '');
} else {
localStorage.setItem('USER_INFO', JSON.stringify(action.payload));
}
return {
...state,
user: action.payload,
};
},
setUserList(state: any, action: { payload: any }) {
return {
...state,
userList: action.payload,
};
},
},
subscriptions: {
setup({ dispatch }: { dispatch: any }) {
const userInfo = localStorage.getItem('USER_INFO');
if (userInfo) {
dispatch({ type: 'setUserInfo', payload: JSON.parse(userInfo) });
}
},
},
};
export default UserModel;
.modal {
:global {
.ant-modal-body {
padding: 0;
}
.iLab-pro-table {
padding-bottom: 0;
}
}
}
import React, { useState } from 'react';
import { Modal, Button } from 'antd';
import { ProTable } from 'ilab-lib';
import { getDetailList } from '@/services/dashBoard';
import dayjs from 'dayjs';
import { DateType } from '@/constants';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
import styles from './index.less';
interface IProps {
id: number | string;
status?: number;
children: React.ReactNode;
}
const Index: React.FC<IProps> = (props: IProps) => {
const { id, status, children } = props;
const [visible, setVisible] = useState(false);
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(
getDetailList,
formatPaginationParams({ ...params, status, stockListId: id }),
);
const handleCancel = () => {
setVisible(false);
};
const columns: any[] = [
{
title: '序号',
dataIndex: 'index',
render: (text: string, records: any, index: number) => <span>{index + 1}</span>,
},
{
title: '序列号',
dataIndex: 'uniqueCode',
},
{
title: '批次号',
dataIndex: 'goodsName',
},
{
title: 'Compound ID',
dataIndex: 'spec',
},
{
title: 'Sample ID',
dataIndex: 'unitName',
},
{
title: 'Barcode',
dataIndex: 'periodOfValidity',
render: (text: string | number) => (
<span>{text ? dayjs(text).format(DateType.Date) : '-'}</span>
),
},
{
title: '房间',
dataIndex: 'shelfNumber',
},
{
title: '设备',
dataIndex: 'statusZh',
},
{
title: '货位',
dataIndex: 'shelfNumber',
},
{
title: '重量',
dataIndex: 'statusZh',
},
];
return (
<>
<Modal
title="序列号明细"
visible={visible}
width={1190}
className={styles.modal}
onCancel={handleCancel}
footer={[
<Button key="cancel" onClick={handleCancel}>
关闭
</Button>,
]}
>
<ProTable
request={getList}
columns={columns}
rowKey="id"
scroll={{ x: 'max-content' }}
drawerProps={{
filterProps: {
column: 1,
},
width: 328,
}}
/>
</Modal>
<span onClick={() => setVisible(true)}>{children}</span>
</>
);
};
export default Index;
.modal {
:global {
.ant-modal-body {
padding: 0;
}
.iLab-pro-table {
padding-bottom: 0;
}
}
}
import React, { useState } from 'react';
import { Modal, Button } from 'antd';
import { ProTable } from 'ilab-lib';
import { getDetailList } from '@/services/dashBoard';
import dayjs from 'dayjs';
import { DateType } from '@/constants';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
import styles from './index.less';
interface IProps {
id: number | string;
status?: number;
children: React.ReactNode;
}
const Index: React.FC<IProps> = (props: IProps) => {
const { id, status, children } = props;
const [visible, setVisible] = useState(false);
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(
getDetailList,
formatPaginationParams({ ...params, status, stockListId: id }),
);
const handleCancel = () => {
setVisible(false);
};
const columns: any[] = [
{
title: '操作时间',
dataIndex: 'index',
render: (text: string, records: any, index: number) => <span>{index + 1}</span>,
},
{
title: '操作人',
dataIndex: 'uniqueCode',
},
{
title: '操作类型',
dataIndex: 'goodsName',
},
{
title: '单号',
dataIndex: 'spec',
},
{
title: '批次号',
dataIndex: 'unitName',
},
{
title: 'compound ID',
dataIndex: 'periodOfValidity',
render: (text: string | number) => (
<span>{text ? dayjs(text).format(DateType.Date) : '-'}</span>
),
},
{
title: 'sample ID',
dataIndex: 'shelfNumber',
},
{
title: '差异重量',
dataIndex: 'statusZh',
},
{
title: '剩余重量',
dataIndex: 'shelfNumber',
},
];
return (
<>
<Modal
title="库存流水"
visible={visible}
width={1190}
className={styles.modal}
onCancel={handleCancel}
footer={[
<Button key="cancel" onClick={handleCancel}>
关闭
</Button>,
]}
>
<ProTable
request={getList}
columns={columns}
rowKey="id"
scroll={{ x: 'max-content' }}
drawerProps={{
filterProps: {
column: 1,
},
width: 328,
}}
/>
</Modal>
<span onClick={() => setVisible(true)}>{children}</span>
</>
);
};
export default Index;
/*
* @Author: 麦麦
* @Description: 批次库存统计
* @Date: 2022-06-17 10:24:30
* @Last Modified time: 2022-06-17 10:24:30
*/
import React, { useRef } from 'react';
import { ProTable, Icon, DrawerFilter } from 'ilab-lib';
import { Button } from 'antd';
import { ProColumn, ActionType } from 'ilab-lib/dist/ProTable/interface';
import { ActionType as formActionType } from 'ilab-lib/dist/DrawerFilter';
import DialogDetailModal from './components/DialogSerialNumberDetail';
import DialogWaterBills from './components/DialogWaterBills';
import { fetchBatchList } from '@/services/dashboard';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
const Index: React.FC = () => {
const tableRef = useRef<ActionType>();
const formRef = useRef<formActionType>();
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(fetchBatchList, formatPaginationParams({ ...params, id: 1989 }));
const options = [
{
label: 'Compound ID',
name: 'compoundID',
rules: [{ required: true, message: '请输入' }],
},
{
label: 'Sample ID',
name: 'sampleId',
rules: [{ required: true, message: '请输入' }],
},
];
const handleSubmit = (values: any) => {
console.log(values);
};
const handleOpen = (id: number) => () => {
formRef?.current?.open({ compoundID: id });
};
const columns: Array<ProColumn<any>> = [
{
title: '批次号',
dataIndex: 'number',
search: true,
},
{
title: '项目名称',
dataIndex: 'name',
},
{
title: 'compound ID',
dataIndex: 'spec',
search: true,
},
{
title: 'sample ID',
dataIndex: 'categoryName',
},
{
title: '重量',
dataIndex: 'brand',
},
{
title: '明细',
dataIndex: 'actualInventory',
render: (text, records) => <DialogDetailModal id={records.id}><Button size="small" type="link">查看</Button></DialogDetailModal>,
},
{
title: '流水',
dataIndex: 'availableStock',
render: (text, records) => <DialogWaterBills id={records.id}><Button size="small" type="link">查看</Button></DialogWaterBills>,
},
{
title: '操作',
dataIndex: 'id',
render: (id: number) => (<Button onClick={handleOpen(id)} size="small" type="link">编辑</Button>),
},
];
const exportData = () => {
const params = tableRef.current?.getFilterValue();
console.log(params);
// exportGoodsSummary(params, '库存汇总');
};
const toolbar = {
showFilter: true,
actions: [
<div className="labelTitle" key="title">库存统计</div>,
],
slot: [
<Button onClick={exportData} key="print">
<Icon type={'icon-biaoge-daochu'} />
导出
</Button>,
],
};
return (
<>
<ProTable
className="iTable"
request={getList}
actionRef={tableRef}
columns={columns}
rowKey="id"
toolbar={toolbar}
scroll={{ x: 'max-content' }}
drawerProps={{
filterProps: {
column: 1,
},
width: 328,
}}
/>
<DrawerFilter
filterProps={{
column: 1,
}}
width={328}
title="编辑"
okText="确定"
cancelText="取消"
actionRef={formRef}
options={options}
onSubmit={handleSubmit}
><div />
</DrawerFilter>
</>
);
};
export default Index;
/*
* @Author: 麦麦
* @Description: 序列号明细
* @Date: 2022-06-20 10:24:30
* @Last Modified time: 2022-06-17 10:24:30
*/
import React, { useRef } from 'react';
import { ProTable, DrawerFilter } from 'ilab-lib';
import { Button } from 'antd';
import { ProColumn, ActionType } from 'ilab-lib/dist/ProTable/interface';
import { ActionType as formActionType } from 'ilab-lib/dist/DrawerFilter';
import { fetchBatchList } from '@/services/dashboard';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
const Index: React.FC = () => {
const tableRef = useRef<ActionType>();
const formRef = useRef<formActionType>();
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(fetchBatchList, formatPaginationParams({ ...params, id: 1989 }));
const options = [
{
label: 'Compound ID',
name: 'compoundID',
rules: [{ required: true, message: '请输入' }],
},
{
label: 'Sample ID',
name: 'sampleId',
rules: [{ required: true, message: '请输入' }],
},
{
label: 'Barcode',
name: 'barcode',
rules: [{ required: true, message: '请输入' }],
},
];
const handleSubmit = (values: any) => {
console.log(values);
};
const handleOpen = (id: number) => () => {
formRef?.current?.open({ compoundID: id });
};
const columns: Array<ProColumn<any>> = [
{
title: '序号',
dataIndex: 'index',
render: (text: string, records: any, index: number) => <span>{index + 1}</span>,
},
{
title: '分装流水号',
dataIndex: 'number',
search: true,
},
{
title: '流水号',
dataIndex: 'number',
search: true,
},
{
title: '项目名称',
dataIndex: 'name',
},
{
title: 'compound ID',
dataIndex: 'spec',
search: true,
},
{
title: 'sample ID',
dataIndex: 'categoryName',
},
{
title: 'Barcode',
dataIndex: 'brand',
},
{
title: '房间',
dataIndex: 'roomId',
search: true,
},
{
title: '设备',
dataIndex: 'availableStock',
search: true,
},
{
title: '货位',
dataIndex: 'availableStock',
search: true,
},
{
title: '重量',
dataIndex: 'id',
},
{
title: '操作',
dataIndex: 'id',
render: (id: number) => (<Button onClick={handleOpen(id)} size="small" type="link">编辑</Button>),
},
];
const toolbar = {
showFilter: true,
actions: [
<div className="labelTitle" key="title">序列号明细</div>,
],
};
return (
<>
<ProTable
className="iTable"
request={getList}
actionRef={tableRef}
columns={columns}
rowKey="id"
toolbar={toolbar}
scroll={{ x: 'max-content' }}
drawerProps={{
filterProps: {
column: 1,
},
width: 328,
}}
/>
<DrawerFilter
filterProps={{
column: 1,
}}
width={328}
title="编辑"
okText="确定"
cancelText="取消"
actionRef={formRef}
options={options}
onSubmit={handleSubmit}
><div />
</DrawerFilter>
</>
);
};
export default Index;
/*
* @Author: 麦麦
* @Description: 库存流水
* @Date: 2022-06-20 10:24:30
* @Last Modified time: 2022-06-20 12:24:30
*/
import React, { useRef } from 'react';
import { ProTable } from 'ilab-lib';
import { ProColumn, ActionType } from 'ilab-lib/dist/ProTable/interface';
import { fetchBatchList } from '@/services/dashboard';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
const Index: React.FC = () => {
const tableRef = useRef<ActionType>();
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(fetchBatchList, formatPaginationParams({ ...params, id: 1989 }));
const columns: Array<ProColumn<any>> = [
{
title: '操作时间',
dataIndex: 'number',
},
{
title: '操作人',
dataIndex: 'number',
},
{
title: '操作类型',
dataIndex: 'name',
search: true,
order: 2,
},
{
title: '单号',
dataIndex: 'spec',
},
{
title: '批次号',
dataIndex: 'categoryName',
search: true,
order: 1,
},
{
title: '项目',
dataIndex: 'brand',
search: true,
order: 4,
},
{
title: 'compound ID',
dataIndex: 'spec',
search: true,
order: 3,
},
{
title: 'sample ID',
dataIndex: 'categoryName',
},
{
title: '差异重量',
dataIndex: 'roomId',
},
{
title: '剩余重量',
dataIndex: 'availableStock',
},
];
const toolbar = {
showFilter: true,
actions: [
<div className="labelTitle" key="title">库存流水</div>,
],
};
return (
<ProTable
className="iTable"
request={getList}
actionRef={tableRef}
columns={columns}
rowKey="id"
toolbar={toolbar}
scroll={{ x: 'max-content' }}
drawerProps={{
filterProps: {
column: 1,
},
width: 328,
}}
/>
);
};
export default Index;
/*
* @Author: 麦麦
* @Description: 交货称重记录
* @Date: 2022-06-23 17:52:42
* @Last Modified time: 2022-06-23 17:52:42
*/
import React, { useRef } from 'react';
import { ProTable } from 'ilab-lib';
import { ProColumn, ActionType } from 'ilab-lib/dist/ProTable/interface';
import { fetchBatchList } from '@/services/dashboard';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
const Index: React.FC = () => {
const tableRef = useRef<ActionType>();
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(fetchBatchList, formatPaginationParams({ ...params, id: 1989 }));
const columns: Array<ProColumn<any>> = [
{
title: '操作时间',
dataIndex: 'number',
},
{
title: '操作人',
dataIndex: 'number',
},
{
title: '交货单号',
dataIndex: 'name',
search: true,
order: 2,
},
{
title: '分装流水号',
dataIndex: 'spec',
},
{
title: '流水号',
dataIndex: 'categoryName',
search: true,
order: 1,
},
{
title: '项目',
dataIndex: 'brand',
search: true,
order: 4,
},
{
title: 'compound ID',
dataIndex: 'spec',
search: true,
order: 3,
},
{
title: 'sample ID',
dataIndex: 'categoryName',
},
{
title: '化合物重量',
dataIndex: 'roomId',
},
];
const toolbar = {
showFilter: true,
actions: [
<div className="labelTitle" key="title">库存流水</div>,
],
};
return (
<ProTable
className="iTable"
request={getList}
actionRef={tableRef}
columns={columns}
rowKey="id"
toolbar={toolbar}
scroll={{ x: 'max-content' }}
drawerProps={{
filterProps: {
column: 1,
},
width: 328,
}}
/>
);
};
export default Index;
.title {
background: rgb(121, 242, 157);
}
import React from 'react'
const Index = () => {
return (
<div>dashboard</div>
)
}
export default Index
.title {
background: rgb(121, 242, 157);
}
import React from 'react'
const Index = () => {
return (
<div>dashboard</div>
)
}
export default Index
.title {
background: rgb(121, 242, 157);
}
import React from 'react'
const Index = () => {
return (
<div>dashboard</div>
)
}
export default Index
.title {
background: rgb(121, 242, 157);
}
import React from 'react'
const Index = () => {
return (
<div>dashboard</div>
)
}
export default Index
.title {
background: rgb(121, 242, 157);
}
import React from 'react'
const Index = () => {
return (
<div>dashboard</div>
)
}
export default Index
.title {
background: rgb(121, 242, 157);
}
import React from 'react'
const Index = () => {
return (
<div>dashboard</div>
)
}
export default Index
.title {
background: rgb(121, 242, 157);
}
/*
* @Author: 麦麦
* @Description: 化合物修改日志
* @Date: 2022-06-24 09:48:02
* @Last Modified time: 2022-06-24 09:48:02
*/
import React, { useRef } from 'react';
import { ProTable } from 'ilab-lib';
import { ProColumn, ActionType } from 'ilab-lib/dist/ProTable/interface';
import { fetchBatchList } from '@/services/dashboard';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
const Index: React.FC = () => {
const tableRef = useRef<ActionType>();
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(fetchBatchList, formatPaginationParams({ ...params, id: 1989 }));
const columns: Array<ProColumn<any>> = [
{
title: '操作时间',
dataIndex: 'number',
search: true,
},
{
title: '操作人',
dataIndex: 'number',
search: true,
},
{
title: '操作类型',
dataIndex: 'name',
},
{
title: '修改项',
dataIndex: 'spec',
},
{
title: '修改前',
dataIndex: 'categoryName',
},
{
title: '修改后',
dataIndex: 'brand',
},
];
const toolbar = {
showFilter: true,
actions: [
<div className="labelTitle" key="title">化合物修改日志</div>,
],
};
return (
<ProTable
className="iTable"
request={getList}
actionRef={tableRef}
columns={columns}
rowKey="id"
toolbar={toolbar}
scroll={{ x: 'max-content' }}
drawerProps={{
filterProps: {
column: 1,
},
width: 328,
}}
/>
);
};
export default Index;
.loginPage {
position: relative;
width: 100vw;
height: 100vh;
background: url('https://console.scionetest.ilabservice.cloud/5ca50b40fe43c6e93047.png')
no-repeat;
background-size: 100% auto;
&-form {
position: absolute;
top: 10%;
right: 10%;
width: 530px;
background: #fff;
box-shadow: 0 8px 33px 0 #c8d8e0;
border-radius: 12px;
padding: 34px 80px 40px;
.title {
font-size: 32px;
font-weight: 500;
color: #5e6c84;
margin-bottom: 25px;
text-align: center;
}
.selectPrefix {
position: relative;
> span {
position: absolute;
z-index: 9;
top: 15px;
left: 11px;
}
:global {
.ant-select-selector {
padding-left: 32px !important;
font-size: 14px;
}
}
}
.flex {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24px;
:global {
.ant-form-item {
margin-bottom: 0;
}
}
}
}
:global {
.ant-input,
.ant-input-affix-wrapper {
border-radius: 6px;
padding: 6.5px 11px;
font-size: 14px;
}
.ant-btn-lg {
border-radius: 6px;
}
}
}
import React, { useState } from 'react';
import { Form, Input, Button, Select, Checkbox } from 'antd';
import { Icon } from 'ilab-lib';
import { fetchCompanyList, doLogin, getUserInfo } from '@/services/user';
import { get } from 'lodash';
import Cookies from 'js-cookie';
import { useDispatch } from 'dva';
import Encrypt from '@/utils/Encrypt';
import styles from './index.less';
const Index: React.FC = () => {
const dispatch = useDispatch();
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
// 公司列表
const [bizTypeList, setBizTypeList] = useState([]);
const handleChangeInput = (e: { target: { value: any } }) => {
setBizTypeList([]);
form.setFieldsValue({ orgId: null });
const phone = e.target.value;
if (!phone) return;
if (phone.length === 11) {
getCompanyList(phone);
}
};
const getCompanyList = (phone: number) => {
if (!phone) return;
fetchCompanyList({
account: phone,
type: 'saas',
accountType: 'ACCOUNT',
}).then((res: any) => {
if (res.success) {
setBizTypeList(res.data);
const biz = get(res.data, '[0].orgId', null);
if (biz) {
localStorage.setItem('bizType', biz);
form.setFieldsValue({
orgId: biz,
});
}
}
});
};
const onFinish = (values: { [x: string]: any }) => {
setLoading(true);
values.loginType = 'PASSWORD';
values.appId = '611104697126f335578ac201';
values.password = Encrypt.encryptContent(values.password);
doLogin(values).then((res: any) => {
setLoading(false);
Cookies.set('token', res?.data?.accessToken, {
domain: process.env.DOMAIN,
});
const isAdmin = res.data.loginUser.userProduct.systemAdmin;
localStorage.setItem('isAdmin', isAdmin);
// 存用户信息
getUserInfo().then((result: any) => {
dispatch({
type: 'user/setUserInfo',
payload: result.data,
});
});
window.location.href = `${window.location.origin}/dashBoard/batch`;
}).catch((err) => {
console.log('登录错误信息', err?.message);
setLoading(false);
});
};
return (
<div className={styles.loginPage}>
<div className={styles['loginPage-form']}>
<div className={styles.title}>欢迎使用</div>
<Form
form={form}
name="login"
initialValues={{ autoLogin: true }}
onFinish={onFinish}
autoComplete="off"
>
<Form.Item
label=""
name="username"
rules={[{ required: true, message: '请输入用户名!' }]}
>
<Input prefix={<Icon type="icon-yonghuming" />} size="large" maxLength={11} placeholder="请输入手机号/用户名" onChange={handleChangeInput} />
</Form.Item>
<div className={styles.selectPrefix}>
<Icon type="icon-gongsi" className="gongsi" />
<Form.Item name="orgId" rules={[{ required: true, message: '请选择公司!' }]}>
<Select
placeholder="请选择公司"
onChange={(val: any) => {
localStorage.setItem('bizType', val.toString());
}}
size="large"
notFoundContent={<div className="notFoundContent">此手机号暂无公司</div>}
options={bizTypeList.map((o) => ({ label: o.value, value: o.orgId }))}
/>
</Form.Item>
</div>
<Form.Item
name="password"
rules={[{ required: true, message: '请输入密码!' }]}
>
<Input.Password size="large" prefix={<Icon type="icon-mima" />} placeholder="密码" />
</Form.Item>
<div className={styles.flex}>
<Form.Item name="autoLogin" valuePropName="checked">
<Checkbox
style={{ color: '#00A4F5' }}
>
自动登录
</Checkbox>
</Form.Item>
<Button type="link" size="small">忘记密码</Button>
</div>
<Form.Item>
<Button loading={loading} block type="primary" size="large" htmlType="submit">
登录
</Button>
</Form.Item>
</Form>
</div>
</div>
);
};
export default Index;
import React, { useState, useEffect, useRef, useContext } from 'react';
import { Form, Input, Space, Button, Divider } from 'antd';
import { TransferTable, DrawerFilter, Icon } from 'ilab-lib';
import TableContext from '../context';
import { dirTypeList } from '@/constants';
import _ from 'lodash';
import styles from '../index.less';
interface FormActionType {
getFieldsValue: () => void;
setFieldsValue: (val: any) => void;
resetFields: () => void;
open: (val: any) => void;
}
const Create: React.FC = () => {
const formRef = useRef<FormActionType>();
const [id, setId] = useState<any>();
const [info, setInfo] = useState<any>({});
const [type, setType] = useState<number>(0);
const [dataSource, setDataSource] = useState([
{
id: 1,
label: 'Compound ID',
fieldType: 1,
type: 1,
group: 1212,
isRequired: true,
},
]);
const { targetKeys, setTargetKeys, leftKeyList, setLeftKeyList } = useContext(TableContext);
const [pagination, setPagination] = useState<any>({
pageNum: 1,
pageSize: 10,
});
const [pagination2, setPagination2] = useState<any>({
pageNum: 1,
pageSize: 10,
});
useEffect(() => {
const newArr = _.uniq([...targetKeys]);
setLeftKeyList(newArr);
}, [targetKeys]);
const options: any = [
{
label: '字段名称',
name: 'label',
valueType: 'text',
fieldProps: {
disabled: !!id && info?.fieldType === 1 && info?.label === '溶剂',
placeholder: '请输入字段名称',
},
rules: [{ required: true, message: '请输入字段名称' }],
},
{
label: '字段属性',
name: 'fieldType',
valueType: 'select',
valueEnum: new Map([
[1, '系统默认'],
[2, '自定义字段'],
]),
fieldProps: {
disabled: true,
placeholder: '请选择字段属性',
},
rules: [{ required: true, message: '请选择字段属性' }],
},
{
label: '字段类型',
name: 'type',
valueType: 'select',
valueEnum: new Map([
[1, '文本'],
[8, '数字'],
[7, '日期'],
[3, '下拉选项'],
[4, '多选项'],
]),
fieldProps: {
placeholder: '请选择字段类型',
disabled: !!id,
onChange: (value: React.SetStateAction<number>) => setType(value),
},
rules: [{ required: true, message: '请选择字段类型' }],
},
{
label: '保留小数位数',
name: 'format',
valueType: 'select',
valueEnum: new Map([
[0, 0],
[1, 1],
[2, 2],
]),
show: type === 8,
fieldProps: {
placeholder: '请选择保留小数位数',
},
},
{
label: '日期格式',
name: 'format',
valueType: 'select',
valueEnum: new Map([[0, 'YYYY-MM-DD']]),
show: type === 7,
fieldProps: {
placeholder: '请选择日期格式',
},
},
{
label: '选项',
name: 'selects',
valueType: 'custom',
show: [3, 4].includes(type),
fieldProps: {
placeholder: '请添加选项值',
},
customRender: (
<Form.List name="selects" initialValue={['']}>
{(fields, { add, remove }, { errors }) => (
<>
{fields.map((field) => (
<Form.Item required={false} key={field.key}>
<Form.Item
{...field}
className={styles.item}
name={[field.name, 'value']}
validateTrigger={['onChange', 'onBlur']}
rules={[
{
required: true,
whitespace: true,
message: '请输入选项值',
},
]}
noStyle
>
<Input
placeholder="请输入选项值"
style={{ width: '90%' }}
allowClear
maxLength={50}
/>
</Form.Item>
{fields.length > 1 ? (
<Icon
type="icon-biaoge-shanchu"
className={styles['delete-button']}
onClick={() => {
remove(field.name);
}}
/>
) : null}
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
icon={<Icon type="icon-biaoge-xinzeng" />}
block
>
添加
</Button>
<Form.ErrorList errors={errors} />
</Form.Item>
</>
)}
</Form.List>
),
},
{
label: '描述信息',
name: 'description',
valueType: 'textarea',
fieldProps: {
maxLength: 200,
placeholder: '请输入描述信息',
},
},
{
label: '是否必填',
name: 'isRequired',
valueType: 'select',
valueEnum: new Map([
[1, '是'],
[0, '否'],
]),
fieldProps: {
disabled: !!id && info?.fieldType === 1 && info?.label === '溶剂',
placeholder: '请选择是否必填',
},
rules: [{ required: true, message: '请选择是否必填' }],
},
];
const handleChange = (nextTargetKeys: any, direction: any, moveKeys: any) => {
setTargetKeys([...targetKeys, ...moveKeys]);
};
const handleMove = (e: any, index: any, t: any) => {
e.stopPropagation();
const data = [...targetKeys];
let data2 = [];
if (t === 1) {
data2 = swapArray(data, index - 1, index);
} else {
data2 = swapArray(data, index, index + 1);
}
setTargetKeys(data2);
};
const swapArray = (arr: any, index1: any, index2: any) => {
arr[index1] = arr.splice(index2, 1, arr[index1])[0];
return arr;
};
const handleDel = (e: any, row: any) => {
e.stopPropagation();
const data = [...targetKeys];
const arr = _.remove(data, (n) => {
return n !== row.id;
});
setTargetKeys(arr);
};
const leftTableColumns = [
{
title: '字段名称',
dataIndex: 'label',
ellipsis: true,
width: 100,
},
{
title: '字段属性',
dataIndex: 'fieldType',
width: 100,
ellipsis: true,
render: (text: any) => (text === 1 ? '系统字段' : '自定义字段'),
},
{
title: '字段类型',
dataIndex: 'type',
ellipsis: true,
render: (text: string) => <span>{dirTypeList[text] || '-'}</span>,
},
{
title: '字段分组',
dataIndex: 'group',
ellipsis: true,
},
{
title: '是否必填',
dataIndex: 'isRequired',
ellipsis: true,
render: (text: any) => (text ? '是' : '否'),
},
{
title: '操作',
dataIndex: 'operateOptz',
fixed: 'right',
width: 120,
render: (text: any, row: any, index: any) => {
return (
<div className="dirBtnBox">
<Button
type="text"
size="small"
disabled={row.fieldType === 1}
onClick={(e) => {
e.stopPropagation();
}}
>
编辑
</Button>
<Divider type="vertical" style={{ margin: '0 2px' }} />
<Button
type="text"
size="small"
disabled={row.fieldType === 1}
>
删除
</Button>
</div>
);
},
},
];
const rightTableColumns = (target: any) => {
return [
{
dataIndex: 'label',
title: '字段名称',
ellipsis: true,
width: 80,
},
{
dataIndex: 'operateOptz',
title: '操作',
width: 120,
render: (text: any, row: any, index: any) => {
return (
<Space>
<Button type="link" size="small" onClick={(e) => handleDel(e, row)}>
删除
</Button>
<Button
type="link"
size="small"
disabled={index === 0 && pagination2.pageNum === 1}
onClick={(e) =>
handleMove(
e,
(pagination2.pageNum - 1) * pagination2.pageSize + index,
1,
)
}
>
上移
</Button>
<Button
type="link"
size="small"
disabled={
(pagination2.pageNum - 1) * pagination2.pageSize +
index +
1 ===
target.length
}
onClick={(e) =>
handleMove(
e,
(pagination2.pageNum - 1) * pagination2.pageSize + index,
2,
)
}
>
下移
</Button>
</Space>
);
},
},
];
};
return (
<TransferTable
dataSource={dataSource}
targetKeys={targetKeys}
allTargets={leftKeyList}
titles={[
<Space>
<Button size="middle">导入</Button>
<DrawerFilter
key="add"
actionRef={formRef}
options={options}
title={id ? '编辑' : '新增'}
okText="确定"
cancelText="取消"
width={320}
filterProps={{
column: 1,
}}
formProps={{
initialValues: {
fieldType: !id && 2,
format: 0,
},
}}
onSubmit={async (values) => {
try {
console.log(values);
} catch (err) {
console.log(err);
}
}}
onClose={() => setId(null)}
>
<Button
onClick={() => {
setId(null);
setType(0);
}}
icon={<Icon type="icon-biaoge-xinzeng" />}
size="middle"
>
新增
</Button>
</DrawerFilter>
</Space>,
]}
selectAllLabels={[
() => (<div className={styles.title}>表单字段选择</div>),
() => <div>已选择 ({targetKeys?.length}项)</div>,
]}
oneWay
showSelectAll={false}
style={{ padding: '8px 36px 24px' }}
onChange={handleChange}
filterOption={
(inputValue: any, item: any) => {
return item.title.indexOf(inputValue) !== -1 || item.tag.indexOf(inputValue) !== -1;
}
}
onChangePage={(pager: any, dir: string) => {
if (dir === 'left') {
setPagination(pager);
} else {
setPagination2(pager);
}
}}
paginationLeft={pagination}
paginationRight={pagination2}
leftColumns={leftTableColumns}
rightColumns={rightTableColumns(targetKeys)}
/>
);
};
export default Create;
.modalForm {
color: #344563;
width: 100%;
.items {
margin-top: 8px;
padding: 24px 16px;
background-color: #f5f6fa;
&-title {
margin-bottom: 12px;
span {
color: #8791a3;
margin-right: 7px;
}
}
}
.head {
font-size: 16px;
font-weight: 500;
color: #344563;
margin: 16px 0;
}
}
import React from 'react';
import { Modal, Icon } from 'ilab-lib';
import { Form, Radio, Select, Space } from 'antd';
import { UserModelState } from '@/models/user';
import { connect } from 'umi';
import styles from './index.less';
interface IProps {
title?: React.ReactNode;
children: React.ReactNode;
user: UserModelState;
onOk: (values: any) => void;
info?: any;
}
const Index = (props: IProps) => {
const [form] = Form.useForm();
const { title = '设置审批人', user, onOk, info, children } = props;
const { userList = [] } = user;
return (
<Modal
okText="确定"
cancelText="取消"
destroyOnClose
title={title}
callback={() => {
form.resetFields();
if (info) {
form.setFieldsValue(info);
}
}}
render={
<Form
name="addForm"
className={styles.modalForm}
layout="vertical"
initialValues={{
approvalType: 'ALL',
}}
form={form}
autoComplete="off"
>
<div>指定成员</div>
<div className={styles.items}>
<div className={styles['items-title']}>
<Icon type="icon-chakanxiangqing" style={{ fontSize: 16 }} />
单个节点审批限制5人
</div>
<Form.Item
label=""
name="approvers"
rules={[{ required: true, message: '请选择' },
{
validator: (_: any, value: any) => (value && value?.length > 5
? Promise.reject(new Error('单个节点审批限制5人'))
: Promise.resolve()),
},
]}
>
<Select
placeholder="请选择"
mode="multiple"
allowClear
style={{ width: '100%' }}
options={userList.map((item: { value: any; key: any }) => ({ label: item.value, value: item.key }))}
/>
</Form.Item>
</div>
<Form.Item
label={<div className={styles.head}>审批人为多个时:</div>}
name="approvalType"
>
<Radio.Group>
<Space direction="vertical" >
<Radio value="ALL">会签(须所有审批人同意)</Radio>
<Radio value="ANY">或签(一名审批人同意或拒绝即可)</Radio>
</Space>
</Radio.Group>
</Form.Item>
</Form>
}
onOk={async () => {
await form.validateFields();
const values = form.getFieldsValue();
onOk && onOk(values);
}}
>
{children}
</Modal>
);
};
export default connect(
({
user,
}: {
user: UserModelState;
}) => ({
user,
}),
)(Index);
import React, { useState } from 'react';
import { Form, Select, Switch } from 'antd';
import ModalAdd from './ModalAdd';
import { Icon } from 'ilab-lib';
import { approvalType } from '@/constants';
import styles from '../index.less';
const ProcessDesign: React.FC = () => {
const [form] = Form.useForm();
const [list, setList] = useState<any[]>([]);
const handleEdit = (index: number, values: any) => {
list[index] = values;
setList([...list]);
};
const handleAdd = (values: any) => {
list.push(values);
setList([...list]);
};
const handleRemove = (e: React.MouseEvent<HTMLDivElement, MouseEvent>, index: number) => {
e.stopPropagation();
list.splice(index, 1);
setList([...list]);
};
return (
<div className={styles.process}>
<Form
name="processForm"
form={form}
style={{ width: '100%' }}
autoComplete="off"
>
<Form.Item
label="审批配置"
name="approvalStatus"
valuePropName="checked"
>
<Switch checkedChildren="开" unCheckedChildren="关" />
</Form.Item>
<div className={styles.title}>默认审批流程</div>
<div className={styles.items}>
<div className={styles['items-title']}>审批人 <span>最多3级审批</span></div>
<div className={styles['items-list']}>
{
list.map((item: { approvalType: string; approvers: number[] }, index: number) => (
<div className={styles['items-li']} key={index}>
<div className={styles['items-label']}>{approvalType.get(item.approvalType)}</div>
<div className={styles['items-remove']} onClick={(e) => handleRemove(e, index)}><Icon type="icon-shanchurenyuan" /></div>
<ModalAdd info={item} onOk={(values: any) => handleEdit(index, values)}>
<div className={styles['items-main']}>
<img src={require('@/assets/group.png')} className="headerIcon" />
<span>指定成员{item.approvers?.length || 0}</span>
</div>
</ModalAdd>
</div>
))
}
{
list.length < 3 && (
<ModalAdd onOk={handleAdd}><div className={styles['items-add']} /></ModalAdd>
)
}
</div>
</div>
<Form.Item
label="消息提醒"
name="status"
valuePropName="checked"
>
<Switch checkedChildren="开" unCheckedChildren="关" />
</Form.Item>
<Form.Item
label="通知方式"
name="notifyType"
>
<>
通过
<Select
placeholder="请选择"
style={{ width: 240, margin: '0 8px' }}
mode="multiple"
allowClear
>
<Select.Option value={'1'}>短信</Select.Option>
<Select.Option value={'2'}>邮箱</Select.Option>
</Select>
通知处理人
</>
</Form.Item>
</Form>
</div>
);
};
export default ProcessDesign;
import React from 'react';
interface ITableContext {
targetKeys: string[]; // 表单设计右侧已选择ids
selectRows: any[]; // 表单设计右侧已选择rows
config: any; // 流程设计
leftKeyList: any[]; // 表单设计左侧ids
setTargetKeys: React.Dispatch<React.SetStateAction<string[]>>;
setSelectRows: React.Dispatch<React.SetStateAction<any[]>>;
setConfig: React.Dispatch<React.SetStateAction<any>>;
setLeftKeyList: React.Dispatch<React.SetStateAction<any[]>>;
}
const TabContext = React.createContext<ITableContext>({
targetKeys: [],
selectRows: [],
config: {},
leftKeyList: [],
setTargetKeys: () => {},
setSelectRows: () => {},
setConfig: () => {},
setLeftKeyList: () => {},
});
export default TabContext;
.content {
margin-top: 16px;
background-color: #fff;
:global {
.ant-transfer-list-header {
height: auto;
}
.ant-table {
margin: 16px 24px;
}
}
}
.item {
position: relative;
margin-bottom: 16px;
}
.delete-button {
position: absolute;
right: 6px;
top: 10px;
}
.title {
color: #344563;
font-weight: 500;
}
.process {
padding: 8px 32px;
color: #344563;
.title {
font-size: 16px;
line-height: 18px;
font-weight: 500;
}
.items {
padding: 24px;
margin: 12px 0;
font-size: 16px;
background-color: #f5f6fa;
border-radius: 4px;
&-title {
font-size: 14px;
font-weight: 500;
margin-bottom: 16px;
span {
font-weight: 300;
margin-left: 12px;
}
}
&-list {
display: flex;
color: #5e6c84;
font-size: 12px;
align-items: center;
> * + * {
margin-left: 32px;
}
}
&-li {
position: relative;
display: flex;
box-shadow: 0px 2px 2px 0px rgba(27, 65, 131, 0.2);
transition: all 0.2s;
cursor: pointer;
&:hover {
box-shadow: 0px 6px 6px 0px rgba(27, 65, 131, 0.2);
}
&::after {
position: absolute;
top: 40%;
right: -25px;
content: '';
width: 15px;
height: 20px;
background: url('../../../../assets/arrows.png') no-repeat;
background-size: 100% 100%;
}
}
&-label {
background-color: #e9eef6;
width: 20px;
font-size: 14px;
font-weight: 500;
text-align: center;
line-height: 24px;
padding: 10px 0;
vertical-align: middle;
}
&-main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 12px 10px 8px;
background-color: #fff;
img {
width: 36px;
height: 36px;
margin-bottom: 4px;
}
span {
line-height: 1;
}
}
&-remove {
position: absolute;
right: 0;
top: 0;
font-size: 12px;
border: 4px solid transparent;
}
&-add {
width: 48px;
height: 48px;
background: url('../../../../assets/add.png') no-repeat;
background-size: 100% 100%;
cursor: pointer;
}
}
}
import React, { useState, useEffect } from 'react';
import { Form, Input, Select, Tabs, Switch, Space, Button } from 'antd';
import { FooterBar } from 'ilab-lib';
import TableContext from './context';
import { useDispatch } from 'dva';
import { useSafeState } from 'ahooks';
import FormDesign from './FormDesign';
import { fetchUserList } from '@/services/user';
import ProcessDesign from './ProcessDesign';
import styles from './index.less';
const { TabPane } = Tabs;
const Create: React.FC = () => {
const [form] = Form.useForm();
const dispatch = useDispatch();
const [list, setList] = useState([]);
const [targetKeys, setTargetKeys] = useSafeState<string[]>([]);
const [selectRows, setSelectRows] = useSafeState<any[]>([]);
const [leftKeyList, setLeftKeyList] = useSafeState<any[]>([]);
const [config, setConfig] = useSafeState<any>({});
useEffect(() => {
fetchUserList().then((res) => {
dispatch({
type: 'user/setUserList',
payload: res.data,
});
});
}, []);
return (
<TableContext.Provider
value={{
targetKeys,
selectRows,
config,
leftKeyList,
setTargetKeys,
setSelectRows,
setConfig,
setLeftKeyList,
}}
>
<Form
name="processForm"
form={form}
style={{ width: '100%' }}
layout="inline"
autoComplete="off"
>
<Form.Item
label="流程名称"
name="name"
rules={[{ required: true, message: '请输入流程名称' }]}
>
<Input style={{ width: '100%' }} placeholder="请输入" />
</Form.Item>
<Form.Item
label="关联项目"
name="project"
rules={[{ required: true, message: '请选择关联项目' }]}
>
<Select style={{ width: '240px' }} placeholder="请选择">
{list?.map((item: any) => (
<Select.Option value={item.value} key={item.value}>
{item.label}
</Select.Option>
))}
</Select>
</Form.Item>
<div style={{ flex: 1, justifyContent: 'end', display: 'flex' }}>
<Form.Item
label=""
name="checkbox"
valuePropName="checked"
>
<Switch checkedChildren="启用" unCheckedChildren="停用" />
</Form.Item>
</div>
</Form>
<div className={styles.content}>
<Tabs defaultActiveKey="1" tabBarExtraContent={{ left: <div style={{ width: 36 }} /> }}>
<TabPane tab="表单设计" key="1">
<FormDesign />
</TabPane>
<TabPane tab="流程设计" key="2">
<ProcessDesign />
</TabPane>
</Tabs>
</div>
<FooterBar>
<Space>
<Button>取消</Button>
<Button type="primary">保存</Button>
</Space>
</FooterBar>
</TableContext.Provider>
);
};
export default Create;
/*
* @Author: 麦麦
* @Description: 交货流程设置
* @Date: 2022-06-21 16:46:50
* @Last Modified time: 2022-06-21 16:46:50
*/
import React, { useRef, useState } from 'react';
import { ProTable, Icon, Confirm } from 'ilab-lib';
import { Button, Divider } from 'antd';
import { history } from 'umi';
import { ProColumn, ActionType } from 'ilab-lib/dist/ProTable/interface';
import { ActionType as formActionType } from 'ilab-lib/dist/DrawerFilter';
import { fetchRoomList, getRoomInfo } from '@/services/manager';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
const Index: React.FC = () => {
const formRef = useRef<formActionType>();
const tableRef = useRef<ActionType>();
const [selectedRows, setSelectedRows] = useState<any[]>([]);
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(fetchRoomList, formatPaginationParams({ ...params, id: 1989 }));
const handleOpen = (id: number) => () => {
getRoomInfo(id).then((res) => {
formRef?.current?.open(res.data);
});
};
const columns: Array<ProColumn<any>> = [
{
title: '交货模板名称',
dataIndex: 'name',
search: true,
},
{
title: '创建人',
dataIndex: 'creator',
search: true,
},
{
title: '启用状态',
dataIndex: 'status',
valueType: 'select',
valueEnum: new Map([
[1, { status: 'success', text: '启用' }],
[2, { status: 'default', text: '关闭' }],
]),
},
{
title: '创建时间',
dataIndex: 'create_at',
search: true,
},
{
title: '关联项目',
dataIndex: 'project',
search: true,
},
{
title: '操作',
dataIndex: 'id',
render: (id) => {
return (
<>
<a onClick={handleOpen(id)}>编辑</a>
<Divider type="vertical" />
<a href="#">删除</a>
</>
);
},
},
];
const handleDel = () => {
Confirm({
title: '删除房间',
content: (
<div className="model-info-box">
<img src={require('@/assets/delete_bg_info.png')} alt="pic" />
<h2>确定要删除该房间吗?</h2>
</div>
),
onOk: async () => {
console.log(selectedRows);
},
});
};
const toolbar = {
showFilter: true,
actions: [
<div className="labelTitle" key="title">交货流程设置</div>,
],
slot: [
<Button key="add" onClick={() => history.push('/managerCenter/consignmentProcess/add')}>
<Icon type="icon-biaoge-xinzeng" />
新增
</Button>,
<Button key="del" disabled={!selectedRows.length} onClick={handleDel}>
<Icon type="icon-biaoge-shanchu" />
删除
</Button>,
],
};
return (
<ProTable
className="iTable"
request={getList}
actionRef={tableRef}
columns={columns}
rowSelection={{
type: 'checkbox',
selectedRowKeys: selectedRows,
onChange: (keys: any[]) => {
setSelectedRows(keys);
},
}}
onRow={(value: any) => {
return {
onClick: () => {
if (selectedRows.indexOf(value.id) === -1) {
// 判断选中逻辑
setSelectedRows([...selectedRows, value.id]);
} else {
setSelectedRows(selectedRows.filter((o: any) => o !== value.id));
}
},
};
}}
rowKey="id"
toolbar={toolbar}
scroll={{ x: 'max-content' }}
drawerProps={{
filterProps: {
column: 1,
},
width: 328,
}}
/>
);
};
export default Index;
.title {
background: rgb(121, 242, 157);
}
/*
* @Author: 麦麦
* @Description: 设备管理
* @Date: 2022-06-21 14:51:42
* @Last Modified time: 2022-06-21 14:51:42
*/
import React, { useRef, useState } from 'react';
import { ProTable, Icon, DrawerFilter, SingleImageUpload, Confirm } from 'ilab-lib';
import { Button, Divider } from 'antd';
import { ProColumn, ActionType } from 'ilab-lib/dist/ProTable/interface';
import { ActionType as formActionType } from 'ilab-lib/dist/DrawerFilter';
import { fetchRoomList, getRoomInfo } from '@/services/manager';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
import roomDefault from '@/assets/room_default.png';
const Index: React.FC = () => {
const formRef = useRef<formActionType>();
const tableRef = useRef<ActionType>();
const [selectedRows, setSelectedRows] = useState<any[]>([]);
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(fetchRoomList, formatPaginationParams({ ...params, id: 1989 }));
const options: any = [
{
label: '设备编号',
name: 'number',
rules: [{ required: true, message: '请输入设备编号' }],
},
{
label: '设备名称',
name: 'name',
rules: [{ required: true, message: '请输入设备名称' }],
},
{
label: '所属房间',
name: 'roomName',
rules: [{ required: true, message: '请输入房间名称' }],
},
{
label: '设备图片',
name: 'imageUrl',
valueType: 'custom',
customRender: (
<SingleImageUpload
url="/api/labbase/v1/secure/oss/uploadfile"
params={{ type: 'room' }}
/>
),
},
{
label: '层',
name: 'buildName',
rules: [{ required: true, message: '请输入' }],
},
{
label: '列',
name: 'floorName',
rules: [{ required: true, message: '请输入' }],
},
];
const handleSubmit = (values: any) => {
console.log(values);
};
const handleOpen = (id: number) => () => {
getRoomInfo(id).then((res) => {
formRef?.current?.open(res.data);
});
};
const columns: Array<ProColumn<any>> = [
{
title: '设备编号',
dataIndex: 'number',
search: true,
},
{
title: '设备名称',
dataIndex: 'name',
search: true,
},
{
title: '房间名称',
dataIndex: 'roomName',
search: true,
},
{
title: '货位数量',
dataIndex: 'num',
},
{
title: '操作',
dataIndex: 'operation',
render: (id) => {
return (
<>
<a onClick={handleOpen(id)}>编辑</a>
<Divider type="vertical" />
<a href="#">删除</a>
</>
);
},
},
];
const handleDel = () => {
Confirm({
title: '删除房间',
content: (
<div className="model-info-box">
<img src={require('@/assets/delete_bg_info.png')} alt="pic" />
<h2>确定要删除该设备吗?</h2>
</div>
),
onOk: async () => {
console.log(selectedRows);
},
});
};
const toolbar = {
showFilter: true,
actions: [
<div className="labelTitle" key="title">设备管理</div>,
],
slot: [
<DrawerFilter
width={382}
key="add"
actionRef={formRef}
filterProps={{
column: 1,
}}
title="编辑"
okText="确定"
cancelText="取消"
options={options}
formProps={{
initialValues: {
imageUrl: [roomDefault],
},
}}
onSubmit={handleSubmit}
>
<Button key="add">
<Icon type="icon-biaoge-xinzeng" />
新增
</Button>
</DrawerFilter>,
<Button key="del" disabled={!selectedRows.length} onClick={handleDel}>
<Icon type="icon-biaoge-shanchu" />
删除
</Button>,
],
};
return (
<ProTable
className="iTable"
request={getList}
actionRef={tableRef}
columns={columns}
rowSelection={{
type: 'checkbox',
selectedRowKeys: selectedRows,
onChange: (keys: any[]) => {
setSelectedRows(keys);
},
}}
onRow={(value: any) => {
return {
onClick: () => {
if (selectedRows.indexOf(value.id) === -1) {
// 判断选中逻辑
setSelectedRows([...selectedRows, value.id]);
} else {
setSelectedRows(selectedRows.filter((o: any) => o !== value.id));
}
},
};
}}
rowKey="id"
toolbar={toolbar}
scroll={{ x: 'max-content' }}
drawerProps={{
filterProps: {
column: 1,
},
width: 328,
}}
/>
);
};
export default Index;
/*
* @Author: 麦麦
* @Description: 房间管理
* @Date: 2022-06-20 14:40:16
* @Last Modified time: 2022-06-20 14:40:16
*/
import React, { useRef, useState } from 'react';
import { ProTable, Icon, DrawerFilter, Confirm } from 'ilab-lib';
import { Button, Divider } from 'antd';
import { ProColumn, ActionType } from 'ilab-lib/dist/ProTable/interface';
import { ActionType as formActionType } from 'ilab-lib/dist/DrawerFilter';
import { fetchRoomList, getRoomInfo } from '@/services/manager';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
import roomDefault from '@/assets/room_default.png';
import ModalPrint from '@/components/ModalPrint';
const Index: React.FC = () => {
const formRef = useRef<formActionType>();
const tableRef = useRef<ActionType>();
const [selectedRows, setSelectedRows] = useState<any[]>([]);
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(fetchRoomList, formatPaginationParams({ ...params, id: 1989 }));
const options: any = [
{
label: '货位编号',
name: 'number',
rules: [{ required: true, message: '请输入货位编号' }],
},
{
label: '房间',
name: 'roomName',
rules: [{ required: true, message: '请选择' }],
},
{
label: '存储设备',
name: 'siteName',
rules: [{ required: true, message: '请选择' }],
},
{
label: '层',
name: 'storey',
valueType: 'number',
rules: [{ required: true, message: '请输入' }],
},
{
label: '格',
name: 'division',
valueType: 'number',
rules: [{ required: true, message: '请输入' }],
},
];
const handleSubmit = (values: any) => {
console.log(values);
};
const handleOpen = (id: number) => () => {
getRoomInfo(id).then((res) => {
formRef?.current?.open(res.data);
});
};
const columns: Array<ProColumn<any>> = [
{
title: '货位编号',
dataIndex: 'number',
search: true,
},
{
title: '房间名称',
dataIndex: 'name',
search: true,
},
{
title: '存储设备名称',
dataIndex: 'name',
search: true,
},
{
title: '层',
dataIndex: 'storey',
},
{
title: '格',
dataIndex: 'division',
},
{
title: '操作',
dataIndex: 'id',
render: (id) => {
return (
<>
<a onClick={handleOpen(id)}>编辑</a>
<Divider type="vertical" />
<a href="#">删除</a>
</>
);
},
},
];
const handleDel = () => {
Confirm({
title: '删除房间',
content: (
<div className="model-info-box">
<img src={require('@/assets/delete_bg_info.png')} alt="pic" />
<h2>确定要删除该货位吗?</h2>
</div>
),
onOk: async () => {
console.log(selectedRows);
},
});
};
const toolbar = {
showFilter: true,
actions: [
<div className="labelTitle" key="title">货位管理</div>,
],
slot: [
<DrawerFilter
width={382}
key="add"
actionRef={formRef}
filterProps={{
column: 1,
}}
destroyOnClose
title="编辑"
okText="确定"
cancelText="取消"
options={options}
formProps={{
initialValues: {
imageUrl: [roomDefault],
},
}}
onSubmit={handleSubmit}
>
<Button>
<Icon type="icon-biaoge-xinzeng" />
新增
</Button>
</DrawerFilter>,
<ModalPrint
type={1}
printData={[{
sId: '121313143',
cId: '123124512r',
batchNo: '2143251423223',
barCode: '124231423',
}]}
>
<Button key="print">
<Icon type="icon-biaoge-xinzeng" />
打印货位标签
</Button>
</ModalPrint>,
<Button key="del" disabled={!selectedRows.length} onClick={handleDel}>
<Icon type="icon-biaoge-shanchu" />
删除
</Button>,
],
};
return (
<ProTable
className="iTable"
request={getList}
actionRef={tableRef}
columns={columns}
rowSelection={{
type: 'checkbox',
selectedRowKeys: selectedRows,
onChange: (keys: any[]) => {
setSelectedRows(keys);
},
}}
onRow={(value: any) => {
return {
onClick: () => {
if (selectedRows.indexOf(value.id) === -1) {
// 判断选中逻辑
setSelectedRows([...selectedRows, value.id]);
} else {
setSelectedRows(selectedRows.filter((o: any) => o !== value.id));
}
},
};
}}
rowKey="id"
toolbar={toolbar}
scroll={{ x: 'max-content' }}
drawerProps={{
filterProps: {
column: 1,
},
width: 328,
}}
/>
);
};
export default Index;
import React from 'react';
import { Modal } from 'ilab-lib';
import { Form, Input } from 'antd';
import classnames from 'classnames';
import './index.less';
interface IProps {
title: React.ReactNode;
children: React.ReactNode;
style?: React.CSSProperties;
className?: string;
}
const ModalAddPrint: React.FC<IProps> = (props: IProps) => {
const [form] = Form.useForm();
const { style, className, title, children } = props;
return (
<Modal
cancelText="取消"
okText="确定"
title={title}
style={style}
className={classnames('addPrintModal', className)}
render={
<Form
name="addPrintForm"
form={form}
style={{ width: 360 }}
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
autoComplete="off"
>
<Form.Item
label="打印机名称"
name="printName"
rules={[{ required: true, message: '请输入打印机名称' }]}
>
<Input placeholder="请输入" />
</Form.Item>
<Form.Item
label="打印机模式"
name="name"
rules={[{ required: true, message: '请输入打印机名称' }]}
>
<Input placeholder="请输入" />
</Form.Item>
</Form>
}
>{children}
</Modal>
);
};
export default ModalAddPrint;
/*
* @Author: 麦麦
* @Description: 硬件管理
* @Date: 2022-06-21 17:50:41
* @Last Modified time: 2022-06-21 17:50:41
*/
import React, { useRef, useState } from 'react';
import { ProTable, Icon, Confirm } from 'ilab-lib';
import { Button, Divider } from 'antd';
import { ProColumn, ActionType } from 'ilab-lib/dist/ProTable/interface';
import { ActionType as formActionType } from 'ilab-lib/dist/DrawerFilter';
import { fetchRoomList, getRoomInfo } from '@/services/manager';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
const Index: React.FC = () => {
const formRef = useRef<formActionType>();
const tableRef = useRef<ActionType>();
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(fetchRoomList, formatPaginationParams({ ...params, id: 1989 }));
const handleOpen = (id: number) => () => {
getRoomInfo(id).then((res) => {
formRef?.current?.open(res.data);
});
};
const columns: Array<ProColumn<any>> = [
{
title: '打印机名称',
dataIndex: 'name',
},
{
title: '创建人',
dataIndex: '打印机类型',
},
{
title: '打印模式',
dataIndex: 'status',
valueType: 'select',
valueEnum: new Map([
[1, 'AO打印(标准模式)'],
[2, 'AO打印(端桥模式)'],
]),
},
{
title: '操作',
dataIndex: 'id',
render: (id) => {
return (
<>
<a onClick={handleOpen(id)}>编辑</a>
<Divider type="vertical" />
<a href="#">删除</a>
</>
);
},
},
];
const toolbar = {
actions: [
<div className="labelTitle" key="title">打印机</div>,
],
slot: [
<Button key="add">
<Icon type="icon-biaoge-xinzeng" />
添加打印机
</Button>,
],
};
return (
<ProTable
className="iTable"
request={getList}
actionRef={tableRef}
columns={columns}
rowKey="id"
toolbar={toolbar}
scroll={{ x: 'max-content' }}
/>
);
};
export default Index;
.title {
background: rgb(121, 242, 157);
}
import React from 'react';
import ModalPrint from '@/components/ModalPrint';
const Index = () => {
return (
<div>
<ModalPrint
type={1}
printData={[{
sId: '121313143',
cId: '123124512r',
batchNo: '2143251423223',
barCode: '124231423',
}]}
>打印
</ModalPrint>
</div>
);
};
export default Index;
import React from 'react';
import { Modal } from 'ilab-lib';
import classnames from 'classnames';
import './index.less';
interface IProps {
title: React.ReactNode;
url: string;
children: React.ReactNode;
style?: React.CSSProperties;
className?: string;
}
const ModalPreview: React.FC<IProps> = (props: IProps) => {
const { style, className, title, url, children } = props;
return (
<Modal
cancelText="关闭"
title={title}
style={style}
className={classnames('printModal', className)}
render={
<div className="preview_img">
<img src={url} alt="pic" />
</div>
}
footer={null}
>{children}
</Modal>
);
};
export default ModalPreview;
.preview_img {
text-align: center;
img {
max-width: 100%;
width: 360px;
margin: 20px auto;
border: none;
}
}
/*
* @Author: 麦麦
* @Description: 打印模板
* @Date: 2022-06-21 16:03:15
* @Last Modified time: 2022-06-21 16:03:15
*/
import React, { useRef } from 'react';
import { ProTable } from 'ilab-lib';
import { Button } from 'antd';
import { ProColumn, ActionType } from 'ilab-lib/dist/ProTable/interface';
import { fetchBatchList } from '@/services/dashboard';
import ModalPreview from './ModalPreview';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
const Index: React.FC = () => {
const tableRef = useRef<ActionType>();
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(fetchBatchList, formatPaginationParams({ ...params, id: 1989 }));
const columns: Array<ProColumn<any>> = [
{
title: '打印模板名称',
dataIndex: 'name',
},
{
title: '关联业务',
dataIndex: 'relevance',
},
{
title: '操作',
dataIndex: 'id',
render: (id: number) => (<ModalPreview title="交货标签模版" url="https://prod-saas-5.oss-cn-shanghai.aliyuncs.com/room/0d7c8bd36edf4c728268231ec3d78e79/goodsTemplate.png"><Button size="small" type="link">预览</Button></ModalPreview>),
},
];
const toolbar = {
actions: [
<div className="labelTitle" key="title">打印模板</div>,
],
};
return (
<ProTable
className="iTable"
request={getList}
actionRef={tableRef}
columns={columns}
rowKey="id"
toolbar={toolbar}
scroll={{ x: 'max-content' }}
/>
);
};
export default Index;
/*
* @Author: 麦麦
* @Description: 房间管理
* @Date: 2022-06-20 14:40:16
* @Last Modified time: 2022-06-20 14:40:16
*/
import React, { useRef, useState } from 'react';
import { ProTable, Icon, DrawerFilter, Confirm } from 'ilab-lib';
import { Button, Divider } from 'antd';
import { ProColumn, ActionType } from 'ilab-lib/dist/ProTable/interface';
import { ActionType as formActionType } from 'ilab-lib/dist/DrawerFilter';
import { fetchRoomList, getRoomInfo } from '@/services/manager';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
import roomDefault from '@/assets/room_default.png';
const Index: React.FC = () => {
const formRef = useRef<formActionType>();
const tableRef = useRef<ActionType>();
const [selectedRows, setSelectedRows] = useState<any[]>([]);
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(fetchRoomList, formatPaginationParams({ ...params, id: 1989 }));
const options: any = [
{
label: '房间编号',
name: 'roomNo',
rules: [{ required: true, message: '请输入房间编号' }],
},
{
label: '房间名称',
name: 'roomName',
rules: [{ required: true, message: '请输入房间名称' }],
},
{
label: '园区',
name: 'workplace',
rules: [{ required: true, message: '请输入' }],
},
{
label: '幢',
name: 'buildingNo',
rules: [{ required: true, message: '请输入' }],
},
{
label: '楼',
name: 'floorNo',
rules: [{ required: true, message: '请输入' }],
},
];
const handleSubmit = (values: any) => {
console.log(values);
};
const handleOpen = (id: number) => () => {
getRoomInfo(id).then((res) => {
formRef?.current?.open(res.data);
});
};
const columns: Array<ProColumn<any>> = [
{
title: '房间编号',
dataIndex: 'roomNo',
search: true,
},
{
title: '房间名称',
dataIndex: 'roomName',
search: true,
},
{
title: '操作',
dataIndex: 'id',
render: (id) => {
return (
<>
<a onClick={handleOpen(id)}>编辑</a>
<Divider type="vertical" />
<a href="#">删除</a>
</>
);
},
},
];
const handleDel = () => {
Confirm({
title: '删除房间',
content: (
<div className="model-info-box">
<img src={require('@/assets/delete_bg_info.png')} alt="pic" />
<h2>确定要删除该房间吗?</h2>
</div>
),
onOk: async () => {
console.log(selectedRows);
},
});
};
const toolbar = {
showFilter: true,
actions: [
<div className="labelTitle" key="title">房间管理</div>,
],
slot: [
<DrawerFilter
width={382}
key="add"
actionRef={formRef}
filterProps={{
column: 1,
}}
destroyOnClose
title="编辑"
okText="确定"
cancelText="取消"
options={options}
formProps={{
initialValues: {
imageUrl: [roomDefault],
},
}}
onSubmit={handleSubmit}
>
<Button key="add">
<Icon type="icon-biaoge-xinzeng" />
新增
</Button>
</DrawerFilter>,
<Button key="del" disabled={!selectedRows.length} onClick={handleDel}>
<Icon type="icon-biaoge-shanchu" />
删除
</Button>,
],
};
return (
<ProTable
className="iTable"
request={getList}
actionRef={tableRef}
columns={columns}
rowSelection={{
type: 'checkbox',
selectedRowKeys: selectedRows,
onChange: (keys: any[]) => {
setSelectedRows(keys);
},
}}
onRow={(value: any) => {
return {
onClick: () => {
if (selectedRows.indexOf(value.id) === -1) {
// 判断选中逻辑
setSelectedRows([...selectedRows, value.id]);
} else {
setSelectedRows(selectedRows.filter((o: any) => o !== value.id));
}
},
};
}}
rowKey="id"
toolbar={toolbar}
scroll={{ x: 'max-content' }}
drawerProps={{
filterProps: {
column: 1,
},
width: 328,
}}
/>
);
};
export default Index;
.title {
background: rgb(121, 242, 157);
}
import React from 'react'
const Index = () => {
return (
<div>dashboard</div>
)
}
export default Index
.title {
background: rgb(121, 242, 157);
}
/*
* @Author: 麦麦
* @Description: 账号管理
* @Date: 2022-06-24 10:40:41
* @Last Modified time: 2022-06-24 10:40:41
*/
import React, { useRef, useState } from 'react';
import { ProTable, DrawerFilter } from 'ilab-lib';
import { Input } from 'antd';
import { ProColumn, ActionType } from 'ilab-lib/dist/ProTable/interface';
import { ActionType as formActionType } from 'ilab-lib/dist/DrawerFilter';
import { fetchBatchList } from '@/services/dashboard';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
import { IField } from 'ilab-lib/dist/TableFilter';
const Index: React.FC = () => {
const tableRef = useRef<ActionType>();
const formRef = useRef<formActionType>();
const [text, setText] = useState('');
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(fetchBatchList, formatPaginationParams({ ...params, id: 1989, text }));
const columns: Array<ProColumn<any>> = [
{
title: '工号',
dataIndex: 'number',
},
{
title: '姓名',
dataIndex: 'number',
},
{
title: '组长',
dataIndex: 'name',
},
{
title: '主任',
dataIndex: 'spec',
},
{
title: '账号',
dataIndex: 'categoryName',
},
{
title: '手机号',
dataIndex: 'brand',
},
{
title: '角色',
dataIndex: 'brand',
},
{
title: '操作',
dataIndex: 'operation',
render: (_: string, row: any) => <a onClick={() => formRef?.current?.open(row)}>编辑</a>,
},
];
const options: IField[] = [
{
label: '姓名',
name: '角色',
rules: [{ required: true, message: '请输入' }],
},
{
label: '角色',
name: 'roleId',
valueType: 'select',
valueEnum: new Map([]),
rules: [{ required: true, message: '请选择' }],
},
];
const toolbar = {
actions: [
<div className="labelTitle" key="title">账号管理</div>,
],
slot: [
<Input.Search
key="search"
placeholder="请输入姓名搜索"
value={text}
allowClear
onChange={(e) => setText(e.target.value)}
onSearch={(val) => {
setText(val);
setTimeout(() => {
tableRef.current?.reload();
}, 0);
}}
/>,
],
};
return (
<>
<ProTable
className="iTable"
request={getList}
actionRef={tableRef}
columns={columns}
rowKey="id"
toolbar={toolbar}
scroll={{ x: 'max-content' }}
drawerProps={{
filterProps: {
column: 1,
},
width: 328,
}}
/>
<DrawerFilter
filterProps={{
column: 1,
}}
width={328}
title="编辑"
okText="确定"
cancelText="取消"
actionRef={formRef}
options={options}
onSubmit={() => {}}
><div />
</DrawerFilter>
</>
);
};
export default Index;
.title {
background: rgb(121, 242, 157);
}
/*
* @Author: 麦麦
* @Description: 项目管理
* @Date: 2022-06-24 11:36:36
* @Last Modified time: 2022-06-24 11:36:36
*/
import React, { useRef, useState } from 'react';
import { ProTable, Icon, DrawerFilter, Confirm } from 'ilab-lib';
import { Button, Divider, Input } from 'antd';
import { ProColumn, ActionType } from 'ilab-lib/dist/ProTable/interface';
import { ActionType as formActionType } from 'ilab-lib/dist/DrawerFilter';
import { fetchRoomList, getRoomInfo } from '@/services/manager';
import { formatPaginationParams, formatTableRequest } from '@/utils/mindex';
import roomDefault from '@/assets/room_default.png';
const Index: React.FC = () => {
const formRef = useRef<formActionType>();
const tableRef = useRef<ActionType>();
const [selectedRows, setSelectedRows] = useState<any[]>([]);
const [text, setText] = useState('');
const getList = (params: { [key: string]: any } = {}) =>
formatTableRequest(fetchRoomList, formatPaginationParams({ ...params, id: 1989 }));
const options: any = [
{
label: '项目名称',
name: 'projectName',
rules: [{ required: true, message: '请输入项目名称' }],
},
{
label: '项目负责人',
name: 'owner',
rules: [{ required: true, message: '请选择项目负责人' }],
},
{
label: '项目组成员',
name: 'teams',
rules: [{ required: true, message: '请选择项目组成员' }],
},
{
label: '状态',
name: 'status',
valueType: 'select',
valueEnum: new Map([
[1, '开'],
[2, '关'],
]),
},
];
const handleSubmit = (values: any) => {
console.log(values);
};
const handleOpen = (id: number) => () => {
getRoomInfo(id).then((res) => {
formRef?.current?.open(res.data);
});
};
const columns: Array<ProColumn<any>> = [
{
title: '项目名称',
dataIndex: 'projectName',
},
{
title: '项目负责人',
dataIndex: 'roomName',
},
{
title: '项目组成员',
dataIndex: 'roomName',
},
{
title: '状态',
dataIndex: 'status',
valueType: 'select',
valueEnum: new Map([
[1, '开启'],
[2, '关闭'],
]),
},
{
title: '操作',
dataIndex: 'id',
render: (id) => {
return (
<>
<a onClick={handleOpen(id)}>编辑</a>
<Divider type="vertical" />
<a href="#">删除</a>
</>
);
},
},
];
const handleDel = () => {
Confirm({
title: '删除房间',
content: (
<div className="model-info-box">
<img src={require('@/assets/delete_bg_info.png')} alt="pic" />
<h2>确定要删除该房间吗?</h2>
</div>
),
onOk: async () => {
console.log(selectedRows);
},
});
};
const toolbar = {
actions: [
<div className="labelTitle" key="title">项目管理</div>,
],
slot: [
<DrawerFilter
width={382}
key="add"
actionRef={formRef}
filterProps={{
column: 1,
}}
destroyOnClose
title="编辑"
okText="确定"
cancelText="取消"
options={options}
formProps={{
initialValues: {
imageUrl: [roomDefault],
},
}}
onSubmit={handleSubmit}
>
<Button key="add">
<Icon type="icon-biaoge-xinzeng" />
新增
</Button>
</DrawerFilter>,
<Button key="del" disabled={!selectedRows.length} onClick={handleDel}>
<Icon type="icon-biaoge-shanchu" />
删除
</Button>,
<Input.Search
key="search"
placeholder="请输入姓名搜索"
value={text}
allowClear
onChange={(e) => setText(e.target.value)}
onSearch={(val) => {
setText(val);
setTimeout(() => {
tableRef.current?.reload();
}, 0);
}}
/>,
],
};
return (
<ProTable
className="iTable"
request={getList}
actionRef={tableRef}
columns={columns}
rowSelection={{
type: 'checkbox',
selectedRowKeys: selectedRows,
onChange: (keys: any[]) => {
setSelectedRows(keys);
},
}}
onRow={(value: any) => {
return {
onClick: () => {
if (selectedRows.indexOf(value.id) === -1) {
// 判断选中逻辑
setSelectedRows([...selectedRows, value.id]);
} else {
setSelectedRows(selectedRows.filter((o: any) => o !== value.id));
}
},
};
}}
rowKey="id"
toolbar={toolbar}
scroll={{ x: 'max-content' }}
drawerProps={{
filterProps: {
column: 1,
},
width: 328,
}}
/>
);
};
export default Index;
import React from 'react';
const index = () => {
return (
<div>index</div>
);
};
export default index;
import React from 'react';
import { Form, Input, message } from 'antd';
import { Modal } from 'ilab-lib';
import { addOrEditName } from '@/services/system';
interface ModalProps {
info?: any;
title?: string;
onOk?: () => void;
children?: React.ReactNode;
}
const Index: React.FC<ModalProps> = (props: ModalProps) => {
const { info = {}, title = '新增角色', onOk, children } = props;
const [form] = Form.useForm();
return (
<Modal
title={title}
callback={() => {
form.setFieldsValue(info);
}}
render={
<Form
name="addForm"
form={form}
style={{ width: 360 }}
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
autoComplete="off"
>
<Form.Item
label="角色名称"
name="name"
rules={[{ required: true, message: '请输入角色名称' }]}
>
<Input style={{ width: '100%' }} placeholder="请输入角色名称" />
</Form.Item>
</Form>
}
onOk={async () => {
await form.validateFields();
const values = form.getFieldsValue();
addOrEditName({ ...info, ...values }).then(() => {
message.success('操作成功');
onOk && onOk();
});
}}
okText="确定"
cancelText="取消"
>
{children}
</Modal>
);
};
export default Index;
.role {
display: flex;
width: 100%;
height: 100%;
color: #344563;
&-list {
width: 358px;
height: calc(100vh - 142px);
overflow-y: auto;
.operate {
display: flex;
align-items: center;
margin-bottom: 24px;
> * + * {
margin-left: 12px;
}
}
.add {
cursor: pointer;
}
.list {
display: flex;
flex-direction: column;
&-item {
display: flex;
align-items: center;
font-size: 14px;
padding: 8px;
cursor: pointer;
transition: all 0.2s;
.more {
display: none;
}
&:hover {
.more {
display: block;
}
}
&__icon {
font-size: 16px;
margin-right: 8px;
color: #b8bcc0;
}
&__name {
flex: 1;
}
&__active {
color: #00a4f5;
background: #eef9ff;
.more {
display: block;
}
.list-item__icon {
color: #00a4f5;
}
}
}
}
}
&-content {
flex: 1;
margin-left: 24px;
}
}
:global {
.powerTabale {
border: 1px solid #dee3ea;
border-radius: 6px 6px 0 0;
margin-top: 24px;
}
.powerTableTitle {
// border: 1px solid #c7cdd7;
border-bottom: none;
background: #e3eaf1;
padding: 16px 0;
border-radius: 6px 6px 0 0;
.powerTableCol {
text-align: center;
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
}
}
.powerTableContent {
border-bottom: 1px solid #e4e6e7;
&:last-child {
border-bottom: none;
}
.powerTableName {
border-right: 1px solid #e4e6e7;
display: flex;
flex-direction: column;
& > div {
// height: 50%;
overflow: hidden;
flex: 1;
display: flex;
padding: 20px 10px;
justify-content: center;
align-items: center;
border-bottom: 1px solid #e4e6e7;
&:last-child {
border-bottom: none;
}
}
}
.powerTableValue {
display: flex;
justify-content: center;
align-items: center;
color: #344563;
// padding: 16px 0;
border-right: 1px solid #e4e6e7;
text-align: center;
border-bottom: none;
&:last-child {
border-right: none;
}
.shanchuIcon {
padding: 10px;
background: #fbfdff;
box-shadow: 0px 2px 6px 0px rgb(0 0 0 / 4%);
border-radius: 6px;
border: 1px solid #e9edf0;
cursor: pointer;
&:hover {
opacity: 0.9;
}
}
}
.powerTableGroup {
color: #344563;
padding: 10px 20px;
border-right: 1px solid #e4e6e7;
}
}
}
import React, { useState, useEffect } from 'react';
import { Card, Input, Row, Col, Spin, Menu, Dropdown, message, Button } from 'antd';
import { Icon, Caption, Confirm } from 'ilab-lib';
import classnames from 'classnames';
import { history } from 'umi';
import NoDataPNG from '@/components/NoData';
import ModalEdit from './ModalEdit';
import { fetchRoleList, fetchRoleTreeList, deleteRole } from '@/services/system';
import styles from './index.less';
interface itemProps {
changeable: boolean;
id: number;
name: string;
}
const Index: React.FC = () => {
const [userType, setUserType] = useState<any[]>([]);
const [current, setCurrent] = useState<itemProps>();
const [info, setInfo] = useState([]);
const [loading, setLoading] = useState(false);
const onSearch = (val: string) => {
console.log(val);
};
useEffect(() => {
gitRoleList(true);
}, []);
useEffect(() => {
try {
if (current) {
setLoading(true);
fetchRoleTreeList(current?.id).then((res) => {
setInfo(res.data);
setLoading(false);
});
}
} catch (err) {
setLoading(false);
}
}, [current]);
const gitRoleList = (toTop?: boolean) => {
fetchRoleList().then((res) => {
const list = res.data || [];
const manager = list.filter((item: { changeable: boolean }) => !item.changeable);
const general = list.filter((item: { changeable: boolean }) => item.changeable);
setUserType([...manager, ...general]);
toTop && setCurrent(general.length > 0 && general[0]);
});
};
const handleRemove = (item: itemProps) => () => {
Confirm({
title: '删除当前角色',
content: (
<div className="model-info-box">
<img src={require('@/assets/delete_bg_info.png')} alt="pic" />
<h2>你确定要删除当前角色【{item.name}】吗?</h2>
</div>
),
onOk: async () => {
deleteRole({ id: item.id }).then((res) => {
if (res?.success) {
message.success('删除成功');
gitRoleList(true);
}
});
},
});
};
return (
<div className={styles.role}>
<div className={styles['role-list']}>
<Card title="角色列表">
<div className={styles.operate}>
<Input.Search placeholder={'搜索'} onSearch={onSearch} />
<ModalEdit onOk={() => gitRoleList(true)}>
<Icon className={styles.add} type="icon-biaoge-xinzeng" />
</ModalEdit>
</div>
<div className={styles.list}>
{userType.map((item: itemProps) => (
<div
key={item.id}
onClick={() => setCurrent(item)}
className={classnames(styles['list-item'], item.id === current?.id && styles['list-item__active'])}
>
<Icon type={item.changeable ? 'icon-jiaose' : 'icon-morenjiaose'} className={styles['list-item__icon']} />
<span className={styles['list-item__name']}>{item.name}</span>
{
item.changeable && (
<Dropdown
overlay={<Menu
items={[
{
label: <ModalEdit info={item} onOk={gitRoleList}><Icon type="icon-zhongmingming" style={{ marginRight: 10 }} />重命名</ModalEdit>,
key: '1',
},
{
label: <div onClick={handleRemove(item)}><Icon type="icon-shanchu" style={{ marginRight: 10 }} />删除</div>,
key: '2',
},
]}
/>}
>
<span className={styles.more}>
<Icon type="icon-gengduo" className="handle_group" />
</span>
</Dropdown>)
}
</div>
))}
</div>
</Card>
</div>
<div className={styles['role-content']} >
<Card title={`角色权限-${current?.name}`}>
<Caption>角色权限总览</Caption>
<Spin spinning={loading}>
{info.length > 0 ? (
<div className="powerTabale">
<Row className="powerTableTitle">
<Col span={!current?.changeable ? 24 : 22}>
<Row>
<Col span={4} className="powerTableCol">
模块名称
</Col>
<Col span={4} className="powerTableCol">
资源名称
</Col>
<Col span={16} className="powerTableCol">
操作权限
</Col>
</Row>
</Col>
{!current?.changeable ? null : (
<Col span={2} className="powerTableCol">
操作
</Col>
)}
</Row>
<Row className="powerTableContent">
<Col span={!current?.changeable ? 24 : 22}>
{
(info[0].details || []).map((item: any, index: number) => {
return (
<Row key={index} className="powerTableContent">
<Col span={4} className="powerTableValue">
{item.moduleName || '-'}
</Col>
<Col span={4} className="powerTableName">
{item.resources.map((list: any) => <div>{list.pageName || '-'}</div>)}
</Col>
<Col span={16} className="powerTableName" >
{item.resources.map((list: any) => <div>{list.operations ? list.operations?.join(',') : '全部操作'}</div>)}
</Col>
</Row>
);
})
}
</Col>
{
current?.changeable && (
<Col span={2} className="powerTableValue">
<a href="">删除</a>
</Col>)
}
</Row>
</div>
) : (
<>
<div style={{ textAlign: 'center', width: '100%' }}>
<NoDataPNG img="noAccess.png" name="暂未开启任何角色权限" />
</div>
{current?.id ? (
<div style={{ textAlign: 'center', width: '100%' }}>
<Button
key="filter"
onClick={() => {
history.push(`/permissions/role/add?id=${current?.id}`);
}}
>
<Icon type="icon-biaoge-xinzeng" />
新增权限
</Button>
</div>
) : null}
</>
)}
</Spin>
</Card>
</div>
</div>
);
};
export default Index;
import request from '@/utils/request';
// 列表
export const fetchBatchList = async (params: any = {}) => {
return request({
url: `/api/inventory/secure/stock/adjust/detail/${params.id}`,
method: 'GET',
params,
});
};
// 序列号明细列表
export const getDetailList = async (params: {[x: string]: any}) => {
return request({
url: '/api/inventory/secure/stock/adjust/goods',
method: 'GET',
params,
});
};
import request from '@/utils/request';
// 房间列表
export const fetchRoomList = async (params: any = {}) => {
return request({
url: '/api/inventory/secure/manger/room',
method: 'GET',
params,
});
};
// 登录--账号密码
export const doLogin = async (params: any = {}) => {
const payload = { ...params };
return request({
url: '/api/labbase/v1/auth/login/account',
method: 'POST',
data: payload,
});
};
// 公司列表
export const getRoomInfo = async (id: number) => {
return request({
url: `/api/inventory/secure/manger/room/${id}`,
method: 'GET',
});
};
import request from '@/utils/request';
// 房间列表
export const fetchRoomList = async (params: any = {}) => {
return request({
url: '/compound/secure/deliveryflow/page',
method: 'GET',
params,
});
};
// 登录--账号密码
export const doLogin = async (params: any = {}) => {
const payload = { ...params };
return request({
url: '/api/labbase/v1/auth/login/account',
method: 'POST',
data: payload,
});
};
// 公司列表
export const getRoomInfo = async (id: number) => {
return request({
url: `/api/inventory/secure/manger/room/${id}`,
method: 'GET',
});
};
import request from '@/utils/request';
// 公共打印机
export const getPrinterLists = async (params: any) => {
return request({
url: '/api/inventory/secure/printer/regexp/url',
method: 'GET',
params,
});
};
export const getPrinterModules = async () => {
return request({
url: '/api/inventory/secure/printLabelConfig/page/list',
method: 'GET',
params: { pageSize: 999, pageNum: 1 },
});
};
// 获取打印机列表
export const getPrintLists = async () => {
return request({
url: '/api/inventory/secure/printer/all/list',
method: 'GET',
});
};
import request from '@/utils/request';
// 权限列表
export const fetchRoleList = async () => {
return request({
url: '/api/base/secure/product/role/list',
method: 'GET',
});
};
// 权限总览
export const fetchRoleTreeList = async (id: number) => {
return request({
url: `/api/labbase/v1/secure/permission/${id}`,
method: 'GET',
});
};
// 新增/编辑权限名
export const addOrEditName = async (params: {[x: string]: any}) => {
return request({
url: '/api/labbase/v1/secure/permission/role/save',
method: 'POST',
data: params,
});
};
// 删除权限
export const deleteRole = async (params: {[x: string]: any}) => {
return request({
url: '/api/labbase/v1/secure/permission/role/delete',
method: 'DELETE',
params,
});
};
// // 权限树状楼层结构
// export const fetchRoleTreeList = async (params: any = {}) => {
// return request({
// url: '/api/labbase/v1/secure/location/tree/noroom',
// method: 'GET',
// params,
// });
// };
import request from '@/utils/request';
// 公司列表
export const fetchCompanyList = async (params: any = {}) => {
return request({
url: '/api/labbase/v1/company/all',
method: 'GET',
params,
});
};
// 用户列表
export const fetchUserList = async (params: any = {}) => {
return request({
url: '/api/labbase/v1/secure/account/user/all',
method: 'GET',
params,
});
};
// 登录--账号密码
export const doLogin = async (params: any = {}) => {
return request({
url: '/api/labbase/v1/auth/login/account',
method: 'POST',
data: params,
});
};
// 退出登录
export const doLogOut = async () => {
return request({
url: 'api/labbase/v1/auth/secure/logout',
method: 'POST',
});
};
// 公司列表
export const getUserInfo = async (params: any = {}) => {
return request({
url: '/api/base/secure/user/org/one/ext',
method: 'GET',
params,
});
};
const CryptoJS = require('crypto-js');
const Encrypt = {
// btoa method // this._btoa 为重写的方法,也可以用系统自带的 bota方法 ,只需去掉"this._"
_btoa(s) {
const base64hash = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
if (/([^\u0000-\u00ff])/.test(s)) {
throw new Error('INVALID_CHARACTER_ERR');
}
let i = 0;
let prev;
let ascii;
let mod;
const result = [];
while (i < s.length) {
ascii = s.charCodeAt(i);
mod = i % 3;
switch (mod) {
// 第一个6位只需要让8位二进制右移两位
case 0:
result.push(base64hash.charAt(ascii >> 2));
break;
// 第二个6位 = 第一个8位的后两位 + 第二个8位的前4位
case 1:
result.push(base64hash.charAt((prev & 3) << 4 | (ascii >> 4)));
break;
// 第三个6位 = 第二个8位的后4位 + 第三个8位的前2位
// 第4个6位 = 第三个8位的后6位
case 2:
result.push(base64hash.charAt((prev & 0x0f) << 2 | (ascii >> 6)));
result.push(base64hash.charAt(ascii & 0x3f));
break;
}
prev = ascii;
i++;
}
// 循环结束后看mod, 为0 证明需补3个6位,第一个为最后一个8位的最后两位后面补4个0。另外两个6位对应的是异常的“=”;
// mod为1,证明还需补两个6位,一个是最后一个8位的后4位补两个0,另一个对应异常的“=”
if (mod === 0) {
result.push(base64hash.charAt((prev & 3) << 4));
result.push('==');
} else if (mod === 1) {
result.push(base64hash.charAt((prev & 0x0f) << 2));
result.push('=');
}
return result.join('');
},
// content="123456" key="MTg3NTg1MTY3NzI="
encrypt(content, key) {
const sKey = CryptoJS.enc.Utf8.parse(key);
const sContent = CryptoJS.enc.Utf8.parse(content);
const encrypted = CryptoJS.AES.encrypt(sContent, sKey, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
return encrypted.toString();
},
// this._btoa 为重写的方法,也可以用系统自带的 bota方法 ,只需去掉"this._"
getSecret(str) {
let str_s = str;
let encodeData = this._btoa(encodeURIComponent(str));
while (encodeData.length < 16) {
str_s += '_';
encodeData = this._btoa(encodeURIComponent(str_s));
}
return encodeData.slice(encodeData.length - 16, encodeData.length);
},
encryptContent(text) {
const AuthTokenKey = '79c2965a4c113d89'; // AES密钥
const base64 = Encrypt.getSecret(AuthTokenKey);
const ciphertext = Encrypt.encrypt(text, base64).toString();
return ciphertext;
},
};
module.exports = Encrypt;
/**
* 深度遍历寻找目标叶子节点
* @param tree {Array} 嵌套数组
* @param childrenKey {String} 子节点key
* @param key {String} 目标key值
* @param value {Any} 目标value值
* @return target | null
*/
export function deepQuery(
tree: any[],
childrenKey: string,
key: string,
value: any,
): any | null {
let retNode = null;
function deepSearch(
ITree: any[],
IChildrenKey: string,
IKey: string,
IValue: any,
) {
for (let i = 0; i < ITree.length; i++) {
if (IValue === ITree[i][IKey]) {
retNode = ITree[i];
break;
} else if (ITree[i][IChildrenKey] && ITree[i][IChildrenKey].length > 0) {
deepSearch(ITree[i][IChildrenKey], IChildrenKey, IKey, IValue);
}
}
}
deepSearch(tree, childrenKey, key, value);
return retNode;
}
/**
* 将请求格式化成 pro-table 使用的 request 格式
* @param {Function} method 调用请求
* @param {Object} params 调用请求
* @param {Function} method 重构列表数据
* @return {Promise}
*/
interface RequestData {
success: boolean;
data?: any[];
total: number;
}
export function formatTableRequest(
method: any,
params: {[x: string]: any},
query?: {[x: string]: any} | undefined,
formatList?: (list: any[]) => any[],
): Promise<RequestData> {
if (!query) query = params;
return new Promise((resolve, reject) => {
method(query, params)
.then((res: any) => {
const resList = res.data?.records || res.data || [];
const newList = formatList ? formatList(resList) : resList;
resolve({
success: res.success,
data: newList,
total: res.data?.total || 0,
});
})
.catch((err: Error) => {
reject(err);
});
});
}
// 格式化请求分数数据
export function formatPaginationParams(
params: {
current?: number;
pageSize?: number;
[key: string]: any;
} = { current: 1, pageSize: 10 },
) {
const { current, ...rest } = params;
return {
...rest,
pageNum: current,
};
}
// 获取附件名字
export const getFileName = (url: string) => {
let cutIndex = url.lastIndexOf('/');
if (cutIndex === -1) {
cutIndex = url.lastIndexOf('\\');
}
return url.substr(cutIndex + 1);
};
export const download = (url: string) => {
const link = document.createElement('a');
link.style.display = 'none';
link.href= url
link.download = getFileName(url); // 下载的文件名
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
/**
* 删除对象中所有的空值
* @param obj
*/
export const removeObjectNull = (obj: { [key: string]: any }) => {
const newObj: { [key: string]: any } = {};
Object.keys(obj || {}).forEach((key) => {
if (obj[key]) {
newObj[key] = obj[key];
}
});
return newObj;
};
/**
* 把小数保留bitNum位小数,不够补0
* @param number
* @param bitNum
* @returns {string|number}
*/
export function changeDecimal(number: number | string, bitNum: number) {
const f_x = parseFloat(`${number}`);
if (isNaN(f_x)) {
return 0;
}
let s_x = number.toString();
let pos_decimal = s_x.indexOf('.');
if (pos_decimal < 0) {
pos_decimal = s_x.length;
if (bitNum !== 0) {
s_x += '.';
} else {
return s_x;
}
}
if (s_x.length <= pos_decimal + bitNum) {
while (s_x.length <= pos_decimal + bitNum) {
s_x += '0';
}
} else {
s_x = s_x.substring(0, pos_decimal + bitNum + 1);
}
return s_x;
}
import { getPrinterModules } from '@/services/print';
import { message } from 'antd';
import dayjs from 'dayjs';
interface printInfoType {
printDirect: string;
printMode: number;
[key: string]: any;
}
// @ts-ignore
const { LODOP } = window;
console.log(LODOP);
// 1in = 2.54cm = 25.4mm = 72pt = 96px
// --------------------------------------print_text 思源黑体 CN Bold
export const directCode = {
1: (item: any, info?: any) => {
const content = [
() => LODOP.SET_PRINTER_INDEX(info?.printName),
() => LODOP.SET_PRINT_PAGESIZE('1mm', '106mm', '13mm', ''),
() => LODOP.SET_PRINT_STYLE('FontSize', 5),
() => LODOP.SET_PRINT_STYLE('Bold', 1),
() => LODOP.ADD_PRINT_TEXTA('batchNo', '2mm', '2mm', '45mm', '10mm', `批次号:${item.batchNo || '-'}
`),
() => LODOP.ADD_PRINT_TEXTA('sId', '4mm', '2mm', '45mm', '10mm', `SID:${item.sId || '-'}`),
() => LODOP.ADD_PRINT_TEXTA('cId', '6mm', '2mm', '45mm', '10mm', `CID:${item.cId || '-'}`),
() => LODOP.ADD_PRINT_BARCODE('barCode', '9mm', '2mm', '33mm', '10mm', '128Auto', item.barCode),
() => LODOP.SET_PRINT_STYLEA(0, 'ShowBarText', 0),
];
content.forEach((it, ind) => {
// const last = content.length - 1 === ind;
it();
// if (!last) {
// ind === 0
// ? LODOP.SET_PRINT_TEXT_STYLE(0, '思源黑体 CN Bold', 9, 1)
// : LODOP.SET_PRINT_TEXT_STYLE(0, '思源黑体 CN Bold', 6.5, 1);
// LODOP.SET_PRINT_STYLEA(0, 'TextNeatRow', true);
// }
});
},
};
const printOperate = (moduleName: string, item: any, module: any) => {
module[moduleName]();
};
// text打印
export const printOrder = async (moduleName: any, printDatas: any, printInfo?: printInfoType) => {
const printTemplate = await getPrinterModules();
console.log('printTemplate', printTemplate);
// if (!printTemplate?.length) {
// message.warning('请先添加打印模板');
// return;
// }
const interfaceModuleName = {};
(printTemplate?.data?.records || [])?.forEach((item: any) => {
interfaceModuleName[item.enLabelName] = item.printText;
});
LODOP.PRINT_INIT('start');
// printMode 1: A0 2: 端桥 | printerType: 1:普通 2:高频 3:超高频
// if (printInfo?.printMode === 2) {
// LODOP.SET_BRIDGE_INDEX(printInfo?.printDirect);
// } else {
// LODOP.SET_PRINTER_INDEX(printInfo?.printDirect);
// }
console.log(printInfo?.printName);
LODOP.SET_PRINT_PAGESIZE(0, '50mm', '30mm', 'location');
printDatas?.map((item: any) => {
LODOP.NEWPAGE();
// 使用接口数据 ===== start ======
// printOperate(
// moduleName,
// {
// ...item,
// ...printInfo,
// validityFormat: item?.validityPeriod
// ? dayjs(item.validityPeriod).format('YYYY/MM/DD')
// : '-',
// configTime: item?.configTime ? dayjs(item.configTime).format('YYYY/MM/DD') : '-',
// validTime: item?.validTime ? dayjs(item.validTime).format('YYYY/MM/DD') : '-',
// },
// interfaceModuleName,
// );
// 使用接口数据 ===== end ======
// 本地打印调试 ===== start ======
directCode[moduleName](item, printInfo);
// 本地打印调试 ===== end ======
LODOP.SET_PRINT_STYLEA(0, 'QRCodeVersion', 2);
LODOP.SET_PRINT_STYLEA(0, 'QRCodeErrorLevel', 'L');
});
LODOP.SET_PRINT_MODE('CATCH_PRINT_STATUS', true);
if (LODOP.CVERSION) {
LODOP.On_Return = async function (_: string, Value: string) {
if (Value === '(AO)Printer not found !!') {
message.error('打印失败, AO打印机没找到');
}
};
LODOP.PRINT();
} else console.log(LODOP.PRINTA());
};
import axios from 'axios';
import Cookies from 'js-cookie';
import { history } from 'umi';
import { message } from 'antd';
import { BaseResponse } from '@/interface';
import { DEFAULT_TIP_MESSAGE } from '@/constants';
/**
* 错误处理
* @param data {Object} 请求返回的信息
*/
export function handleError(data: BaseResponse): void {
const msg = data.message || DEFAULT_TIP_MESSAGE;
message.error(msg);
}
const request = axios.create({
baseURL: process.env.BASE_API,
timeout: 5000,
headers: { 'Content-Type': 'application/json;charset=utf-8' },
});
// request interceptor
request.interceptors.request.use(
(config: any) => {
const token = Cookies.get('token');
if (token) {
config.headers.token = token;
}
// 防止 GET 请求缓存GET
if (config.method === 'get') {
const t = new Date().getTime();
config.params = config.params ? { ...config.params, t } : { t };
}
return config;
},
(error) => {
if (error.status === '504') {
message.error('网关超时,请重试!');
} else {
message.error(`网络异常[-${error.status}]`);
console.log(error); // for debug
}
Promise.reject(error);
},
);
// response interceptor
request.interceptors.response.use(
(response) => {
const res: BaseResponse = response.data;
if (typeof res.data === 'object') {
if (!res?.success) {
handleError(res);
return Promise.reject(res);
}
}
return res;
},
(error) => {
message.destroy();
if (error.response?.status === 401) {
window.location.href = '/login';
return message.error(error.message);
} else if (error.response?.status === 500) {
return message.error('服务器繁忙,请稍后再试');
} else {
handleError(error);
return Promise.reject(error);
}
},
);
export default request;
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"jsx": "react-jsx",
"esModuleInterop": true,
"sourceMap": true,
"baseUrl": "./",
"strict": true,
"paths": {
"@/*": ["src/*"],
"@@/*": ["src/.umi/*"]
},
"allowSyntheticDefaultImports": true
},
"include": [
"mock/**/*",
"src/**/*",
"config/**/*",
".umirc.ts",
"typings.d.ts"
],
"exclude": [
"node_modules",
"lib",
"es",
"dist",
"typings",
"**/__test__",
"test",
"docs",
"tests"
]
}
declare module '*.css';
declare module '*.less';
declare module '*.png';
declare module '*.svg' {
export function ReactComponent(props: React.SVGProps<SVGSVGElement>): React.ReactElement
const url: string
export default url
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论