feat:初始化
This commit is contained in:
parent
dbbfe346b3
commit
67fc8f005e
3
.eslintrc.js
Normal file
3
.eslintrc.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: require.resolve('@umijs/max/eslint'),
|
||||
};
|
||||
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
/node_modules
|
||||
/.env.local
|
||||
/.umirc.local.ts
|
||||
/config/config.local.ts
|
||||
/src/.umi
|
||||
/src/.umi-production
|
||||
/src/.umi-test
|
||||
/.umi
|
||||
/.umi-production
|
||||
/.umi-test
|
||||
/dist
|
||||
/.mfsu
|
||||
.swc
|
||||
17
.lintstagedrc
Normal file
17
.lintstagedrc
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"*.{md,json}": [
|
||||
"prettier --cache --write"
|
||||
],
|
||||
"*.{js,jsx}": [
|
||||
"max lint --fix --eslint-only",
|
||||
"prettier --cache --write"
|
||||
],
|
||||
"*.{css,less}": [
|
||||
"max lint --fix --stylelint-only",
|
||||
"prettier --cache --write"
|
||||
],
|
||||
"*.ts?(x)": [
|
||||
"max lint --fix --eslint-only",
|
||||
"prettier --cache --parser=typescript --write"
|
||||
]
|
||||
}
|
||||
3
.prettierignore
Normal file
3
.prettierignore
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
.umi
|
||||
.umi-production
|
||||
8
.prettierrc
Normal file
8
.prettierrc
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"printWidth": 80,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"proseWrap": "never",
|
||||
"overrides": [{ "files": ".prettierrc", "options": { "parser": "json" } }],
|
||||
"plugins": ["prettier-plugin-organize-imports", "prettier-plugin-packagejson"]
|
||||
}
|
||||
3
.stylelintrc.js
Normal file
3
.stylelintrc.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: require.resolve('@umijs/max/stylelint'),
|
||||
};
|
||||
23
.umirc.ts
Normal file
23
.umirc.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { defineConfig } from '@umijs/max';
|
||||
|
||||
export default defineConfig({
|
||||
antd: {},
|
||||
access: {},
|
||||
model: {},
|
||||
initialState: {},
|
||||
request: {},
|
||||
layout: {},
|
||||
npmClient: 'npm',
|
||||
define: {
|
||||
'process.env.TOKEN_NAME': process.env.TOKEN_NAME,
|
||||
'process.env.GUARD_NAME': process.env.GUARD_NAME,
|
||||
},
|
||||
proxy: {
|
||||
'/api/': {
|
||||
target: 'http://0.0.0.0:8000',
|
||||
// target: 'https://loanos-test.nchl.net/',
|
||||
changeOrigin: true,
|
||||
pathRewrite: { '^': '' },
|
||||
},
|
||||
},
|
||||
});
|
||||
3
Dockerfile
Normal file
3
Dockerfile
Normal file
@ -0,0 +1,3 @@
|
||||
FROM nexus.zzwb.cc:18444/nginx:1.21-alpine
|
||||
COPY dist /usr/share/nginx/html
|
||||
COPY docker/nginx.conf /etc/nginx/nginx.conf
|
||||
56
docker/nginx.conf
Executable file
56
docker/nginx.conf
Executable file
@ -0,0 +1,56 @@
|
||||
# Generated by nginxconfig.io
|
||||
# See nginxconfig.txt for the configuration share link
|
||||
|
||||
user nginx;
|
||||
pid /var/run/nginx.pid;
|
||||
worker_processes auto;
|
||||
worker_rlimit_nofile 65535;
|
||||
|
||||
# Load modules
|
||||
include /etc/nginx/modules-enabled/*.conf;
|
||||
|
||||
events {
|
||||
multi_accept on;
|
||||
worker_connections 65535;
|
||||
}
|
||||
|
||||
http {
|
||||
charset utf-8;
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
server_tokens off;
|
||||
log_not_found off;
|
||||
types_hash_max_size 2048;
|
||||
types_hash_bucket_size 64;
|
||||
client_max_body_size 16M;
|
||||
|
||||
# MIME
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# Logging
|
||||
access_log /dev/stdout;
|
||||
error_log /dev/stderr warn;
|
||||
|
||||
# example.com
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
|
||||
# index.html fallback
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# gzip
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
|
||||
}
|
||||
}
|
||||
4
gencode.json
Normal file
4
gencode.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"url": "http://0.0.0.0:8000/api/docs/openapi",
|
||||
"module": "Admin"
|
||||
}
|
||||
19173
package-lock.json
generated
Normal file
19173
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
35
package.json
Normal file
35
package.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"private": true,
|
||||
"author": "helloworld <hello@world.com>",
|
||||
"scripts": {
|
||||
"build": "max build",
|
||||
"dev": "max dev",
|
||||
"format": "prettier --cache --write .",
|
||||
"postinstall": "max setup",
|
||||
"setup": "max setup",
|
||||
"start": "npm run dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.0.1",
|
||||
"@ant-design/pro-components": "^2.4.4",
|
||||
"@antv/s2": "^2.0.0-next.25",
|
||||
"@antv/s2-react": "^2.0.0-next.24",
|
||||
"@umijs/max": "^4.3.10",
|
||||
"antd": "^5.4.0",
|
||||
"axios": "^1.7.2",
|
||||
"dayjs": "^1.11.12",
|
||||
"lodash": "^4.17.21",
|
||||
"react-use": "^17.5.1",
|
||||
"valtio": "^1.13.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.33",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"gencode-ts-cli": "^0.0.1",
|
||||
"lint-staged": "^13.2.0",
|
||||
"prettier": "^2.8.7",
|
||||
"prettier-plugin-organize-imports": "^3.2.2",
|
||||
"prettier-plugin-packagejson": "^2.4.3",
|
||||
"typescript": "^5.0.3"
|
||||
}
|
||||
}
|
||||
14149
pnpm-lock.yaml
generated
Normal file
14149
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
src/access.ts
Normal file
10
src/access.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export default (initialState: API.UserInfo) => {
|
||||
// 在这里按照初始化数据定义项目中的权限,统一管理
|
||||
// 参考文档 https://umijs.org/docs/max/access
|
||||
const canSeeAdmin = !!(
|
||||
initialState && initialState.name !== 'dontHaveAccess'
|
||||
);
|
||||
return {
|
||||
canSeeAdmin,
|
||||
};
|
||||
};
|
||||
21
src/app.tsx
Normal file
21
src/app.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
// 运行时配置
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
LayoutConfig,
|
||||
MyRootContainer,
|
||||
requestConfig,
|
||||
stateActions,
|
||||
} from './common';
|
||||
|
||||
export const request = requestConfig;
|
||||
|
||||
export async function getInitialState(): Promise<any> {
|
||||
return await stateActions.me();
|
||||
}
|
||||
|
||||
export const layout = LayoutConfig;
|
||||
|
||||
export function rootContainer(container: React.ReactNode) {
|
||||
return React.createElement(MyRootContainer, null, container);
|
||||
}
|
||||
0
src/assets/.gitkeep
Normal file
0
src/assets/.gitkeep
Normal file
BIN
src/assets/bitcoin.webp
Normal file
BIN
src/assets/bitcoin.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
13
src/common/components/MyAccess.tsx
Normal file
13
src/common/components/MyAccess.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { Access, useAccess } from '@umijs/max';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export default function MyAccess({
|
||||
children,
|
||||
theKey,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
theKey: string;
|
||||
}) {
|
||||
const access = useAccess();
|
||||
return <Access accessible={access.canAccess(theKey)}>{children}</Access>;
|
||||
}
|
||||
138
src/common/components/MyButtons.tsx
Normal file
138
src/common/components/MyButtons.tsx
Normal file
@ -0,0 +1,138 @@
|
||||
import {
|
||||
DeleteOutlined,
|
||||
EditOutlined,
|
||||
EyeOutlined,
|
||||
PlusOutlined,
|
||||
RollbackOutlined,
|
||||
SaveFilled,
|
||||
} from '@ant-design/icons';
|
||||
import { Button, ButtonProps, Dropdown, Popconfirm } from 'antd';
|
||||
import { MyResponseType } from '..';
|
||||
|
||||
type MyButtonsType = { title?: string } & ButtonProps;
|
||||
|
||||
export const MyButtons = {
|
||||
Create({ title, ...rest }: MyButtonsType): JSX.Element {
|
||||
return (
|
||||
<Button type="primary" icon={<PlusOutlined />} {...rest}>
|
||||
{title}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
Default({
|
||||
onConfirm,
|
||||
isConfirm,
|
||||
title,
|
||||
description = '确定要取消?',
|
||||
...rest
|
||||
}: {
|
||||
onConfirm?: () => void;
|
||||
isConfirm?: boolean;
|
||||
description?: string;
|
||||
} & MyButtonsType): JSX.Element {
|
||||
return isConfirm ? (
|
||||
<Popconfirm
|
||||
title="提示"
|
||||
description={description}
|
||||
okText="是"
|
||||
cancelText="否"
|
||||
onConfirm={onConfirm}
|
||||
>
|
||||
<Button size="small" {...rest}>
|
||||
{title}
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
) : (
|
||||
<Button size="small" {...rest}>
|
||||
{title}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
View({ title, ...rest }: MyButtonsType): JSX.Element {
|
||||
return (
|
||||
<Button type="link" size="small" icon={<EyeOutlined />} {...rest}>
|
||||
{title ?? '查看'}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
Edit({ title = '编辑', ...rest }: MyButtonsType): JSX.Element {
|
||||
return (
|
||||
<Button type="link" size="small" icon={<EditOutlined />} {...rest}>
|
||||
{title}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
Save({ title = '保存', ...rest }: MyButtonsType): JSX.Element {
|
||||
return (
|
||||
<Button type="primary" icon={<SaveFilled />} {...rest}>
|
||||
{title}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
Delete({
|
||||
onConfirm,
|
||||
title = '删除',
|
||||
...rest
|
||||
}: { onConfirm: () => void } & MyButtonsType): JSX.Element {
|
||||
return (
|
||||
<Popconfirm
|
||||
title="删除提示"
|
||||
description="确定要删除,将不可恢复?"
|
||||
okText="是"
|
||||
cancelText="否"
|
||||
onConfirm={onConfirm}
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
{...rest}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
);
|
||||
},
|
||||
Export({
|
||||
api,
|
||||
params,
|
||||
title,
|
||||
...rest
|
||||
}: {
|
||||
api: (data: any) => Promise<MyResponseType>;
|
||||
params?: Record<string, any>;
|
||||
} & MyButtonsType): JSX.Element {
|
||||
return (
|
||||
<Dropdown
|
||||
menu={{
|
||||
onClick: ({ item, key }: any) => {
|
||||
console.log(item, key);
|
||||
api?.({ ...params, ...{ download_type: key } });
|
||||
},
|
||||
items: [
|
||||
// {
|
||||
// key: 'page',
|
||||
// label: '导出当前页',
|
||||
// },
|
||||
{
|
||||
key: 'query',
|
||||
label: '按条件导出',
|
||||
},
|
||||
// {
|
||||
// key: 'all',
|
||||
// label: '导出全部',
|
||||
// },
|
||||
],
|
||||
}}
|
||||
placement="bottomLeft"
|
||||
arrow
|
||||
>
|
||||
<Button key="MyExportButton" icon={<RollbackOutlined />} {...rest}>
|
||||
{title || '导出'}
|
||||
</Button>
|
||||
</Dropdown>
|
||||
);
|
||||
},
|
||||
SoftDelete() {},
|
||||
};
|
||||
37
src/common/components/MyIcons.tsx
Normal file
37
src/common/components/MyIcons.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import {
|
||||
AuditOutlined,
|
||||
BankOutlined,
|
||||
BarChartOutlined,
|
||||
BarcodeOutlined,
|
||||
ClusterOutlined,
|
||||
ControlOutlined,
|
||||
CreditCardOutlined,
|
||||
SettingOutlined,
|
||||
ShopOutlined,
|
||||
UserOutlined,
|
||||
} from '@ant-design/icons';
|
||||
|
||||
export type MyIconsType =
|
||||
| 'BarcodeOutlined'
|
||||
| 'AuditOutlined'
|
||||
| 'ShopOutlined'
|
||||
| 'BarChartOutlined'
|
||||
| 'SettingOutlined'
|
||||
| 'ControlOutlined'
|
||||
| 'ClusterOutlined'
|
||||
| 'BankOutlined'
|
||||
| 'UserOutlined'
|
||||
| 'CreditCardOutlined';
|
||||
|
||||
export const MyIcons = {
|
||||
BarcodeOutlined: <BarcodeOutlined />,
|
||||
AuditOutlined: <AuditOutlined />,
|
||||
ShopOutlined: <ShopOutlined />,
|
||||
BarChartOutlined: <BarChartOutlined />,
|
||||
SettingOutlined: <SettingOutlined />,
|
||||
ControlOutlined: <ControlOutlined />,
|
||||
ClusterOutlined: <ClusterOutlined />,
|
||||
BankOutlined: <BankOutlined />,
|
||||
UserOutlined: <UserOutlined />,
|
||||
CreditCardOutlined: <CreditCardOutlined />,
|
||||
};
|
||||
18
src/common/components/MyStatistics.tsx
Normal file
18
src/common/components/MyStatistics.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { Flex, Statistic } from 'antd';
|
||||
|
||||
export function MyStatistics({ items = {} }: { items?: Record<string, any> }) {
|
||||
return (
|
||||
<Flex style={{ padding: '0px 20px 20px 20px' }}>
|
||||
{Object.keys(items).map((key) => {
|
||||
return (
|
||||
<Statistic
|
||||
key={key}
|
||||
title={key}
|
||||
value={items[key]}
|
||||
style={{ marginRight: 50 }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
15
src/common/components/MyTag.tsx
Normal file
15
src/common/components/MyTag.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { Tag } from 'antd';
|
||||
import { MyProEnumItemProps } from '../typings';
|
||||
|
||||
export default function MyTag({
|
||||
enums,
|
||||
value,
|
||||
}: {
|
||||
enums: MyProEnumItemProps;
|
||||
value: string;
|
||||
}) {
|
||||
const item = enums[value] ?? undefined;
|
||||
if (!item) return <>-</>;
|
||||
console.log('item', value, item);
|
||||
return <Tag color={item.color}>{item.text}</Tag>;
|
||||
}
|
||||
58
src/common/components/formFields/MyColorPicker.tsx
Normal file
58
src/common/components/formFields/MyColorPicker.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
import { ColorPicker } from 'antd';
|
||||
|
||||
export function MyColorPicker(props: any) {
|
||||
return (
|
||||
<ColorPicker
|
||||
allowClear
|
||||
arrow
|
||||
showText
|
||||
format="hex"
|
||||
defaultValue={null}
|
||||
presets={[
|
||||
{
|
||||
label: 'Recommended',
|
||||
colors: [
|
||||
'#000000',
|
||||
'#000000E0',
|
||||
'#000000A6',
|
||||
'#00000073',
|
||||
'#00000040',
|
||||
'#00000026',
|
||||
'#0000001A',
|
||||
'#00000012',
|
||||
'#0000000A',
|
||||
'#00000005',
|
||||
'#F5222D',
|
||||
'#FA8C16',
|
||||
'#FADB14',
|
||||
'#8BBB11',
|
||||
'#52C41A',
|
||||
'#13A8A8',
|
||||
'#1677FF',
|
||||
'#2F54EB',
|
||||
'#722ED1',
|
||||
'#EB2F96',
|
||||
'#F5222D4D',
|
||||
'#FA8C164D',
|
||||
'#FADB144D',
|
||||
'#8BBB114D',
|
||||
'#52C41A4D',
|
||||
'#13A8A84D',
|
||||
'#1677FF4D',
|
||||
'#2F54EB4D',
|
||||
'#722ED14D',
|
||||
'#EB2F964D',
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Recent',
|
||||
colors: [],
|
||||
},
|
||||
]}
|
||||
{...props}
|
||||
onChange={(color) => {
|
||||
props.onChange?.(color.toHexString());
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
21
src/common/components/formFields/MyEnumRadioGroup.tsx
Normal file
21
src/common/components/formFields/MyEnumRadioGroup.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { MyProEnumItemProps } from '@/common/typings';
|
||||
import { Radio } from 'antd';
|
||||
|
||||
export default function MyEnumRadioGroup(props: {
|
||||
enums: MyProEnumItemProps;
|
||||
value: string | number | undefined;
|
||||
onChange: (value: string | number | undefined) => void;
|
||||
}) {
|
||||
const options = Object.entries(props.enums).map(([, value]) => {
|
||||
return { label: value.text, value: value.value };
|
||||
});
|
||||
return (
|
||||
<Radio.Group
|
||||
options={options}
|
||||
onChange={(e) => props?.onChange(e.target.value)}
|
||||
value={props.value || options[0].value}
|
||||
optionType="button"
|
||||
buttonStyle="solid"
|
||||
/>
|
||||
);
|
||||
}
|
||||
19
src/common/components/formFields/MyIconSelect.tsx
Normal file
19
src/common/components/formFields/MyIconSelect.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { Select, SelectProps, Space } from 'antd';
|
||||
import { MyIcons } from '../MyIcons';
|
||||
|
||||
export function MyIconSelect(props: SelectProps) {
|
||||
return (
|
||||
<Select allowClear {...props}>
|
||||
{Object?.entries(MyIcons).map(([key, Icon]) => {
|
||||
return (
|
||||
<Select.Option value={key} label={key} key={key}>
|
||||
<Space>
|
||||
{Icon}
|
||||
{key}
|
||||
</Space>
|
||||
</Select.Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
12
src/common/components/formFields/MyMoneyInput.tsx
Normal file
12
src/common/components/formFields/MyMoneyInput.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { InputNumber } from 'antd';
|
||||
|
||||
export function MyMoneyInput(props: any) {
|
||||
return (
|
||||
<InputNumber
|
||||
addonBefore="¥"
|
||||
precision={2}
|
||||
formatter={(value) => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
5
src/common/components/formFields/MyPercentInput.tsx
Normal file
5
src/common/components/formFields/MyPercentInput.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { InputNumber } from 'antd';
|
||||
|
||||
export function MyPercentInput(props: any) {
|
||||
return <InputNumber addonAfter="%" precision={2} {...props} />;
|
||||
}
|
||||
54
src/common/components/formFields/MyTreeCheckable.tsx
Normal file
54
src/common/components/formFields/MyTreeCheckable.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import { Tree, TreeProps } from 'antd';
|
||||
import { DataNode } from 'antd/es/tree';
|
||||
import { Key, useEffect, useState } from 'react';
|
||||
import { MyProFormFieldProps, MyResponseType } from '../../typings';
|
||||
|
||||
export function MyTreeCheckable(
|
||||
props: {
|
||||
api: (data?: any) => Promise<MyResponseType>;
|
||||
} & TreeProps<DataNode> &
|
||||
MyProFormFieldProps<Key[]>,
|
||||
) {
|
||||
const [treeData, setTreeData] = useState<DataNode[]>([]);
|
||||
|
||||
const processTree = (item: any): DataNode => {
|
||||
return {
|
||||
...item,
|
||||
key: item.id,
|
||||
title: item.id + '_' + item.name,
|
||||
children: item.children?.map(processTree),
|
||||
};
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
props.api?.({ guard_name: process.env.GUARD_NAME }).then((res: any) => {
|
||||
const data = res.data?.map(processTree);
|
||||
setTreeData(data);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onCheck: TreeProps['onCheck'] = (checkedKeys, info) => {
|
||||
console.log('onCheck', checkedKeys, info);
|
||||
const ids: Key[] = [];
|
||||
info.checkedNodes?.forEach((item) => {
|
||||
if (item.children?.length === 0) {
|
||||
ids.push(item.key);
|
||||
}
|
||||
});
|
||||
console.log('ids', ids);
|
||||
props.onChange?.(ids);
|
||||
};
|
||||
|
||||
return (treeData?.length ?? 0) > 0 ? (
|
||||
<Tree
|
||||
checkable
|
||||
defaultExpandAll
|
||||
treeData={treeData}
|
||||
onCheck={onCheck}
|
||||
checkedKeys={props.value ?? []}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
}
|
||||
137
src/common/components/formFields/MyUploadImages.tsx
Normal file
137
src/common/components/formFields/MyUploadImages.tsx
Normal file
@ -0,0 +1,137 @@
|
||||
import { MyProFormFieldProps } from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { PlusOutlined, UploadOutlined } from '@ant-design/icons';
|
||||
import { Button, Modal, Upload, UploadFile, UploadProps } from 'antd';
|
||||
import { RcFile } from 'antd/es/upload';
|
||||
import axios from 'axios';
|
||||
import { useSetState } from 'react-use';
|
||||
|
||||
type MyType = {
|
||||
uploadType?: 'image' | 'video' | 'audio' | 'file';
|
||||
max?: number;
|
||||
} & UploadProps<any> &
|
||||
MyProFormFieldProps<UploadFile[]>;
|
||||
|
||||
const getBase64 = (file: RcFile): Promise<string> =>
|
||||
new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => resolve(reader.result as string);
|
||||
reader.onerror = (error) => reject(error);
|
||||
});
|
||||
|
||||
export function MyUploadImages({
|
||||
value,
|
||||
onChange,
|
||||
uploadType = 'image',
|
||||
max = 1,
|
||||
...rest
|
||||
}: MyType) {
|
||||
const [preview, setPreview] = useSetState<{
|
||||
open: boolean;
|
||||
image: string;
|
||||
title: string;
|
||||
}>({
|
||||
open: false,
|
||||
image: '',
|
||||
title: '',
|
||||
});
|
||||
|
||||
console.log('MyUploadImages');
|
||||
|
||||
const handleCancel = () => setPreview({ open: false });
|
||||
|
||||
const handlePreview = async (file: UploadFile) => {
|
||||
if (!file.url && !file.preview) {
|
||||
file.preview = await getBase64(file.originFileObj as RcFile);
|
||||
}
|
||||
|
||||
setPreview({
|
||||
open: true,
|
||||
image: file.url || file.preview,
|
||||
title: file.name || file.url!.substring(file.url!.lastIndexOf('/') + 1),
|
||||
});
|
||||
};
|
||||
|
||||
const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
|
||||
console.log('newFileList', newFileList);
|
||||
const _newFileList: UploadFile<any>[] = newFileList.map((file) => {
|
||||
if (!file.percent) return file;
|
||||
return {
|
||||
name: file.name,
|
||||
status: file.status,
|
||||
uid: file.uid,
|
||||
url: file.response,
|
||||
size: file.size ?? 0,
|
||||
type: file.type ?? '',
|
||||
};
|
||||
});
|
||||
onChange?.(_newFileList);
|
||||
};
|
||||
|
||||
const uploadButton =
|
||||
uploadType === 'image' ? (
|
||||
<div>
|
||||
<PlusOutlined />
|
||||
<div style={{ marginTop: 8 }}>Upload</div>
|
||||
</div>
|
||||
) : (
|
||||
<Button icon={<UploadOutlined />}>Click to Upload</Button>
|
||||
);
|
||||
|
||||
const customRequest = ({ file, onError, onProgress, onSuccess }: any) => {
|
||||
Apis.Auth.PreUpload({
|
||||
filename: file.name,
|
||||
}).then(async (res) => {
|
||||
axios
|
||||
.put(res.data.url, file, {
|
||||
headers: res.data.headers,
|
||||
onUploadProgress: ({ total, loaded }) => {
|
||||
if (total)
|
||||
onProgress(
|
||||
{ percent: Math.round((loaded / total) * 100).toFixed(2) },
|
||||
file,
|
||||
);
|
||||
},
|
||||
})
|
||||
.then(({ data: response }) => {
|
||||
console.log('response', response);
|
||||
if (response.errorMessage) {
|
||||
onError(response, file);
|
||||
} else {
|
||||
onSuccess(res.data.url.split('?')[0], file);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
abort() {
|
||||
console.log('upload progress is aborted.');
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Upload
|
||||
accept={uploadType === 'image' ? 'image/*' : '*'}
|
||||
fileList={value}
|
||||
listType={uploadType === 'image' ? 'picture-card' : 'text'}
|
||||
onPreview={handlePreview}
|
||||
onChange={handleChange}
|
||||
customRequest={customRequest}
|
||||
{...rest}
|
||||
>
|
||||
{!value?.length || (value && value.length < max) ? uploadButton : null}
|
||||
</Upload>
|
||||
<Modal
|
||||
open={preview.open}
|
||||
title={preview.title}
|
||||
footer={null}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<img alt="preview" style={{ width: '100%' }} src={preview.image} />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
94
src/common/components/layout/AvatarProps.tsx
Normal file
94
src/common/components/layout/AvatarProps.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
import { MyButtons } from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import {
|
||||
LogoutOutlined,
|
||||
MenuFoldOutlined,
|
||||
UnlockOutlined,
|
||||
UserOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { history } from '@umijs/max';
|
||||
import { Avatar, Drawer, Dropdown, MenuProps, Space } from 'antd';
|
||||
import { useState } from 'react';
|
||||
import { stateActions } from '../../libs/valtio/actions';
|
||||
import ChangePassword from './ChangePassword';
|
||||
import MyTasks from './Tasks';
|
||||
|
||||
export default function AvatarProps({ user }: { user: any }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const showDrawer = () => {
|
||||
setOpenDrawer(true);
|
||||
};
|
||||
const onClose = () => {
|
||||
setOpenDrawer(false);
|
||||
};
|
||||
const items: MenuProps['items'] = [
|
||||
{
|
||||
key: 'changePassword',
|
||||
label: (
|
||||
<Space
|
||||
onClick={() => {
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
<UnlockOutlined />
|
||||
修改密码
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'logout',
|
||||
label: (
|
||||
<Space
|
||||
onClick={() => {
|
||||
Apis.Auth.Logout().then(() => {
|
||||
stateActions.setLogout();
|
||||
history.push('/login');
|
||||
});
|
||||
}}
|
||||
>
|
||||
<LogoutOutlined />
|
||||
退出登录
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Space>
|
||||
<MyButtons.Default
|
||||
type="link"
|
||||
icon={<MenuFoldOutlined />}
|
||||
size="middle"
|
||||
onClick={() => {
|
||||
showDrawer();
|
||||
}}
|
||||
title="任务"
|
||||
/>
|
||||
<Dropdown menu={{ items }} placement="bottomRight">
|
||||
<Space>
|
||||
<Avatar
|
||||
style={{ backgroundColor: '#87d068' }}
|
||||
icon={<UserOutlined />}
|
||||
size={28}
|
||||
/>
|
||||
<span>{user?.username}</span>
|
||||
</Space>
|
||||
</Dropdown>
|
||||
</Space>
|
||||
<ChangePassword open={open} setOpen={setOpen} />
|
||||
<Drawer
|
||||
title="任务"
|
||||
placement="right"
|
||||
closable={false}
|
||||
onClose={onClose}
|
||||
open={openDrawer}
|
||||
key="right"
|
||||
width={500}
|
||||
>
|
||||
<MyTasks item={openDrawer} />
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
39
src/common/components/layout/ChangePassword.tsx
Normal file
39
src/common/components/layout/ChangePassword.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { ModalForm, ProFormText } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function ChangePassword({
|
||||
open,
|
||||
setOpen,
|
||||
}: {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<ModalForm<ApiTypes.Auth.ChangePassword>
|
||||
open={open}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
title="修改密码"
|
||||
onFinish={async (values) => {
|
||||
return Apis.Auth.ChangePassword(values)
|
||||
.then(() => {
|
||||
message.success('修改密码成功');
|
||||
setOpen(false);
|
||||
})
|
||||
.catch(() => false);
|
||||
}}
|
||||
modalProps={{
|
||||
onCancel: () => setOpen(false),
|
||||
}}
|
||||
>
|
||||
<ProFormText.Password name="old_password" label="原密码" required />
|
||||
<ProFormText.Password name="new_password" label="新密码" required />
|
||||
<ProFormText.Password
|
||||
name="re_new_password"
|
||||
label="重复新密码"
|
||||
required
|
||||
/>
|
||||
</ModalForm>
|
||||
);
|
||||
}
|
||||
40
src/common/components/layout/MyCommonModal.tsx
Normal file
40
src/common/components/layout/MyCommonModal.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { Button, Modal } from 'antd';
|
||||
import { ReactNode, useState } from 'react';
|
||||
|
||||
export function MyCommonModal(props: {
|
||||
title: string;
|
||||
width: number;
|
||||
button_text: string;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const showModal = () => {
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={showModal} size="small">
|
||||
{props.button_text}
|
||||
</Button>
|
||||
<Modal
|
||||
title={props.title}
|
||||
open={isModalOpen}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
width={props.width || 800}
|
||||
destroyOnClose
|
||||
>
|
||||
{props.children}
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
104
src/common/components/layout/MyImportModal.tsx
Normal file
104
src/common/components/layout/MyImportModal.tsx
Normal file
@ -0,0 +1,104 @@
|
||||
import { MyResponseType } from '@/common';
|
||||
import { ImportOutlined, InboxOutlined } from '@ant-design/icons';
|
||||
import { Button, Flex, Modal, Upload, message } from 'antd';
|
||||
import { useState } from 'react';
|
||||
|
||||
type MyImportModalType = {
|
||||
title?: string;
|
||||
type?: any;
|
||||
params?: Record<string, any>;
|
||||
templateApi?: () => Promise<MyResponseType>;
|
||||
importApi: (data: any) => Promise<MyResponseType>;
|
||||
reload?: () => void;
|
||||
};
|
||||
|
||||
export function MyImportModal(props: MyImportModalType) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [formData, setFormData] = useState<FormData | undefined>(undefined);
|
||||
|
||||
const { title = '批量导入', params = {}, type = 'primary' } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
{type === 'danger' ? (
|
||||
<Button onClick={() => setOpen(true)} type="primary" danger>
|
||||
<ImportOutlined />
|
||||
{title}
|
||||
</Button>
|
||||
) : (
|
||||
<Button onClick={() => setOpen(true)} type="primary">
|
||||
<ImportOutlined />
|
||||
{title}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Modal
|
||||
title={title}
|
||||
open={open}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={() => {
|
||||
if (!formData) return;
|
||||
setLoading(true);
|
||||
props
|
||||
?.importApi(formData)
|
||||
.then(() => {
|
||||
message.success('导入操作成功');
|
||||
setLoading(false);
|
||||
setOpen(false);
|
||||
props.reload?.();
|
||||
return true;
|
||||
})
|
||||
.catch(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}}
|
||||
confirmLoading={loading}
|
||||
destroyOnClose={true}
|
||||
maskClosable={false}
|
||||
footer={(dom) => {
|
||||
return (
|
||||
<Flex style={{ width: '100%' }} justify="space-between">
|
||||
<Button onClick={() => props?.templateApi?.()}>下载模板</Button>
|
||||
<div>{dom}</div>
|
||||
</Flex>
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Upload.Dragger
|
||||
accept=".xls,.xlsx,text/csv,.csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
maxCount={1}
|
||||
beforeUpload={(file) => {
|
||||
const isExcelFile =
|
||||
file.type === 'application/vnd.ms-excel' ||
|
||||
file.type ===
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||
const isCSVFile = file.type === 'text/csv';
|
||||
if (!isExcelFile && !isCSVFile) {
|
||||
message.error(`${file.name} is not an Excel or CSV file`);
|
||||
return Upload.LIST_IGNORE;
|
||||
}
|
||||
return true;
|
||||
}}
|
||||
customRequest={({ file }) => {
|
||||
const formData = new FormData();
|
||||
formData.append('upload_file', file);
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
formData.append(key, value);
|
||||
});
|
||||
setFormData(formData);
|
||||
console.log('formData', formData, file);
|
||||
}}
|
||||
>
|
||||
<p className="ant-upload-drag-icon">
|
||||
<InboxOutlined />
|
||||
</p>
|
||||
<p className="ant-upload-text">单击或拖动文件到此区域进行上传</p>
|
||||
<p className="ant-upload-hint">
|
||||
支持单次上传。严禁上传公司数据或其他被禁止的文件。
|
||||
</p>
|
||||
</Upload.Dragger>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
31
src/common/components/layout/MyLoading.tsx
Normal file
31
src/common/components/layout/MyLoading.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import { Spin } from 'antd';
|
||||
import { useMyState } from '../..';
|
||||
|
||||
export function MyLoading() {
|
||||
const { snap } = useMyState();
|
||||
if (snap.session.loading === 0) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
position: 'fixed',
|
||||
zIndex: 9999,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0)',
|
||||
}}
|
||||
>
|
||||
<Spin size="large">
|
||||
<div style={{ paddingTop: '70px' }} />
|
||||
Loading...
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
0
src/common/components/layout/MyLoginPage.tsx
Normal file
0
src/common/components/layout/MyLoginPage.tsx
Normal file
27
src/common/components/layout/MyPageContainer.tsx
Normal file
27
src/common/components/layout/MyPageContainer.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { PageContainer, PageContainerProps } from '@ant-design/pro-components';
|
||||
import { Space } from 'antd';
|
||||
|
||||
export function MyPageContainer({
|
||||
title,
|
||||
children,
|
||||
...rest
|
||||
}: PageContainerProps) {
|
||||
return (
|
||||
<PageContainer
|
||||
fixedHeader
|
||||
header={{
|
||||
title: title,
|
||||
style: { backgroundColor: '#FFF' },
|
||||
}}
|
||||
token={{
|
||||
paddingBlockPageContainerContent: 0,
|
||||
paddingInlinePageContainerContent: 0,
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||||
{children}
|
||||
</Space>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
21
src/common/components/layout/MyRootContainer.tsx
Normal file
21
src/common/components/layout/MyRootContainer.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { ConfigProvider } from 'antd';
|
||||
import { ReactNode } from 'react';
|
||||
import { MyLoading } from './MyLoading';
|
||||
|
||||
export function MyRootContainer({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
token: {
|
||||
fontSize: 14,
|
||||
borderRadius: 1,
|
||||
// colorPrimary: '#00928a',
|
||||
// colorInfo: '#00928a',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<MyLoading />
|
||||
{children}
|
||||
</ConfigProvider>
|
||||
);
|
||||
}
|
||||
106
src/common/components/layout/Tasks.tsx
Normal file
106
src/common/components/layout/Tasks.tsx
Normal file
@ -0,0 +1,106 @@
|
||||
import { MyButtons, renderTextHelper } from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { SysTasksStatusEnum } from '@/gen/Enums';
|
||||
import { UndoOutlined } from '@ant-design/icons';
|
||||
import { Card, Descriptions, Pagination, Space } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function Tasks(props: any) {
|
||||
const [option, setOption] = useState<any>({});
|
||||
const getList = (page: number) => {
|
||||
Apis.SysTasks.List({ perPage: 10, page: page })
|
||||
.then((res) => {
|
||||
setOption(res);
|
||||
})
|
||||
.catch(() => false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (props?.item) {
|
||||
getList(1);
|
||||
}
|
||||
}, [props?.item]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
{!option?.data?.length ? (
|
||||
<div
|
||||
style={{ color: '#999', textAlign: 'center', padding: '20px 0' }}
|
||||
>
|
||||
暂无任务数据
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{option?.data?.map((res: any) => {
|
||||
return (
|
||||
<Card
|
||||
title={res?.name}
|
||||
key={res?.id}
|
||||
extra={
|
||||
<Space>
|
||||
<MyButtons.Delete
|
||||
size="middle"
|
||||
type="default"
|
||||
onConfirm={() =>
|
||||
Apis.SysTasks.Delete({
|
||||
id: res.id,
|
||||
}).then(() => getList(1))
|
||||
}
|
||||
/>
|
||||
<MyButtons.Default
|
||||
icon={<UndoOutlined />}
|
||||
size="middle"
|
||||
onClick={() => getList(1)}
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Descriptions column={1}>
|
||||
<Descriptions.Item label="开始时间:">
|
||||
{res?.start_at || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="成功数量:">
|
||||
{res?.success_count || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="失败数量:">
|
||||
{res?.fail_count || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="状态:">
|
||||
<renderTextHelper.Tag
|
||||
Enums={SysTasksStatusEnum}
|
||||
value={res?.status}
|
||||
key="status"
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="结束时间:">
|
||||
{res?.end_at || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="错误信息:">
|
||||
{res?.error_message || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="失败的策路:">
|
||||
<Space>
|
||||
{res?.is_error_stop ? '失败后暂停' : '失败后续集执行'}
|
||||
{res?.is_error_rollback ? '失败后全部回滚' : '失败后不回滚'}
|
||||
</Space>
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
<div style={{ display: 'flex', justifyContent: 'end' }}>
|
||||
<Pagination
|
||||
defaultCurrent={option?.meta?.current_page}
|
||||
total={option?.meta?.total}
|
||||
onChange={(page) => {
|
||||
getList(page);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
</>
|
||||
);
|
||||
}
|
||||
11
src/common/components/props/MyDrawerProps.ts
Normal file
11
src/common/components/props/MyDrawerProps.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { DrawerProps } from 'antd';
|
||||
|
||||
export const MyDrawerProps = {
|
||||
props: {
|
||||
footer: null,
|
||||
maskClosable: false,
|
||||
placement: 'bottom',
|
||||
width: '80%',
|
||||
height: 'calc(100% - 50px)',
|
||||
} as DrawerProps,
|
||||
};
|
||||
13
src/common/components/props/MyModalFormProps.ts
Normal file
13
src/common/components/props/MyModalFormProps.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export const MyModalFormProps = {
|
||||
props: {
|
||||
layout: 'vertical',
|
||||
labelAlign: 'left',
|
||||
wrapperCol: { span: 20 },
|
||||
style: { padding: '15px' },
|
||||
layoutType: 'ModalForm',
|
||||
grid: true,
|
||||
modalProps: {
|
||||
maskClosable: false,
|
||||
},
|
||||
} as any,
|
||||
};
|
||||
52
src/common/components/props/MyProTableProps.ts
Normal file
52
src/common/components/props/MyProTableProps.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { ParamsType, ProTableProps } from '@ant-design/pro-components';
|
||||
import { SortOrder } from 'antd/es/table/interface';
|
||||
|
||||
export const MyProTableProps = {
|
||||
props: {
|
||||
scroll: { x: 'max-content', scrollToFirstRowOnChange: true },
|
||||
bordered: true,
|
||||
size: 'small',
|
||||
rowKey: 'id',
|
||||
pagination: {
|
||||
showTotal: (total) => `总共${total}条`,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
},
|
||||
search: {
|
||||
defaultCollapsed: false,
|
||||
},
|
||||
} as ProTableProps<Record<string, any>, ParamsType, 'text'>,
|
||||
|
||||
request: async (
|
||||
params: Record<string, any> & {
|
||||
pageSize?: number;
|
||||
current?: number;
|
||||
keyword?: string;
|
||||
},
|
||||
sort: Record<string, SortOrder>,
|
||||
api: (data?: any) => any,
|
||||
setParams?: (params: any) => void,
|
||||
setRes?: (res: any) => void,
|
||||
defaultParams?: Record<string, any>,
|
||||
) => {
|
||||
const sortKeys = Object.keys(sort);
|
||||
const sorter =
|
||||
sortKeys.length > 0 ? [sortKeys[0], sort[sortKeys[0]]] : undefined;
|
||||
const { current, pageSize, ...rest } = params;
|
||||
const body = {
|
||||
page: current,
|
||||
perPage: pageSize,
|
||||
...rest,
|
||||
sorter: sorter,
|
||||
...defaultParams,
|
||||
};
|
||||
setParams?.(body);
|
||||
const data = await api(body);
|
||||
setRes?.(data);
|
||||
return {
|
||||
data: data.data,
|
||||
success: data.success,
|
||||
total: data.meta?.total,
|
||||
};
|
||||
},
|
||||
};
|
||||
214
src/common/components/schema/MyColumns.tsx
Normal file
214
src/common/components/schema/MyColumns.tsx
Normal file
@ -0,0 +1,214 @@
|
||||
import { MyResponseType, renderTextHelper } from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { ProColumns } from '@ant-design/pro-components';
|
||||
import { Popconfirm, Space, Tag } from 'antd';
|
||||
|
||||
type ReturnType = ProColumns<Record<string, any>, 'text'>;
|
||||
|
||||
export const MyColumns = {
|
||||
ID(props?: ReturnType): ReturnType {
|
||||
return { title: 'ID', dataIndex: 'id', hideInSearch: true, ...props };
|
||||
},
|
||||
DayStatus: (start: string, end: string) => {
|
||||
const now = new Date();
|
||||
const startDate = new Date(start);
|
||||
const endDate = new Date(end);
|
||||
// 判断当前时间与开始时间和结束时间的关系
|
||||
if (now < startDate) {
|
||||
return (
|
||||
<Tag color="green" style={{ cursor: 'pointer' }}>
|
||||
未开始
|
||||
</Tag>
|
||||
);
|
||||
} else if (now > endDate) {
|
||||
return (
|
||||
<Tag color="default" style={{ cursor: 'pointer' }}>
|
||||
已结束
|
||||
</Tag>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Tag color="#f50" style={{ cursor: 'pointer' }}>
|
||||
进行中
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
},
|
||||
Images({ ...rest }: ReturnType): ReturnType {
|
||||
return {
|
||||
hideInSearch: true,
|
||||
renderText: renderTextHelper.Images,
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
SoftDelete({
|
||||
onRestore,
|
||||
onSoftDelete,
|
||||
...rest
|
||||
}: {
|
||||
onRestore: (data: { id: number }) => Promise<MyResponseType>;
|
||||
onSoftDelete: (data: { id: number }) => Promise<MyResponseType>;
|
||||
} & ReturnType): ReturnType {
|
||||
return {
|
||||
title: '启/禁用',
|
||||
render: (_, item, index, action) =>
|
||||
item?.deleted_at ? (
|
||||
<Popconfirm
|
||||
title="启用"
|
||||
description="您确认启用吗?"
|
||||
onConfirm={() => {
|
||||
onRestore?.({ id: item.id }).then(() => action?.reload());
|
||||
}}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<Tag color="gray" style={{ cursor: 'pointer' }}>
|
||||
已禁用
|
||||
</Tag>
|
||||
</Popconfirm>
|
||||
) : (
|
||||
<Popconfirm
|
||||
title="禁用"
|
||||
description="您确认禁用吗?"
|
||||
onConfirm={() => {
|
||||
onSoftDelete?.({ id: item.id }).then(() => action?.reload());
|
||||
}}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<Tag color="green" style={{ cursor: 'pointer' }}>
|
||||
已启用
|
||||
</Tag>
|
||||
</Popconfirm>
|
||||
),
|
||||
search: false,
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
CreatedAt(props?: ReturnType): ReturnType {
|
||||
return {
|
||||
title: '创建时间',
|
||||
dataIndex: 'created_at',
|
||||
hideInSearch: true,
|
||||
valueType: 'dateTime',
|
||||
sorter: true,
|
||||
align: 'right',
|
||||
...props,
|
||||
};
|
||||
},
|
||||
UpdatedAt(): ReturnType {
|
||||
return {
|
||||
title: '最近修改',
|
||||
dataIndex: 'updated_at',
|
||||
hideInSearch: true,
|
||||
valueType: 'dateTime',
|
||||
sorter: true,
|
||||
align: 'right',
|
||||
};
|
||||
},
|
||||
FinishedAt(): ReturnType {
|
||||
return {
|
||||
title: '完成时间',
|
||||
dataIndex: 'finished_at',
|
||||
hideInSearch: true,
|
||||
valueType: 'dateTime',
|
||||
sorter: true,
|
||||
align: 'right',
|
||||
};
|
||||
},
|
||||
Boolean({ label, ...rest }: { label?: string[] } & ReturnType): ReturnType {
|
||||
const option: { value: boolean; label: string; color: string }[] = [
|
||||
{ value: false, label: label?.[0] ?? '否', color: 'gray' },
|
||||
{ value: true, label: label?.[1] ?? '是', color: 'green' },
|
||||
];
|
||||
return {
|
||||
align: 'center',
|
||||
request: async () => option,
|
||||
renderText(text: boolean) {
|
||||
const item = option.find((item) => item.value === Boolean(text));
|
||||
return <Tag color={item?.color}>{item?.label}</Tag>;
|
||||
},
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
YesOrNo({
|
||||
yes = '已',
|
||||
no = '未',
|
||||
...rest
|
||||
}: {
|
||||
yes?: string;
|
||||
no?: string;
|
||||
} & ReturnType): ReturnType {
|
||||
return {
|
||||
align: 'center',
|
||||
renderText(text) {
|
||||
return (
|
||||
<Tag bordered={false} color={text ? 'processing' : 'error'}>
|
||||
{text ? yes : no}
|
||||
</Tag>
|
||||
);
|
||||
},
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
EnumTag({ ...rest }: ReturnType): ReturnType {
|
||||
return {
|
||||
align: 'center',
|
||||
renderText(text: string | number) {
|
||||
const _enum = rest.valueEnum ?? {};
|
||||
if (!_enum) return <>-</>;
|
||||
const item = _enum[text] ?? undefined;
|
||||
if (!item) return <>-</>;
|
||||
return <Tag color={item.color}>{item.text}</Tag>;
|
||||
},
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
Option({ ...rest }: ReturnType): ReturnType {
|
||||
return {
|
||||
title: '操作',
|
||||
valueType: 'option',
|
||||
align: 'right',
|
||||
fixed: 'right',
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
Network(props?: ReturnType): ReturnType {
|
||||
return {
|
||||
title: 'network',
|
||||
...props,
|
||||
render: (_: any, item: any) => {
|
||||
const data = props?.dataIndex === 'network' ? item.network : item;
|
||||
return (
|
||||
<Space>
|
||||
<img width={24} src={data.icon[0].url} />
|
||||
<div>{data.name}</div>
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
valueType: 'select',
|
||||
key: 'networks_id',
|
||||
fieldProps: {
|
||||
fieldNames: { value: 'id', label: 'name' },
|
||||
optionRender: (item: any) => {
|
||||
return (
|
||||
<Space>
|
||||
<img width={24} src={item.data.icon[0].url} />
|
||||
<div>{item.data.name}</div>
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
},
|
||||
request: async () => (await Apis.Select.Networks()).data,
|
||||
};
|
||||
},
|
||||
Token({ ...rest }) {
|
||||
return {
|
||||
title: 'Token',
|
||||
renderText(text: string) {
|
||||
return <Tag color="blue">{text}</Tag>;
|
||||
},
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
};
|
||||
183
src/common/components/schema/MyFormItems.tsx
Normal file
183
src/common/components/schema/MyFormItems.tsx
Normal file
@ -0,0 +1,183 @@
|
||||
import {
|
||||
MyColorPicker,
|
||||
MyIconSelect,
|
||||
MyMoneyInput,
|
||||
MyPercentInput,
|
||||
MyUploadImages,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { ProFormColumnsType } from '@ant-design/pro-components';
|
||||
// import { MyRichText } from '../components/FormFields/MyRichText';
|
||||
|
||||
type ReturnType = ProFormColumnsType<any, 'text'>;
|
||||
type PropsType = { required?: boolean } & ReturnType;
|
||||
|
||||
export const MyFormItems = {
|
||||
Text(props: PropsType): ReturnType {
|
||||
return {
|
||||
formItemProps: {
|
||||
...(props?.required ? rulesHelper.text : {}),
|
||||
},
|
||||
...props,
|
||||
};
|
||||
},
|
||||
Integer(props: { min?: number } & PropsType): ReturnType {
|
||||
return {
|
||||
valueType: 'digit',
|
||||
formItemProps: {
|
||||
...(props?.required ? rulesHelper.number : {}),
|
||||
},
|
||||
fieldProps: {
|
||||
min: props.min ?? 1,
|
||||
},
|
||||
...props,
|
||||
};
|
||||
},
|
||||
Numeric(props: PropsType): ReturnType {
|
||||
return {
|
||||
valueType: 'digit',
|
||||
formItemProps: {
|
||||
...(props?.required ? rulesHelper.number : {}),
|
||||
},
|
||||
fieldProps: {
|
||||
precision: 2,
|
||||
min: 0,
|
||||
},
|
||||
...props,
|
||||
};
|
||||
},
|
||||
// RichText(props: PropsType): ReturnType {
|
||||
// return {
|
||||
// renderFormItem: () => <MyRichText />,
|
||||
// formItemProps: {
|
||||
// ...(props?.required ? rulesHelper.richtext : {}),
|
||||
// },
|
||||
// ...props,
|
||||
// };
|
||||
// },
|
||||
UploadImages({
|
||||
max = 1,
|
||||
help,
|
||||
...rest
|
||||
}: { max?: number; help?: string } & PropsType): ReturnType {
|
||||
return {
|
||||
renderFormItem() {
|
||||
return <MyUploadImages max={max} />;
|
||||
},
|
||||
formItemProps: {
|
||||
help,
|
||||
...(rest.required ?? false ? rulesHelper.upload({ max }) : {}),
|
||||
},
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
EnumRadio(props: PropsType): ReturnType {
|
||||
return {
|
||||
valueType: 'radioButton',
|
||||
proFieldProps: {
|
||||
placeholder: `请选择${props.title}`,
|
||||
},
|
||||
formItemProps: {
|
||||
...(props?.required ? rulesHelper.text : {}),
|
||||
},
|
||||
fieldProps: {
|
||||
buttonStyle: 'solid',
|
||||
},
|
||||
...props,
|
||||
};
|
||||
},
|
||||
EnumCheckbox(props: PropsType): ReturnType {
|
||||
return {
|
||||
valueType: 'checkbox',
|
||||
proFieldProps: {
|
||||
placeholder: `请选择${props.title}`,
|
||||
},
|
||||
formItemProps: {
|
||||
...(props?.required ? rulesHelper.array : {}),
|
||||
},
|
||||
...props,
|
||||
};
|
||||
},
|
||||
EnumSelect(props: PropsType): ReturnType {
|
||||
return {
|
||||
valueType: 'select',
|
||||
proFieldProps: {
|
||||
placeholder: `请选择${props.title}`,
|
||||
},
|
||||
formItemProps: {
|
||||
...(props?.required ? rulesHelper.text : {}),
|
||||
},
|
||||
...props,
|
||||
};
|
||||
},
|
||||
Switch(props: PropsType): ReturnType {
|
||||
return {
|
||||
valueType: 'switch',
|
||||
initialValue: props.initialValue ?? false,
|
||||
...props,
|
||||
};
|
||||
},
|
||||
DatePicker(props: PropsType): ReturnType {
|
||||
return {
|
||||
valueType: 'date',
|
||||
formItemProps: {
|
||||
...(props?.required ? rulesHelper.text : {}),
|
||||
},
|
||||
...props,
|
||||
};
|
||||
},
|
||||
Select(props: PropsType): ReturnType {
|
||||
return {
|
||||
valueType: 'select',
|
||||
proFieldProps: {
|
||||
placeholder: `请选择${props.title}`,
|
||||
},
|
||||
...props,
|
||||
};
|
||||
},
|
||||
// EnumSelect(props: PropsType): ReturnType {
|
||||
// const valueType = props.valueType ?? 'radioButton';
|
||||
// const buttonStyle =
|
||||
// valueType === 'radioButton'
|
||||
// ? {
|
||||
// buttonStyle: 'solid',
|
||||
// }
|
||||
// : {};
|
||||
|
||||
// },
|
||||
IconSelect(props?: PropsType): ReturnType {
|
||||
return {
|
||||
key: 'icon',
|
||||
title: '图标',
|
||||
renderFormItem: () => <MyIconSelect />,
|
||||
...props,
|
||||
};
|
||||
},
|
||||
ColorPicker(props?: PropsType): ReturnType {
|
||||
return {
|
||||
key: 'color',
|
||||
dataIndex: 'color',
|
||||
title: '颜色',
|
||||
renderFormItem: () => <MyColorPicker />,
|
||||
...props,
|
||||
};
|
||||
},
|
||||
Percent(props?: PropsType): ReturnType {
|
||||
return {
|
||||
renderFormItem: () => <MyPercentInput />,
|
||||
formItemProps: {
|
||||
...(props?.required ? rulesHelper.number : {}),
|
||||
},
|
||||
...props,
|
||||
};
|
||||
},
|
||||
Money(props?: PropsType): ReturnType {
|
||||
return {
|
||||
renderFormItem: () => <MyMoneyInput />,
|
||||
formItemProps: {
|
||||
...(props?.required ? rulesHelper.number : {}),
|
||||
},
|
||||
...props,
|
||||
};
|
||||
},
|
||||
};
|
||||
41
src/common/index.tsx
Normal file
41
src/common/index.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
// components
|
||||
export * from './components/formFields/MyColorPicker';
|
||||
export * from './components/formFields/MyIconSelect';
|
||||
export * from './components/formFields/MyMoneyInput';
|
||||
export * from './components/formFields/MyPercentInput';
|
||||
export * from './components/formFields/MyTreeCheckable';
|
||||
export * from './components/formFields/MyUploadImages';
|
||||
|
||||
export * from './components/layout/MyCommonModal';
|
||||
export * from './components/layout/MyImportModal';
|
||||
export * from './components/layout/MyPageContainer';
|
||||
export * from './components/layout/MyRootContainer';
|
||||
|
||||
export * from './components/props/MyDrawerProps';
|
||||
export * from './components/props/MyModalFormProps';
|
||||
export * from './components/props/MyProTableProps';
|
||||
|
||||
export * from './components/schema/MyColumns';
|
||||
export * from './components/schema/MyFormItems';
|
||||
|
||||
export * from './components/MyButtons';
|
||||
export * from './components/MyIcons';
|
||||
export * from './components/MyStatistics';
|
||||
|
||||
// libs
|
||||
export * from './libs/umi/layoutConfig';
|
||||
export * from './libs/umi/requestConfig';
|
||||
|
||||
export * from './libs/valtio/actions';
|
||||
export * from './libs/valtio/state';
|
||||
|
||||
// pages
|
||||
export * from './pages/MyLoginPage1';
|
||||
export * from './pages/NewMyS2Table';
|
||||
|
||||
// utils
|
||||
export * from './utils/renderTextHelper';
|
||||
export * from './utils/rulesHelper';
|
||||
|
||||
// typings
|
||||
export * from './typings.d';
|
||||
5
src/common/libs/umi/access.tsx
Normal file
5
src/common/libs/umi/access.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export default function MyAccess({ children }: { children: ReactNode }) {
|
||||
return <div>MyAccess</div>;
|
||||
}
|
||||
77
src/common/libs/umi/layoutConfig.tsx
Normal file
77
src/common/libs/umi/layoutConfig.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
import Logo from '@/assets/bitcoin.webp';
|
||||
import { MyIcons, MyIconsType, PermissionsType, useMyState } from '@/common';
|
||||
import { Link, RuntimeConfig, history } from '@umijs/max';
|
||||
import AvatarProps from '../../components/layout/AvatarProps';
|
||||
|
||||
const loopMenu = (permissions: PermissionsType[] | undefined) => {
|
||||
let tree: PermissionsType[] = [];
|
||||
let map: Record<number, PermissionsType> = {};
|
||||
permissions?.forEach((permission) => {
|
||||
map[permission.id] = {
|
||||
path: permission.type === 'Button' ? 'null' : permission.path,
|
||||
name: permission.name,
|
||||
icon: permission.icon && MyIcons[permission.icon as MyIconsType],
|
||||
children: [],
|
||||
hideInMenu: permission.type === 'Button',
|
||||
};
|
||||
});
|
||||
|
||||
permissions?.forEach((permission) => {
|
||||
let node = map[permission.id];
|
||||
if (permission.parent_id !== null) {
|
||||
map[permission.parent_id].children.push(node);
|
||||
} else {
|
||||
tree.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
return tree?.[0]?.children;
|
||||
};
|
||||
|
||||
export const LayoutConfig: RuntimeConfig['layout'] = () => {
|
||||
const { snap } = useMyState();
|
||||
|
||||
return {
|
||||
title: snap.session.campus?.name ?? '总后台',
|
||||
logo: <img src={Logo} style={{ height: '30px' }} />,
|
||||
layout: 'mix',
|
||||
colorPrimary: '#1890ff',
|
||||
siderWidth: 220,
|
||||
pure: history.location.pathname === '/login',
|
||||
avatarProps: {
|
||||
render: () => <AvatarProps user={snap.session.user} />,
|
||||
},
|
||||
waterMarkProps: {
|
||||
content: snap.session.user?.username,
|
||||
},
|
||||
collapsedButtonRender: false,
|
||||
token: {
|
||||
bgLayout: '#eef0f3',
|
||||
header: {
|
||||
colorBgHeader: '#001529',
|
||||
colorHeaderTitle: '#FFF',
|
||||
colorTextRightActionsItem: '#FFF',
|
||||
heightLayoutHeader: 50,
|
||||
},
|
||||
sider: {
|
||||
colorMenuBackground: '#FFF',
|
||||
},
|
||||
},
|
||||
menuItemRender: (item, dom) => <Link to={item.path || '/'}>{dom}</Link>,
|
||||
menu: {
|
||||
params: snap.session.permissions,
|
||||
request: async () => {
|
||||
let objjs: any = [];
|
||||
snap.session.permissions?.forEach((res: any) => {
|
||||
objjs.push(res);
|
||||
});
|
||||
let data = objjs.sort((a: any, b: any) => {
|
||||
return a._lft - b._lft;
|
||||
});
|
||||
const menus = loopMenu(data);
|
||||
return Promise.resolve(menus);
|
||||
},
|
||||
},
|
||||
unAccessible: <div>unAccessible</div>,
|
||||
};
|
||||
};
|
||||
87
src/common/libs/umi/requestConfig.ts
Normal file
87
src/common/libs/umi/requestConfig.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import type { RequestConfig } from '@umijs/max';
|
||||
import { message } from 'antd';
|
||||
import { history } from 'umi';
|
||||
import { stateActions } from '../valtio/actions';
|
||||
import { state } from '../valtio/state';
|
||||
|
||||
const downloadFile = (disposition: string, data: any) => {
|
||||
const blob = new Blob([data]);
|
||||
const start = "filename*=utf-8''";
|
||||
let fileName = '';
|
||||
let dis = disposition.replace('UTF-8', 'utf-8');
|
||||
// console.log(dis, 'dis');
|
||||
if (dis.includes(start)) {
|
||||
fileName = dis.substr(dis.indexOf(start) + start.length);
|
||||
fileName = decodeURI(fileName);
|
||||
}
|
||||
if ('download' in document.createElement('a')) {
|
||||
// 非IE下载
|
||||
const elink: any = document.createElement('a');
|
||||
elink.download = fileName;
|
||||
elink.style.display = 'none';
|
||||
elink.href = URL.createObjectURL(blob);
|
||||
document.body.appendChild(elink);
|
||||
elink.click();
|
||||
URL.revokeObjectURL(elink.href); // 释放URL 对象
|
||||
document.body.removeChild(elink);
|
||||
} else {
|
||||
// IE10+下载
|
||||
// navigator.msSaveBlob(blob, fileName);
|
||||
}
|
||||
};
|
||||
|
||||
export const requestConfig: RequestConfig = {
|
||||
baseURL: '/api/',
|
||||
timeout: 1000 * 60,
|
||||
method: 'POST',
|
||||
errorConfig: {
|
||||
errorThrower: (res) => {
|
||||
console.log('errorThrower', res);
|
||||
},
|
||||
// 错误接收及处理
|
||||
errorHandler: (error: any) => {
|
||||
if (error) {
|
||||
message.error(error.errorMessage);
|
||||
switch (error.errorCode) {
|
||||
case 10000:
|
||||
if (history.location.pathname !== '/login') history.push('/login');
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// 请求拦截器
|
||||
requestInterceptors: [
|
||||
(url: string, options: any) => {
|
||||
stateActions.addLoading();
|
||||
return {
|
||||
url: url,
|
||||
options: {
|
||||
...options,
|
||||
headers: {
|
||||
authorization: 'Bearer ' + state.storage.access_token,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
],
|
||||
|
||||
// 响应拦截器
|
||||
responseInterceptors: [
|
||||
(response: any) => {
|
||||
stateActions.subLoading();
|
||||
const { headers } = response;
|
||||
// console.log(headers['content-disposition'], 'response');
|
||||
if (headers['content-disposition']) {
|
||||
downloadFile(headers['content-disposition'], response.data);
|
||||
return false;
|
||||
}
|
||||
const { data = {} as any } = response;
|
||||
if (!data.success && data.type !== 'application/vnd.ms-excel') {
|
||||
return Promise.reject(response.data);
|
||||
}
|
||||
return response;
|
||||
},
|
||||
],
|
||||
};
|
||||
50
src/common/libs/valtio/actions.ts
Normal file
50
src/common/libs/valtio/actions.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { MyResponseType } from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { state } from './state';
|
||||
|
||||
export const stateActions = {
|
||||
addLoading() {
|
||||
state.session.loading++;
|
||||
},
|
||||
subLoading() {
|
||||
state.session.loading--;
|
||||
},
|
||||
setReady(val: boolean) {
|
||||
state.session.ready = val;
|
||||
},
|
||||
setLogin(res: MyResponseType) {
|
||||
state.session.user = res.data.user;
|
||||
state.session.campus = res.data.campus;
|
||||
state.session.permissions = res.data.permissions;
|
||||
if (res.data?.token?.access_token)
|
||||
state.storage.access_token = res.data?.token?.access_token;
|
||||
// 解析apis
|
||||
const apiKeys: string[] = [];
|
||||
res.data.permissions.forEach((permission: any) => {
|
||||
if (permission.key) {
|
||||
apiKeys.push(permission.key);
|
||||
}
|
||||
});
|
||||
// console.log('apis', apis);
|
||||
state.session.apiKeys = apiKeys;
|
||||
},
|
||||
setLogout() {
|
||||
state.session.user = undefined;
|
||||
state.session.campus = undefined;
|
||||
state.session.permissions = undefined;
|
||||
state.storage.access_token = undefined;
|
||||
},
|
||||
me: async () => {
|
||||
const res = await Apis.Auth.Me();
|
||||
if (res.success) {
|
||||
stateActions.setLogin(res);
|
||||
return {
|
||||
name: state.session.user.username,
|
||||
apiKeys: state.session.apiKeys,
|
||||
// permissions: res.data.permissions,
|
||||
};
|
||||
} else {
|
||||
return { name: '未登录', apiKeys: [] };
|
||||
}
|
||||
},
|
||||
};
|
||||
60
src/common/libs/valtio/state.ts
Normal file
60
src/common/libs/valtio/state.ts
Normal file
@ -0,0 +1,60 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { proxy, snapshot, subscribe, useSnapshot } from 'valtio';
|
||||
|
||||
function proxyWithPersistant<V>(
|
||||
val: V,
|
||||
opts: {
|
||||
key: string;
|
||||
},
|
||||
) {
|
||||
const local = localStorage.getItem(opts.key);
|
||||
const state = proxy(local ? JSON.parse(local) : val);
|
||||
subscribe(state, () => {
|
||||
localStorage.setItem(opts.key, JSON.stringify(snapshot(state)));
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
type StorageType = {
|
||||
access_token: string | undefined;
|
||||
};
|
||||
|
||||
const storage: StorageType = proxyWithPersistant(
|
||||
{
|
||||
access_token: undefined,
|
||||
},
|
||||
{
|
||||
key: process.env.TOKEN_NAME as string,
|
||||
},
|
||||
);
|
||||
|
||||
type SessionType = {
|
||||
ready: boolean;
|
||||
user?: any;
|
||||
campus?: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
permissions?: any;
|
||||
apiKeys: string[];
|
||||
loading: number;
|
||||
};
|
||||
|
||||
const session: SessionType = proxy({
|
||||
ready: false,
|
||||
user: undefined,
|
||||
campus: undefined,
|
||||
permissions: undefined,
|
||||
apiKeys: [],
|
||||
loading: 0,
|
||||
});
|
||||
|
||||
export const state = proxy({
|
||||
storage,
|
||||
session,
|
||||
});
|
||||
|
||||
export function useMyState() {
|
||||
const snap = useSnapshot(state);
|
||||
return { snap };
|
||||
}
|
||||
125
src/common/pages/MyLoginPage1.tsx
Normal file
125
src/common/pages/MyLoginPage1.tsx
Normal file
@ -0,0 +1,125 @@
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import {
|
||||
FieldTimeOutlined,
|
||||
LockOutlined,
|
||||
UserOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {
|
||||
LoginFormPage,
|
||||
ProConfigProvider,
|
||||
ProFormText,
|
||||
} from '@ant-design/pro-components';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { stateActions } from '..';
|
||||
|
||||
export function MyLoginPage1() {
|
||||
const navigate = useNavigate();
|
||||
const [getCaptcha, setCaptcha] = useState<any>({});
|
||||
const methods = {
|
||||
getCaptcha: () => {
|
||||
Apis.Auth.Captcha().then(async (res) => {
|
||||
setCaptcha(res?.data);
|
||||
console.log(res, 'res');
|
||||
});
|
||||
},
|
||||
};
|
||||
useEffect(() => {
|
||||
methods?.getCaptcha();
|
||||
}, []);
|
||||
return (
|
||||
<ProConfigProvider>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: '#f8f8f8',
|
||||
height: '100vh',
|
||||
}}
|
||||
>
|
||||
<LoginFormPage<ApiTypes.Auth.Login>
|
||||
title="欢迎使用后台管理系统"
|
||||
backgroundVideoUrl="https://gw.alipayobjects.com/v/huamei_gcee1x/afts/video/jXRBRK_VAwoAAAAAAAAAAAAAK4eUAQBr"
|
||||
subTitle="Admin management system"
|
||||
onFinish={async (values: any) => {
|
||||
Apis.Auth.Login({
|
||||
...values,
|
||||
...{ captcha_key: getCaptcha?.key },
|
||||
})
|
||||
.then(async (res) => {
|
||||
await stateActions.setLogin(res);
|
||||
navigate('/');
|
||||
})
|
||||
.catch((err: any) => {
|
||||
methods?.getCaptcha();
|
||||
console.log(err, 'rr');
|
||||
});
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<ProFormText
|
||||
name="username"
|
||||
fieldProps={{
|
||||
size: 'large',
|
||||
prefix: <UserOutlined className={'prefixIcon'} />,
|
||||
}}
|
||||
placeholder={'登录账号'}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入登录账号!',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ProFormText.Password
|
||||
name="password"
|
||||
fieldProps={{
|
||||
size: 'large',
|
||||
prefix: <LockOutlined className={'prefixIcon'} />,
|
||||
}}
|
||||
placeholder={'登录密码'}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入密码!',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<ProFormText
|
||||
name="captcha"
|
||||
fieldProps={{
|
||||
size: 'large',
|
||||
prefix: <FieldTimeOutlined className={'prefixIcon'} />,
|
||||
}}
|
||||
placeholder={'验证码'}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入验证码!',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
height: '40px',
|
||||
border: '1px solid #eee',
|
||||
padding: '0 2px',
|
||||
margin: '0 10px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderRadius: '3px',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onClick={() => {
|
||||
methods?.getCaptcha();
|
||||
}}
|
||||
>
|
||||
<img height={32} width={100} src={getCaptcha?.img} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</LoginFormPage>
|
||||
</div>
|
||||
</ProConfigProvider>
|
||||
);
|
||||
}
|
||||
51
src/common/pages/NewMyS2Table/MyS2TableComponent.tsx
Normal file
51
src/common/pages/NewMyS2Table/MyS2TableComponent.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import { SheetComponent } from '@antv/s2-react';
|
||||
import { DataType } from '.';
|
||||
|
||||
export function MyS2TableComponent(props: DataType) {
|
||||
return (
|
||||
<SheetComponent
|
||||
sheetType="pivot"
|
||||
dataCfg={{
|
||||
fields: props.fields,
|
||||
data: props.data,
|
||||
meta: props.meta,
|
||||
}}
|
||||
adaptive={true}
|
||||
options={{
|
||||
height: 888,
|
||||
hierarchyType: props.config?.hierarchyType,
|
||||
// style: {
|
||||
// layoutWidthType: 'colAdaptive',
|
||||
// },
|
||||
totals: {
|
||||
row: {
|
||||
showGrandTotals: props.config.rowGrandTotals,
|
||||
showSubTotals: props.config.rowSubTotals,
|
||||
reverseGrandTotalsLayout: true,
|
||||
reverseSubTotalsLayout: true,
|
||||
calcGrandTotals: {
|
||||
aggregation: 'SUM',
|
||||
},
|
||||
calcSubTotals: {
|
||||
aggregation: 'SUM',
|
||||
},
|
||||
// grandTotalsGroupDimensions: ['day'],
|
||||
// subTotalsGroupDimensions: ['day'],
|
||||
},
|
||||
col: {
|
||||
showGrandTotals: props.config.colGrandTotals,
|
||||
showSubTotals: props.config.colSubTotals,
|
||||
reverseGrandTotalsLayout: true,
|
||||
reverseSubTotalsLayout: true,
|
||||
calcGrandTotals: {
|
||||
aggregation: 'SUM',
|
||||
},
|
||||
calcSubTotals: {
|
||||
aggregation: 'SUM',
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
106
src/common/pages/NewMyS2Table/MyS2TableExtra.tsx
Normal file
106
src/common/pages/NewMyS2Table/MyS2TableExtra.tsx
Normal file
@ -0,0 +1,106 @@
|
||||
import { Button, Radio, Space, Switch, Typography } from 'antd';
|
||||
import { DataType } from '.';
|
||||
|
||||
function downloadCSV(json: any) {
|
||||
if (json === undefined) return;
|
||||
// 提取表头
|
||||
const headers = Object.keys(json[0]);
|
||||
// 提取数据行
|
||||
const rows = json.map((item: any) => Object.values(item));
|
||||
|
||||
// 构建CSV内容
|
||||
let csvContent = '';
|
||||
// 添加表头
|
||||
csvContent += headers.join(',') + '\r\n';
|
||||
// 添加数据行
|
||||
rows.forEach((row: any) => {
|
||||
csvContent += row.join(',') + '\r\n';
|
||||
});
|
||||
|
||||
// 创建一个Blob对象,使用CSV内容,并设置类型为text/csv
|
||||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||
|
||||
// 创建一个链接元素
|
||||
const link = document.createElement('a');
|
||||
// 设置下载的文件名,默认为'download.csv'
|
||||
if (link.download !== undefined) {
|
||||
const url = URL.createObjectURL(blob);
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', 'download.csv');
|
||||
// 触发点击事件以下载文件
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
// 清理DOM
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
}
|
||||
|
||||
export function MyS2TableExtra({
|
||||
config,
|
||||
data,
|
||||
setConfig,
|
||||
}: {
|
||||
config: DataType['config'];
|
||||
data: DataType['data'];
|
||||
setConfig: (config: DataType['config']) => void;
|
||||
}) {
|
||||
return (
|
||||
<Space>
|
||||
<Typography.Text style={{ marginLeft: 20 }}>行小计:</Typography.Text>
|
||||
<Switch
|
||||
value={config?.rowSubTotals || false}
|
||||
onChange={(e) => {
|
||||
setConfig({ ...config, rowSubTotals: Boolean(e) });
|
||||
}}
|
||||
/>
|
||||
<Typography.Text style={{ marginLeft: 20 }}>行总计:</Typography.Text>
|
||||
<Switch
|
||||
value={config?.rowGrandTotals || false}
|
||||
onChange={(e) => {
|
||||
setConfig({ ...config, rowGrandTotals: Boolean(e) });
|
||||
}}
|
||||
/>
|
||||
<Typography.Text style={{ marginLeft: 20 }}>列小计:</Typography.Text>
|
||||
<Switch
|
||||
value={config?.colSubTotals || false}
|
||||
onChange={(e) => {
|
||||
setConfig({ ...config, colSubTotals: Boolean(e) });
|
||||
}}
|
||||
/>
|
||||
<Typography.Text style={{ marginLeft: 20 }}>列总计:</Typography.Text>
|
||||
<Switch
|
||||
value={config?.colGrandTotals || false}
|
||||
onChange={(e) => {
|
||||
setConfig({ ...config, colGrandTotals: Boolean(e) });
|
||||
}}
|
||||
/>
|
||||
<Typography.Text style={{ marginLeft: 20 }}>类型:</Typography.Text>
|
||||
<Radio.Group
|
||||
size="small"
|
||||
options={[
|
||||
{ label: '树形', value: 'tree' },
|
||||
{ label: '平铺', value: 'grid' },
|
||||
]}
|
||||
value={config?.hierarchyType || 'tree'}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
hierarchyType: e.target.value,
|
||||
})
|
||||
}
|
||||
optionType="button"
|
||||
buttonStyle="solid"
|
||||
style={{ marginRight: 20 }}
|
||||
/>
|
||||
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => {
|
||||
downloadCSV(data);
|
||||
}}
|
||||
>
|
||||
新的导出
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
73
src/common/pages/NewMyS2Table/MySwitcherFields.tsx
Normal file
73
src/common/pages/NewMyS2Table/MySwitcherFields.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
import { Switcher } from '@antv/s2-react';
|
||||
import { useSetState } from 'react-use';
|
||||
import { DataType } from '.';
|
||||
|
||||
export function MySwitcherFields({
|
||||
fields,
|
||||
setFields,
|
||||
}: {
|
||||
fields: DataType['fields'];
|
||||
setFields: (fields: DataType['fields']) => void;
|
||||
}) {
|
||||
const [switcherFields, setSwitcherFields] = useSetState(() => {
|
||||
return {
|
||||
rows: {
|
||||
selectable: true,
|
||||
allowEmpty: false,
|
||||
items: fields.rows?.map((item) => {
|
||||
return { id: item };
|
||||
}),
|
||||
},
|
||||
columns: {
|
||||
selectable: true,
|
||||
allowEmpty: true,
|
||||
items: fields.columns?.map((item) => {
|
||||
return { id: item };
|
||||
}),
|
||||
},
|
||||
values: {
|
||||
selectable: true,
|
||||
allowEmpty: false,
|
||||
items: fields.values?.map((item) => {
|
||||
return { id: item };
|
||||
}),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
function onSwitcherChange(result: any): void {
|
||||
const fields = {
|
||||
rows: result.rows.items
|
||||
.filter((item: { checked?: boolean }) => item.checked ?? true)
|
||||
.map((item: { id: string }) => item.id),
|
||||
columns: result.columns.items
|
||||
.filter((item: { checked?: boolean }) => item.checked ?? true)
|
||||
.map((item: { id: string }) => item.id),
|
||||
values: result.values.items
|
||||
.filter((item: { checked?: boolean }) => item.checked ?? true)
|
||||
.map((item: { id: string }) => item.id),
|
||||
};
|
||||
|
||||
setSwitcherFields({
|
||||
rows: {
|
||||
selectable: true,
|
||||
allowEmpty: false,
|
||||
items: result.rows.items,
|
||||
},
|
||||
columns: {
|
||||
selectable: true,
|
||||
allowEmpty: true,
|
||||
items: result.columns.items,
|
||||
},
|
||||
values: {
|
||||
selectable: true,
|
||||
allowEmpty: false,
|
||||
items: result.values.items,
|
||||
},
|
||||
});
|
||||
|
||||
setFields?.(fields);
|
||||
}
|
||||
|
||||
return <Switcher {...switcherFields} onSubmit={onSwitcherChange} />;
|
||||
}
|
||||
22
src/common/pages/NewMyS2Table/Query.tsx
Normal file
22
src/common/pages/NewMyS2Table/Query.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import { BetaSchemaForm, ProCard } from '@ant-design/pro-components';
|
||||
import { PropsType } from '.';
|
||||
|
||||
export default function Query({
|
||||
columns,
|
||||
doRequest,
|
||||
}: {
|
||||
columns?: PropsType['columns'];
|
||||
doRequest: (values: any) => void;
|
||||
}) {
|
||||
return (
|
||||
<ProCard>
|
||||
<BetaSchemaForm
|
||||
layoutType="QueryFilter"
|
||||
onFinish={async (values: Record<string, any>) => {
|
||||
doRequest(values);
|
||||
}}
|
||||
columns={columns as any}
|
||||
/>
|
||||
</ProCard>
|
||||
);
|
||||
}
|
||||
116
src/common/pages/NewMyS2Table/index.tsx
Normal file
116
src/common/pages/NewMyS2Table/index.tsx
Normal file
@ -0,0 +1,116 @@
|
||||
import { MyPageContainer } from '@/common';
|
||||
import { MyResponseType } from '@/common/typings';
|
||||
import { ProCard, ProFormColumnsType } from '@ant-design/pro-components';
|
||||
import { Fields, HierarchyType, Meta, RawData } from '@antv/s2';
|
||||
import { Flex, Space } from 'antd';
|
||||
import { useEffect } from 'react';
|
||||
import { useSetState } from 'react-use';
|
||||
import { MyS2TableComponent } from './MyS2TableComponent';
|
||||
import { MyS2TableExtra } from './MyS2TableExtra';
|
||||
import { MySwitcherFields } from './MySwitcherFields';
|
||||
import Query from './Query';
|
||||
|
||||
export type PropsType = {
|
||||
title: string;
|
||||
api: (values?: any) => Promise<MyResponseType>;
|
||||
defaultParams?: Record<string, any>;
|
||||
columns?: ProFormColumnsType<Record<string, any>, 'text'>[] | undefined;
|
||||
};
|
||||
|
||||
export type DataType = {
|
||||
config: {
|
||||
hierarchyType: HierarchyType | undefined;
|
||||
rowGrandTotals: boolean;
|
||||
rowSubTotals: boolean;
|
||||
colGrandTotals: boolean;
|
||||
colSubTotals: boolean;
|
||||
};
|
||||
data: RawData[];
|
||||
fields: Fields;
|
||||
meta?: Meta[];
|
||||
query?: Record<string, any>;
|
||||
};
|
||||
|
||||
export function NewMyS2Table(props: PropsType) {
|
||||
const [data, setData] = useSetState<DataType>(undefined);
|
||||
|
||||
// 请求数据
|
||||
function doRequest() {
|
||||
props
|
||||
.api?.({ ...props.defaultParams, ...data.query, fields: data.fields })
|
||||
.then((resp) => {
|
||||
if (!data.config)
|
||||
setData({
|
||||
config: resp.data.config,
|
||||
data: resp.data.data,
|
||||
fields: resp.data.fields,
|
||||
meta: resp.data.meta,
|
||||
});
|
||||
else setData({ data: resp.data.data });
|
||||
});
|
||||
}
|
||||
|
||||
// // 如果没有columns,直接发起请求
|
||||
// useEffect(() => {
|
||||
// if (props.columns === undefined) doRequest({});
|
||||
// }, []);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('useEffect query', data.query, data.fields);
|
||||
if (!data.query && !data.fields) {
|
||||
if (props.columns === undefined) {
|
||||
doRequest();
|
||||
}
|
||||
} else {
|
||||
doRequest();
|
||||
}
|
||||
}, [data.query, data.fields]);
|
||||
|
||||
return (
|
||||
<MyPageContainer title={props.title}>
|
||||
{props.columns && (
|
||||
<Query
|
||||
columns={props.columns}
|
||||
doRequest={(values) => {
|
||||
setData({ query: values });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<ProCard>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<Flex
|
||||
justify="space-between"
|
||||
style={{ width: '100%', margin: '10px 0' }}
|
||||
>
|
||||
{data.fields && (
|
||||
<MySwitcherFields
|
||||
fields={data.fields}
|
||||
setFields={(fields: DataType['fields']) => {
|
||||
console.log('fields', fields);
|
||||
setData({ fields });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{data.config && (
|
||||
<MyS2TableExtra
|
||||
config={data.config}
|
||||
setConfig={(config) => {
|
||||
setData({ config });
|
||||
}}
|
||||
data={data.data}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
{data.config && (
|
||||
<MyS2TableComponent
|
||||
config={data.config}
|
||||
fields={data.fields}
|
||||
meta={data.meta}
|
||||
data={data.data}
|
||||
/>
|
||||
)}
|
||||
</Space>
|
||||
</ProCard>
|
||||
</MyPageContainer>
|
||||
);
|
||||
}
|
||||
68
src/common/typings.d.ts
vendored
Normal file
68
src/common/typings.d.ts
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
import { ProColumns, ProFormColumnsType } from '@ant-design/pro-components';
|
||||
|
||||
type MyColumnsType =
|
||||
| ProFormColumnsType<any, 'text'>
|
||||
| ProColumns<any, FormFieldType | 'text' | any>;
|
||||
|
||||
type MyResponseType = {
|
||||
success?: boolean;
|
||||
data?: any;
|
||||
errorCode?: number;
|
||||
errorMessage?: string;
|
||||
showType?: ErrorShowType;
|
||||
meta?: MyPaginationMetaType;
|
||||
};
|
||||
|
||||
type MyPaginationMetaType = {
|
||||
current_page: number;
|
||||
last_page: number;
|
||||
per_page: number;
|
||||
total: number;
|
||||
};
|
||||
|
||||
type MyParamsType<T> = {
|
||||
q: T;
|
||||
p?: { page: number; perPage: number };
|
||||
t?: { sorter: { sort: string; order: string } };
|
||||
};
|
||||
|
||||
type PermissionsType = MenuDataItem & {
|
||||
parent_id: number;
|
||||
};
|
||||
|
||||
type MyModalRefType = {
|
||||
showModal: (data: MyModalProps) => void;
|
||||
hideModal: () => void;
|
||||
};
|
||||
|
||||
// export type MyModalFormProps = {
|
||||
// refresh: () => void;
|
||||
// item?: Record<string, any>;
|
||||
// title: string;
|
||||
// } & ModalFormProps;
|
||||
|
||||
type MyProFormFieldProps<T> = {
|
||||
value?: T;
|
||||
onChange?: (value: T) => void;
|
||||
};
|
||||
|
||||
// type MyEnumItemProps = {
|
||||
// label: string;
|
||||
// value: string | number;
|
||||
// color: string;
|
||||
// textColor: string;
|
||||
// };
|
||||
|
||||
export type MyBetaModalFormProps = {
|
||||
title?: string;
|
||||
// action: ActionType | undefined;
|
||||
reload?: () => void;
|
||||
item?: Record<string, any>;
|
||||
};
|
||||
|
||||
export type MyProEnumItemProps = {
|
||||
[key: string]: {
|
||||
text: string;
|
||||
color?: string;
|
||||
};
|
||||
};
|
||||
14
src/common/utils/mockHelper.ts
Normal file
14
src/common/utils/mockHelper.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export function error(msg: string = '系统错误', code: number = 999) {
|
||||
return {
|
||||
errorCode: code,
|
||||
success: false,
|
||||
errorMessage: msg,
|
||||
};
|
||||
}
|
||||
|
||||
export function mockSuccess(data: any) {
|
||||
return {
|
||||
success: true,
|
||||
data: data,
|
||||
};
|
||||
}
|
||||
53
src/common/utils/renderTextHelper.tsx
Normal file
53
src/common/utils/renderTextHelper.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import { Image, Space, Tag } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
export const renderTextHelper = {
|
||||
TagList(items: { label: string; value: string; color: string }[]) {
|
||||
return (
|
||||
<Space>
|
||||
{items?.map((item) => (
|
||||
<Tag color={item.color} key={item.value}>
|
||||
{item.label}
|
||||
</Tag>
|
||||
))}
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
Tag({
|
||||
Enums,
|
||||
value,
|
||||
isColor = true,
|
||||
}: {
|
||||
Enums?: any;
|
||||
value?: any;
|
||||
isColor?: any;
|
||||
}) {
|
||||
let item: any = Object.values(Enums).find((data: any) => {
|
||||
return data.value === '' + value;
|
||||
});
|
||||
return isColor ? (
|
||||
<Tag color={item?.color}>{item?.text}</Tag>
|
||||
) : (
|
||||
<>{item?.text}</>
|
||||
);
|
||||
},
|
||||
Images(images: string[]) {
|
||||
return (
|
||||
<Image.PreviewGroup>
|
||||
{images?.map((img: any) => (
|
||||
<Image
|
||||
key={img.uid}
|
||||
width={100}
|
||||
// height={100}
|
||||
src={img.url}
|
||||
/>
|
||||
))}
|
||||
</Image.PreviewGroup>
|
||||
);
|
||||
},
|
||||
RelativeTo(value: string) {
|
||||
return dayjs().to(dayjs(value));
|
||||
},
|
||||
};
|
||||
109
src/common/utils/rulesHelper.ts
Normal file
109
src/common/utils/rulesHelper.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import { Rule } from 'antd/es/form';
|
||||
|
||||
export const rulesHelper = {
|
||||
text: {
|
||||
required: true,
|
||||
rules: [
|
||||
() => ({
|
||||
validator(_, value) {
|
||||
return value && value !== ''
|
||||
? Promise.resolve()
|
||||
: Promise.reject(new Error('不能为空'));
|
||||
},
|
||||
}),
|
||||
] as Rule[],
|
||||
},
|
||||
upload(props?: { max?: number; errMsg?: string }) {
|
||||
return {
|
||||
required: true,
|
||||
rules: [
|
||||
() => ({
|
||||
validator(_, value) {
|
||||
if (!value) return Promise.reject(new Error('请上传'));
|
||||
let doneUploads = value?.filter(
|
||||
(item: { status: string }) => item.status === 'done',
|
||||
);
|
||||
if (!doneUploads) return Promise.reject(new Error('请上传'));
|
||||
|
||||
return doneUploads.length > (props?.max ?? 1)
|
||||
? Promise.reject(new Error(props?.errMsg ?? '请上传'))
|
||||
: Promise.resolve();
|
||||
},
|
||||
}),
|
||||
] as Rule[],
|
||||
};
|
||||
},
|
||||
number: {
|
||||
required: true,
|
||||
rules: [
|
||||
() => ({
|
||||
validator(_, value) {
|
||||
if (value !== undefined && value !== '' && !isNaN(Number(value))) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject(new Error('请输入数字,且不能为空'));
|
||||
}
|
||||
},
|
||||
}),
|
||||
] as Rule[],
|
||||
},
|
||||
boolean: {
|
||||
required: true,
|
||||
rules: [
|
||||
() => ({
|
||||
validator(_, value) {
|
||||
return value !== undefined && value !== ''
|
||||
? Promise.resolve()
|
||||
: Promise.reject(new Error('不能为空'));
|
||||
},
|
||||
}),
|
||||
] as Rule[],
|
||||
},
|
||||
richtext: {
|
||||
required: true,
|
||||
rules: [
|
||||
() => ({
|
||||
validator(_, value) {
|
||||
if (value && value !== '<p><br></p>') {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject(new Error('不能为空'));
|
||||
}
|
||||
},
|
||||
}),
|
||||
] as Rule[],
|
||||
},
|
||||
array: {
|
||||
required: true,
|
||||
rules: [
|
||||
() => ({
|
||||
validator(_, value) {
|
||||
if (value && value.length > 0) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject(new Error('不能为空'));
|
||||
}
|
||||
},
|
||||
}),
|
||||
] as Rule[],
|
||||
},
|
||||
cascader: {
|
||||
required: true,
|
||||
rules: [
|
||||
() => ({
|
||||
validator(_, value) {
|
||||
if (value && value.length > 0) {
|
||||
value.forEach((item: any) => {
|
||||
if (item === undefined) {
|
||||
return Promise.reject(new Error('不能为空'));
|
||||
}
|
||||
});
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject(new Error('不能为空'));
|
||||
}
|
||||
},
|
||||
}),
|
||||
] as Rule[],
|
||||
},
|
||||
};
|
||||
205
src/components/Selects.tsx
Normal file
205
src/components/Selects.tsx
Normal file
@ -0,0 +1,205 @@
|
||||
import { rulesHelper } from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { ProColumns, ProFormColumnsType } from '@ant-design/pro-components';
|
||||
|
||||
type ReturnType = ProColumns<any, 'text'> & ProFormColumnsType<any, 'text'>;
|
||||
type PropsType = { required?: boolean } & ReturnType;
|
||||
|
||||
export const Selects = {
|
||||
Agents(props?: PropsType): ReturnType {
|
||||
const {
|
||||
title = '代理',
|
||||
key = 'agents_id',
|
||||
dataIndex = ['agent', 'name'],
|
||||
required = false,
|
||||
hideInTable = false,
|
||||
...rest
|
||||
} = props ?? {};
|
||||
|
||||
return {
|
||||
title: title,
|
||||
key: key,
|
||||
dataIndex: dataIndex,
|
||||
valueType: 'select',
|
||||
hideInTable: hideInTable,
|
||||
formItemProps: { ...(required ? rulesHelper.number : {}) },
|
||||
fieldProps: {
|
||||
showSearch: false,
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
},
|
||||
request: async () => (await Apis.Select.Agents()).data,
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
LoanCompany(props?: PropsType): ReturnType {
|
||||
const {
|
||||
title = '资金方',
|
||||
key = 'loan_companies_id',
|
||||
dataIndex = ['loan_company', 'name'],
|
||||
required = false,
|
||||
hideInTable = false,
|
||||
...rest
|
||||
} = props ?? {};
|
||||
|
||||
return {
|
||||
title: title,
|
||||
key: key,
|
||||
dataIndex: dataIndex,
|
||||
valueType: 'select',
|
||||
hideInTable: hideInTable,
|
||||
formItemProps: { ...(required ? rulesHelper.number : {}) },
|
||||
fieldProps: {
|
||||
showSearch: false,
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
},
|
||||
request: async () => (await Apis.Select.LoanCompanies()).data,
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
Markets(props?: PropsType): ReturnType {
|
||||
const {
|
||||
title = '选择上游',
|
||||
key = 'markets_id',
|
||||
dataIndex = ['market', 'name'],
|
||||
required = false,
|
||||
hideInTable = false,
|
||||
...rest
|
||||
} = props ?? {};
|
||||
|
||||
return {
|
||||
title: title,
|
||||
key: key,
|
||||
dataIndex: dataIndex,
|
||||
valueType: 'select',
|
||||
hideInTable: hideInTable,
|
||||
formItemProps: { ...(required ? rulesHelper.number : {}) },
|
||||
fieldProps: {
|
||||
showSearch: false,
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
},
|
||||
request: async () => (await Apis.Select.Markets()).data,
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
Bosses(props?: PropsType): ReturnType {
|
||||
const {
|
||||
title = '老板',
|
||||
key = 'bosses_id',
|
||||
dataIndex = ['boss', 'name'],
|
||||
required = false,
|
||||
hideInTable = false,
|
||||
...rest
|
||||
} = props ?? {};
|
||||
|
||||
return {
|
||||
title: title,
|
||||
key: key,
|
||||
dataIndex: dataIndex,
|
||||
valueType: 'select',
|
||||
hideInTable: hideInTable,
|
||||
formItemProps: { ...(required ? rulesHelper.number : {}) },
|
||||
fieldProps: {
|
||||
showSearch: false,
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
},
|
||||
request: async () => (await Apis.Select.Bosses()).data,
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
Factories(props?: PropsType): ReturnType {
|
||||
const {
|
||||
title = '下游厂家',
|
||||
key = 'factories_id',
|
||||
dataIndex = ['factory', 'name'],
|
||||
required = false,
|
||||
hideInTable = false,
|
||||
...rest
|
||||
} = props ?? {};
|
||||
|
||||
return {
|
||||
title: title,
|
||||
key: key,
|
||||
dataIndex: dataIndex,
|
||||
valueType: 'select',
|
||||
hideInTable: hideInTable,
|
||||
formItemProps: { ...(required ? rulesHelper.number : {}) },
|
||||
fieldProps: {
|
||||
showSearch: false,
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
},
|
||||
request: async () => (await Apis.Select.Factories()).data,
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
Platforms(props?: PropsType): ReturnType {
|
||||
const {
|
||||
title = '平台',
|
||||
key = 'platforms_id',
|
||||
dataIndex = ['platform', 'name'],
|
||||
required = false,
|
||||
hideInTable = false,
|
||||
...rest
|
||||
} = props ?? {};
|
||||
|
||||
return {
|
||||
title: title,
|
||||
key: key,
|
||||
dataIndex: dataIndex,
|
||||
valueType: 'select',
|
||||
hideInTable: hideInTable,
|
||||
formItemProps: { ...(required ? rulesHelper.number : {}) },
|
||||
fieldProps: {
|
||||
showSearch: false,
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
},
|
||||
request: async () => (await Apis.Select.Platforms()).data,
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
Merchants(props?: PropsType): ReturnType {
|
||||
const {
|
||||
title = '商户',
|
||||
key = 'merchants_id',
|
||||
dataIndex = ['merchant', 'name'],
|
||||
required = false,
|
||||
hideInTable = false,
|
||||
...rest
|
||||
} = props ?? {};
|
||||
|
||||
return {
|
||||
title: title,
|
||||
key: key,
|
||||
dataIndex: dataIndex,
|
||||
valueType: 'select',
|
||||
hideInTable: hideInTable,
|
||||
formItemProps: { ...(required ? rulesHelper.number : {}) },
|
||||
fieldProps: {
|
||||
showSearch: false,
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
},
|
||||
request: async () => (await Apis.Select.Merchants()).data,
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
};
|
||||
90
src/components/SysSelects.tsx
Normal file
90
src/components/SysSelects.tsx
Normal file
@ -0,0 +1,90 @@
|
||||
import { rulesHelper } from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { ProColumns, ProFormColumnsType } from '@ant-design/pro-components';
|
||||
|
||||
type ReturnType = ProColumns<any, 'text'> & ProFormColumnsType<any, 'text'>;
|
||||
type PropsType = { required?: boolean } & ReturnType;
|
||||
|
||||
export const SysSelects = {
|
||||
Api(props?: PropsType): ReturnType {
|
||||
const { required = false, ...rest } = props ?? {};
|
||||
|
||||
return {
|
||||
title: '后端API',
|
||||
dataIndex: 'backend_apis',
|
||||
valueType: 'treeSelect',
|
||||
hideInTable: true,
|
||||
formItemProps: { ...(required ? rulesHelper.number : {}) },
|
||||
fieldProps: {
|
||||
showSearch: true,
|
||||
allowClear: true,
|
||||
treeDefaultExpandAll: true,
|
||||
dropdownStyle: { maxHeight: 400, overflow: 'auto' },
|
||||
multiple: true,
|
||||
fieldNames: {
|
||||
label: 'value',
|
||||
value: 'value',
|
||||
children: 'children',
|
||||
},
|
||||
},
|
||||
request: async () =>
|
||||
(await Apis.SysPermissions.SelectApi()).data?.children,
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
SysPermissionsTree(props?: { guard_name: string } & PropsType): ReturnType {
|
||||
const { guard_name = 'Admin', ...rest } = props ?? {};
|
||||
|
||||
return {
|
||||
key: 'parent_id',
|
||||
title: '上级菜单',
|
||||
valueType: 'treeSelect',
|
||||
request: async () => {
|
||||
return Apis.SysPermissions.Tree({ guard_name: guard_name }).then(
|
||||
(res) => res.data,
|
||||
);
|
||||
},
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
autoClearSearchValue: true,
|
||||
bordered: true,
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
filterTreeNode: true,
|
||||
showSearch: true,
|
||||
treeNodeFilterProp: 'title',
|
||||
treeDefaultExpandAll: true,
|
||||
},
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
SysRoles(props?: PropsType): ReturnType {
|
||||
const {
|
||||
title = '角色',
|
||||
key = 'roles_id',
|
||||
required = false,
|
||||
hideInTable = true,
|
||||
...rest
|
||||
} = props ?? {};
|
||||
|
||||
return {
|
||||
title: title,
|
||||
key: key,
|
||||
valueType: 'select',
|
||||
hideInTable: hideInTable,
|
||||
formItemProps: { ...(required ? rulesHelper.number : {}) },
|
||||
fieldProps: {
|
||||
mode: 'multiple',
|
||||
showSearch: false,
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
},
|
||||
request: async () => (await Apis.SysRoles.Select()).data,
|
||||
...rest,
|
||||
};
|
||||
},
|
||||
};
|
||||
360
src/gen/ApiTypes.d.ts
vendored
Normal file
360
src/gen/ApiTypes.d.ts
vendored
Normal file
@ -0,0 +1,360 @@
|
||||
declare namespace ApiTypes {
|
||||
namespace Admins {
|
||||
type List = {
|
||||
"username"?: string; // 模糊搜索:名称
|
||||
};
|
||||
type Store = {
|
||||
"username": string; // 用户名
|
||||
"password": string; // 密码
|
||||
"roles_id"?: string[]; // 角色
|
||||
};
|
||||
type Update = {
|
||||
"id": number; // id
|
||||
"username": string; // 用户名/手机号
|
||||
"password"?: string; // 密码,[hidden]
|
||||
"roles_id"?: string[]; // 角色
|
||||
};
|
||||
type Delete = {
|
||||
"id": number; // id
|
||||
};
|
||||
}
|
||||
namespace Agents {
|
||||
type List = {
|
||||
"name"?: string; // 模糊搜索:名称
|
||||
};
|
||||
type Store = {
|
||||
"name": string; // 简称
|
||||
"full_name"?: string; // 全称
|
||||
"address"?: string; // 地址
|
||||
"contact"?: string; // 联系人
|
||||
"phone"?: string; // 联系电话
|
||||
};
|
||||
type Update = {
|
||||
"id": number; // id
|
||||
"name": string; // 简称
|
||||
"full_name"?: string; // 全称
|
||||
"address"?: string; // 地址
|
||||
"contact"?: string; // 联系人
|
||||
"phone"?: string; // 联系电话
|
||||
};
|
||||
type Show = {
|
||||
"id": number; // id
|
||||
};
|
||||
type Delete = {
|
||||
"id": number; // id
|
||||
};
|
||||
}
|
||||
namespace Auth {
|
||||
type Login = {
|
||||
"username": string; // 用户名
|
||||
"password": string; // 密码
|
||||
"captcha": string; // 验证码
|
||||
"captcha_key": string; // 验证码key
|
||||
};
|
||||
type ChangePassword = {
|
||||
"old_password": string; // 老密码
|
||||
"new_password": string; // 新密码
|
||||
"re_new_password": string; // 重复新密码
|
||||
};
|
||||
type PreUpload = {
|
||||
"filename": string; // 文件名称
|
||||
};
|
||||
}
|
||||
namespace Bosses {
|
||||
type List = {
|
||||
"name"?: string; // 模糊搜索:名称
|
||||
};
|
||||
type Store = {
|
||||
"name": string; // 简称
|
||||
"full_name"?: string; // 全称
|
||||
"address"?: string; // 地址
|
||||
"contact"?: string; // 联系人
|
||||
"phone"?: string; // 联系电话
|
||||
};
|
||||
type Update = {
|
||||
"id": number; // id
|
||||
"name": string; // 简称
|
||||
"full_name"?: string; // 全称
|
||||
"address"?: string; // 地址
|
||||
"contact"?: string; // 联系人
|
||||
"phone"?: string; // 联系电话
|
||||
};
|
||||
type Show = {
|
||||
"id": number; // id
|
||||
};
|
||||
type Delete = {
|
||||
"id": number; // id
|
||||
};
|
||||
}
|
||||
namespace Factories {
|
||||
type List = {
|
||||
"name"?: string; // 模糊搜索:名称
|
||||
};
|
||||
type Store = {
|
||||
"name": string; // 厂家名称
|
||||
"public_key": string; // 下游公钥
|
||||
};
|
||||
type Update = {
|
||||
"id": number; // id
|
||||
"name": string; // 厂家名称
|
||||
"public_key": string; // 下游公钥
|
||||
};
|
||||
type Delete = {
|
||||
"id": number; // id
|
||||
};
|
||||
}
|
||||
namespace LoanCompanies {
|
||||
type List = {
|
||||
"type"?: string; // 用户类型,[enum:LoanCompaniesTypeEnum]
|
||||
"name"?: string; // 模糊搜索:名称
|
||||
};
|
||||
type Store = {
|
||||
"type"?: string; // 用户类型,[enum:LoanCompaniesTypeEnum]
|
||||
"name": string; // 简称
|
||||
"full_name"?: string; // 全称
|
||||
"address"?: string; // 地址
|
||||
"contact"?: string; // 联系人
|
||||
"phone"?: string; // 联系电话
|
||||
"up_merchant_no": string; // 上游商户号
|
||||
};
|
||||
type Update = {
|
||||
"id": number; // id
|
||||
"type"?: string; // 用户类型,[enum:LoanCompaniesTypeEnum]
|
||||
"name": string; // 简称
|
||||
"full_name"?: string; // 全称
|
||||
"address"?: string; // 地址
|
||||
"contact"?: string; // 联系人
|
||||
"phone"?: string; // 联系电话
|
||||
"up_merchant_no": string; // 上游商户号
|
||||
};
|
||||
type Show = {
|
||||
"id": number; // id
|
||||
};
|
||||
type Delete = {
|
||||
"id": number; // id
|
||||
};
|
||||
}
|
||||
namespace LoanDailyDetails {
|
||||
type List = {
|
||||
"merchants_id"?: number; // 商户ID
|
||||
"date"?: Date; // 日期
|
||||
"order_status"?: string; // 订单状态:LoansOrderStatusEnum
|
||||
};
|
||||
type Export = {
|
||||
"merchants_id"?: number; // 商户ID
|
||||
"date"?: Date; // 日期
|
||||
"order_status"?: string; // 订单状态:LoansOrderStatusEnum
|
||||
};
|
||||
}
|
||||
namespace Loans {
|
||||
type List = {
|
||||
"type"?: string; // 算法类型,[enum:LoansTypeEnum]
|
||||
"name"?: string; // 显示名称
|
||||
"order_status"?: string; // 订单状态,[enum:LoansOrderStatusEnum]
|
||||
};
|
||||
type Store = {
|
||||
"merchants_id": number; // 商户id,[ref:merchants]
|
||||
"loan_companies_id": number; // 资金方id,[ref:loan_companies]
|
||||
"name": string; // 显示名称
|
||||
"type"?: string; // 算法类型,[enum:LoansTypeEnum]
|
||||
"loan_days"?: number; // 贷款天数
|
||||
"started_at": Date; // 开始时间
|
||||
"total_due_principal": number; // 总应还-本金
|
||||
"total_due_interest"?: number; // 总应还-利息
|
||||
};
|
||||
type Delete = {
|
||||
"id": number; // id
|
||||
};
|
||||
}
|
||||
namespace Markets {
|
||||
type List = {
|
||||
"name"?: string; // 模糊搜索:名称
|
||||
};
|
||||
type Store = {
|
||||
"name": string; // 市场名称
|
||||
"market_no": string; // 市场编号
|
||||
"public_key": string; // 上游公钥
|
||||
};
|
||||
type Update = {
|
||||
"id": number; // id
|
||||
"name": string; // 市场名称
|
||||
"market_no": string; // 市场编号
|
||||
"public_key": string; // 上游公钥
|
||||
};
|
||||
type Delete = {
|
||||
"id": number; // id
|
||||
};
|
||||
}
|
||||
namespace MerchantCounters {
|
||||
type List = {
|
||||
"merchants_id"?: number; // 商户id,[ref:merchants]
|
||||
};
|
||||
type Store = {
|
||||
"merchants_id": number; // 商户id,[ref:merchants]
|
||||
"markets_id": number; // 上游市场id,[ref:markets]
|
||||
"name": string; // 名称
|
||||
"up_merchant_no": string; // 上游商家编号
|
||||
"commissions": string[]; // 分账配置
|
||||
};
|
||||
type Update = {
|
||||
"id": number; // id
|
||||
"name": string; // 名称
|
||||
"up_merchant_no": string; // 上游商家编号
|
||||
"commissions": string[]; // 分账配置
|
||||
};
|
||||
type Delete = {
|
||||
"id": number; // id
|
||||
};
|
||||
}
|
||||
namespace Merchants {
|
||||
type List = {
|
||||
"agents_id"?: number; // 代理id,[ref:agents]
|
||||
"bosses_id"?: number; // 老板id,[ref:bosses]
|
||||
"factories_id"?: number; // 厂家id,[ref:factories]
|
||||
"platforms_id"?: number; // 平台id,[ref:platforms]
|
||||
"name"?: string; // 商户简称
|
||||
"full_name"?: string; // 商户全称呼
|
||||
"plat_merchant_no"?: string; // 平台商户号
|
||||
};
|
||||
type Store = {
|
||||
"agents_id": number; // 代理id,[ref:agents]
|
||||
"bosses_id": number; // 老板id,[ref:bosses]
|
||||
"factories_id": number; // 厂家id,[ref:factories]
|
||||
"platforms_id": number; // 平台id,[ref:platforms]
|
||||
"name": string; // 商户简称
|
||||
"full_name": string; // 商户全称呼
|
||||
};
|
||||
type Update = {
|
||||
"id": number; // id
|
||||
"agents_id": number; // 代理id,[ref:agents]
|
||||
"bosses_id": number; // 老板id,[ref:bosses]
|
||||
"factories_id": number; // 厂家id,[ref:factories]
|
||||
"platforms_id": number; // 平台id,[ref:platforms]
|
||||
"name": string; // 商户简称
|
||||
"full_name": string; // 商户全称呼
|
||||
};
|
||||
type Delete = {
|
||||
"id": number; // id
|
||||
};
|
||||
}
|
||||
namespace OrgUsers {
|
||||
type List = {
|
||||
"type"?: string; // 机构类型,[enum:OrgUsersTypeEnum]
|
||||
"org_name"?: string; // 机构名称
|
||||
"org_id"?: number; // 机构id
|
||||
"username"?: string; // 用户名
|
||||
};
|
||||
type Store = {
|
||||
"type": string; // 用户类型,[enum:OrgUsersTypeEnum]
|
||||
"org_name": string; // 机构名称
|
||||
"username": string; // 用户名
|
||||
"password": string; // 密码,[hidden]
|
||||
};
|
||||
type Update = {
|
||||
"id": number; // id
|
||||
"password": string; // 密码,[hidden]
|
||||
};
|
||||
type Show = {
|
||||
"id": number; // id
|
||||
};
|
||||
type Delete = {
|
||||
"id": number; // id
|
||||
};
|
||||
}
|
||||
namespace PayOrders {
|
||||
type List = {
|
||||
"merchant_name"?: string; // 模糊搜索:商户简称
|
||||
"type"?: string; // 交易类型,[enum:PayOrdersTypeEnum]
|
||||
"order_status"?: string; // 订单状态,[enum:PayOrderStatusEnum]
|
||||
"day_date"?: Date; // 日期
|
||||
"down_order_no"?: string; // 下游订单号
|
||||
"up_order_no"?: string; // 上游订单号
|
||||
"up_tx_no"?: string; // 上游交易流水号
|
||||
"amount_order"?: string; // 金额
|
||||
"up_order_at_range"?: string[]; // 上游订单时间范围
|
||||
};
|
||||
}
|
||||
namespace Platforms {
|
||||
type List = {
|
||||
"name"?: string; // 模糊搜索:名称
|
||||
};
|
||||
type Store = {
|
||||
"name": string; // 平台名称
|
||||
"private_key": string; // 平台私钥
|
||||
"public_key": string; // 平台公钥
|
||||
};
|
||||
type Update = {
|
||||
"id": number; // id
|
||||
"name": string; // 平台名称
|
||||
"private_key": string; // 平台私钥
|
||||
"public_key": string; // 平台公钥
|
||||
};
|
||||
type Delete = {
|
||||
"id": number; // id
|
||||
};
|
||||
}
|
||||
namespace Select {
|
||||
}
|
||||
namespace SysPermissions {
|
||||
type List = {
|
||||
"parent_id"?: number; // 上级ID
|
||||
"guard_name": string; //
|
||||
};
|
||||
type Tree = {
|
||||
"parent_id"?: number; // 上级ID
|
||||
"guard_name": string; //
|
||||
};
|
||||
type Store = {
|
||||
"name": string; //
|
||||
"key"?: string; //
|
||||
"guard_name": string; //
|
||||
"icon"?: string; // 图标
|
||||
"type": string; // 类型:SysPermissionsTypeEnum
|
||||
"backend_apis"?: string[]; // 后台api
|
||||
"path"?: string; // 路由
|
||||
"parent_id"?: number; //
|
||||
};
|
||||
type Update = {
|
||||
"id": number; // ID
|
||||
"name": string; //
|
||||
"key"?: string; //
|
||||
"guard_name": string; //
|
||||
"icon"?: string; // 图标
|
||||
"type": string; // 类型:SysPermissionsTypeEnum
|
||||
"backend_apis"?: string[]; // 后台api
|
||||
"path"?: string; // 路由
|
||||
"parent_id"?: number; //
|
||||
};
|
||||
type Delete = {
|
||||
"id": number; // ID
|
||||
};
|
||||
type Move = {
|
||||
"id": number; // ID
|
||||
"type": string; // 类型:up 升级,down 降级
|
||||
};
|
||||
}
|
||||
namespace SysRoles {
|
||||
type List = {
|
||||
"name"?: string; // 模糊搜索:名称
|
||||
};
|
||||
type Store = {
|
||||
"name": string; // 名称
|
||||
"color"?: string; // 颜色
|
||||
};
|
||||
type Update = {
|
||||
"id": number; // ID
|
||||
"name": string; // 名称
|
||||
"color"?: string; // 颜色
|
||||
};
|
||||
type Delete = {
|
||||
"id": number; // ID
|
||||
};
|
||||
type GetPermissions = {
|
||||
"id": number; // ID
|
||||
};
|
||||
type SetPermissions = {
|
||||
"id": number; // ID
|
||||
"permissions_ids": string[]; // 权限ID
|
||||
};
|
||||
}
|
||||
}
|
||||
270
src/gen/Apis.ts
Normal file
270
src/gen/Apis.ts
Normal file
@ -0,0 +1,270 @@
|
||||
import { MyResponseType } from '@/common';
|
||||
import { request } from '@umijs/max';
|
||||
|
||||
export const Apis = {
|
||||
Admins: {
|
||||
List(data?: ApiTypes.Admins.List): Promise<MyResponseType> {
|
||||
return request('admin/admins/list', { data });
|
||||
},
|
||||
Store(data: ApiTypes.Admins.Store): Promise<MyResponseType> {
|
||||
return request('admin/admins/store', { data });
|
||||
},
|
||||
Update(data: ApiTypes.Admins.Update): Promise<MyResponseType> {
|
||||
return request('admin/admins/update', { data });
|
||||
},
|
||||
Delete(data: ApiTypes.Admins.Delete): Promise<MyResponseType> {
|
||||
return request('admin/admins/delete', { data });
|
||||
},
|
||||
},
|
||||
Agents: {
|
||||
List(data?: ApiTypes.Agents.List): Promise<MyResponseType> {
|
||||
return request('admin/agents/list', { data });
|
||||
},
|
||||
Store(data: ApiTypes.Agents.Store): Promise<MyResponseType> {
|
||||
return request('admin/agents/store', { data });
|
||||
},
|
||||
Update(data: ApiTypes.Agents.Update): Promise<MyResponseType> {
|
||||
return request('admin/agents/update', { data });
|
||||
},
|
||||
Show(data: ApiTypes.Agents.Show): Promise<MyResponseType> {
|
||||
return request('admin/agents/show', { data });
|
||||
},
|
||||
Delete(data: ApiTypes.Agents.Delete): Promise<MyResponseType> {
|
||||
return request('admin/agents/delete', { data });
|
||||
},
|
||||
},
|
||||
Auth: {
|
||||
Captcha(): Promise<MyResponseType> {
|
||||
return request('admin/auth/captcha', {});
|
||||
},
|
||||
Login(data: ApiTypes.Auth.Login): Promise<MyResponseType> {
|
||||
return request('admin/auth/login', { data });
|
||||
},
|
||||
Logout(): Promise<MyResponseType> {
|
||||
return request('admin/auth/logout', {});
|
||||
},
|
||||
Me(): Promise<MyResponseType> {
|
||||
return request('admin/auth/me', {});
|
||||
},
|
||||
ChangePassword(data: ApiTypes.Auth.ChangePassword): Promise<MyResponseType> {
|
||||
return request('admin/auth/change_password', { data });
|
||||
},
|
||||
PreUpload(data: ApiTypes.Auth.PreUpload): Promise<MyResponseType> {
|
||||
return request('admin/auth/pre_upload', { data });
|
||||
},
|
||||
},
|
||||
Bosses: {
|
||||
List(data?: ApiTypes.Bosses.List): Promise<MyResponseType> {
|
||||
return request('admin/bosses/list', { data });
|
||||
},
|
||||
Store(data: ApiTypes.Bosses.Store): Promise<MyResponseType> {
|
||||
return request('admin/bosses/store', { data });
|
||||
},
|
||||
Update(data: ApiTypes.Bosses.Update): Promise<MyResponseType> {
|
||||
return request('admin/bosses/update', { data });
|
||||
},
|
||||
Show(data: ApiTypes.Bosses.Show): Promise<MyResponseType> {
|
||||
return request('admin/bosses/show', { data });
|
||||
},
|
||||
Delete(data: ApiTypes.Bosses.Delete): Promise<MyResponseType> {
|
||||
return request('admin/bosses/delete', { data });
|
||||
},
|
||||
},
|
||||
Factories: {
|
||||
List(data?: ApiTypes.Factories.List): Promise<MyResponseType> {
|
||||
return request('admin/factories/list', { data });
|
||||
},
|
||||
Store(data: ApiTypes.Factories.Store): Promise<MyResponseType> {
|
||||
return request('admin/factories/store', { data });
|
||||
},
|
||||
Update(data: ApiTypes.Factories.Update): Promise<MyResponseType> {
|
||||
return request('admin/factories/update', { data });
|
||||
},
|
||||
Delete(data: ApiTypes.Factories.Delete): Promise<MyResponseType> {
|
||||
return request('admin/factories/delete', { data });
|
||||
},
|
||||
},
|
||||
LoanCompanies: {
|
||||
List(data?: ApiTypes.LoanCompanies.List): Promise<MyResponseType> {
|
||||
return request('admin/loan_companies/list', { data });
|
||||
},
|
||||
Store(data: ApiTypes.LoanCompanies.Store): Promise<MyResponseType> {
|
||||
return request('admin/loan_companies/store', { data });
|
||||
},
|
||||
Update(data: ApiTypes.LoanCompanies.Update): Promise<MyResponseType> {
|
||||
return request('admin/loan_companies/update', { data });
|
||||
},
|
||||
Show(data: ApiTypes.LoanCompanies.Show): Promise<MyResponseType> {
|
||||
return request('admin/loan_companies/show', { data });
|
||||
},
|
||||
Delete(data: ApiTypes.LoanCompanies.Delete): Promise<MyResponseType> {
|
||||
return request('admin/loan_companies/delete', { data });
|
||||
},
|
||||
},
|
||||
LoanDailyDetails: {
|
||||
List(data?: ApiTypes.LoanDailyDetails.List): Promise<MyResponseType> {
|
||||
return request('admin/loan_daily_details/list', { data });
|
||||
},
|
||||
Export(data?: ApiTypes.LoanDailyDetails.Export): Promise<MyResponseType> {
|
||||
return request('admin/loan_daily_details/export', { responseType: 'blob',data });
|
||||
},
|
||||
},
|
||||
Loans: {
|
||||
List(data?: ApiTypes.Loans.List): Promise<MyResponseType> {
|
||||
return request('admin/loans/list', { data });
|
||||
},
|
||||
Store(data: ApiTypes.Loans.Store): Promise<MyResponseType> {
|
||||
return request('admin/loans/store', { data });
|
||||
},
|
||||
Delete(data: ApiTypes.Loans.Delete): Promise<MyResponseType> {
|
||||
return request('admin/loans/delete', { data });
|
||||
},
|
||||
},
|
||||
Markets: {
|
||||
List(data?: ApiTypes.Markets.List): Promise<MyResponseType> {
|
||||
return request('admin/markets/list', { data });
|
||||
},
|
||||
Store(data: ApiTypes.Markets.Store): Promise<MyResponseType> {
|
||||
return request('admin/markets/store', { data });
|
||||
},
|
||||
Update(data: ApiTypes.Markets.Update): Promise<MyResponseType> {
|
||||
return request('admin/markets/update', { data });
|
||||
},
|
||||
Delete(data: ApiTypes.Markets.Delete): Promise<MyResponseType> {
|
||||
return request('admin/markets/delete', { data });
|
||||
},
|
||||
},
|
||||
MerchantCounters: {
|
||||
List(data?: ApiTypes.MerchantCounters.List): Promise<MyResponseType> {
|
||||
return request('admin/merchant_counters/list', { data });
|
||||
},
|
||||
Store(data: ApiTypes.MerchantCounters.Store): Promise<MyResponseType> {
|
||||
return request('admin/merchant_counters/store', { data });
|
||||
},
|
||||
Update(data: ApiTypes.MerchantCounters.Update): Promise<MyResponseType> {
|
||||
return request('admin/merchant_counters/update', { data });
|
||||
},
|
||||
Delete(data: ApiTypes.MerchantCounters.Delete): Promise<MyResponseType> {
|
||||
return request('admin/merchant_counters/delete', { data });
|
||||
},
|
||||
},
|
||||
Merchants: {
|
||||
List(data?: ApiTypes.Merchants.List): Promise<MyResponseType> {
|
||||
return request('admin/merchants/list', { data });
|
||||
},
|
||||
Store(data: ApiTypes.Merchants.Store): Promise<MyResponseType> {
|
||||
return request('admin/merchants/store', { data });
|
||||
},
|
||||
Update(data: ApiTypes.Merchants.Update): Promise<MyResponseType> {
|
||||
return request('admin/merchants/update', { data });
|
||||
},
|
||||
Delete(data: ApiTypes.Merchants.Delete): Promise<MyResponseType> {
|
||||
return request('admin/merchants/delete', { data });
|
||||
},
|
||||
},
|
||||
OrgUsers: {
|
||||
List(data?: ApiTypes.OrgUsers.List): Promise<MyResponseType> {
|
||||
return request('admin/org_users/list', { data });
|
||||
},
|
||||
Store(data: ApiTypes.OrgUsers.Store): Promise<MyResponseType> {
|
||||
return request('admin/org_users/store', { data });
|
||||
},
|
||||
Update(data: ApiTypes.OrgUsers.Update): Promise<MyResponseType> {
|
||||
return request('admin/org_users/update', { data });
|
||||
},
|
||||
Show(data: ApiTypes.OrgUsers.Show): Promise<MyResponseType> {
|
||||
return request('admin/org_users/show', { data });
|
||||
},
|
||||
Delete(data: ApiTypes.OrgUsers.Delete): Promise<MyResponseType> {
|
||||
return request('admin/org_users/delete', { data });
|
||||
},
|
||||
},
|
||||
PayOrders: {
|
||||
List(data?: ApiTypes.PayOrders.List): Promise<MyResponseType> {
|
||||
return request('admin/pay_orders/list', { data });
|
||||
},
|
||||
},
|
||||
Platforms: {
|
||||
List(data?: ApiTypes.Platforms.List): Promise<MyResponseType> {
|
||||
return request('admin/platforms/list', { data });
|
||||
},
|
||||
Store(data: ApiTypes.Platforms.Store): Promise<MyResponseType> {
|
||||
return request('admin/platforms/store', { data });
|
||||
},
|
||||
Update(data: ApiTypes.Platforms.Update): Promise<MyResponseType> {
|
||||
return request('admin/platforms/update', { data });
|
||||
},
|
||||
Delete(data: ApiTypes.Platforms.Delete): Promise<MyResponseType> {
|
||||
return request('admin/platforms/delete', { data });
|
||||
},
|
||||
},
|
||||
Select: {
|
||||
Agents(): Promise<MyResponseType> {
|
||||
return request('admin/select/agents', {});
|
||||
},
|
||||
LoanCompanies(): Promise<MyResponseType> {
|
||||
return request('admin/select/loan_companies', {});
|
||||
},
|
||||
Factories(): Promise<MyResponseType> {
|
||||
return request('admin/select/factories', {});
|
||||
},
|
||||
Platforms(): Promise<MyResponseType> {
|
||||
return request('admin/select/platforms', {});
|
||||
},
|
||||
Bosses(): Promise<MyResponseType> {
|
||||
return request('admin/select/bosses', {});
|
||||
},
|
||||
Markets(): Promise<MyResponseType> {
|
||||
return request('admin/select/markets', {});
|
||||
},
|
||||
Merchants(): Promise<MyResponseType> {
|
||||
return request('admin/select/merchants', {});
|
||||
},
|
||||
},
|
||||
SysPermissions: {
|
||||
List(data: ApiTypes.SysPermissions.List): Promise<MyResponseType> {
|
||||
return request('admin/sys_permissions/list', { data });
|
||||
},
|
||||
Tree(data: ApiTypes.SysPermissions.Tree): Promise<MyResponseType> {
|
||||
return request('admin/sys_permissions/tree', { data });
|
||||
},
|
||||
Store(data: ApiTypes.SysPermissions.Store): Promise<MyResponseType> {
|
||||
return request('admin/sys_permissions/store', { data });
|
||||
},
|
||||
Update(data: ApiTypes.SysPermissions.Update): Promise<MyResponseType> {
|
||||
return request('admin/sys_permissions/update', { data });
|
||||
},
|
||||
Delete(data: ApiTypes.SysPermissions.Delete): Promise<MyResponseType> {
|
||||
return request('admin/sys_permissions/delete', { data });
|
||||
},
|
||||
Move(data: ApiTypes.SysPermissions.Move): Promise<MyResponseType> {
|
||||
return request('admin/sys_permissions/move', { data });
|
||||
},
|
||||
SelectApi(): Promise<MyResponseType> {
|
||||
return request('admin/sys_permissions/select_api', {});
|
||||
},
|
||||
},
|
||||
SysRoles: {
|
||||
List(data?: ApiTypes.SysRoles.List): Promise<MyResponseType> {
|
||||
return request('admin/sys_roles/list', { data });
|
||||
},
|
||||
Store(data: ApiTypes.SysRoles.Store): Promise<MyResponseType> {
|
||||
return request('admin/sys_roles/store', { data });
|
||||
},
|
||||
Update(data: ApiTypes.SysRoles.Update): Promise<MyResponseType> {
|
||||
return request('admin/sys_roles/update', { data });
|
||||
},
|
||||
Delete(data: ApiTypes.SysRoles.Delete): Promise<MyResponseType> {
|
||||
return request('admin/sys_roles/delete', { data });
|
||||
},
|
||||
Select(): Promise<MyResponseType> {
|
||||
return request('admin/sys_roles/select', {});
|
||||
},
|
||||
GetPermissions(data: ApiTypes.SysRoles.GetPermissions): Promise<MyResponseType> {
|
||||
return request('admin/sys_roles/get_permissions', { data });
|
||||
},
|
||||
SetPermissions(data: ApiTypes.SysRoles.SetPermissions): Promise<MyResponseType> {
|
||||
return request('admin/sys_roles/set_permissions', { data });
|
||||
},
|
||||
},
|
||||
}
|
||||
72
src/gen/Enums.ts
Normal file
72
src/gen/Enums.ts
Normal file
@ -0,0 +1,72 @@
|
||||
// CommissionTypeEnum
|
||||
export const CommissionTypeEnum= {
|
||||
'Platform': {"text":"平台","color":"#1890ff","value":"Platform"},
|
||||
'Agent': {"text":"代理","color":"#722ed1","value":"Agent"},
|
||||
'Repayment': {"text":"还款","color":"#f5222d","value":"Repayment"},
|
||||
'Merchant': {"text":"商家","color":"#52c41a","value":"Merchant"},
|
||||
'Other': {"text":"其他","color":"#bfbfbf","value":"Other"},
|
||||
};
|
||||
|
||||
// LoanCompaniesTypeEnum
|
||||
export const LoanCompaniesTypeEnum= {
|
||||
'DeviceManufacturer': {"text":"设备商","color":"#459423","value":"DeviceManufacturer"},
|
||||
'CapitalProvider': {"text":"资金方","color":"#13952d","value":"CapitalProvider"},
|
||||
'SupplyChain': {"text":"供应链","color":"#033fcc","value":"SupplyChain"},
|
||||
};
|
||||
|
||||
// LoansOrderStatusEnum
|
||||
export const LoansOrderStatusEnum= {
|
||||
'NotStart': {"text":"未开始","color":"#983054","value":"NotStart"},
|
||||
'OnGoing': {"text":"进行中","color":"#a36451","value":"OnGoing"},
|
||||
'Finished': {"text":"已完成","color":"#0ce4b6","value":"Finished"},
|
||||
'Overdue': {"text":"逾期","color":"#7723f8","value":"Overdue"},
|
||||
};
|
||||
|
||||
// LoansTypeEnum
|
||||
export const LoansTypeEnum= {
|
||||
'Daily': {"text":"每日还款(等本等息)","color":"#e847d5","value":"Daily"},
|
||||
};
|
||||
|
||||
// OrgUserTypeEnum
|
||||
export const OrgUsersTypeEnum= {
|
||||
'Agent': {"text":"代理","color":"#bd1087","value":"Agent"},
|
||||
'Bosses': {"text":"老板","color":"#5892ea","value":"Bosses"},
|
||||
'LoanCompanies': {"text":"资金方","color":"#7e2070","value":"LoanCompanies"},
|
||||
};
|
||||
|
||||
// PayOrderStatusEnum
|
||||
export const PayOrderStatusEnum= {
|
||||
'1': {"text":"待支付","color":"#faad14","value":"1"},
|
||||
'2': {"text":"支付成功","color":"#52c41a","value":"2"},
|
||||
'3': {"text":"支付失败","color":"#f5222d","value":"3"},
|
||||
'9': {"text":"待轮询","color":"#1890ff","value":"9"},
|
||||
'0': {"text":"其他","color":"#bfbfbf","value":"0"},
|
||||
};
|
||||
|
||||
// PayOrdersTypeEnum
|
||||
export const PayOrdersTypeEnum= {
|
||||
'1': {"text":"支付","color":"#52c41a","value":"1"},
|
||||
'2': {"text":"退款","color":"#faad14","value":"2"},
|
||||
};
|
||||
|
||||
// SysModuleEnum
|
||||
export const SysModuleEnum= {
|
||||
'Admin': {"text":"管理员","color":"#cf1322","value":"Admin"},
|
||||
'Customer': {"text":"客户","color":"#d4b106","value":"Customer"},
|
||||
};
|
||||
|
||||
// SysPermissionsTypeEnum
|
||||
export const SysPermissionsTypeEnum= {
|
||||
'Directory': {"text":"目录","color":"#6d7e14","value":"Directory"},
|
||||
'Page': {"text":"页面","color":"#4d9a13","value":"Page"},
|
||||
'Button': {"text":"按钮","color":"#97224f","value":"Button"},
|
||||
};
|
||||
|
||||
// SysTasksStatusEnum
|
||||
export const SysTasksStatusEnum= {
|
||||
'1': {"text":"未开始","color":"#bfbfbf","value":"1"},
|
||||
'2': {"text":"未开始","color":"#1890ff","value":"2"},
|
||||
'3': {"text":"已完成","color":"#a0d911","value":"3"},
|
||||
'4': {"text":"错误","color":"#f5222d","value":"4"},
|
||||
};
|
||||
|
||||
11
src/global.scss
Normal file
11
src/global.scss
Normal file
@ -0,0 +1,11 @@
|
||||
.ant-pro-page-container {
|
||||
.ant-page-header-heading-title {
|
||||
line-height: 51px !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: 700;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.ant-pro-grid-content {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
65
src/pages/agents/index.tsx
Normal file
65
src/pages/agents/index.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
import {
|
||||
MyButtons,
|
||||
MyColumns,
|
||||
MyPageContainer,
|
||||
MyProTableProps,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
import { Space } from 'antd';
|
||||
import Create from './modals/Create';
|
||||
import Update from './modals/Update';
|
||||
|
||||
export default function Index({ title = '代理商' }) {
|
||||
return (
|
||||
<MyPageContainer title={title}>
|
||||
<ProTable
|
||||
{...MyProTableProps.props}
|
||||
request={async (params, sort) =>
|
||||
MyProTableProps.request(params, sort, Apis.Agents.List)
|
||||
}
|
||||
toolBarRender={(action) => [
|
||||
<Create key="Create" reload={action?.reload} title={title} />,
|
||||
]}
|
||||
columns={[
|
||||
MyColumns.ID(),
|
||||
{
|
||||
title: '简称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '全称',
|
||||
dataIndex: 'full_name',
|
||||
},
|
||||
{
|
||||
dataIndex: 'address',
|
||||
title: '地址',
|
||||
},
|
||||
{
|
||||
dataIndex: 'contact',
|
||||
title: '联系人',
|
||||
},
|
||||
{
|
||||
dataIndex: 'phone',
|
||||
title: '联系电话',
|
||||
},
|
||||
MyColumns.CreatedAt(),
|
||||
MyColumns.Option({
|
||||
render: (_, item: any, index, action) => (
|
||||
<Space key={index}>
|
||||
<Update item={item} reload={action?.reload} title={title} />
|
||||
<MyButtons.Delete
|
||||
onConfirm={() =>
|
||||
Apis.Agents.Delete({ id: item.id }).then(() =>
|
||||
action?.reload(),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
</MyPageContainer>
|
||||
);
|
||||
}
|
||||
54
src/pages/agents/modals/Create.tsx
Normal file
54
src/pages/agents/modals/Create.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Create(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.Agents.Store>
|
||||
{...MyModalFormProps.props}
|
||||
title={`添加${props.title}`}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
trigger={<MyButtons.Create title={`添加${props.title}`} />}
|
||||
onFinish={async (values) =>
|
||||
Apis.Agents.Store(values)
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
key: 'name',
|
||||
title: '简称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'full_name',
|
||||
title: '全称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
title: '地址',
|
||||
},
|
||||
{
|
||||
key: 'contact',
|
||||
title: '联系人',
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
title: '联系电话',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
55
src/pages/agents/modals/Update.tsx
Normal file
55
src/pages/agents/modals/Update.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Update(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.Agents.Update>
|
||||
{...MyModalFormProps.props}
|
||||
title={`编辑${props.title}`}
|
||||
trigger={<MyButtons.Edit />}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
request={() => Promise.resolve(props.item)}
|
||||
onFinish={async (values) =>
|
||||
Apis.Agents.Update({ ...values, id: props.item?.id ?? 0 })
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
key: 'name',
|
||||
title: '简称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'full_name',
|
||||
title: '全称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
title: '地址',
|
||||
},
|
||||
{
|
||||
key: 'contact',
|
||||
title: '联系人',
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
title: '联系电话',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
65
src/pages/bosses/index.tsx
Normal file
65
src/pages/bosses/index.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
import {
|
||||
MyButtons,
|
||||
MyColumns,
|
||||
MyPageContainer,
|
||||
MyProTableProps,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
import { Space } from 'antd';
|
||||
import Create from './modals/Create';
|
||||
import Update from './modals/Update';
|
||||
|
||||
export default function Index({ title = '超市老板' }) {
|
||||
return (
|
||||
<MyPageContainer title={title}>
|
||||
<ProTable
|
||||
{...MyProTableProps.props}
|
||||
request={async (params, sort) =>
|
||||
MyProTableProps.request(params, sort, Apis.Bosses.List)
|
||||
}
|
||||
toolBarRender={(action) => [
|
||||
<Create key="Create" reload={action?.reload} title={title} />,
|
||||
]}
|
||||
columns={[
|
||||
MyColumns.ID(),
|
||||
{
|
||||
title: '简称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '全称',
|
||||
dataIndex: 'full_name',
|
||||
},
|
||||
{
|
||||
dataIndex: 'address',
|
||||
title: '地址',
|
||||
},
|
||||
{
|
||||
dataIndex: 'contact',
|
||||
title: '联系人',
|
||||
},
|
||||
{
|
||||
dataIndex: 'phone',
|
||||
title: '联系电话',
|
||||
},
|
||||
MyColumns.CreatedAt(),
|
||||
MyColumns.Option({
|
||||
render: (_, item: any, index, action) => (
|
||||
<Space key={index}>
|
||||
<Update item={item} reload={action?.reload} title={title} />
|
||||
<MyButtons.Delete
|
||||
onConfirm={() =>
|
||||
Apis.Bosses.Delete({ id: item.id }).then(() =>
|
||||
action?.reload(),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
</MyPageContainer>
|
||||
);
|
||||
}
|
||||
54
src/pages/bosses/modals/Create.tsx
Normal file
54
src/pages/bosses/modals/Create.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Create(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.Bosses.Store>
|
||||
{...MyModalFormProps.props}
|
||||
title={`添加${props.title}`}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
trigger={<MyButtons.Create title={`添加${props.title}`} />}
|
||||
onFinish={async (values) =>
|
||||
Apis.Bosses.Store(values)
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
key: 'name',
|
||||
title: '简称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'full_name',
|
||||
title: '全称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
title: '地址',
|
||||
},
|
||||
{
|
||||
key: 'contact',
|
||||
title: '联系人',
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
title: '联系电话',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
55
src/pages/bosses/modals/Update.tsx
Normal file
55
src/pages/bosses/modals/Update.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Update(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.Bosses.Update>
|
||||
{...MyModalFormProps.props}
|
||||
title={`编辑${props.title}`}
|
||||
trigger={<MyButtons.Edit />}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
request={() => Promise.resolve(props.item)}
|
||||
onFinish={async (values) =>
|
||||
Apis.Bosses.Update({ ...values, id: props.item?.id ?? 0 })
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
key: 'name',
|
||||
title: '简称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'full_name',
|
||||
title: '全称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
title: '地址',
|
||||
},
|
||||
{
|
||||
key: 'contact',
|
||||
title: '联系人',
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
title: '联系电话',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
55
src/pages/factories/index.tsx
Normal file
55
src/pages/factories/index.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import {
|
||||
MyButtons,
|
||||
MyColumns,
|
||||
MyPageContainer,
|
||||
MyProTableProps,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
import { Space } from 'antd';
|
||||
import Create from './modals/Create';
|
||||
import Update from './modals/Update';
|
||||
|
||||
export default function Index({ title = '下游厂家' }) {
|
||||
return (
|
||||
<MyPageContainer title={title}>
|
||||
<ProTable
|
||||
{...MyProTableProps.props}
|
||||
search={false}
|
||||
request={async (params, sort) =>
|
||||
MyProTableProps.request(params, sort, Apis.Factories.List)
|
||||
}
|
||||
toolBarRender={(action) => [
|
||||
<Create key="Create" reload={action?.reload} title={title} />,
|
||||
]}
|
||||
columns={[
|
||||
MyColumns.ID(),
|
||||
{
|
||||
title: '厂家名称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '下游公钥',
|
||||
dataIndex: 'public_key',
|
||||
},
|
||||
MyColumns.CreatedAt(),
|
||||
MyColumns.UpdatedAt(),
|
||||
MyColumns.Option({
|
||||
render: (_, item: any, index, action) => (
|
||||
<Space key={index}>
|
||||
<Update item={item} reload={action?.reload} title={title} />
|
||||
<MyButtons.Delete
|
||||
onConfirm={() =>
|
||||
Apis.Factories.Delete({ id: item.id }).then(() =>
|
||||
action?.reload(),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
</MyPageContainer>
|
||||
);
|
||||
}
|
||||
43
src/pages/factories/modals/Create.tsx
Normal file
43
src/pages/factories/modals/Create.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Create(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.Factories.Store>
|
||||
{...MyModalFormProps.props}
|
||||
title={`添加${props.title}`}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
trigger={<MyButtons.Create title={`添加${props.title}`} />}
|
||||
onFinish={async (values) =>
|
||||
Apis.Factories.Store(values)
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
key: 'name',
|
||||
title: '厂家名称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'public_key',
|
||||
title: '下游公钥',
|
||||
valueType: 'textarea',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
44
src/pages/factories/modals/Update.tsx
Normal file
44
src/pages/factories/modals/Update.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Update(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.Factories.Update>
|
||||
{...MyModalFormProps.props}
|
||||
title={`编辑${props.title}`}
|
||||
trigger={<MyButtons.Edit />}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
request={() => Promise.resolve(props.item)}
|
||||
onFinish={async (values) =>
|
||||
Apis.Factories.Update({ ...values, id: props.item?.id ?? 0 })
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
key: 'name',
|
||||
title: '厂家名称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'public_key',
|
||||
title: '下游公钥',
|
||||
valueType: 'textarea',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
3
src/pages/index.tsx
Normal file
3
src/pages/index.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
export default function index() {
|
||||
return <div>index</div>;
|
||||
}
|
||||
75
src/pages/loan_companies/index.tsx
Normal file
75
src/pages/loan_companies/index.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import {
|
||||
MyButtons,
|
||||
MyColumns,
|
||||
MyPageContainer,
|
||||
MyProTableProps,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { LoanCompaniesTypeEnum } from '@/gen/Enums';
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
import { Space } from 'antd';
|
||||
import Create from './modals/Create';
|
||||
import Update from './modals/Update';
|
||||
|
||||
export default function Index({ title = '资金方' }) {
|
||||
return (
|
||||
<MyPageContainer title={title}>
|
||||
<ProTable
|
||||
{...MyProTableProps.props}
|
||||
request={async (params, sort) =>
|
||||
MyProTableProps.request(params, sort, Apis.LoanCompanies.List)
|
||||
}
|
||||
toolBarRender={(action) => [
|
||||
<Create key="Create" reload={action?.reload} title={title} />,
|
||||
]}
|
||||
columns={[
|
||||
MyColumns.ID(),
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
valueEnum: LoanCompaniesTypeEnum,
|
||||
},
|
||||
{
|
||||
title: '简称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '全称',
|
||||
dataIndex: 'full_name',
|
||||
},
|
||||
{
|
||||
title: '上游商户号',
|
||||
dataIndex: 'up_merchant_no',
|
||||
},
|
||||
{
|
||||
dataIndex: 'address',
|
||||
title: '地址',
|
||||
},
|
||||
{
|
||||
dataIndex: 'contact',
|
||||
title: '联系人',
|
||||
},
|
||||
{
|
||||
dataIndex: 'phone',
|
||||
title: '联系电话',
|
||||
},
|
||||
MyColumns.CreatedAt(),
|
||||
MyColumns.Option({
|
||||
render: (_, item: any, index, action) => (
|
||||
<Space key={index}>
|
||||
<Update item={item} reload={action?.reload} title={title} />
|
||||
<MyButtons.Delete
|
||||
onConfirm={() =>
|
||||
Apis.LoanCompanies.Delete({ id: item.id }).then(() =>
|
||||
action?.reload(),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
</MyPageContainer>
|
||||
);
|
||||
}
|
||||
66
src/pages/loan_companies/modals/Create.tsx
Normal file
66
src/pages/loan_companies/modals/Create.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyColumns,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { LoanCompaniesTypeEnum } from '@/gen/Enums';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Create(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.LoanCompanies.Store>
|
||||
{...MyModalFormProps.props}
|
||||
title={`添加${props.title}`}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
trigger={<MyButtons.Create title={`添加${props.title}`} />}
|
||||
onFinish={async (values) =>
|
||||
Apis.LoanCompanies.Store(values)
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
MyColumns.EnumTag({
|
||||
key: 'type',
|
||||
title: '类型',
|
||||
valueEnum: LoanCompaniesTypeEnum,
|
||||
}),
|
||||
{
|
||||
key: 'name',
|
||||
title: '简称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'full_name',
|
||||
title: '全称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'up_merchant_no',
|
||||
title: '上游商户号',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
title: '地址',
|
||||
},
|
||||
{
|
||||
key: 'contact',
|
||||
title: '联系人',
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
title: '联系电话',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
67
src/pages/loan_companies/modals/Update.tsx
Normal file
67
src/pages/loan_companies/modals/Update.tsx
Normal file
@ -0,0 +1,67 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyColumns,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { LoanCompaniesTypeEnum } from '@/gen/Enums';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Update(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.LoanCompanies.Update>
|
||||
{...MyModalFormProps.props}
|
||||
title={`编辑${props.title}`}
|
||||
trigger={<MyButtons.Edit />}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
request={() => Promise.resolve(props.item)}
|
||||
onFinish={async (values) =>
|
||||
Apis.LoanCompanies.Update({ ...values, id: props.item?.id ?? 0 })
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
MyColumns.EnumTag({
|
||||
key: 'type',
|
||||
title: '类型',
|
||||
valueEnum: LoanCompaniesTypeEnum,
|
||||
}),
|
||||
{
|
||||
key: 'name',
|
||||
title: '简称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'full_name',
|
||||
title: '全称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'up_merchant_no',
|
||||
title: '上游商户号',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
title: '地址',
|
||||
},
|
||||
{
|
||||
key: 'contact',
|
||||
title: '联系人',
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
title: '联系电话',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
104
src/pages/loan_daily_details/index.tsx
Normal file
104
src/pages/loan_daily_details/index.tsx
Normal file
@ -0,0 +1,104 @@
|
||||
import {
|
||||
MyButtons,
|
||||
MyColumns,
|
||||
MyPageContainer,
|
||||
MyProTableProps,
|
||||
} from '@/common';
|
||||
import { Selects } from '@/components/Selects';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { LoansOrderStatusEnum } from '@/gen/Enums';
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function Index({ title = '还款记录' }) {
|
||||
const [query, setQuery] = useState();
|
||||
|
||||
return (
|
||||
<MyPageContainer title={`${title}管理`}>
|
||||
<ProTable
|
||||
{...MyProTableProps.props}
|
||||
request={async (params, sort) =>
|
||||
MyProTableProps.request(
|
||||
params,
|
||||
sort,
|
||||
Apis.LoanDailyDetails.List,
|
||||
setQuery,
|
||||
)
|
||||
}
|
||||
toolBarRender={() => [
|
||||
<MyButtons.Export
|
||||
title="还款计划导出"
|
||||
key="Export"
|
||||
api={Apis.LoanDailyDetails.Export}
|
||||
params={query}
|
||||
/>,
|
||||
]}
|
||||
columns={[
|
||||
MyColumns.ID(),
|
||||
{
|
||||
title: '所属商户',
|
||||
dataIndex: 'merchants_name',
|
||||
search: false,
|
||||
},
|
||||
Selects.Merchants({
|
||||
hideInTable: true,
|
||||
}),
|
||||
{
|
||||
title: '所属资金方',
|
||||
dataIndex: 'loan_companies_name',
|
||||
search: false,
|
||||
},
|
||||
Selects.LoanCompany({
|
||||
hideInTable: true,
|
||||
}),
|
||||
MyColumns.EnumTag({
|
||||
title: '订单状态',
|
||||
dataIndex: 'order_status',
|
||||
valueEnum: LoansOrderStatusEnum,
|
||||
}),
|
||||
{
|
||||
title: '日期',
|
||||
dataIndex: 'date',
|
||||
},
|
||||
{
|
||||
title: '应还-本金',
|
||||
dataIndex: 'due_principal',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
{
|
||||
title: '应还-利息',
|
||||
dataIndex: 'due_interest',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
{
|
||||
title: '应还-递延本金',
|
||||
dataIndex: 'due_deferred_principal',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
{
|
||||
title: '应还-总金额',
|
||||
dataIndex: 'due_total_amount',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
{
|
||||
title: '已还-总金额',
|
||||
dataIndex: 'paid_total_amount',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
{
|
||||
title: '未还-总金额',
|
||||
dataIndex: 'remaining_total_amount',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
// MyColumns.CreatedAt(),
|
||||
]}
|
||||
/>
|
||||
</MyPageContainer>
|
||||
);
|
||||
}
|
||||
193
src/pages/loans/index.tsx
Normal file
193
src/pages/loans/index.tsx
Normal file
@ -0,0 +1,193 @@
|
||||
import {
|
||||
MyButtons,
|
||||
MyColumns,
|
||||
MyPageContainer,
|
||||
MyProTableProps,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { LoansOrderStatusEnum, LoansTypeEnum } from '@/gen/Enums';
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
import { Space } from 'antd';
|
||||
import Create from './modals/Create';
|
||||
|
||||
export default function Index({ title = '贷款订单' }) {
|
||||
return (
|
||||
<MyPageContainer title={`${title}管理`}>
|
||||
<ProTable
|
||||
{...MyProTableProps.props}
|
||||
request={async (params, sort) =>
|
||||
MyProTableProps.request(params, sort, Apis.Loans.List)
|
||||
}
|
||||
toolBarRender={(action) => [
|
||||
<Create key="Create" reload={action?.reload} title={title} />,
|
||||
]}
|
||||
columns={[
|
||||
MyColumns.ID(),
|
||||
{
|
||||
title: '所属商户',
|
||||
dataIndex: ['merchant', 'name'],
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '所属资金方',
|
||||
dataIndex: ['loan_company', 'name'],
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
MyColumns.EnumTag({
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
valueEnum: LoansTypeEnum,
|
||||
}),
|
||||
MyColumns.EnumTag({
|
||||
title: '订单状态',
|
||||
dataIndex: 'order_status',
|
||||
valueEnum: LoansOrderStatusEnum,
|
||||
}),
|
||||
{
|
||||
title: '贷款天数',
|
||||
dataIndex: 'loan_days',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '开始时间',
|
||||
dataIndex: 'started_at',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '结束时间',
|
||||
dataIndex: 'ended_at',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '总应还',
|
||||
search: false,
|
||||
children: [
|
||||
{
|
||||
title: '本金',
|
||||
dataIndex: 'total_due_principal',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
{
|
||||
title: '利息',
|
||||
dataIndex: 'total_due_interest',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
{
|
||||
title: '总金额',
|
||||
dataIndex: 'total_due_amount',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '每日应还',
|
||||
search: false,
|
||||
children: [
|
||||
{
|
||||
title: '本金',
|
||||
dataIndex: 'daily_due_principal',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
{
|
||||
title: '利息',
|
||||
dataIndex: 'daily_due_interest',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
{
|
||||
title: '总金额',
|
||||
dataIndex: 'daily_due_amount',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
title: '已还',
|
||||
search: false,
|
||||
children: [
|
||||
{
|
||||
title: '天数',
|
||||
dataIndex: 'repaid_days',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '本金',
|
||||
dataIndex: 'repaid_total_principal',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
{
|
||||
title: '利息',
|
||||
dataIndex: 'repaid_total_interest',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
{
|
||||
title: '总金额',
|
||||
dataIndex: 'repaid_total_amount',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '剩余应还',
|
||||
search: false,
|
||||
children: [
|
||||
{
|
||||
title: '天数',
|
||||
dataIndex: 'remaining_due_days',
|
||||
search: false,
|
||||
},
|
||||
|
||||
{
|
||||
title: '本金',
|
||||
dataIndex: 'remaining_due_principal',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
|
||||
{
|
||||
title: '利息',
|
||||
dataIndex: 'remaining_due_interest',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
|
||||
{
|
||||
title: '总金额',
|
||||
dataIndex: 'remaining_due_amount',
|
||||
search: false,
|
||||
valueType: 'money',
|
||||
},
|
||||
],
|
||||
},
|
||||
MyColumns.CreatedAt(),
|
||||
MyColumns.Option({
|
||||
render: (_, item: any, index, action) => (
|
||||
<Space key={index}>
|
||||
<MyButtons.Delete
|
||||
onConfirm={() =>
|
||||
Apis.Loans.Delete({ id: item.id }).then(() =>
|
||||
action?.reload(),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
</MyPageContainer>
|
||||
);
|
||||
}
|
||||
90
src/pages/loans/modals/Create.tsx
Normal file
90
src/pages/loans/modals/Create.tsx
Normal file
@ -0,0 +1,90 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyFormItems,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Selects } from '@/components/Selects';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { LoansTypeEnum } from '@/gen/Enums';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { Form, message } from 'antd';
|
||||
|
||||
export default function Create(props: MyBetaModalFormProps) {
|
||||
const [form] = Form.useForm();
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.Loans.Store>
|
||||
{...MyModalFormProps.props}
|
||||
title={`添加${props.title}`}
|
||||
width="600px"
|
||||
wrapperCol={{ span: 24 }}
|
||||
trigger={<MyButtons.Create title={`添加${props.title}`} />}
|
||||
onFinish={async (values) =>
|
||||
Apis.Loans.Store(values)
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
form={form}
|
||||
onOpenChange={(open: boolean) => {
|
||||
if (open) {
|
||||
form.setFieldsValue({ repayment_date: 21, loan_terms: 3 });
|
||||
}
|
||||
}}
|
||||
columns={[
|
||||
Selects.Merchants({
|
||||
colProps: { span: 24 },
|
||||
required: true,
|
||||
}),
|
||||
Selects.LoanCompany({
|
||||
colProps: { span: 24 },
|
||||
required: true,
|
||||
}),
|
||||
{
|
||||
title: '名称',
|
||||
key: 'name',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
MyFormItems.EnumRadio({
|
||||
title: '算法类型',
|
||||
key: 'type',
|
||||
valueEnum: LoansTypeEnum,
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
}),
|
||||
{
|
||||
title: '贷款天数',
|
||||
key: 'loan_days',
|
||||
colProps: { span: 12 },
|
||||
formItemProps: { ...rulesHelper.number },
|
||||
width: '100%',
|
||||
},
|
||||
{
|
||||
title: '还款起始日',
|
||||
key: 'started_at',
|
||||
valueType: 'date',
|
||||
colProps: { span: 12 },
|
||||
formItemProps: { ...rulesHelper.number },
|
||||
width: '100%',
|
||||
},
|
||||
MyFormItems.Money({
|
||||
title: '总应还-本金',
|
||||
key: 'total_due_principal',
|
||||
colProps: { span: 12 },
|
||||
formItemProps: { ...rulesHelper.number },
|
||||
width: '100%',
|
||||
}),
|
||||
MyFormItems.Money({
|
||||
title: '总应还-利息',
|
||||
key: 'total_due_interest',
|
||||
colProps: { span: 12 },
|
||||
formItemProps: { ...rulesHelper.number },
|
||||
width: '100%',
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
5
src/pages/login.tsx
Normal file
5
src/pages/login.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { MyLoginPage1 } from '@/common';
|
||||
|
||||
export default function Login() {
|
||||
return <MyLoginPage1 />;
|
||||
}
|
||||
60
src/pages/markets/index.tsx
Normal file
60
src/pages/markets/index.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
import {
|
||||
MyButtons,
|
||||
MyColumns,
|
||||
MyPageContainer,
|
||||
MyProTableProps,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
import { Space } from 'antd';
|
||||
import Create from './modals/Create';
|
||||
import Update from './modals/Update';
|
||||
|
||||
export default function Index({ title = '上游市场' }) {
|
||||
return (
|
||||
<MyPageContainer title={title}>
|
||||
<ProTable
|
||||
{...MyProTableProps.props}
|
||||
search={false}
|
||||
request={async (params, sort) =>
|
||||
MyProTableProps.request(params, sort, Apis.Markets.List)
|
||||
}
|
||||
toolBarRender={(action) => [
|
||||
<Create key="Create" reload={action?.reload} title={title} />,
|
||||
]}
|
||||
columns={[
|
||||
MyColumns.ID(),
|
||||
{
|
||||
title: '市场名称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '市场编号',
|
||||
dataIndex: 'market_no',
|
||||
},
|
||||
{
|
||||
title: '上游公钥',
|
||||
dataIndex: 'public_key',
|
||||
search: false,
|
||||
},
|
||||
MyColumns.UpdatedAt(),
|
||||
MyColumns.CreatedAt(),
|
||||
MyColumns.Option({
|
||||
render: (_, item: any, index, action) => (
|
||||
<Space key={index}>
|
||||
<Update item={item} reload={action?.reload} title={title} />
|
||||
<MyButtons.Delete
|
||||
onConfirm={() =>
|
||||
Apis.Markets.Delete({ id: item.id }).then(() =>
|
||||
action?.reload(),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
</MyPageContainer>
|
||||
);
|
||||
}
|
||||
48
src/pages/markets/modals/Create.tsx
Normal file
48
src/pages/markets/modals/Create.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Create(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.Markets.Store>
|
||||
{...MyModalFormProps.props}
|
||||
title={`添加${props.title}`}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
trigger={<MyButtons.Create title={`添加${props.title}`} />}
|
||||
onFinish={async (values) =>
|
||||
Apis.Markets.Store(values)
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
key: 'name',
|
||||
title: '市场名称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'market_no',
|
||||
title: '市场编号',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'public_key',
|
||||
title: '上游公钥',
|
||||
valueType: 'textarea',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
49
src/pages/markets/modals/Update.tsx
Normal file
49
src/pages/markets/modals/Update.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Update(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.Markets.Update>
|
||||
{...MyModalFormProps.props}
|
||||
title={`编辑${props.title}`}
|
||||
trigger={<MyButtons.Edit />}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
request={() => Promise.resolve(props.item)}
|
||||
onFinish={async (values) =>
|
||||
Apis.Markets.Update({ ...values, id: props.item?.id ?? 0 })
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
key: 'name',
|
||||
title: '市场名称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'market_no',
|
||||
title: '市场编号',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'public_key',
|
||||
title: '上游公钥',
|
||||
valueType: 'textarea',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
136
src/pages/merchants/index.tsx
Normal file
136
src/pages/merchants/index.tsx
Normal file
@ -0,0 +1,136 @@
|
||||
import {
|
||||
MyButtons,
|
||||
MyColumns,
|
||||
MyPageContainer,
|
||||
MyProTableProps,
|
||||
} from '@/common';
|
||||
import { Selects } from '@/components/Selects';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
import { Space } from 'antd';
|
||||
import Create from './modals/Create';
|
||||
import CreateCounters from './modals/CreateCounters';
|
||||
import Update from './modals/Update';
|
||||
import UpdateCounters from './modals/UpdateCounters';
|
||||
|
||||
export default function Index({ title = '商户' }) {
|
||||
const ExpandedRowRender = (e: any) => {
|
||||
const columns = [
|
||||
{ title: 'id', dataIndex: 'id' },
|
||||
{ title: '上游', dataIndex: ['market', 'name'] },
|
||||
{ title: '名称', dataIndex: 'name' },
|
||||
{ title: '上游商家编号', dataIndex: 'up_merchant_no' },
|
||||
{ title: '平台商户号', dataIndex: 'plat_merchant_no' },
|
||||
{ title: '平台柜台号', dataIndex: 'plat_counter_no' },
|
||||
MyColumns.Option({
|
||||
render: (_, item: any, index, action) => (
|
||||
<Space key={index}>
|
||||
<UpdateCounters item={item} reload={action?.reload} title="柜台" />
|
||||
<MyButtons.Delete
|
||||
onConfirm={() =>
|
||||
Apis.MerchantCounters.Delete({ id: item.id }).then(() =>
|
||||
action?.reload(),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
}),
|
||||
];
|
||||
return (
|
||||
<ProTable
|
||||
{...MyProTableProps.props}
|
||||
pagination={false}
|
||||
columns={columns}
|
||||
request={async (params, sort) =>
|
||||
MyProTableProps.request(
|
||||
{ ...params, merchants_id: e?.id },
|
||||
sort,
|
||||
Apis.MerchantCounters.List,
|
||||
)
|
||||
}
|
||||
toolBarRender={(action) => [
|
||||
<CreateCounters
|
||||
key="Create"
|
||||
reload={action?.reload}
|
||||
title="添加柜台"
|
||||
item={{ merchants_id: e?.id }}
|
||||
/>,
|
||||
]}
|
||||
headerTitle="柜台信息"
|
||||
search={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<MyPageContainer title={title}>
|
||||
<ProTable
|
||||
{...MyProTableProps.props}
|
||||
request={async (params, sort) =>
|
||||
MyProTableProps.request(params, sort, Apis.Merchants.List)
|
||||
}
|
||||
toolBarRender={(action) => [
|
||||
<Create key="Create" reload={action?.reload} title={title} />,
|
||||
]}
|
||||
expandable={{
|
||||
expandedRowRender: ExpandedRowRender,
|
||||
}}
|
||||
columns={[
|
||||
MyColumns.ID(),
|
||||
Selects.Agents({ dataIndex: 'agents_name' }),
|
||||
Selects.Bosses({ dataIndex: 'bosses_name' }),
|
||||
Selects.Factories({ dataIndex: 'factories_name' }),
|
||||
Selects.Platforms({ dataIndex: 'platforms_name' }),
|
||||
// {
|
||||
// title: '代理',
|
||||
// dataIndex: 'agents_name',
|
||||
// search: false,
|
||||
// },
|
||||
// {
|
||||
// title: '厂家',
|
||||
// dataIndex: 'factories_name',
|
||||
// search: false,
|
||||
// },
|
||||
// {
|
||||
// title: '平台',
|
||||
// dataIndex: 'platforms_name',
|
||||
// search: false,
|
||||
// },
|
||||
// {
|
||||
// title: '老板',
|
||||
// dataIndex: 'bosses_name',
|
||||
// search: false,
|
||||
// },
|
||||
{
|
||||
title: '简称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '全称',
|
||||
dataIndex: 'full_name',
|
||||
},
|
||||
{
|
||||
title: '平台商户号',
|
||||
dataIndex: 'plat_merchant_no',
|
||||
},
|
||||
MyColumns.CreatedAt(),
|
||||
MyColumns.Option({
|
||||
render: (_, item: any, index, action) => (
|
||||
<Space key={index}>
|
||||
<Update item={item} reload={action?.reload} title={title} />
|
||||
<MyButtons.Delete
|
||||
onConfirm={() =>
|
||||
Apis.Merchants.Delete({ id: item.id }).then(() =>
|
||||
action?.reload(),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
</MyPageContainer>
|
||||
);
|
||||
}
|
||||
47
src/pages/merchants/modals/Create.tsx
Normal file
47
src/pages/merchants/modals/Create.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Selects } from '@/components/Selects';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Create(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.Merchants.Store>
|
||||
{...MyModalFormProps.props}
|
||||
title={`添加${props.title}`}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
trigger={<MyButtons.Create title={`添加${props.title}`} />}
|
||||
onFinish={async (values) =>
|
||||
Apis.Merchants.Store(values)
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
Selects.Agents({ required: true }),
|
||||
Selects.Bosses({ required: true }),
|
||||
Selects.Factories({ required: true }),
|
||||
Selects.Platforms({ required: true }),
|
||||
{
|
||||
key: 'name',
|
||||
title: '简称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'full_name',
|
||||
title: '全称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
88
src/pages/merchants/modals/CreateCounters.tsx
Normal file
88
src/pages/merchants/modals/CreateCounters.tsx
Normal file
@ -0,0 +1,88 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyFormItems,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Selects } from '@/components/Selects';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { CommissionTypeEnum } from '@/gen/Enums';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function CreateCounters(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.MerchantCounters.Store>
|
||||
{...MyModalFormProps.props}
|
||||
title={`添加${props.title}`}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="800px"
|
||||
trigger={<MyButtons.Create title={`添加${props.title}`} />}
|
||||
onFinish={async (values) =>
|
||||
Apis.MerchantCounters.Store({ ...props?.item, ...values })
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
Selects.Markets({ required: true }),
|
||||
{
|
||||
key: 'name',
|
||||
title: '名称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'up_merchant_no',
|
||||
title: '上游商家编号',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
valueType: 'formList',
|
||||
dataIndex: 'commissions',
|
||||
title: '分佣配置',
|
||||
formItemProps: { ...rulesHelper.array },
|
||||
fieldProps: {
|
||||
copyIconProps: false,
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
valueType: 'group',
|
||||
colProps: { span: 24 },
|
||||
columns: [
|
||||
{
|
||||
key: 'seq',
|
||||
colProps: { span: 3 },
|
||||
title: '序号',
|
||||
width: '100%',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
MyFormItems.EnumSelect({
|
||||
key: 'type',
|
||||
title: '类型',
|
||||
colProps: { span: 4 },
|
||||
valueEnum: CommissionTypeEnum,
|
||||
required: true,
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
}),
|
||||
{
|
||||
key: 'merchant_no',
|
||||
colProps: { span: 9 },
|
||||
title: '商户编号',
|
||||
},
|
||||
{
|
||||
key: 'percent',
|
||||
colProps: { span: 8 },
|
||||
title: '分佣比例%(0为自动计算)',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
48
src/pages/merchants/modals/Update.tsx
Normal file
48
src/pages/merchants/modals/Update.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Selects } from '@/components/Selects';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Update(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.Merchants.Update>
|
||||
{...MyModalFormProps.props}
|
||||
title={`编辑${props.title}`}
|
||||
trigger={<MyButtons.Edit />}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
request={() => Promise.resolve(props.item)}
|
||||
onFinish={async (values) =>
|
||||
Apis.Merchants.Update({ ...values, id: props.item?.id ?? 0 })
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
Selects.Agents({ required: true }),
|
||||
Selects.Bosses({ required: true }),
|
||||
Selects.Factories({ required: true }),
|
||||
Selects.Platforms({ required: true }),
|
||||
{
|
||||
key: 'name',
|
||||
title: '简称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'full_name',
|
||||
title: '全称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
89
src/pages/merchants/modals/UpdateCounters.tsx
Normal file
89
src/pages/merchants/modals/UpdateCounters.tsx
Normal file
@ -0,0 +1,89 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyFormItems,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Selects } from '@/components/Selects';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { CommissionTypeEnum } from '@/gen/Enums';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function UpdateCounters(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.MerchantCounters.Update>
|
||||
{...MyModalFormProps.props}
|
||||
title={`编辑${props.title}`}
|
||||
trigger={<MyButtons.Edit />}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="800px"
|
||||
request={() => Promise.resolve(props.item)}
|
||||
onFinish={async (values) =>
|
||||
Apis.MerchantCounters.Update({ ...values, id: props.item?.id ?? 0 })
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
Selects.Markets({ required: true }),
|
||||
{
|
||||
key: 'name',
|
||||
title: '名称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'up_merchant_no',
|
||||
title: '上游商家编号',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
valueType: 'formList',
|
||||
dataIndex: 'commissions',
|
||||
title: '分佣配置',
|
||||
formItemProps: { ...rulesHelper.array },
|
||||
fieldProps: {
|
||||
copyIconProps: false,
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
valueType: 'group',
|
||||
colProps: { span: 24 },
|
||||
columns: [
|
||||
{
|
||||
key: 'seq',
|
||||
colProps: { span: 3 },
|
||||
title: '序号',
|
||||
width: '100%',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
MyFormItems.EnumSelect({
|
||||
key: 'type',
|
||||
title: '类型',
|
||||
colProps: { span: 4 },
|
||||
valueEnum: CommissionTypeEnum,
|
||||
required: true,
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
}),
|
||||
{
|
||||
key: 'merchant_no',
|
||||
colProps: { span: 9 },
|
||||
title: '商户编号',
|
||||
},
|
||||
{
|
||||
key: 'percent',
|
||||
colProps: { span: 8 },
|
||||
title: '分佣比例%(0为自动计算)',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
71
src/pages/org_users/index.tsx
Normal file
71
src/pages/org_users/index.tsx
Normal file
@ -0,0 +1,71 @@
|
||||
import {
|
||||
MyButtons,
|
||||
MyColumns,
|
||||
MyPageContainer,
|
||||
MyProTableProps,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
import { Space } from 'antd';
|
||||
import Create from './modals/Create';
|
||||
import Update from './modals/Update';
|
||||
import { OrgUsersTypeEnum } from '@/gen/Enums';
|
||||
|
||||
export default function Index({ title = '账号管理' }) {
|
||||
return (
|
||||
<MyPageContainer title={title}>
|
||||
<ProTable
|
||||
{...MyProTableProps.props}
|
||||
request={async (params, sort) =>
|
||||
MyProTableProps.request(params, sort, Apis.OrgUsers.List)
|
||||
}
|
||||
toolBarRender={(action) => [
|
||||
<Create key="Create" reload={action?.reload} title={title} />,
|
||||
]}
|
||||
columns={[
|
||||
MyColumns.ID(),
|
||||
MyColumns.EnumTag({
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
valueEnum: OrgUsersTypeEnum,
|
||||
}),
|
||||
{
|
||||
title: '机构名称',
|
||||
dataIndex: ['org', 'name'],
|
||||
key: 'org_name'
|
||||
},
|
||||
{
|
||||
title: '账号',
|
||||
dataIndex: 'username',
|
||||
},
|
||||
{
|
||||
title: 'last_login_ip',
|
||||
dataIndex: 'last_login_ip',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: 'last_login_at',
|
||||
dataIndex: 'last_login_at',
|
||||
search: false,
|
||||
},
|
||||
MyColumns.UpdatedAt(),
|
||||
MyColumns.CreatedAt(),
|
||||
MyColumns.Option({
|
||||
render: (_, item: any, index, action) => (
|
||||
<Space key={index}>
|
||||
<Update item={item} reload={action?.reload} title={title} />
|
||||
<MyButtons.Delete
|
||||
onConfirm={() =>
|
||||
Apis.OrgUsers.Delete({ id: item.id }).then(() =>
|
||||
action?.reload(),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
</MyPageContainer>
|
||||
);
|
||||
}
|
||||
57
src/pages/org_users/modals/Create.tsx
Normal file
57
src/pages/org_users/modals/Create.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyColumns,
|
||||
MyFormItems,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Selects } from '@/components/Selects';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { OrgUsersTypeEnum } from '@/gen/Enums';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Create(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.OrgUsers.Store>
|
||||
{...MyModalFormProps.props}
|
||||
title={`添加${props.title}`}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
trigger={<MyButtons.Create title={`添加${props.title}`} />}
|
||||
onFinish={async (values) =>
|
||||
Apis.OrgUsers.Store(values)
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
MyFormItems.EnumRadio({
|
||||
key: 'type',
|
||||
title: '类型',
|
||||
valueEnum: OrgUsersTypeEnum
|
||||
}),
|
||||
{
|
||||
key: 'org_name',
|
||||
title: '主体名称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'username',
|
||||
title: '用户名',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'password',
|
||||
title: '密码',
|
||||
valueType: 'password',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
55
src/pages/org_users/modals/Update.tsx
Normal file
55
src/pages/org_users/modals/Update.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Selects } from '@/components/Selects';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Update(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.OrgUsers.Update>
|
||||
{...MyModalFormProps.props}
|
||||
title={`编辑${props.title}`}
|
||||
trigger={<MyButtons.Edit />}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
request={() => Promise.resolve(props.item)}
|
||||
onFinish={async (values) =>
|
||||
Apis.OrgUsers.Update({ ...values, id: props.item?.id ?? 0 })
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
key: 'type',
|
||||
title: '类型',
|
||||
readonly: true,
|
||||
},
|
||||
{
|
||||
key: ['org','name'],
|
||||
title: '机构名称',
|
||||
readonly: true,
|
||||
},
|
||||
{
|
||||
key: 'username',
|
||||
title: '用户名',
|
||||
readonly: true,
|
||||
},
|
||||
{
|
||||
key: 'password',
|
||||
title: '密码',
|
||||
valueType: 'password',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
75
src/pages/pay_orders/index.tsx
Normal file
75
src/pages/pay_orders/index.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import { MyColumns, MyPageContainer, MyProTableProps } from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { PayOrderStatusEnum, PayOrdersTypeEnum } from '@/gen/Enums';
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
|
||||
export default function Index({ title = '支付明细' }) {
|
||||
return (
|
||||
<MyPageContainer title={title}>
|
||||
<ProTable
|
||||
{...MyProTableProps.props}
|
||||
request={async (params, sort) =>
|
||||
MyProTableProps.request(params, sort, Apis.PayOrders.List)
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
title: '交易时间',
|
||||
search: false,
|
||||
dataIndex: 'up_order_at',
|
||||
},
|
||||
{
|
||||
title: '交易时间范围',
|
||||
key: 'up_order_at_range',
|
||||
valueType: 'dateRange',
|
||||
hideInTable: true,
|
||||
},
|
||||
{
|
||||
title: '商户',
|
||||
dataIndex: 'merchant_name',
|
||||
},
|
||||
MyColumns.EnumTag({
|
||||
title: '交易类型',
|
||||
dataIndex: 'type',
|
||||
valueEnum: PayOrdersTypeEnum,
|
||||
}),
|
||||
{
|
||||
title: '下单金额',
|
||||
dataIndex: 'amount_order',
|
||||
},
|
||||
MyColumns.EnumTag({
|
||||
title: '订单状态',
|
||||
dataIndex: 'order_status',
|
||||
valueEnum: PayOrderStatusEnum,
|
||||
}),
|
||||
{
|
||||
title: '平台商户号',
|
||||
dataIndex: 'plat_merchant_no',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '平台柜台号',
|
||||
dataIndex: 'plat_counter_no',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '上游商家编号',
|
||||
dataIndex: 'up_merchant_no',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '下游订单号',
|
||||
dataIndex: 'down_order_no',
|
||||
},
|
||||
{
|
||||
title: '上游订单号',
|
||||
dataIndex: 'up_order_no',
|
||||
},
|
||||
{
|
||||
title: '上游流水号',
|
||||
dataIndex: 'up_tx_no',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</MyPageContainer>
|
||||
);
|
||||
}
|
||||
61
src/pages/platforms/index.tsx
Normal file
61
src/pages/platforms/index.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import {
|
||||
MyButtons,
|
||||
MyColumns,
|
||||
MyPageContainer,
|
||||
MyProTableProps,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
import { Space } from 'antd';
|
||||
import Create from './modals/Create';
|
||||
import Update from './modals/Update';
|
||||
|
||||
export default function Index({ title = '平台' }) {
|
||||
return (
|
||||
<MyPageContainer title={title}>
|
||||
<ProTable
|
||||
{...MyProTableProps.props}
|
||||
search={false}
|
||||
request={async (params, sort) =>
|
||||
MyProTableProps.request(params, sort, Apis.Platforms.List)
|
||||
}
|
||||
toolBarRender={(action) => [
|
||||
<Create key="Create" reload={action?.reload} title={title} />,
|
||||
]}
|
||||
columns={[
|
||||
MyColumns.ID(),
|
||||
{
|
||||
title: '平台名称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '平台私钥',
|
||||
dataIndex: 'private_key',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: '平台公钥',
|
||||
dataIndex: 'public_key',
|
||||
search: false,
|
||||
},
|
||||
MyColumns.UpdatedAt(),
|
||||
MyColumns.CreatedAt(),
|
||||
MyColumns.Option({
|
||||
render: (_, item: any, index, action) => (
|
||||
<Space key={index}>
|
||||
<Update item={item} reload={action?.reload} title={title} />
|
||||
<MyButtons.Delete
|
||||
onConfirm={() =>
|
||||
Apis.Platforms.Delete({ id: item.id }).then(() =>
|
||||
action?.reload(),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
</MyPageContainer>
|
||||
);
|
||||
}
|
||||
49
src/pages/platforms/modals/Create.tsx
Normal file
49
src/pages/platforms/modals/Create.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Create(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.Platforms.Store>
|
||||
{...MyModalFormProps.props}
|
||||
title={`添加${props.title}`}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
trigger={<MyButtons.Create title={`添加${props.title}`} />}
|
||||
onFinish={async (values) =>
|
||||
Apis.Platforms.Store(values)
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
key: 'name',
|
||||
title: '平台名称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'private_key',
|
||||
title: '平台私钥',
|
||||
valueType: 'textarea',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'public_key',
|
||||
title: '平台公钥',
|
||||
valueType: 'textarea',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
50
src/pages/platforms/modals/Update.tsx
Normal file
50
src/pages/platforms/modals/Update.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import {
|
||||
MyBetaModalFormProps,
|
||||
MyButtons,
|
||||
MyModalFormProps,
|
||||
rulesHelper,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default function Update(props: MyBetaModalFormProps) {
|
||||
return (
|
||||
<BetaSchemaForm<ApiTypes.Platforms.Update>
|
||||
{...MyModalFormProps.props}
|
||||
title={`编辑${props.title}`}
|
||||
trigger={<MyButtons.Edit />}
|
||||
wrapperCol={{ span: 24 }}
|
||||
width="500px"
|
||||
request={() => Promise.resolve(props.item)}
|
||||
onFinish={async (values) =>
|
||||
Apis.Platforms.Update({ ...values, id: props.item?.id ?? 0 })
|
||||
.then(() => {
|
||||
props.reload?.();
|
||||
message.success(props.title + '成功');
|
||||
return true;
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
key: 'name',
|
||||
title: '平台名称',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'private_key',
|
||||
title: '平台私钥',
|
||||
valueType: 'textarea',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
{
|
||||
key: 'public_key',
|
||||
title: '平台公钥',
|
||||
valueType: 'textarea',
|
||||
formItemProps: { ...rulesHelper.text },
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
68
src/pages/system/admins/index.tsx
Normal file
68
src/pages/system/admins/index.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
import {
|
||||
MyButtons,
|
||||
MyColumns,
|
||||
MyPageContainer,
|
||||
MyProTableProps,
|
||||
renderTextHelper,
|
||||
} from '@/common';
|
||||
import { Apis } from '@/gen/Apis';
|
||||
import { ProTable } from '@ant-design/pro-components';
|
||||
import { Space } from 'antd';
|
||||
import Create from './modals/Create';
|
||||
import Update from './modals/Update';
|
||||
|
||||
export default function Index({ title = '管理员' }) {
|
||||
return (
|
||||
<MyPageContainer title={title}>
|
||||
<ProTable
|
||||
{...MyProTableProps.props}
|
||||
search={false}
|
||||
request={async (params, sort) =>
|
||||
MyProTableProps.request(params, sort, Apis.Admins.List)
|
||||
}
|
||||
toolBarRender={(action) => [
|
||||
<Create key="Create" reload={action?.reload} title={title} />,
|
||||
]}
|
||||
columns={[
|
||||
MyColumns.ID(),
|
||||
{
|
||||
title: '账号',
|
||||
dataIndex: 'username',
|
||||
},
|
||||
{
|
||||
title: '角色',
|
||||
dataIndex: 'roles',
|
||||
renderText: renderTextHelper.TagList,
|
||||
hideInSearch: true,
|
||||
},
|
||||
{
|
||||
title: 'last_login_ip',
|
||||
dataIndex: 'last_login_ip',
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
title: 'last_login_at',
|
||||
dataIndex: 'last_login_at',
|
||||
search: false,
|
||||
},
|
||||
MyColumns.UpdatedAt(),
|
||||
MyColumns.CreatedAt(),
|
||||
MyColumns.Option({
|
||||
render: (_, item: any, index, action) => (
|
||||
<Space key={index}>
|
||||
<Update item={item} reload={action?.reload} title={title} />
|
||||
<MyButtons.Delete
|
||||
onConfirm={() =>
|
||||
Apis.Admins.Delete({ id: item.id }).then(() =>
|
||||
action?.reload(),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
</MyPageContainer>
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user