@@ -0,0 +1,16 @@ | |||||
# http://editorconfig.org | |||||
root = true | |||||
[*] | |||||
indent_style = space | |||||
indent_size = 2 | |||||
end_of_line = lf | |||||
charset = utf-8 | |||||
trim_trailing_whitespace = true | |||||
insert_final_newline = true | |||||
[*.md] | |||||
trim_trailing_whitespace = false | |||||
[Makefile] | |||||
indent_style = tab |
@@ -0,0 +1,9 @@ | |||||
/lambda/ | |||||
/scripts | |||||
/config | |||||
.history | |||||
public | |||||
dist | |||||
.umi | |||||
mock | |||||
src |
@@ -0,0 +1,8 @@ | |||||
module.exports = { | |||||
extends: [require.resolve('@umijs/fabric/dist/eslint')], | |||||
globals: { | |||||
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true, | |||||
page: true, | |||||
REACT_APP_ENV: true, | |||||
}, | |||||
}; |
@@ -0,0 +1,23 @@ | |||||
**/*.svg | |||||
package.json | |||||
.umi | |||||
.umi-production | |||||
/dist | |||||
.dockerignore | |||||
.DS_Store | |||||
.eslintignore | |||||
*.png | |||||
*.toml | |||||
docker | |||||
.editorconfig | |||||
Dockerfile* | |||||
.gitignore | |||||
.prettierignore | |||||
LICENSE | |||||
.eslintcache | |||||
*.lock | |||||
yarn-error.log | |||||
.history | |||||
CNAME | |||||
/build | |||||
/public |
@@ -0,0 +1,5 @@ | |||||
const fabric = require('@umijs/fabric'); | |||||
module.exports = { | |||||
...fabric.prettier, | |||||
}; |
@@ -0,0 +1,5 @@ | |||||
const fabric = require('@umijs/fabric'); | |||||
module.exports = { | |||||
...fabric.stylelint, | |||||
}; |
@@ -0,0 +1,142 @@ | |||||
def getHost(){ | |||||
def remote = [:] | |||||
remote.name = 'eip' | |||||
remote.host = '10.6.1.50' | |||||
remote.user = 'root' | |||||
remote.port = 22 | |||||
remote.password = '120962839' | |||||
remote.allowAnyHosts = true | |||||
return remote | |||||
} | |||||
pipeline{ | |||||
agent any | |||||
parameters { | |||||
choice( | |||||
description: 'EIP环境', | |||||
name: 'environment', | |||||
choices: ['dev1'] | |||||
) | |||||
choice( | |||||
description: '执行操作(发布|回滚)', | |||||
name: 'operation', | |||||
choices: ['develop', 'rollback'] | |||||
) | |||||
choice( | |||||
description: '是否下载包', | |||||
name: 'isDownloadPackage', | |||||
choices: ['是','否'] | |||||
) | |||||
string( | |||||
name: 'tag', | |||||
defaultValue: '', | |||||
description: '版本tag' | |||||
) | |||||
} | |||||
stages | |||||
{ | |||||
stage('Prepare') { | |||||
steps { | |||||
script{ | |||||
echo "1.Prepare Stage" | |||||
echo "当前环境${params.environment}" | |||||
if(params.operation=='develop') | |||||
{ | |||||
checkout scm | |||||
script { | |||||
build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() | |||||
if (env.BRANCH_NAME != 'master') { | |||||
build_tag = "${env.BRANCH_NAME}-${build_tag}" | |||||
} | |||||
} | |||||
} | |||||
else{ | |||||
build_tag="${params.tag}" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
stage('Test') { | |||||
steps { | |||||
script{ | |||||
echo "2.Test Stage" | |||||
} | |||||
} | |||||
} | |||||
stage('Build') { | |||||
steps { | |||||
script{ | |||||
echo "3.Build Docker Image Stage" | |||||
if(params.operation=='develop') | |||||
{ | |||||
nodejs("nodejs") { | |||||
} | |||||
if(params.isDownloadPackage=='是') | |||||
{ | |||||
sh "npm install --unsafe-perm=true --allow-root" | |||||
sh "rm -rf ./dist/*" | |||||
sh "npm run build" | |||||
} | |||||
sh "docker build -t 10.2.1.24:10242/bpa/kitchenweb:${build_tag} ." | |||||
} | |||||
} | |||||
} | |||||
} | |||||
stage('Push') { | |||||
steps { | |||||
script{ | |||||
echo "4.Push Docker Image Stage" | |||||
withCredentials([usernamePassword(credentialsId: 'harbor', passwordVariable: 'harborPassword', usernameVariable: 'harborUser')]) { | |||||
sh "docker login -u ${harborUser} -p ${harborPassword} 10.2.1.24:10242" | |||||
if(params.operation=='develop') | |||||
{ | |||||
sh "docker push 10.2.1.24:10242/bpa/kitchenweb:${build_tag}" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
stage('Deploy') { | |||||
steps { | |||||
script{ | |||||
echo "5. Deploy Stage" | |||||
// server = getHost() | |||||
// sshCommand remote: server, command: """ | |||||
// /root/eip/web/shell/linux-eipweb.install.sh eipweb ${build_tag} 80 | |||||
// """ | |||||
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s_kitchenweb.yaml" | |||||
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s_kitchenweb.yaml" | |||||
sh "sed -i 's/<NAMESPACES>/${params.environment}/' k8s_kitchenweb.yaml" | |||||
sh "kubectl apply -f k8s_kitchenweb.yaml --record" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,14 @@ | |||||
// https://umijs.org/config/ | |||||
import { defineConfig } from 'umi'; | |||||
export default defineConfig({ | |||||
plugins: [ | |||||
// https://github.com/zthxxx/react-dev-inspector | |||||
'react-dev-inspector/plugins/umi/react-inspector', | |||||
], | |||||
// https://github.com/zthxxx/react-dev-inspector#inspector-loader-props | |||||
inspectorConfig: { | |||||
exclude: [], | |||||
babelPlugins: [], | |||||
babelOptions: {}, | |||||
}, | |||||
}); |
@@ -0,0 +1,74 @@ | |||||
// https://umijs.org/config/ | |||||
import { defineConfig } from 'umi'; | |||||
import { join } from 'path'; | |||||
import defaultSettings from './defaultSettings'; | |||||
import proxy from './proxy'; | |||||
import routes from './routes'; | |||||
const { REACT_APP_ENV } = process.env; | |||||
export default defineConfig({ | |||||
hash: true, | |||||
antd: {}, | |||||
dva: { | |||||
hmr: true, | |||||
}, | |||||
define: { | |||||
'process.env.UMI_ENV': process.env.UMI_ENV || 'dev', | |||||
}, | |||||
layout: { | |||||
// https://umijs.org/zh-CN/plugins/plugin-layout | |||||
locale: false, | |||||
siderWidth: 208, | |||||
...defaultSettings, | |||||
}, | |||||
// https://umijs.org/zh-CN/plugins/plugin-locale | |||||
locale: { | |||||
// default zh-CN | |||||
default: 'zh-CN', | |||||
antd: true, | |||||
// default true, when it is true, will use `navigator.language` overwrite default | |||||
baseNavigator: true, | |||||
}, | |||||
dynamicImport: { | |||||
loading: '@ant-design/pro-layout/es/PageLoading', | |||||
}, | |||||
targets: { | |||||
ie: 11, | |||||
}, | |||||
// umi routes: https://umijs.org/docs/routing | |||||
routes, | |||||
// Theme for antd: https://ant.design/docs/react/customize-theme-cn | |||||
theme: { | |||||
'primary-color': defaultSettings.primaryColor, | |||||
}, | |||||
// esbuild is father build tools | |||||
// https://umijs.org/plugins/plugin-esbuild | |||||
esbuild: {}, | |||||
title: false, | |||||
ignoreMomentLocale: true, | |||||
proxy: proxy[REACT_APP_ENV || 'dev'], | |||||
manifest: { | |||||
basePath: '/', | |||||
}, | |||||
// Fast Refresh 热更新 | |||||
fastRefresh: {}, | |||||
openAPI: [ | |||||
{ | |||||
requestLibPath: "import { request } from 'umi'", | |||||
// 或者使用在线的版本 | |||||
// schemaPath: "https://gw.alipayobjects.com/os/antfincdn/M%24jrzTTYJN/oneapi.json" | |||||
schemaPath: join(__dirname, 'oneapi.json'), | |||||
mock: false, | |||||
}, | |||||
{ | |||||
requestLibPath: "import { request } from 'umi'", | |||||
schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/CA1dOm%2631B/openapi.json', | |||||
projectName: 'swagger', | |||||
}, | |||||
], | |||||
nodeModulesTransform: { | |||||
type: 'none', | |||||
}, | |||||
mfsu: {}, | |||||
webpack5: {}, | |||||
exportStatic: {}, | |||||
}); |
@@ -0,0 +1,16 @@ | |||||
const Settings = { | |||||
navTheme: 'dark', | |||||
// 拂晓蓝 | |||||
primaryColor: '#FA541C', //'#1890ff', | |||||
layout: 'side', | |||||
contentWidth: 'Fluid', | |||||
fixedHeader: false, | |||||
fixSiderbar: true, | |||||
colorWeak: false, | |||||
title: '黑菠萝智慧餐厨', | |||||
pwa: false, | |||||
logo: '/logo.svg', | |||||
iconfontUrl: '', | |||||
}; | |||||
export default Settings; |
@@ -0,0 +1,619 @@ | |||||
{ | |||||
"openapi": "3.0.1", | |||||
"info": { | |||||
"title": "Ant Design Pro", | |||||
"version": "1.0.0" | |||||
}, | |||||
"servers": [ | |||||
{ | |||||
"url": "http://localhost:44324/" | |||||
}, | |||||
{ | |||||
"url": "https://localhost:44324/" | |||||
} | |||||
], | |||||
"paths": { | |||||
"/kitchen/api/currentUser": { | |||||
"get": { | |||||
"tags": [ | |||||
"api" | |||||
], | |||||
"description": "获取当前的用户", | |||||
"operationId": "currentUser", | |||||
"responses": { | |||||
"200": { | |||||
"description": "Success", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/CurrentUser" | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"401": { | |||||
"description": "Error", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/ErrorResponse" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"x-swagger-router-controller": "api" | |||||
}, | |||||
"/kitchen/api/login/captcha": { | |||||
"post": { | |||||
"description": "发送验证码", | |||||
"operationId": "getFakeCaptcha", | |||||
"tags": [ | |||||
"login" | |||||
], | |||||
"parameters": [ | |||||
{ | |||||
"name": "phone", | |||||
"in": "query", | |||||
"description": "手机号", | |||||
"schema": { | |||||
"type": "string" | |||||
} | |||||
} | |||||
], | |||||
"responses": { | |||||
"200": { | |||||
"description": "Success", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/FakeCaptcha" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"/kitchen/api/login/outLogin": { | |||||
"post": { | |||||
"description": "登录接口", | |||||
"operationId": "outLogin", | |||||
"tags": [ | |||||
"login" | |||||
], | |||||
"responses": { | |||||
"200": { | |||||
"description": "Success", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"type": "object" | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"401": { | |||||
"description": "Error", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/ErrorResponse" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"x-swagger-router-controller": "api" | |||||
}, | |||||
"/kitchen/api/login/account": { | |||||
"post": { | |||||
"tags": [ | |||||
"login" | |||||
], | |||||
"description": "登录接口", | |||||
"operationId": "login", | |||||
"requestBody": { | |||||
"description": "登录系统", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/LoginParams" | |||||
} | |||||
} | |||||
}, | |||||
"required": true | |||||
}, | |||||
"responses": { | |||||
"200": { | |||||
"description": "Success", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/LoginResult" | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"401": { | |||||
"description": "Error", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/ErrorResponse" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"x-codegen-request-body-name": "body" | |||||
}, | |||||
"x-swagger-router-controller": "api" | |||||
}, | |||||
"/kitchen/api/notices": { | |||||
"summary": "getNotices", | |||||
"description": "NoticeIconItem", | |||||
"get": { | |||||
"tags": [ | |||||
"api" | |||||
], | |||||
"operationId": "getNotices", | |||||
"responses": { | |||||
"200": { | |||||
"description": "Success", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/NoticeIconList" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"/kitchen/api/rule": { | |||||
"get": { | |||||
"tags": [ | |||||
"rule" | |||||
], | |||||
"description": "获取规则列表", | |||||
"operationId": "rule", | |||||
"parameters": [ | |||||
{ | |||||
"name": "current", | |||||
"in": "query", | |||||
"description": "当前的页码", | |||||
"schema": { | |||||
"type": "number" | |||||
} | |||||
}, | |||||
{ | |||||
"name": "pageSize", | |||||
"in": "query", | |||||
"description": "页面的容量", | |||||
"schema": { | |||||
"type": "number" | |||||
} | |||||
} | |||||
], | |||||
"responses": { | |||||
"200": { | |||||
"description": "Success", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/RuleList" | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"401": { | |||||
"description": "Error", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/ErrorResponse" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"post": { | |||||
"tags": [ | |||||
"rule" | |||||
], | |||||
"description": "新建规则", | |||||
"operationId": "addRule", | |||||
"responses": { | |||||
"200": { | |||||
"description": "Success", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/RuleListItem" | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"401": { | |||||
"description": "Error", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/ErrorResponse" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"put": { | |||||
"tags": [ | |||||
"rule" | |||||
], | |||||
"description": "新建规则", | |||||
"operationId": "updateRule", | |||||
"responses": { | |||||
"200": { | |||||
"description": "Success", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/RuleListItem" | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"401": { | |||||
"description": "Error", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/ErrorResponse" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"delete": { | |||||
"tags": [ | |||||
"rule" | |||||
], | |||||
"description": "删除规则", | |||||
"operationId": "removeRule", | |||||
"responses": { | |||||
"200": { | |||||
"description": "Success", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"type": "object" | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"401": { | |||||
"description": "Error", | |||||
"content": { | |||||
"application/json": { | |||||
"schema": { | |||||
"$ref": "#/components/schemas/ErrorResponse" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"x-swagger-router-controller": "api" | |||||
}, | |||||
"/swagger": { | |||||
"x-swagger-pipe": "swagger_raw" | |||||
} | |||||
}, | |||||
"components": { | |||||
"schemas": { | |||||
"CurrentUser": { | |||||
"type": "object", | |||||
"properties": { | |||||
"name": { | |||||
"type": "string" | |||||
}, | |||||
"avatar": { | |||||
"type": "string" | |||||
}, | |||||
"userid": { | |||||
"type": "string" | |||||
}, | |||||
"email": { | |||||
"type": "string" | |||||
}, | |||||
"signature": { | |||||
"type": "string" | |||||
}, | |||||
"title": { | |||||
"type": "string" | |||||
}, | |||||
"group": { | |||||
"type": "string" | |||||
}, | |||||
"tags": { | |||||
"type": "array", | |||||
"items": { | |||||
"type": "object", | |||||
"properties": { | |||||
"key": { | |||||
"type": "string" | |||||
}, | |||||
"label": { | |||||
"type": "string" | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"notifyCount": { | |||||
"type": "integer", | |||||
"format": "int32" | |||||
}, | |||||
"unreadCount": { | |||||
"type": "integer", | |||||
"format": "int32" | |||||
}, | |||||
"country": { | |||||
"type": "string" | |||||
}, | |||||
"access": { | |||||
"type": "string" | |||||
}, | |||||
"geographic": { | |||||
"type": "object", | |||||
"properties": { | |||||
"province": { | |||||
"type": "object", | |||||
"properties": { | |||||
"label": { | |||||
"type": "string" | |||||
}, | |||||
"key": { | |||||
"type": "string" | |||||
} | |||||
} | |||||
}, | |||||
"city": { | |||||
"type": "object", | |||||
"properties": { | |||||
"label": { | |||||
"type": "string" | |||||
}, | |||||
"key": { | |||||
"type": "string" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
"address": { | |||||
"type": "string" | |||||
}, | |||||
"phone": { | |||||
"type": "string" | |||||
} | |||||
} | |||||
}, | |||||
"LoginResult": { | |||||
"type": "object", | |||||
"properties": { | |||||
"status": { | |||||
"type": "string" | |||||
}, | |||||
"type": { | |||||
"type": "string" | |||||
}, | |||||
"currentAuthority": { | |||||
"type": "string" | |||||
} | |||||
} | |||||
}, | |||||
"PageParams": { | |||||
"type": "object", | |||||
"properties": { | |||||
"current": { | |||||
"type": "number" | |||||
}, | |||||
"pageSize": { | |||||
"type": "number" | |||||
} | |||||
} | |||||
}, | |||||
"RuleListItem": { | |||||
"type": "object", | |||||
"properties": { | |||||
"key": { | |||||
"type": "integer", | |||||
"format": "int32" | |||||
}, | |||||
"disabled": { | |||||
"type": "boolean" | |||||
}, | |||||
"href": { | |||||
"type": "string" | |||||
}, | |||||
"avatar": { | |||||
"type": "string" | |||||
}, | |||||
"name": { | |||||
"type": "string" | |||||
}, | |||||
"owner": { | |||||
"type": "string" | |||||
}, | |||||
"desc": { | |||||
"type": "string" | |||||
}, | |||||
"callNo": { | |||||
"type": "integer", | |||||
"format": "int32" | |||||
}, | |||||
"status": { | |||||
"type": "integer", | |||||
"format": "int32" | |||||
}, | |||||
"updatedAt": { | |||||
"type": "string", | |||||
"format": "datetime" | |||||
}, | |||||
"createdAt": { | |||||
"type": "string", | |||||
"format": "datetime" | |||||
}, | |||||
"progress": { | |||||
"type": "integer", | |||||
"format": "int32" | |||||
} | |||||
} | |||||
}, | |||||
"RuleList": { | |||||
"type": "object", | |||||
"properties": { | |||||
"data": { | |||||
"type": "array", | |||||
"items": { | |||||
"$ref": "#/components/schemas/RuleListItem" | |||||
} | |||||
}, | |||||
"total": { | |||||
"type": "integer", | |||||
"description": "列表的内容总数", | |||||
"format": "int32" | |||||
}, | |||||
"success": { | |||||
"type": "boolean" | |||||
} | |||||
} | |||||
}, | |||||
"FakeCaptcha": { | |||||
"type": "object", | |||||
"properties": { | |||||
"code": { | |||||
"type": "integer", | |||||
"format": "int32" | |||||
}, | |||||
"status": { | |||||
"type": "string" | |||||
} | |||||
} | |||||
}, | |||||
"LoginParams": { | |||||
"type": "object", | |||||
"properties": { | |||||
"username": { | |||||
"type": "string" | |||||
}, | |||||
"password": { | |||||
"type": "string" | |||||
}, | |||||
"autoLogin": { | |||||
"type": "boolean" | |||||
}, | |||||
"type": { | |||||
"type": "string" | |||||
} | |||||
} | |||||
}, | |||||
"ErrorResponse": { | |||||
"required": [ | |||||
"errorCode" | |||||
], | |||||
"type": "object", | |||||
"properties": { | |||||
"errorCode": { | |||||
"type": "string", | |||||
"description": "业务约定的错误码" | |||||
}, | |||||
"errorMessage": { | |||||
"type": "string", | |||||
"description": "业务上的错误信息" | |||||
}, | |||||
"success": { | |||||
"type": "boolean", | |||||
"description": "业务上的请求是否成功" | |||||
} | |||||
} | |||||
}, | |||||
"NoticeIconList": { | |||||
"type": "object", | |||||
"properties": { | |||||
"data": { | |||||
"type": "array", | |||||
"items": { | |||||
"$ref": "#/components/schemas/NoticeIconItem" | |||||
} | |||||
}, | |||||
"total": { | |||||
"type": "integer", | |||||
"description": "列表的内容总数", | |||||
"format": "int32" | |||||
}, | |||||
"success": { | |||||
"type": "boolean" | |||||
} | |||||
} | |||||
}, | |||||
"NoticeIconItemType": { | |||||
"title": "NoticeIconItemType", | |||||
"description": "已读未读列表的枚举", | |||||
"type": "string", | |||||
"properties": {}, | |||||
"enum": [ | |||||
"notification", | |||||
"message", | |||||
"event" | |||||
] | |||||
}, | |||||
"NoticeIconItem": { | |||||
"type": "object", | |||||
"properties": { | |||||
"id": { | |||||
"type": "string" | |||||
}, | |||||
"extra": { | |||||
"type": "string", | |||||
"format": "any" | |||||
}, | |||||
"key": { | |||||
"type": "string" | |||||
}, | |||||
"read": { | |||||
"type": "boolean" | |||||
}, | |||||
"avatar": { | |||||
"type": "string" | |||||
}, | |||||
"title": { | |||||
"type": "string" | |||||
}, | |||||
"status": { | |||||
"type": "string" | |||||
}, | |||||
"datetime": { | |||||
"type": "string", | |||||
"format": "date" | |||||
}, | |||||
"description": { | |||||
"type": "string" | |||||
}, | |||||
"type": { | |||||
"extensions": { | |||||
"x-is-enum": true | |||||
}, | |||||
"$ref": "#/components/schemas/NoticeIconItemType" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,89 @@ | |||||
/** | |||||
* 在生产环境 代理是无法生效的,所以这里没有生产环境的配置 | |||||
* ------------------------------- | |||||
* The agent cannot take effect in the production environment | |||||
* so there is no configuration of the production environment | |||||
* For details, please see | |||||
* https://pro.ant.design/docs/deploy | |||||
*/ | |||||
export default { | |||||
dev: { | |||||
'/saasbase/': { | |||||
target: 'http://192.168.1.19:5006', | |||||
changeOrigin: true, | |||||
secure: false, //关闭证书验证 | |||||
pathRewrite: { | |||||
'/saasbase/': '', | |||||
}, | |||||
}, | |||||
// Nginx发布的时候需要配置 | |||||
'/cos/':{ | |||||
target: 'https://hbl-test-1305371387.cos.ap-chengdu.myqcloud.com', | |||||
changeOrigin: true, | |||||
secure: false, //关闭证书验证 | |||||
pathRewrite: { | |||||
'/cos/': '/', | |||||
}, | |||||
} | |||||
}, | |||||
test: { | |||||
'/api/': { | |||||
target: 'http://localhost:5006', | |||||
changeOrigin: true, | |||||
secure: false, | |||||
pathRewrite: { | |||||
'^': '', | |||||
}, | |||||
}, | |||||
}, | |||||
pre: { | |||||
'/api/': { | |||||
target: 'http://localhost:5006', | |||||
changeOrigin: true, | |||||
secure: false, | |||||
pathRewrite: { | |||||
'^': '', | |||||
}, | |||||
}, | |||||
}, | |||||
}; | |||||
// /** | |||||
// * 在生产环境 代理是无法生效的,所以这里没有生产环境的配置 | |||||
// * ------------------------------- | |||||
// * The agent cannot take effect in the production environment | |||||
// * so there is no configuration of the production environment | |||||
// * For details, please see | |||||
// * https://pro.ant.design/docs/deploy | |||||
// */ | |||||
// export default { | |||||
// dev: { | |||||
// '/api/': { | |||||
// target: 'http://114.117.161.250:7002', | |||||
// changeOrigin: true, | |||||
// secure: false, //关闭证书验证 | |||||
// pathRewrite: { | |||||
// '^': '', | |||||
// }, | |||||
// }, | |||||
// }, | |||||
// test: { | |||||
// '/api/': { | |||||
// target: 'http://114.117.161.250:7002', | |||||
// changeOrigin: true, | |||||
// secure: false, | |||||
// pathRewrite: { | |||||
// '^': '', | |||||
// }, | |||||
// }, | |||||
// }, | |||||
// pre: { | |||||
// '/api/': { | |||||
// target: 'http://114.117.161.250:7002', | |||||
// changeOrigin: true, | |||||
// secure: false, | |||||
// pathRewrite: { | |||||
// '^': '', | |||||
// }, | |||||
// }, | |||||
// }, | |||||
// }; |
@@ -0,0 +1,217 @@ | |||||
/** | |||||
* SYS 系统设置 | |||||
* admin 系统用户 | |||||
* erp 供应链管理 | |||||
* basic 基础信息管理 | |||||
* bill 单据管理 | |||||
* crm crm会员管理 | |||||
* franchisee 加盟商 | |||||
* srd 店铺管理 | |||||
**/ | |||||
export default [ | |||||
{ | |||||
path: '/user', | |||||
layout: false, | |||||
routes: [ | |||||
{ | |||||
name: '系统登录', | |||||
path: '/user/login', | |||||
component: './user/login', | |||||
access: 'k1', | |||||
}, | |||||
], | |||||
}, | |||||
{ | |||||
name: '系统管理', | |||||
icon: 'SettingOutlined', | |||||
path: '/sys', | |||||
routes: [ | |||||
{ | |||||
name: '系统菜单', | |||||
icon: 'smile', | |||||
path: '/sys/menus', | |||||
component: './sys/menus', | |||||
access: 'k6', | |||||
}, | |||||
// { | |||||
// name: '字典信息', | |||||
// icon: 'smile', | |||||
// path: '/sys/dictionary/dictdata', | |||||
// component: './sys/dictionary/dictdata', | |||||
// access: 'k6', | |||||
// }, | |||||
{ | |||||
name: '字典类型', | |||||
icon: 'smile', | |||||
path: '/sys/dictionary/dicttype', | |||||
component: './sys/dictionary/dicttype', | |||||
access: 'k6', | |||||
}, | |||||
// { | |||||
// name: '操作日志', | |||||
// icon: 'smile', | |||||
// path: '/sys/log', | |||||
// component: './sys/log', | |||||
// access: 'k3', | |||||
// }, | |||||
// { | |||||
// name: '错误日志', | |||||
// icon: 'smile', | |||||
// path: '/sys/log', | |||||
// component: './sys/log', | |||||
// access: 'k3', | |||||
// }, | |||||
], | |||||
}, | |||||
{ | |||||
name: '加盟商管理', | |||||
icon: 'SettingOutlined', | |||||
path: '/company', | |||||
routes: [ | |||||
{ | |||||
name: '账号管理', | |||||
icon: 'smile', | |||||
path: '/company/account', | |||||
component: './company/account', | |||||
access: 'k2', | |||||
}, | |||||
] | |||||
}, | |||||
{ | |||||
name: '组织管理', | |||||
icon: 'SettingOutlined', | |||||
path: '/org', | |||||
routes: [ | |||||
{ | |||||
name: '机构管理', | |||||
icon: 'smile', | |||||
path: '/org/orgamange', | |||||
component: './org/orgamange', | |||||
access: 'k2', | |||||
}, | |||||
{ | |||||
name: '角色管理', | |||||
icon: 'smile', | |||||
path: '/org/roles', | |||||
component: './org/roles', | |||||
access: 'k5', | |||||
}, | |||||
{ | |||||
name: '用户账号管理', | |||||
icon: 'smile', | |||||
path: '/org/users', | |||||
component: './org/users', | |||||
access: 'k5', | |||||
}, | |||||
] | |||||
}, | |||||
{ | |||||
name: '元数据管理', | |||||
icon: 'DropboxSquareFilled', | |||||
path: '/database', | |||||
routes: [ | |||||
{ | |||||
name: '物料管理', | |||||
icon: 'smile', | |||||
path: '/database/basic/batching', | |||||
component: './database/basic/batching', | |||||
access: 'k7', | |||||
}, | |||||
{ | |||||
name: '商品管理', | |||||
icon: 'smile', | |||||
path: '/database', | |||||
routes: [ | |||||
{ | |||||
name: '商品类型', | |||||
icon: 'smile', | |||||
path: '/database/goods/goodstypemanage', | |||||
component: './database/goods/goodstypemanage', | |||||
access: 'k7', | |||||
}, | |||||
{ | |||||
name: '商品多属性', | |||||
icon: 'smile', | |||||
path: '/database/goods/goodsattribute', | |||||
component: './database/goods/goodsattribute', | |||||
access: 'k7', | |||||
}, | |||||
{ | |||||
name: '商品基础信息', | |||||
icon: 'smile', | |||||
path: '/database/goods/newgoods', | |||||
component: './database/goods/newgoods', | |||||
access: 'k7', | |||||
}, | |||||
{ | |||||
name: '添加商品基础信息', | |||||
icon: 'smile', | |||||
path: '/database/goods/goodsInfo', | |||||
component: './database/goods/goodsInfo', | |||||
access: 'k7', | |||||
}, | |||||
] | |||||
}, | |||||
], | |||||
}, | |||||
{ | |||||
name: '设备管理', | |||||
icon: 'BankFilled', | |||||
path: '/device', | |||||
routes: [ | |||||
{ | |||||
name: '产品管理', | |||||
icon: 'smile', | |||||
path: '/device/deviceType', | |||||
component: './device/deviceType', | |||||
access: 'k12', | |||||
}, | |||||
{ | |||||
name: '设备信息', | |||||
icon: 'smile', | |||||
path: '/device/deviceInfo', | |||||
component: './device/deviceInfo', | |||||
access: 'k14', | |||||
}, | |||||
{ | |||||
name: '版本管理', | |||||
icon: 'smile', | |||||
path: '/device/deviceVesion', | |||||
component: './device/deviceVesion', | |||||
access: 'k14', | |||||
}, | |||||
// { | |||||
// name: '设备工艺信息', | |||||
// icon: 'smile', | |||||
// path: '/device/devicetechnology', | |||||
// component: './device/devicetechnology', | |||||
// access: 'k9', | |||||
// }, | |||||
// { | |||||
// name: '设备商品管理', | |||||
// icon: 'smile', | |||||
// path: '/device/deviceFood', | |||||
// component: './device/deviceFood', | |||||
// access: 'k14', | |||||
// }, | |||||
], | |||||
}, | |||||
{ | |||||
path: '/', | |||||
redirect: '/welcome', | |||||
}, | |||||
{ | |||||
path: '/welcome', | |||||
name: 'welcome', | |||||
icon: 'smile', | |||||
component: './Welcome', | |||||
}, | |||||
//什么都不要想404直接放最后 | |||||
{ | |||||
component: './404', | |||||
}, | |||||
]; |
@@ -0,0 +1,13 @@ | |||||
server { | |||||
listen 80; | |||||
server_name localhost; | |||||
location / { | |||||
root /usr/share/nginx/html; | |||||
index index.html index.htm; | |||||
try_files $uri $uri/ /index.html; | |||||
} | |||||
} | |||||
@@ -0,0 +1,5 @@ | |||||
FROM nginx | |||||
COPY ./dist /usr/share/nginx/html/ | |||||
COPY ./default.conf /etc/nginx/conf.d/ | |||||
EXPOSE 80 | |||||
@@ -0,0 +1,10 @@ | |||||
module.exports = { | |||||
testURL: 'http://localhost:7002/', | |||||
testEnvironment: './tests/PuppeteerEnvironment', | |||||
verbose: false, | |||||
extraSetupFiles: ['./tests/setupTests.js'], | |||||
globals: { | |||||
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false, | |||||
localStorage: null, | |||||
}, | |||||
}; |
@@ -0,0 +1,12 @@ | |||||
{ | |||||
"compilerOptions": { | |||||
"emitDecoratorMetadata": true, | |||||
"experimentalDecorators": true, | |||||
"baseUrl": ".", | |||||
"paths": { | |||||
"@/*": [ | |||||
"./src/*" | |||||
] | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,57 @@ | |||||
apiVersion: apps/v1 | |||||
kind: Deployment | |||||
metadata: | |||||
name: kitchenweb | |||||
namespace: kube-<NAMESPACES> | |||||
spec: | |||||
selector: | |||||
matchLabels: | |||||
app: kitchenweb | |||||
replicas: 1 | |||||
template: | |||||
metadata: | |||||
labels: | |||||
app: kitchenweb | |||||
spec: | |||||
containers: | |||||
- image: 10.2.1.24:10242/bpa/kitchenweb:<BUILD_TAG> | |||||
imagePullPolicy: IfNotPresent | |||||
name: kitchenweb | |||||
env: | |||||
- name: branch | |||||
value: <BRANCH_NAME> | |||||
- name: TZ | |||||
value: Asia/Shanghai | |||||
ports: | |||||
- containerPort: 80 | |||||
protocol: TCP | |||||
volumeMounts: | |||||
- mountPath: "/etc/localtime" | |||||
name: timezone | |||||
resources: | |||||
requests: | |||||
cpu: "100m" | |||||
memory: "112Mi" | |||||
limits: | |||||
cpu: "500m" | |||||
memory: "512Mi" | |||||
volumes: | |||||
- name: timezone | |||||
hostPath: | |||||
path: /usr/share/zoneinfo/Asia/Shanghai | |||||
--- | |||||
kind: Service | |||||
apiVersion: v1 | |||||
metadata: | |||||
labels: | |||||
app: kitchenweb | |||||
name: kitchenweb | |||||
namespace: kube-<NAMESPACES> | |||||
spec: | |||||
type: ClusterIP | |||||
ports: | |||||
- port: 80 | |||||
name: http | |||||
selector: | |||||
app: kitchenweb |
@@ -0,0 +1,178 @@ | |||||
// eslint-disable-next-line import/no-extraneous-dependencies | |||||
import moment from 'moment'; | |||||
import { parse } from 'url'; // mock tableListDataSource | |||||
const genList = (current, pageSize) => { | |||||
const tableListDataSource = []; | |||||
for (let i = 0; i < pageSize; i += 1) { | |||||
const index = (current - 1) * 10 + i; | |||||
tableListDataSource.push({ | |||||
key: index, | |||||
disabled: i % 6 === 0, | |||||
href: 'https://ant.design', | |||||
avatar: [ | |||||
'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', | |||||
'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', | |||||
][i % 2], | |||||
name: `TradeCode ${index}`, | |||||
owner: '曲丽丽', | |||||
desc: '这是一段描述', | |||||
callNo: Math.floor(Math.random() * 1000), | |||||
status: Math.floor(Math.random() * 10) % 4, | |||||
updatedAt: moment().format('YYYY-MM-DD'), | |||||
createdAt: moment().format('YYYY-MM-DD'), | |||||
progress: Math.ceil(Math.random() * 100), | |||||
}); | |||||
} | |||||
tableListDataSource.reverse(); | |||||
return tableListDataSource; | |||||
}; | |||||
let tableListDataSource = genList(1, 100); | |||||
function getRule(req, res, u) { | |||||
let realUrl = u; | |||||
if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') { | |||||
realUrl = req.url; | |||||
} | |||||
const { current = 1, pageSize = 10 } = req.query; | |||||
const params = parse(realUrl, true).query; | |||||
let dataSource = [...tableListDataSource].slice((current - 1) * pageSize, current * pageSize); | |||||
const sorter = JSON.parse(params.sorter || '{}'); | |||||
if (sorter) { | |||||
dataSource = dataSource.sort((prev, next) => { | |||||
let sortNumber = 0; | |||||
Object.keys(sorter).forEach((key) => { | |||||
if (sorter[key] === 'descend') { | |||||
if (prev[key] - next[key] > 0) { | |||||
sortNumber += -1; | |||||
} else { | |||||
sortNumber += 1; | |||||
} | |||||
return; | |||||
} | |||||
if (prev[key] - next[key] > 0) { | |||||
sortNumber += 1; | |||||
} else { | |||||
sortNumber += -1; | |||||
} | |||||
}); | |||||
return sortNumber; | |||||
}); | |||||
} | |||||
if (params.filter) { | |||||
const filter = JSON.parse(params.filter); | |||||
if (Object.keys(filter).length > 0) { | |||||
dataSource = dataSource.filter((item) => { | |||||
return Object.keys(filter).some((key) => { | |||||
if (!filter[key]) { | |||||
return true; | |||||
} | |||||
if (filter[key].includes(`${item[key]}`)) { | |||||
return true; | |||||
} | |||||
return false; | |||||
}); | |||||
}); | |||||
} | |||||
} | |||||
if (params.name) { | |||||
dataSource = dataSource.filter((data) => data?.name?.includes(params.name || '')); | |||||
} | |||||
const result = { | |||||
data: dataSource, | |||||
total: tableListDataSource.length, | |||||
success: true, | |||||
pageSize, | |||||
current: parseInt(`${params.current}`, 10) || 1, | |||||
}; | |||||
return res.json(result); | |||||
} | |||||
function postRule(req, res, u, b) { | |||||
let realUrl = u; | |||||
if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') { | |||||
realUrl = req.url; | |||||
} | |||||
const body = (b && b.body) || req.body; | |||||
const { method, name, desc, key } = body; | |||||
switch (method) { | |||||
/* eslint no-case-declarations:0 */ | |||||
case 'delete': | |||||
tableListDataSource = tableListDataSource.filter((item) => key.indexOf(item.key) === -1); | |||||
break; | |||||
case 'post': | |||||
(() => { | |||||
const i = Math.ceil(Math.random() * 10000); | |||||
const newRule = { | |||||
key: tableListDataSource.length, | |||||
href: 'https://ant.design', | |||||
avatar: [ | |||||
'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', | |||||
'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', | |||||
][i % 2], | |||||
name, | |||||
owner: '曲丽丽', | |||||
desc, | |||||
callNo: Math.floor(Math.random() * 1000), | |||||
status: Math.floor(Math.random() * 10) % 2, | |||||
updatedAt: moment().format('YYYY-MM-DD'), | |||||
createdAt: moment().format('YYYY-MM-DD'), | |||||
progress: Math.ceil(Math.random() * 100), | |||||
}; | |||||
tableListDataSource.unshift(newRule); | |||||
return res.json(newRule); | |||||
})(); | |||||
return; | |||||
case 'update': | |||||
(() => { | |||||
let newRule = {}; | |||||
tableListDataSource = tableListDataSource.map((item) => { | |||||
if (item.key === key) { | |||||
newRule = { ...item, desc, name }; | |||||
return { ...item, desc, name }; | |||||
} | |||||
return item; | |||||
}); | |||||
return res.json(newRule); | |||||
})(); | |||||
return; | |||||
default: | |||||
break; | |||||
} | |||||
const result = { | |||||
list: tableListDataSource, | |||||
pagination: { | |||||
total: tableListDataSource.length, | |||||
}, | |||||
}; | |||||
res.json(result); | |||||
} | |||||
export default { | |||||
'GET /api/rule': getRule, | |||||
'POST /api/rule': postRule, | |||||
}; |
@@ -0,0 +1,105 @@ | |||||
const getNotices = (req, res) => { | |||||
res.json({ | |||||
data: [ | |||||
{ | |||||
id: '000000001', | |||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', | |||||
title: '你收到了 14 份新周报', | |||||
datetime: '2017-08-09', | |||||
type: 'notification', | |||||
}, | |||||
{ | |||||
id: '000000002', | |||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png', | |||||
title: '你推荐的 曲妮妮 已通过第三轮面试', | |||||
datetime: '2017-08-08', | |||||
type: 'notification', | |||||
}, | |||||
{ | |||||
id: '000000003', | |||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png', | |||||
title: '这种模板可以区分多种通知类型', | |||||
datetime: '2017-08-07', | |||||
read: true, | |||||
type: 'notification', | |||||
}, | |||||
{ | |||||
id: '000000004', | |||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', | |||||
title: '左侧图标用于区分不同的类型', | |||||
datetime: '2017-08-07', | |||||
type: 'notification', | |||||
}, | |||||
{ | |||||
id: '000000005', | |||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', | |||||
title: '内容不要超过两行字,超出时自动截断', | |||||
datetime: '2017-08-07', | |||||
type: 'notification', | |||||
}, | |||||
{ | |||||
id: '000000006', | |||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', | |||||
title: '曲丽丽 评论了你', | |||||
description: '描述信息描述信息描述信息', | |||||
datetime: '2017-08-07', | |||||
type: 'message', | |||||
clickClose: true, | |||||
}, | |||||
{ | |||||
id: '000000007', | |||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', | |||||
title: '朱偏右 回复了你', | |||||
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像', | |||||
datetime: '2017-08-07', | |||||
type: 'message', | |||||
clickClose: true, | |||||
}, | |||||
{ | |||||
id: '000000008', | |||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', | |||||
title: '标题', | |||||
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像', | |||||
datetime: '2017-08-07', | |||||
type: 'message', | |||||
clickClose: true, | |||||
}, | |||||
{ | |||||
id: '000000009', | |||||
title: '任务名称', | |||||
description: '任务需要在 2017-01-12 20:00 前启动', | |||||
extra: '未开始', | |||||
status: 'todo', | |||||
type: 'event', | |||||
}, | |||||
{ | |||||
id: '000000010', | |||||
title: '第三方紧急代码变更', | |||||
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务', | |||||
extra: '马上到期', | |||||
status: 'urgent', | |||||
type: 'event', | |||||
}, | |||||
{ | |||||
id: '000000011', | |||||
title: '信息安全考试', | |||||
description: '指派竹尔于 2017-01-09 前完成更新并发布', | |||||
extra: '已耗时 8 天', | |||||
status: 'doing', | |||||
type: 'event', | |||||
}, | |||||
{ | |||||
id: '000000012', | |||||
title: 'ABCD 版本发布', | |||||
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务', | |||||
extra: '进行中', | |||||
status: 'processing', | |||||
type: 'event', | |||||
}, | |||||
], | |||||
}); | |||||
}; | |||||
export default { | |||||
'GET /kitchen/api/notices': getNotices, | |||||
}; |
@@ -0,0 +1,7 @@ | |||||
export default { | |||||
'/saasbase/api/auth_routes': { | |||||
'/form/advanced-form': { | |||||
authority: ['admin', 'user'], | |||||
}, | |||||
}, | |||||
}; |
@@ -0,0 +1,207 @@ | |||||
const waitTime = (time = 100) => { | |||||
return new Promise((resolve) => { | |||||
setTimeout(() => { | |||||
resolve(true); | |||||
}, time); | |||||
}); | |||||
}; | |||||
async function getFakeCaptcha(req, res) { | |||||
await waitTime(2000); | |||||
return res.json('captcha-xxx'); | |||||
} | |||||
const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION } = process.env; | |||||
/** | |||||
* 当前用户的权限,如果为空代表没登录 | |||||
* current user access, if is '', user need login | |||||
* 如果是 pro 的预览,默认是有权限的 | |||||
*/ | |||||
let access = ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site' ? 'admin' : ''; | |||||
const getAccess = () => { | |||||
return access; | |||||
}; // 代码中会兼容本地 service mock 以及部署站点的静态数据 | |||||
export default { | |||||
// 支持值为 Object 和 Array | |||||
'GET /kitchen/api/currentUser': (req, res) => { | |||||
if (!getAccess()) { | |||||
res.status(401).send({ | |||||
data: { | |||||
isLogin: false, | |||||
}, | |||||
errorCode: '401', | |||||
errorMessage: '请先登录!', | |||||
success: true, | |||||
}); | |||||
return; | |||||
} | |||||
res.send({ | |||||
name: 'Serati Ma', | |||||
avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png', | |||||
userid: '00000001', | |||||
email: 'antdesign@alipay.com', | |||||
signature: '海纳百川,有容乃大', | |||||
title: '交互专家', | |||||
group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED', | |||||
tags: [ | |||||
{ | |||||
key: '0', | |||||
label: '很有想法的', | |||||
}, | |||||
{ | |||||
key: '1', | |||||
label: '专注设计', | |||||
}, | |||||
{ | |||||
key: '2', | |||||
label: '辣~', | |||||
}, | |||||
{ | |||||
key: '3', | |||||
label: '大长腿', | |||||
}, | |||||
{ | |||||
key: '4', | |||||
label: '川妹子', | |||||
}, | |||||
{ | |||||
key: '5', | |||||
label: '海纳百川', | |||||
}, | |||||
], | |||||
notifyCount: 12, | |||||
unreadCount: 11, | |||||
country: 'China', | |||||
access: getAccess(), | |||||
geographic: { | |||||
province: { | |||||
label: '浙江省', | |||||
key: '330000', | |||||
}, | |||||
city: { | |||||
label: '杭州市', | |||||
key: '330100', | |||||
}, | |||||
}, | |||||
address: '西湖区工专路 77 号', | |||||
phone: '0752-268888888', | |||||
}); | |||||
}, | |||||
// GET POST 可省略 | |||||
'GET /kitchen/api/users': [ | |||||
{ | |||||
key: '1', | |||||
name: 'John Brown', | |||||
age: 32, | |||||
address: 'New York No. 1 Lake Park', | |||||
}, | |||||
{ | |||||
key: '2', | |||||
name: 'Jim Green', | |||||
age: 42, | |||||
address: 'London No. 1 Lake Park', | |||||
}, | |||||
{ | |||||
key: '3', | |||||
name: 'Joe Black', | |||||
age: 32, | |||||
address: 'Sidney No. 1 Lake Park', | |||||
}, | |||||
], | |||||
'POST /kitchen/api/login/account': async (req, res) => { | |||||
const { password, username, type } = req.body; | |||||
await waitTime(2000); | |||||
if (password === 'ant.design' && username === 'admin') { | |||||
res.send({ | |||||
status: 'ok', | |||||
type, | |||||
currentAuthority: 'admin', | |||||
}); | |||||
access = 'admin'; | |||||
return; | |||||
} | |||||
if (password === 'ant.design' && username === 'user') { | |||||
res.send({ | |||||
status: 'ok', | |||||
type, | |||||
currentAuthority: 'user', | |||||
}); | |||||
access = 'user'; | |||||
return; | |||||
} | |||||
if (type === 'mobile') { | |||||
res.send({ | |||||
status: 'ok', | |||||
type, | |||||
currentAuthority: 'admin', | |||||
}); | |||||
access = 'admin'; | |||||
return; | |||||
} | |||||
res.send({ | |||||
status: 'error', | |||||
type, | |||||
currentAuthority: 'guest', | |||||
}); | |||||
access = 'guest'; | |||||
}, | |||||
'POST /kitchen/api/login/outLogin': (req, res) => { | |||||
access = ''; | |||||
res.send({ | |||||
data: {}, | |||||
success: true, | |||||
}); | |||||
}, | |||||
'POST /kitchen/api/register': (req, res) => { | |||||
res.send({ | |||||
status: 'ok', | |||||
currentAuthority: 'user', | |||||
success: true, | |||||
}); | |||||
}, | |||||
'GET /kitchen/api/500': (req, res) => { | |||||
res.status(500).send({ | |||||
timestamp: 1513932555104, | |||||
status: 500, | |||||
error: 'error', | |||||
message: 'error', | |||||
path: '/base/category/list', | |||||
}); | |||||
}, | |||||
'GET /kitchen/api/404': (req, res) => { | |||||
res.status(404).send({ | |||||
timestamp: 1513932643431, | |||||
status: 404, | |||||
error: 'Not Found', | |||||
message: 'No message available', | |||||
path: '/base/category/list/2121212', | |||||
}); | |||||
}, | |||||
'GET /kitchen/api/403': (req, res) => { | |||||
res.status(403).send({ | |||||
timestamp: 1513932555104, | |||||
status: 403, | |||||
error: 'Unauthorized', | |||||
message: 'Unauthorized', | |||||
path: '/base/category/list', | |||||
}); | |||||
}, | |||||
'GET /kitchen/api/401': (req, res) => { | |||||
res.status(401).send({ | |||||
timestamp: 1513932555104, | |||||
status: 401, | |||||
error: 'Unauthorized', | |||||
message: 'Unauthorized', | |||||
path: '/base/category/list', | |||||
}); | |||||
}, | |||||
'GET /kitchen/api/login/captcha': getFakeCaptcha, | |||||
}; |
@@ -0,0 +1,129 @@ | |||||
{ | |||||
"name": "ant-design-pro", | |||||
"version": "5.0.0", | |||||
"private": true, | |||||
"description": "An out-of-box UI solution for enterprise applications", | |||||
"scripts": { | |||||
"analyze": "cross-env ANALYZE=1 umi build", | |||||
"build": "umi build", | |||||
"deploy": "npm run build && npm run gh-pages", | |||||
"dev": "npm run start:dev", | |||||
"gh-pages": "gh-pages -d dist", | |||||
"i18n-remove": "pro i18n-remove --locale=zh-CN --write", | |||||
"postinstall": "umi g tmp", | |||||
"lint": "umi g tmp && npm run lint:js && npm run lint:style && npm run lint:prettier", | |||||
"lint-staged": "lint-staged", | |||||
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ", | |||||
"lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src && npm run lint:style", | |||||
"lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src", | |||||
"lint:prettier": "prettier -c --write \"src/**/*\" --end-of-line auto", | |||||
"lint:style": "stylelint --fix \"src/**/*.less\" --syntax less", | |||||
"openapi": "umi openapi", | |||||
"precommit": "lint-staged", | |||||
"prettier": "prettier -c --write \"src/**/*\"", | |||||
"start": "cross-env UMI_ENV=dev umi dev", | |||||
"start:umi-ui": "HOST=0.0.0.0 UMI-UI=1 umi dev", | |||||
"start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev umi dev", | |||||
"start:no-mock": "cross-env MOCK=none UMI_ENV=dev umi dev", | |||||
"start:no-ui": "cross-env UMI_UI=none UMI_ENV=dev umi dev", | |||||
"start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev umi dev", | |||||
"start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev umi dev", | |||||
"pretest": "node ./tests/beforeTest", | |||||
"test": "umi test", | |||||
"test:all": "node ./tests/run-tests.js", | |||||
"test:component": "umi test ./src/components", | |||||
"serve": "umi-serve", | |||||
"tsc": "tsc --noEmit" | |||||
}, | |||||
"lint-staged": { | |||||
"**/*.less": "stylelint --syntax less", | |||||
"**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js", | |||||
"**/*.{js,jsx,tsx,ts,less,md,json}": [ | |||||
"prettier --write" | |||||
] | |||||
}, | |||||
"browserslist": [ | |||||
"> 1%", | |||||
"last 2 versions", | |||||
"not ie <= 10" | |||||
], | |||||
"dependencies": { | |||||
"@ant-design/charts": "^1.2.14", | |||||
"@ant-design/icons": "^4.5.0", | |||||
"@ant-design/pro-card": "^1.18.0", | |||||
"@ant-design/pro-descriptions": "^1.10.0", | |||||
"@ant-design/pro-form": "^1.48.0", | |||||
"@ant-design/pro-layout": "^6.29.0", | |||||
"@ant-design/pro-list": "^1.21.37", | |||||
"@ant-design/pro-table": "^2.57.0", | |||||
"@ant-design/pro-utils": "^1.28.0", | |||||
"@umijs/route-utils": "^2.0.3", | |||||
"@wangeditor/editor": "^5.1.15", | |||||
"@wangeditor/editor-for-react": "^1.0.6", | |||||
"antd": "^4.17.0", | |||||
"axios": "^0.26.1", | |||||
"braft-editor": "^2.3.9", | |||||
"classnames": "^2.2.6", | |||||
"cos-js-sdk-v5": "^1.3.5", | |||||
"echarts": "^5.3.3", | |||||
"js-base64": "^3.7.5", | |||||
"js-export-excel": "^1.1.4", | |||||
"linq": "^4.0.0", | |||||
"lodash": "^4.17.11", | |||||
"moment": "^2.25.3", | |||||
"nanoid": "^4.0.2", | |||||
"omit.js": "^2.0.2", | |||||
"qrcode.react": "^1.0.1", | |||||
"react": "^17.0.0", | |||||
"react-custom-scrollbar": "^1.0.0", | |||||
"react-custom-scrollbars": "^4.2.1", | |||||
"react-dev-inspector": "^1.1.1", | |||||
"react-dom": "^17.0.0", | |||||
"react-helmet-async": "^1.0.4", | |||||
"swagger-ui-react": "^4.14.0", | |||||
"umi": "^3.5.0", | |||||
"umi-serve": "^1.9.10" | |||||
}, | |||||
"devDependencies": { | |||||
"@ant-design/pro-cli": "^2.0.2", | |||||
"@types/express": "^4.17.0", | |||||
"@types/history": "^4.7.2", | |||||
"@types/jest": "^27.0.2", | |||||
"@types/lodash": "^4.14.177", | |||||
"@types/react": "^17.0.35", | |||||
"@types/react-dom": "^17.0.0", | |||||
"@types/react-helmet": "^6.1.0", | |||||
"@umijs/fabric": "^2.6.2", | |||||
"@umijs/openapi": "^1.3.6", | |||||
"@umijs/plugin-blocks": "^2.0.5", | |||||
"@umijs/plugin-esbuild": "^1.0.1", | |||||
"@umijs/plugin-openapi": "^1.2.0", | |||||
"@umijs/preset-ant-design-pro": "^1.2.0", | |||||
"@umijs/preset-dumi": "^1.1.32", | |||||
"@umijs/preset-react": "^1.8.27", | |||||
"@umijs/preset-ui": "^2.2.9", | |||||
"@umijs/yorkie": "^2.0.3", | |||||
"carlo": "^0.9.46", | |||||
"cross-env": "^7.0.0", | |||||
"cross-port-killer": "^1.1.1", | |||||
"detect-installer": "^1.0.1", | |||||
"enzyme": "^3.11.0", | |||||
"eslint": "^8.2.0", | |||||
"eslint-plugin-react": "^7.29.4", | |||||
"express": "^4.17.1", | |||||
"gh-pages": "^3.0.0", | |||||
"jsdom-global": "^3.0.2", | |||||
"lint-staged": "^12.0.2", | |||||
"mockjs": "^1.0.1-beta3", | |||||
"prettier": "^2.3.2", | |||||
"puppeteer-core": "^11.0.0", | |||||
"stylelint": "^14.1.0", | |||||
"typescript": "^4.2.2" | |||||
}, | |||||
"engines": { | |||||
"node": ">=10.0.0" | |||||
}, | |||||
"gitHooks": { | |||||
"commit-msg": "fabric verify-commit" | |||||
} | |||||
} |
@@ -0,0 +1 @@ | |||||
preview.pro.ant.design |
@@ -0,0 +1,5 @@ | |||||
<svg width="42" height="42" xmlns="http://www.w3.org/2000/svg"> | |||||
<g> | |||||
<path fill="#070707" d="m6.717392,13.773912l5.6,0c2.8,0 4.7,1.9 4.7,4.7c0,2.8 -2,4.7 -4.9,4.7l-2.5,0l0,4.3l-2.9,0l0,-13.7zm2.9,2.2l0,4.9l1.9,0c1.6,0 2.6,-0.9 2.6,-2.4c0,-1.6 -0.9,-2.4 -2.6,-2.4l-1.9,0l0,-0.1zm8.9,11.5l2.7,0l0,-5.7c0,-1.4 0.8,-2.3 2.2,-2.3c0.4,0 0.8,0.1 1,0.2l0,-2.4c-0.2,-0.1 -0.5,-0.1 -0.8,-0.1c-1.2,0 -2.1,0.7 -2.4,2l-0.1,0l0,-1.9l-2.7,0l0,10.2l0.1,0zm11.7,0.1c-3.1,0 -5,-2 -5,-5.3c0,-3.3 2,-5.3 5,-5.3s5,2 5,5.3c0,3.4 -1.9,5.3 -5,5.3zm0,-2.1c1.4,0 2.2,-1.1 2.2,-3.2c0,-2 -0.8,-3.2 -2.2,-3.2c-1.4,0 -2.2,1.2 -2.2,3.2c0,2.1 0.8,3.2 2.2,3.2z" class="st0" id="Ant-Design-Pro"/> | |||||
</g> | |||||
</svg> |
@@ -0,0 +1,50 @@ | |||||
/** | |||||
* 这里是把登录信息拿来做菜单权限控制 | |||||
* @see https://umijs.org/zh-CN/plugins/plugin-access | |||||
* */ | |||||
// permission 按钮权限标志 | |||||
// function checkPermission(currentUser:API.CurrentUser,permission:string) | |||||
// { | |||||
// return currentUser&& currentUser.data.roles.some( | |||||
// (roles)=>roles.permissions.map(permission=>permission.name).indexof(permission)>-1) | |||||
// } | |||||
export default function access(initialState) { | |||||
const { currentUser, menuData } = initialState || {}; | |||||
return { | |||||
// k1:true, | |||||
// k2:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k2'): false, | |||||
// k3:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k3'): false, | |||||
// k4:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k4'): false, | |||||
// k5:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k5'): false, | |||||
// k6:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k6'): false, | |||||
// k7:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k7'): false, | |||||
// k8:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k8'): false, | |||||
// k9:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k9'): false, | |||||
// k10:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k10'): false, | |||||
// k11:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k11'): false, | |||||
// k12:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k12'): false, | |||||
// k13:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k13'): false, | |||||
// k14:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k14'): false, | |||||
// k15:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k15'): false, | |||||
// k16:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k16'): false, | |||||
// k17:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k17'): false, | |||||
// k18:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k18'): false, | |||||
// k19:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k19'): false, | |||||
// k20:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k20'): false, | |||||
// k21:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k21'): false, | |||||
// k22:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k22'): false, | |||||
// k23:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k23'): false, | |||||
// k24:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k24'): false, | |||||
// k25:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k25'): false, | |||||
// k26:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k26'): false, | |||||
// k27:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k27'): false, | |||||
// k28:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k28'): false, | |||||
// k29:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k29'): false, | |||||
// k30:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k30'): false, | |||||
// k31:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k31'): false, | |||||
// k32:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k32'): false, | |||||
// k33:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k33'): false, | |||||
// k34:dymicMenu!=undefined? dymicMenu.data.some(t=>t.access=='k34'): false, | |||||
}; | |||||
} |
@@ -0,0 +1,467 @@ | |||||
import React from 'react'; | |||||
import { PageLoading } from '@ant-design/pro-layout'; | |||||
import { notification } from 'antd'; | |||||
import { history, Link, RequestConfig } from 'umi'; | |||||
import RightContent from '@/components/RightContent'; | |||||
import Footer from '@/components/Footer'; | |||||
import TagView from '@/components/TagView'; | |||||
import * as Icon from '@ant-design/icons'; | |||||
import api from '@/services/api'; | |||||
const isDev = process.env.NODE_ENV === 'development'; | |||||
const loginPath = '/user/login'; | |||||
/** 获取用户信息比较慢的时候会展示一个 loading */ | |||||
export const initialStateConfig = { | |||||
loading: <PageLoading />, | |||||
}; | |||||
/** | |||||
* @see https://umijs.org/zh-CN/plugins/plugin-initial-state | |||||
* */ | |||||
export async function getInitialState() { | |||||
const fetchUserInfo = async () => { | |||||
try { | |||||
const currentUser = await api.queryCurrent(); | |||||
if (currentUser.data == null && history.location.pathname !== '/gateAdmin') { | |||||
history.push(loginPath); | |||||
} | |||||
return currentUser; | |||||
} catch (error) { | |||||
const { location } = history; // 如果没有登录,重定向到 login | |||||
if (location.pathname != loginPath && history.location.pathname !== '/gateAdmin') { | |||||
history.push(loginPath); | |||||
} | |||||
} | |||||
return undefined; | |||||
}; | |||||
const queryMenuData = async () => { | |||||
try { | |||||
const data = [ | |||||
{ | |||||
path: '/welcome', | |||||
name: '连锁经营平台', | |||||
icon: 'smile', | |||||
hideInMenu: true, | |||||
component: './Welcome', | |||||
}, | |||||
{ | |||||
code: 'sys', | |||||
name: '系统管理', | |||||
icon: 'SettingOutlined', | |||||
path: '/sys', | |||||
routes: [ | |||||
{ | |||||
code: 'Menus', | |||||
name: '系统菜单', | |||||
icon: 'smile', | |||||
path: '/sys/Menus', | |||||
component: './sys/Menus', | |||||
access: 'k6', | |||||
}, | |||||
// { | |||||
// code: 'dictdata', | |||||
// name: '字典信息', | |||||
// icon: 'smile', | |||||
// path: '/sys/dictionary/dictdata', | |||||
// component: './sys/dictionary/dictdata', | |||||
// access: 'k6', | |||||
// }, | |||||
{ | |||||
code: 'dicttype', | |||||
name: '字典类型', | |||||
icon: 'smile', | |||||
path: '/sys/dictionary/dicttype', | |||||
component: './sys/dictionary/dicttype', | |||||
access: 'k6', | |||||
}, | |||||
// { | |||||
// code: 'Log', | |||||
// name: '操作日志', | |||||
// icon: 'smile', | |||||
// path: '/sys/Log', | |||||
// component: './sys/Log', | |||||
// access: 'k3', | |||||
// }, | |||||
// { | |||||
// code: 'Log', | |||||
// name: '错误日志', | |||||
// icon: 'smile', | |||||
// path: '/sys/errLog', | |||||
// component: './sys/errLog', | |||||
// access: 'k3', | |||||
// }, | |||||
], | |||||
}, | |||||
{ | |||||
code: 'company', | |||||
name: '加盟商管理', | |||||
icon: 'TeamOutlined', | |||||
path: '/company', | |||||
routes: [ | |||||
{ | |||||
code: 'account', | |||||
name: '账号管理', | |||||
icon: 'smile', | |||||
path: '/company/account', | |||||
component: './sys/account', | |||||
access: 'k4', | |||||
}, | |||||
] | |||||
}, | |||||
{ | |||||
code: 'orgamange', | |||||
name: '组织管理', | |||||
icon: 'ClusterOutlined', | |||||
path: '/org', | |||||
routes: [ | |||||
{ | |||||
code: 'Org', | |||||
name: '机构管理', | |||||
icon: 'smile', | |||||
path: '/org/orgamange', | |||||
component: './org/orgamange', | |||||
access: 'k2', | |||||
}, | |||||
{ | |||||
code: 'roles', | |||||
name: '角色管理', | |||||
icon: 'smile', | |||||
path: '/org/Roles', | |||||
component: './org/Roles', | |||||
access: 'k2', | |||||
}, | |||||
{ | |||||
code: 'users', | |||||
name: '用户账号管理', | |||||
icon: 'smile', | |||||
path: '/org/users', | |||||
component: './org/users', | |||||
access: 'k2', | |||||
}, | |||||
] | |||||
}, | |||||
{ | |||||
code: 'device', | |||||
name: '设备管理', | |||||
icon: 'PrinterOutlined', | |||||
path: '/device', | |||||
routes: [ | |||||
{ | |||||
code: 'deviceType', | |||||
name: '产品管理', | |||||
icon: 'smile', | |||||
path: '/device/deviceType', | |||||
component: './device/deviceType', | |||||
access: 'k12', | |||||
}, | |||||
{ | |||||
code: 'deviceInfo', | |||||
name: '设备信息', | |||||
icon: 'smile', | |||||
path: '/device/deviceInfo', | |||||
component: './device/deviceInfo', | |||||
access: 'k14', | |||||
}, | |||||
{ | |||||
code: 'deviceVesion', | |||||
name: '版本管理', | |||||
icon: 'smile', | |||||
path: '/device/deviceVesion', | |||||
component: './device/deviceVesion', | |||||
access: 'k14', | |||||
}, | |||||
// { | |||||
// code: 'devicetechnology', | |||||
// name: '设备工艺信息', | |||||
// icon: 'smile', | |||||
// path: '/device/devicetechnology', | |||||
// component: './device/devicetechnology', | |||||
// access: 'k7', | |||||
// }, | |||||
// { | |||||
// code: 'deviceFood', | |||||
// name: '设备商品管理', | |||||
// icon: 'smile', | |||||
// path: '/device/deviceFood', | |||||
// component: './device/deviceFood', | |||||
// access: 'k14', | |||||
// }, | |||||
], | |||||
}, | |||||
{ | |||||
code: 'database', | |||||
name: '元数据管理', | |||||
icon: 'DatabaseOutlined', | |||||
path: '/database', | |||||
routes:[ | |||||
{ | |||||
code: 'batching', | |||||
name: '物料管理', | |||||
icon: 'smile', | |||||
path: '/basic', | |||||
routes:[ | |||||
{ | |||||
code: 'batching', | |||||
name: '物料基础信息', | |||||
icon: 'smile', | |||||
path: '/database/basic/batching', | |||||
component: './database/basic/batching', | |||||
access: 'k7', | |||||
}, | |||||
] | |||||
}, | |||||
{ | |||||
code: 'goods', | |||||
name: '商品管理', | |||||
icon: 'smile', | |||||
path: '/goods', | |||||
routes:[ | |||||
{ | |||||
code: 'goods-type', | |||||
name: '商品类型', | |||||
icon: 'smile', | |||||
path: '/database/goods/goodstypemanage', | |||||
component: './database/goods/goodstypemanage', | |||||
access: 'k11', | |||||
}, | |||||
{ | |||||
code: 'goodsattribute', | |||||
name: '商品属性', | |||||
icon: 'smile', | |||||
path: '/database/goods/goodsattribute', | |||||
component: './database/goods/goodsattribute', | |||||
access: 'k10', | |||||
}, | |||||
{ | |||||
code: 'newgoods', | |||||
name: '商品基础信息', | |||||
icon: 'smile', | |||||
path: '/database/goods/newgoods', | |||||
component: './database/goods/newgoods', | |||||
access: 'k10', | |||||
}, | |||||
] | |||||
}, | |||||
] | |||||
}, | |||||
]; | |||||
// api.queryMenus(); | |||||
return data; | |||||
} catch (error) { | |||||
history.push(loginPath); | |||||
} | |||||
return []; | |||||
}; | |||||
// 如果是登录页面,不执行 | |||||
if (history.location.pathname !== loginPath) { | |||||
const currentUser = await fetchUserInfo(); | |||||
var tempMenu = await queryMenuData(); | |||||
//创建菜单 | |||||
//await syncMenus(tempMenu); | |||||
if (!isDev) { | |||||
var data = await dymicMenus(currentUser.data.id); | |||||
tempMenu = data.data; | |||||
} | |||||
return { | |||||
fetchUserInfo, | |||||
currentUser, | |||||
menuData: tempMenu, | |||||
settings: {}, | |||||
}; | |||||
} | |||||
return { | |||||
fetchUserInfo, | |||||
settings: {}, | |||||
}; | |||||
} | |||||
/** | |||||
* 同步菜单 | |||||
* @param {*} args | |||||
* @returns | |||||
*/ | |||||
const syncMenus = async (args) => { | |||||
return await api.queryAndSyncMenu({ json: JSON.stringify(args) }); | |||||
}; | |||||
/** | |||||
* 动态菜单 | |||||
* @param {*} userid | |||||
* @returns | |||||
*/ | |||||
const dymicMenus = async (userid) => { | |||||
return await api.DymicMenus(userid); | |||||
}; | |||||
/** | |||||
* | |||||
* 动态路由 | |||||
* | |||||
*/ | |||||
const loopMenuItem = (menus) => | |||||
menus?.map(({ icon, children, ...item }) => { | |||||
if (typeof icon != 'undefined' && Icon[icon] != undefined) { | |||||
return { | |||||
...item, | |||||
icon: icon && React.createElement(Icon[icon]), | |||||
children: children && loopMenuItem(children), | |||||
}; | |||||
} else { | |||||
return { | |||||
...item, | |||||
children: children && loopMenuItem(children), | |||||
}; | |||||
} | |||||
}); | |||||
/** | |||||
* 异常处理程序 | |||||
200: '服务器成功返回请求的数据。', | |||||
201: '新建或修改数据成功。', | |||||
202: '一个请求已经进入后台排队(异步任务)。', | |||||
204: '删除数据成功。', | |||||
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。', | |||||
401: '用户没有权限(令牌、用户名、密码错误)。', | |||||
403: '用户得到授权,但是访问是被禁止的。', | |||||
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。', | |||||
405: '请求方法不被允许。', | |||||
406: '请求的格式不可得。', | |||||
410: '请求的资源被永久删除,且不会再得到的。', | |||||
422: '当创建一个对象时,发生一个验证错误。', | |||||
500: '服务器发生错误,请检查服务器。', | |||||
502: '网关错误。', | |||||
503: '服务不可用,服务器暂时过载或维护。', | |||||
504: '网关超时。', | |||||
//-----English | |||||
200: The server successfully returned the requested data. ', | |||||
201: New or modified data is successful. ', | |||||
202: A request has entered the background queue (asynchronous task). ', | |||||
204: Data deleted successfully. ', | |||||
400: 'There was an error in the request sent, and the server did not create or modify data. ', | |||||
401: The user does not have permission (token, username, password error). ', | |||||
403: The user is authorized, but access is forbidden. ', | |||||
404: The request sent was for a record that did not exist. ', | |||||
405: The request method is not allowed. ', | |||||
406: The requested format is not available. ', | |||||
410': | |||||
'The requested resource is permanently deleted and will no longer be available. ', | |||||
422: When creating an object, a validation error occurred. ', | |||||
500: An error occurred on the server, please check the server. ', | |||||
502: Gateway error. ', | |||||
503: The service is unavailable. ', | |||||
504: The gateway timed out. ', | |||||
* @see https://beta-pro.ant.design/docs/request-cn | |||||
*/ | |||||
const codeMessage = { | |||||
200: '服务器成功返回请求的数据。', | |||||
201: '新建或修改数据成功。', | |||||
202: '一个请求已经进入后台排队(异步任务)。', | |||||
204: '删除数据成功。', | |||||
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。', | |||||
401: '用户没有权限(令牌、用户名、密码错误)。', | |||||
403: '用户得到授权,但是访问是被禁止的。', | |||||
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。', | |||||
405: '请求方法不被允许。', | |||||
406: '请求的格式不可得。', | |||||
410: '请求的资源被永久删除,且不会再得到的。', | |||||
422: '当创建一个对象时,发生一个验证错误。', | |||||
500: '服务器发生错误,请检查服务器。', | |||||
502: '网关错误。', | |||||
503: '服务不可用,服务器暂时过载或维护。', | |||||
504: '网关超时。', | |||||
}; | |||||
export const request = { | |||||
errorHandler: (error) => { | |||||
const { response } = error; | |||||
console.log('response', response); | |||||
if (response && response.status !== 200 && response.status !== 422) { | |||||
const errorText = codeMessage[response.status] || response.statusText; | |||||
const { status, url } = response; | |||||
if (response.status === 401 || response.status === 403) { | |||||
notification.error({ | |||||
description: '请先登录。', | |||||
message: '未登录', | |||||
}); | |||||
// history.push('/user/login'); | |||||
const { location } = history; // 如果没有登录,重定向到 login | |||||
if (!location.pathname.includes(loginPath) && history.location.pathname !== '/gateAdmin') { | |||||
history.push(loginPath); | |||||
} | |||||
} else { | |||||
notification.error({ | |||||
description: '网络发生异常,无法连接服务器', | |||||
message: '网络异常', | |||||
}); | |||||
} | |||||
} | |||||
if (!response) { | |||||
notification.error({ | |||||
description: '网络发生异常,无法连接服务器', | |||||
message: '网络异常', | |||||
}); | |||||
} | |||||
throw error; | |||||
}, | |||||
//prefix: `${DOMAIN}`, | |||||
requestInterceptors: [ | |||||
(url, options) => { | |||||
url = decodeURI(encodeURI(url).replace(/%E2%80%8B/g, '')); | |||||
const newOptions = { ...options }; | |||||
const auttoken = localStorage.getItem('token'); | |||||
const userid = localStorage.getItem('userid'); | |||||
newOptions.headers = { | |||||
...newOptions.headers, | |||||
userid: userid, | |||||
Authorization: 'Bearer ' + auttoken, | |||||
LoginType: 1, | |||||
Accept: 'application/json', | |||||
'Content-Type': 'application/json; charset=utf-8', | |||||
}; | |||||
return { url, options: newOptions }; | |||||
}, | |||||
], | |||||
}; | |||||
// ProLayout 支持的api https://procomponents.ant.design/components/layout | |||||
export const layout = ({ initialState }) => { | |||||
return { | |||||
rightContentRender: () => <RightContent />, | |||||
disableContentMargin: false, | |||||
waterMarkProps: { | |||||
content: '黑菠萝技术部', | |||||
}, | |||||
footerRender: () => <Footer />, | |||||
onPageChange: () => { | |||||
const { location } = history; // 如果没有登录,重定向到 login | |||||
// console.log(process.env.UMI_ENV) | |||||
// console.log('location.pathname', location.pathname); | |||||
if (!initialState?.currentUser && (!location.pathname.includes(loginPath) || !location.pathname.includes('/gateAdmin'))) { | |||||
history.push(loginPath); | |||||
} | |||||
}, | |||||
menuDataRender: () => loopMenuItem(initialState?.menuData), | |||||
// childrenRender: (children) => { | |||||
// return ( | |||||
// <> | |||||
// {initialState?.currentUser && location.pathname !== loginPath && location.pathname!='/' ? ( | |||||
// <TagView home="/welcome" current={history.location.pathname}> | |||||
// {children} | |||||
// </TagView> | |||||
// ) : children} | |||||
// </> | |||||
// ); | |||||
// }, | |||||
// 自定义 403 页面 | |||||
// unAccessible: <div>unAccessible</div>, | |||||
...initialState?.settings, | |||||
}; | |||||
}; |
@@ -0,0 +1,79 @@ | |||||
import React, { useEffect } from 'react'; | |||||
import { Button, Card, DatePicker, Col, Row, TreeSelect, Spin } from 'antd'; | |||||
import styles from './index.less'; | |||||
import moment from 'moment'; | |||||
const { RangePicker } = DatePicker; | |||||
/** | |||||
* 条件查询通用头部 | |||||
* @returns | |||||
*/ | |||||
export default function Index(props) { | |||||
const LoadingCard = () => { | |||||
return ( | |||||
<div className={styles['loading-card']}> | |||||
<Spin size="large" /> | |||||
</div> | |||||
) | |||||
} | |||||
return ( | |||||
<div> | |||||
{props.showLoading ? <LoadingCard></LoadingCard> : null} | |||||
<Card className={styles['data-search-card']}> | |||||
<Row gutter={20} className={styles['data-search-row']}> | |||||
<Col xs={24} sm={24} md={24} lg={12} xl={6} className={styles['data-search-item']}> | |||||
<RangePicker size='middle' className={styles['data-search-sufixx']} value={props.timeRange} onChange={(date, dateStrings) => { | |||||
let tempDate = [ | |||||
moment(moment(new Date(dateStrings[0])).format('YYYY-MM-DD 00:00:00')), | |||||
moment(moment(new Date(dateStrings[1])).format('YYYY-MM-DD 23:59:59')), | |||||
] | |||||
props.onTimePickerChange(tempDate); | |||||
}} /> | |||||
</Col> | |||||
<Col xs={24} sm={24} md={24} lg={12} xl={6} className={styles['data-search-item']}> | |||||
<div className={props.searchDayIndex === 0 ? `${styles['data-search-day']} ${styles['search-day-selected']}` : `${styles['data-search-day']}`} onClick={() => props.onChangeTimeIndex(0)}> | |||||
今天 | |||||
</div> | |||||
<div className={props.searchDayIndex === 1 ? `${styles['data-search-day']} ${styles['search-day-selected']}` : `${styles['data-search-day']}`} onClick={() => props.onChangeTimeIndex(1)}> | |||||
昨天 | |||||
</div> | |||||
<div className={props.searchDayIndex === 2 ? `${styles['data-search-day']} ${styles['search-day-selected']}` : `${styles['data-search-day']}`} onClick={() => props.onChangeTimeIndex(2)}> | |||||
近7天 | |||||
</div> | |||||
<div className={props.searchDayIndex === 3 ? `${styles['data-search-day']} ${styles['search-day-selected']}` : `${styles['data-search-day']}`} onClick={() => props.onChangeTimeIndex(3)}> | |||||
近30天 | |||||
</div> | |||||
</Col> | |||||
<Col xs={24} sm={24} md={24} lg={12} xl={6} className={styles['data-search-item']}> | |||||
<TreeSelect | |||||
className={styles['data-search-sufixx']} | |||||
dropdownStyle={{ | |||||
maxHeight: 400, | |||||
overflow: 'auto', | |||||
}} | |||||
value={props.currentOrg.title} | |||||
treeData={props.orgTree} | |||||
onSelect={(value, node) => { | |||||
if (node.type === 2 || node === 3) { | |||||
props.onCurrentOrgChange(node); | |||||
} else { | |||||
props.onCurrentOrgChange(""); | |||||
} | |||||
}} | |||||
placeholder="请选择组织架构" | |||||
treeDefaultExpandAll | |||||
/> | |||||
</Col> | |||||
<Col xs={24} sm={24} md={12} lg={12} xl={6} className={styles['data-search-item']}> | |||||
<div className={styles['data-search-btns']}> | |||||
<Button className={styles['search-btn-item']} onClick={props.onResetSearch}>重置</Button> | |||||
<Button className={styles['search-btn-item']} type="primary" onClick={props.onQueryBtn}>查询</Button> | |||||
</div> | |||||
</Col> | |||||
</Row> | |||||
</Card> | |||||
</div> | |||||
) | |||||
} |
@@ -0,0 +1,93 @@ | |||||
.data-search-card { | |||||
margin-bottom: 20px; | |||||
} | |||||
.data-search-row { | |||||
margin-bottom: 10px; | |||||
} | |||||
.data-search-item { | |||||
display: flex; | |||||
align-items: center; | |||||
margin-bottom: 10px; | |||||
} | |||||
.data-search-prefix { | |||||
margin-right: 10px; | |||||
font-size: 18px; | |||||
} | |||||
.search-btn-item { | |||||
margin-right: 20px; | |||||
} | |||||
.data-search-day { | |||||
padding: 5px 20px; | |||||
border: 1px solid #dedede; | |||||
cursor: pointer; | |||||
} | |||||
.data-search-day:nth-child(1), .data-search-day:nth-child(2), .data-search-day:nth-child(3) { | |||||
border-right: none; | |||||
} | |||||
.search-day-selected { | |||||
border: 1px solid #FA541C !important; | |||||
color: #FA541C; | |||||
} | |||||
.data-search-sufixx { | |||||
width: 100%; | |||||
} | |||||
// 加载中 | |||||
.loading-card { | |||||
position: fixed; | |||||
display: flex; | |||||
align-items: center; | |||||
justify-content: center; | |||||
width: 100vw; | |||||
height: 100vh; | |||||
top: 0; | |||||
left: 0; | |||||
right: 0; | |||||
bottom: 0; | |||||
z-index: 999; | |||||
background-color: rgba(0, 0, 0, 0.5); | |||||
} | |||||
.member-card-box { | |||||
display: flex; | |||||
justify-content: space-between; | |||||
align-items: center; | |||||
margin-bottom: 5px; | |||||
} | |||||
.member-card-prefix { | |||||
font-size: 20px; | |||||
font-weight: 700; | |||||
color: #999; | |||||
font-family: '楷体'; | |||||
} | |||||
.member-card-sufixx { | |||||
font-size: 22px; | |||||
} | |||||
.new-member { | |||||
width: 100%; | |||||
height: 250px; | |||||
} | |||||
.repur-chase { | |||||
width: 100%; | |||||
height: 250px; | |||||
} | |||||
.member-card { | |||||
height: 300px; | |||||
} | |||||
.member-row-common { | |||||
margin-bottom: 10px; | |||||
} |
@@ -0,0 +1,18 @@ | |||||
import { useIntl } from 'umi'; | |||||
import { GithubOutlined } from '@ant-design/icons'; | |||||
import { DefaultFooter } from '@ant-design/pro-layout'; | |||||
export default () => { | |||||
const intl = useIntl(); | |||||
const defaultMessage = intl.formatMessage({ | |||||
id: 'app.copyright.produced', | |||||
defaultMessage: '', | |||||
}); | |||||
return ( | |||||
<DefaultFooter | |||||
copyright={`${defaultMessage}`} | |||||
links={[ | |||||
]} | |||||
/> | |||||
); | |||||
}; |
@@ -0,0 +1,29 @@ | |||||
import { Dropdown, Button,Avatar } from 'antd'; | |||||
import React from 'react'; | |||||
import classNames from 'classnames'; | |||||
import styles from './index.less'; | |||||
import { PlayCircleOutlined, FireOutlined } from '@ant-design/icons'; | |||||
import { useHistory } from "umi" | |||||
const HeaderDropdown = ({ overlayClassName: cls, ...restProps }) => { | |||||
const history = useHistory(); | |||||
return <div style={{ display: 'flex', alignItems: 'center' }}> | |||||
{/* <Button onClick={onQuickStart} type='primary' icon={<PlayCircleOutlined />}>快速开店</Button> | |||||
<Button onClick={onQuickActivity} style={{ marginLeft: '10px' }} icon={<FireOutlined />}>创建活动</Button> */} | |||||
{/* <Avatar | |||||
style={{ | |||||
backgroundColor: '#f56a00', | |||||
verticalAlign: 'middle', | |||||
}} | |||||
size="large" | |||||
gap='4' | |||||
> | |||||
U | |||||
</Avatar> */} | |||||
<Dropdown overlayClassName={classNames(styles.container, cls)} {...restProps} /> | |||||
</div> | |||||
}; | |||||
export default HeaderDropdown; |
@@ -0,0 +1,16 @@ | |||||
@import '~antd/es/style/themes/default.less'; | |||||
.container > * { | |||||
background-color: @popover-bg; | |||||
border-radius: 4px; | |||||
box-shadow: @shadow-1-down; | |||||
} | |||||
@media screen and (max-width: @screen-xs) { | |||||
.container { | |||||
width: 100% !important; | |||||
} | |||||
.container > * { | |||||
border-radius: 0 !important; | |||||
} | |||||
} |
@@ -0,0 +1,83 @@ | |||||
import { SearchOutlined } from '@ant-design/icons'; | |||||
import { AutoComplete, Input } from 'antd'; | |||||
import useMergedState from 'rc-util/es/hooks/useMergedState'; | |||||
import React, { useRef } from 'react'; | |||||
import classNames from 'classnames'; | |||||
import styles from './index.less'; | |||||
const HeaderSearch = (props) => { | |||||
const { | |||||
className, | |||||
defaultValue, | |||||
onVisibleChange, | |||||
placeholder, | |||||
visible, | |||||
defaultVisible, | |||||
...restProps | |||||
} = props; | |||||
const inputRef = useRef(null); | |||||
const [value, setValue] = useMergedState(defaultValue, { | |||||
value: props.value, | |||||
onChange: props.onChange, | |||||
}); | |||||
const [searchMode, setSearchMode] = useMergedState(defaultVisible ?? false, { | |||||
value: props.visible, | |||||
onChange: onVisibleChange, | |||||
}); | |||||
const inputClass = classNames(styles.input, { | |||||
[styles.show]: searchMode, | |||||
}); | |||||
return ( | |||||
<div | |||||
className={classNames(className, styles.headerSearch)} | |||||
onClick={() => { | |||||
setSearchMode(true); | |||||
if (searchMode && inputRef.current) { | |||||
inputRef.current.focus(); | |||||
} | |||||
}} | |||||
onTransitionEnd={({ propertyName }) => { | |||||
if (propertyName === 'width' && !searchMode) { | |||||
if (onVisibleChange) { | |||||
onVisibleChange(searchMode); | |||||
} | |||||
} | |||||
}} | |||||
> | |||||
<SearchOutlined | |||||
key="Icon" | |||||
style={{ | |||||
cursor: 'pointer', | |||||
}} | |||||
/> | |||||
<AutoComplete | |||||
key="AutoComplete" | |||||
className={inputClass} | |||||
value={value} | |||||
options={restProps.options} | |||||
onChange={setValue} | |||||
> | |||||
<Input | |||||
size="small" | |||||
ref={inputRef} | |||||
defaultValue={defaultValue} | |||||
aria-label={placeholder} | |||||
placeholder={placeholder} | |||||
onKeyDown={(e) => { | |||||
if (e.key === 'Enter') { | |||||
if (restProps.onSearch) { | |||||
restProps.onSearch(value); | |||||
} | |||||
} | |||||
}} | |||||
onBlur={() => { | |||||
setSearchMode(false); | |||||
}} | |||||
/> | |||||
</AutoComplete> | |||||
</div> | |||||
); | |||||
}; | |||||
export default HeaderSearch; |
@@ -0,0 +1,25 @@ | |||||
@import '~antd/es/style/themes/default.less'; | |||||
.headerSearch { | |||||
display: inline-flex; | |||||
align-items: center; | |||||
.input { | |||||
width: 0; | |||||
min-width: 0; | |||||
overflow: hidden; | |||||
background: transparent; | |||||
border-radius: 0; | |||||
transition: width 0.3s, margin-left 0.3s; | |||||
:global(.ant-select-selection) { | |||||
background: transparent; | |||||
} | |||||
input { | |||||
box-shadow: none !important; | |||||
} | |||||
&.show { | |||||
width: 210px; | |||||
margin-left: 8px; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,114 @@ | |||||
import { BellOutlined } from '@ant-design/icons'; | |||||
import { Badge, Spin, Tabs } from 'antd'; | |||||
import useMergedState from 'rc-util/es/hooks/useMergedState'; | |||||
import React from 'react'; | |||||
import classNames from 'classnames'; | |||||
import NoticeList from './NoticeList'; | |||||
import HeaderDropdown from '../HeaderDropdown'; | |||||
import styles from './index.less'; | |||||
const { TabPane } = Tabs; | |||||
const NoticeIcon = (props) => { | |||||
const getNotificationBox = () => { | |||||
const { | |||||
children, | |||||
loading, | |||||
onClear, | |||||
onTabChange, | |||||
onItemClick, | |||||
onViewMore, | |||||
clearText, | |||||
viewMoreText, | |||||
} = props; | |||||
if (!children) { | |||||
return null; | |||||
} | |||||
const panes = []; | |||||
React.Children.forEach(children, (child) => { | |||||
if (!child) { | |||||
return; | |||||
} | |||||
const { list, title, count, tabKey, showClear, showViewMore } = child.props; | |||||
const len = list && list.length ? list.length : 0; | |||||
const msgCount = count || count === 0 ? count : len; | |||||
const tabTitle = msgCount > 0 ? `${title} (${msgCount})` : title; | |||||
panes.push( | |||||
<TabPane tab={tabTitle} key={tabKey}> | |||||
<NoticeList | |||||
clearText={clearText} | |||||
viewMoreText={viewMoreText} | |||||
list={list} | |||||
tabKey={tabKey} | |||||
onClear={() => onClear && onClear(title, tabKey)} | |||||
onClick={(item) => onItemClick && onItemClick(item, child.props)} | |||||
onViewMore={(event) => onViewMore && onViewMore(child.props, event)} | |||||
showClear={showClear} | |||||
showViewMore={showViewMore} | |||||
title={title} | |||||
/> | |||||
</TabPane>, | |||||
); | |||||
}); | |||||
return ( | |||||
<> | |||||
<Spin spinning={loading} delay={300}> | |||||
<Tabs className={styles.tabs} onChange={onTabChange}> | |||||
{panes} | |||||
</Tabs> | |||||
</Spin> | |||||
</> | |||||
); | |||||
}; | |||||
const { className, count, bell } = props; | |||||
const [visible, setVisible] = useMergedState(false, { | |||||
value: props.popupVisible, | |||||
onChange: props.onPopupVisibleChange, | |||||
}); | |||||
const noticeButtonClass = classNames(className, styles.noticeButton); | |||||
const notificationBox = getNotificationBox(); | |||||
const NoticeBellIcon = bell || <BellOutlined className={styles.icon} />; | |||||
const trigger = ( | |||||
<span | |||||
className={classNames(noticeButtonClass, { | |||||
opened: visible, | |||||
})} | |||||
> | |||||
<Badge | |||||
count={count} | |||||
style={{ | |||||
boxShadow: 'none', | |||||
}} | |||||
className={styles.badge} | |||||
> | |||||
{NoticeBellIcon} | |||||
</Badge> | |||||
</span> | |||||
); | |||||
if (!notificationBox) { | |||||
return trigger; | |||||
} | |||||
return ( | |||||
<HeaderDropdown | |||||
placement="bottomRight" | |||||
overlay={notificationBox} | |||||
overlayClassName={styles.popover} | |||||
trigger={['click']} | |||||
visible={visible} | |||||
onVisibleChange={setVisible} | |||||
> | |||||
{trigger} | |||||
</HeaderDropdown> | |||||
); | |||||
}; | |||||
NoticeIcon.defaultProps = { | |||||
emptyImage: 'https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg', | |||||
}; | |||||
NoticeIcon.Tab = NoticeList; | |||||
export default NoticeIcon; |
@@ -0,0 +1,97 @@ | |||||
import { Avatar, List } from 'antd'; | |||||
import React from 'react'; | |||||
import classNames from 'classnames'; | |||||
import styles from './NoticeList.less'; | |||||
const NoticeList = ({ | |||||
list = [], | |||||
onClick, | |||||
onClear, | |||||
title, | |||||
onViewMore, | |||||
emptyText, | |||||
showClear = true, | |||||
clearText, | |||||
viewMoreText, | |||||
showViewMore = false, | |||||
}) => { | |||||
if (!list || list.length === 0) { | |||||
return ( | |||||
<div className={styles.notFound}> | |||||
<img | |||||
src="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg" | |||||
alt="not found" | |||||
/> | |||||
<div>{emptyText}</div> | |||||
</div> | |||||
); | |||||
} | |||||
return ( | |||||
<div> | |||||
<List | |||||
className={styles.list} | |||||
dataSource={list} | |||||
renderItem={(item, i) => { | |||||
const itemCls = classNames(styles.item, { | |||||
[styles.read]: item.read, | |||||
}); // eslint-disable-next-line no-nested-ternary | |||||
const leftIcon = item.avatar ? ( | |||||
typeof item.avatar === 'string' ? ( | |||||
<Avatar className={styles.avatar} src={item.avatar} /> | |||||
) : ( | |||||
<span className={styles.iconElement}>{item.avatar}</span> | |||||
) | |||||
) : null; | |||||
return ( | |||||
<List.Item | |||||
className={itemCls} | |||||
key={item.key || i} | |||||
onClick={() => { | |||||
onClick?.(item); | |||||
}} | |||||
> | |||||
<List.Item.Meta | |||||
className={styles.meta} | |||||
avatar={leftIcon} | |||||
title={ | |||||
<div className={styles.title}> | |||||
{item.title} | |||||
<div className={styles.extra}>{item.extra}</div> | |||||
</div> | |||||
} | |||||
description={ | |||||
<div> | |||||
<div className={styles.description}>{item.description}</div> | |||||
<div className={styles.datetime}>{item.datetime}</div> | |||||
</div> | |||||
} | |||||
/> | |||||
</List.Item> | |||||
); | |||||
}} | |||||
/> | |||||
<div className={styles.bottomBar}> | |||||
{showClear ? ( | |||||
<div onClick={onClear}> | |||||
{clearText} {title} | |||||
</div> | |||||
) : null} | |||||
{showViewMore ? ( | |||||
<div | |||||
onClick={(e) => { | |||||
if (onViewMore) { | |||||
onViewMore(e); | |||||
} | |||||
}} | |||||
> | |||||
{viewMoreText} | |||||
</div> | |||||
) : null} | |||||
</div> | |||||
</div> | |||||
); | |||||
}; | |||||
export default NoticeList; |
@@ -0,0 +1,103 @@ | |||||
@import '~antd/es/style/themes/default.less'; | |||||
.list { | |||||
max-height: 400px; | |||||
overflow: auto; | |||||
&::-webkit-scrollbar { | |||||
display: none; | |||||
} | |||||
.item { | |||||
padding-right: 24px; | |||||
padding-left: 24px; | |||||
overflow: hidden; | |||||
cursor: pointer; | |||||
transition: all 0.3s; | |||||
.meta { | |||||
width: 100%; | |||||
} | |||||
.avatar { | |||||
margin-top: 4px; | |||||
background: @component-background; | |||||
} | |||||
.iconElement { | |||||
font-size: 32px; | |||||
} | |||||
&.read { | |||||
opacity: 0.4; | |||||
} | |||||
&:last-child { | |||||
border-bottom: 0; | |||||
} | |||||
&:hover { | |||||
background: @primary-1; | |||||
} | |||||
.title { | |||||
margin-bottom: 8px; | |||||
font-weight: normal; | |||||
} | |||||
.description { | |||||
font-size: 12px; | |||||
line-height: @line-height-base; | |||||
} | |||||
.datetime { | |||||
margin-top: 4px; | |||||
font-size: 12px; | |||||
line-height: @line-height-base; | |||||
} | |||||
.extra { | |||||
float: right; | |||||
margin-top: -1.5px; | |||||
margin-right: 0; | |||||
color: @text-color-secondary; | |||||
font-weight: normal; | |||||
} | |||||
} | |||||
.loadMore { | |||||
padding: 8px 0; | |||||
color: @primary-6; | |||||
text-align: center; | |||||
cursor: pointer; | |||||
&.loadedAll { | |||||
color: rgba(0, 0, 0, 0.25); | |||||
cursor: unset; | |||||
} | |||||
} | |||||
} | |||||
.notFound { | |||||
padding: 73px 0 88px; | |||||
color: @text-color-secondary; | |||||
text-align: center; | |||||
img { | |||||
display: inline-block; | |||||
height: 76px; | |||||
margin-bottom: 16px; | |||||
} | |||||
} | |||||
.bottomBar { | |||||
height: 46px; | |||||
color: @text-color; | |||||
line-height: 46px; | |||||
text-align: center; | |||||
border-top: 1px solid @border-color-split; | |||||
border-radius: 0 0 @border-radius-base @border-radius-base; | |||||
transition: all 0.3s; | |||||
div { | |||||
display: inline-block; | |||||
width: 50%; | |||||
cursor: pointer; | |||||
transition: all 0.3s; | |||||
user-select: none; | |||||
&:only-child { | |||||
width: 100%; | |||||
} | |||||
&:not(:only-child):last-child { | |||||
border-left: 1px solid @border-color-split; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,147 @@ | |||||
import { useEffect, useState } from 'react'; | |||||
import { Tag, message } from 'antd'; | |||||
import { groupBy } from 'lodash'; | |||||
import moment from 'moment'; | |||||
import { useModel } from 'umi'; | |||||
import { getNotices } from '@/services/ant-design-pro/api'; | |||||
import NoticeIcon from './NoticeIcon'; | |||||
import styles from './index.less'; | |||||
const getNoticeData = (notices) => { | |||||
if (!notices || notices.length === 0 || !Array.isArray(notices)) { | |||||
return {}; | |||||
} | |||||
const newNotices = notices.map((notice) => { | |||||
const newNotice = { ...notice }; | |||||
if (newNotice.datetime) { | |||||
newNotice.datetime = moment(notice.datetime).fromNow(); | |||||
} | |||||
if (newNotice.id) { | |||||
newNotice.key = newNotice.id; | |||||
} | |||||
if (newNotice.extra && newNotice.status) { | |||||
const color = { | |||||
todo: '', | |||||
processing: 'blue', | |||||
urgent: 'red', | |||||
doing: 'gold', | |||||
}[newNotice.status]; | |||||
newNotice.extra = ( | |||||
<Tag | |||||
color={color} | |||||
style={{ | |||||
marginRight: 0, | |||||
}} | |||||
> | |||||
{newNotice.extra} | |||||
</Tag> | |||||
); | |||||
} | |||||
return newNotice; | |||||
}); | |||||
return groupBy(newNotices, 'type'); | |||||
}; | |||||
const getUnreadData = (noticeData) => { | |||||
const unreadMsg = {}; | |||||
Object.keys(noticeData).forEach((key) => { | |||||
const value = noticeData[key]; | |||||
if (!unreadMsg[key]) { | |||||
unreadMsg[key] = 0; | |||||
} | |||||
if (Array.isArray(value)) { | |||||
unreadMsg[key] = value.filter((item) => !item.read).length; | |||||
} | |||||
}); | |||||
return unreadMsg; | |||||
}; | |||||
const NoticeIconView = () => { | |||||
const { initialState } = useModel('@@initialState'); | |||||
const { currentUser } = initialState || {}; | |||||
const [notices, setNotices] = useState([]); | |||||
useEffect(() => { | |||||
getNotices().then(({ data }) => setNotices(data || [])); | |||||
}, []); | |||||
const noticeData = getNoticeData(notices); | |||||
const unreadMsg = getUnreadData(noticeData || {}); | |||||
const changeReadState = (id) => { | |||||
setNotices( | |||||
notices.map((item) => { | |||||
const notice = { ...item }; | |||||
if (notice.id === id) { | |||||
notice.read = true; | |||||
} | |||||
return notice; | |||||
}), | |||||
); | |||||
}; | |||||
const clearReadState = (title, key) => { | |||||
setNotices( | |||||
notices.map((item) => { | |||||
const notice = { ...item }; | |||||
if (notice.type === key) { | |||||
notice.read = true; | |||||
} | |||||
return notice; | |||||
}), | |||||
); | |||||
message.success(`${'清空了'} ${title}`); | |||||
}; | |||||
return ( | |||||
<NoticeIcon | |||||
className={styles.action} | |||||
count={currentUser && currentUser.unreadCount} | |||||
onItemClick={(item) => { | |||||
changeReadState(item.id); | |||||
}} | |||||
onClear={(title, key) => clearReadState(title, key)} | |||||
loading={false} | |||||
clearText="清空" | |||||
viewMoreText="查看更多" | |||||
onViewMore={() => message.info('Click on view more')} | |||||
clearClose | |||||
> | |||||
<NoticeIcon.Tab | |||||
tabKey="notification" | |||||
count={unreadMsg.notification} | |||||
list={noticeData.notification} | |||||
title="通知" | |||||
emptyText="你已查看所有通知" | |||||
showViewMore | |||||
/> | |||||
<NoticeIcon.Tab | |||||
tabKey="message" | |||||
count={unreadMsg.message} | |||||
list={noticeData.message} | |||||
title="消息" | |||||
emptyText="您已读完所有消息" | |||||
showViewMore | |||||
/> | |||||
<NoticeIcon.Tab | |||||
tabKey="event" | |||||
title="待办" | |||||
emptyText="你已完成所有待办" | |||||
count={unreadMsg.event} | |||||
list={noticeData.event} | |||||
showViewMore | |||||
/> | |||||
</NoticeIcon> | |||||
); | |||||
}; | |||||
export default NoticeIconView; |
@@ -0,0 +1,35 @@ | |||||
@import '~antd/es/style/themes/default.less'; | |||||
.popover { | |||||
position: relative; | |||||
width: 336px; | |||||
} | |||||
.noticeButton { | |||||
display: inline-block; | |||||
cursor: pointer; | |||||
transition: all 0.3s; | |||||
} | |||||
.icon { | |||||
padding: 4px; | |||||
vertical-align: middle; | |||||
} | |||||
.badge { | |||||
font-size: 16px; | |||||
} | |||||
.tabs { | |||||
:global { | |||||
.ant-tabs-nav-list { | |||||
margin: auto; | |||||
} | |||||
.ant-tabs-nav-scroll { | |||||
text-align: center; | |||||
} | |||||
.ant-tabs-bar { | |||||
margin-bottom: 0; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,98 @@ | |||||
import React, { useCallback } from 'react'; | |||||
import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons'; | |||||
import { Avatar, Menu, Spin } from 'antd'; | |||||
import { history, useModel } from 'umi'; | |||||
import { stringify } from 'querystring'; | |||||
import HeaderDropdown from '../HeaderDropdown'; | |||||
import styles from './index.less'; | |||||
import api from '@/services/api'; | |||||
/** | |||||
* 退出登录,并且将当前的 url 保存 | |||||
*/ | |||||
const loginOut = async () => { | |||||
await api.outLogin(); | |||||
const { query = {}, pathname } = history.location; | |||||
const { redirect } = query; // Note: There may be security issues, please note | |||||
if (window.location.pathname !== '/user/login' && !redirect) { | |||||
history.replace({ | |||||
pathname: '/user/login', | |||||
search: stringify({ | |||||
redirect: pathname, | |||||
}), | |||||
}); | |||||
} | |||||
}; | |||||
const AvatarDropdown = ({ menu }) => { | |||||
const { initialState, setInitialState } = useModel('@@initialState'); | |||||
const onMenuClick = useCallback( | |||||
(event) => { | |||||
const { key } = event; | |||||
if (key === 'logout' && initialState) { | |||||
setInitialState({ ...initialState, currentUser: undefined }); | |||||
loginOut(); | |||||
return; | |||||
} | |||||
history.push(`/account/${key}`); | |||||
}, | |||||
[initialState, setInitialState], | |||||
); | |||||
const loading = ( | |||||
<span className={`${styles.action} ${styles.account}`}> | |||||
<Spin | |||||
size="small" | |||||
style={{ | |||||
marginLeft: 8, | |||||
marginRight: 8, | |||||
}} | |||||
/> | |||||
</span> | |||||
); | |||||
if (!initialState) { | |||||
return loading; | |||||
} | |||||
const { currentUser } = initialState; | |||||
if (!currentUser || !currentUser.succeeded) { | |||||
return loading; | |||||
} | |||||
const menuHeaderDropdown = ( | |||||
<Menu className={styles.menu} selectedKeys={[]} onClick={onMenuClick}> | |||||
{menu && ( | |||||
<Menu.Item key="center"> | |||||
<UserOutlined /> | |||||
个人中心1 | |||||
</Menu.Item> | |||||
)} | |||||
{menu && ( | |||||
<Menu.Item key="settings"> | |||||
<SettingOutlined /> | |||||
个人设置 | |||||
</Menu.Item> | |||||
)} | |||||
{menu && <Menu.Divider />} | |||||
<Menu.Item key="logout"> | |||||
<LogoutOutlined /> | |||||
退出登录 | |||||
</Menu.Item> | |||||
</Menu> | |||||
); | |||||
return ( | |||||
<HeaderDropdown overlay={menuHeaderDropdown}> | |||||
<span className={`${styles.action} ${styles.account}`}> | |||||
<Avatar size="small" className={styles.avatar} gap='4' alt="avatar" >{currentUser.data.name}</Avatar> | |||||
<span className={`${styles.name} anticon`}>{currentUser.data.name}</span> | |||||
</span> | |||||
</HeaderDropdown> | |||||
); | |||||
}; | |||||
export default AvatarDropdown; |
@@ -0,0 +1,63 @@ | |||||
import { Space } from 'antd'; | |||||
import { QuestionCircleOutlined } from '@ant-design/icons'; | |||||
import React from 'react'; | |||||
import { useModel, SelectLang } from 'umi'; | |||||
import Avatar from './AvatarDropdown'; | |||||
import HeaderSearch from '../HeaderSearch'; | |||||
import styles from './index.less'; | |||||
const GlobalHeaderRight = () => { | |||||
const { initialState } = useModel('@@initialState'); | |||||
if (!initialState || !initialState.settings) { | |||||
return null; | |||||
} | |||||
const { navTheme, layout } = initialState.settings; | |||||
let className = styles.right; | |||||
if ((navTheme === 'dark' && layout === 'top') || layout === 'mix') { | |||||
className = `${styles.right} ${styles.dark}`; | |||||
} | |||||
return ( | |||||
<Space className={className}> | |||||
{/* <HeaderSearch | |||||
className={`${styles.action} ${styles.search}`} | |||||
placeholder="站内搜索" | |||||
defaultValue="umi ui" | |||||
options={[ | |||||
{ | |||||
label: <a href="https://umijs.org/zh/guide/umi-ui.html">umi ui</a>, | |||||
value: 'umi ui', | |||||
}, | |||||
{ | |||||
label: <a href="next.ant.design">Ant Design</a>, | |||||
value: 'Ant Design', | |||||
}, | |||||
{ | |||||
label: <a href="https://protable.ant.design/">Pro Table</a>, | |||||
value: 'Pro Table', | |||||
}, | |||||
{ | |||||
label: <a href="https://prolayout.ant.design/">Pro Layout</a>, | |||||
value: 'Pro Layout', | |||||
}, | |||||
]} // onSearch={value => { | |||||
// }} | |||||
/> */} | |||||
{/* <span | |||||
className={styles.action} | |||||
onClick={() => { | |||||
window.open('https://pro.ant.design/docs/getting-started'); | |||||
}} | |||||
> | |||||
<QuestionCircleOutlined /> | |||||
</span> */} | |||||
<Avatar /> | |||||
{/* <SelectLang className={styles.action} /> */} | |||||
</Space> | |||||
); | |||||
}; | |||||
export default GlobalHeaderRight; |
@@ -0,0 +1,62 @@ | |||||
@import '~antd/es/style/themes/default.less'; | |||||
@pro-header-hover-bg: rgba(0, 0, 0, 0.025); | |||||
.menu { | |||||
:global(.anticon) { | |||||
margin-right: 8px; | |||||
} | |||||
:global(.ant-dropdown-menu-item) { | |||||
min-width: 160px; | |||||
} | |||||
} | |||||
.right { | |||||
display: flex; | |||||
float: right; | |||||
height: 48px; | |||||
margin-left: auto; | |||||
overflow: hidden; | |||||
.action { | |||||
display: flex; | |||||
align-items: center; | |||||
height: 48px; | |||||
padding: 0 12px; | |||||
cursor: pointer; | |||||
transition: all 0.3s; | |||||
> span { | |||||
vertical-align: middle; | |||||
} | |||||
&:hover { | |||||
background: @pro-header-hover-bg; | |||||
} | |||||
&:global(.opened) { | |||||
background: @pro-header-hover-bg; | |||||
} | |||||
} | |||||
.search { | |||||
padding: 0 12px; | |||||
&:hover { | |||||
background: transparent; | |||||
} | |||||
} | |||||
.account { | |||||
.avatar { | |||||
margin-right: 8px; | |||||
color: @primary-color; | |||||
vertical-align: top; | |||||
background: rgba(226, 163, 163, 0.85); | |||||
} | |||||
} | |||||
} | |||||
.dark { | |||||
.action { | |||||
&:hover { | |||||
background: #252a3d; | |||||
} | |||||
&:global(.opened) { | |||||
background: #252a3d; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,128 @@ | |||||
import React, { useState, useRef, useEffect } from 'react'; | |||||
import { Scrollbars } from 'react-custom-scrollbars'; | |||||
import { CloseOutlined } from '@ant-design/icons'; | |||||
import { history } from 'umi'; | |||||
// import type { TagsItemType } from '../index'; | |||||
import styles from './index.less'; | |||||
const Tags = ({ tagList, closeTag, closeAllTag, closeOtherTag, refreshTag }) => { | |||||
const [left, setLeft] = useState(0); | |||||
const [top, setTop] = useState(0); | |||||
const [menuVisible, setMenuVisible] = useState(false); | |||||
const [currentTag, setCurrentTag] = useState(); | |||||
const tagListRef = useRef(); | |||||
const contextMenuRef = useRef(); | |||||
const [currentPath, setCurrentPath] = useState(); | |||||
useEffect(() => { | |||||
return () => { | |||||
document.body.removeEventListener('click', handleClickOutside); | |||||
}; | |||||
}, []); | |||||
// 由于react的state不能及时穿透到 document.body.addEventListener去,需要在每次值发送改变时进行解绑和再次监听 | |||||
useEffect(() => { | |||||
document.body.removeEventListener('click', handleClickOutside); | |||||
document.body.addEventListener('click', handleClickOutside); | |||||
}, [menuVisible]); | |||||
const handleClickOutside = (event) => { | |||||
const isOutside = !(contextMenuRef.current && contextMenuRef.current.contains(event.target)); | |||||
if (isOutside && menuVisible) { | |||||
setMenuVisible(false); | |||||
} | |||||
}; | |||||
const openContextMenu = ( | |||||
event, | |||||
tag, | |||||
) => { | |||||
event.preventDefault(); | |||||
const menuMinWidth = 105; | |||||
const clickX = event.clientX; | |||||
const clickY = event.clientY; //事件发生时鼠标的Y坐标 | |||||
const clientWidth = tagListRef.current?.clientWidth || 0; // container width | |||||
const maxLeft = clientWidth - menuMinWidth; // left boundary | |||||
setCurrentTag(tag); | |||||
setMenuVisible(true); | |||||
setTop(clickY); | |||||
// 当鼠标点击位置大于左侧边界时,说明鼠标点击的位置偏右,将菜单放在左边 | |||||
// 反之,当鼠标点击的位置偏左,将菜单放在右边 | |||||
const Left = clickX > maxLeft ? clickX - menuMinWidth + 15 : clickX; | |||||
setLeft(Left); | |||||
}; | |||||
return ( | |||||
<div className={styles.tags_wrapper} ref={tagListRef}> | |||||
<Scrollbars autoHide autoHideTimeout={1000} autoHideDuration={200}> | |||||
{tagList.map((item, i) => { | |||||
if (item.path == history.location.pathname) { | |||||
item.active = true; | |||||
} else { | |||||
item.active = false; | |||||
} | |||||
return ( | |||||
<div | |||||
key={item.path} | |||||
className={item.active ? `${styles.item} ${styles.active}` : styles.item} | |||||
onClick={(e) => { | |||||
e.stopPropagation(); | |||||
setCurrentPath(item.path); | |||||
history.push({ pathname: item.path, query: item.query }); | |||||
}} | |||||
onContextMenu={(e) => openContextMenu(e, item)} | |||||
> | |||||
<span>{item.title}</span> | |||||
{i !== 0 && ( | |||||
<CloseOutlined | |||||
className={styles.icon_close} | |||||
onClick={(e) => { | |||||
e.stopPropagation(); | |||||
closeTag && closeTag(item); | |||||
}} | |||||
/> | |||||
)} | |||||
</div> | |||||
); | |||||
})} | |||||
</Scrollbars> | |||||
{menuVisible ? ( | |||||
<ul | |||||
className={styles.contextmenu} | |||||
style={{ left: `${left}px`, top: `${top}px` }} | |||||
ref={contextMenuRef} | |||||
> | |||||
<li | |||||
onClick={() => { | |||||
setMenuVisible(false); | |||||
currentTag && refreshTag && refreshTag(currentTag); | |||||
}} | |||||
> | |||||
刷新 | |||||
</li> | |||||
<li | |||||
onClick={() => { | |||||
setMenuVisible(false); | |||||
currentTag && closeOtherTag && closeOtherTag(currentTag); | |||||
}} | |||||
> | |||||
关闭其他 | |||||
</li> | |||||
<li | |||||
onClick={() => { | |||||
setMenuVisible(false); | |||||
closeAllTag && closeAllTag(); | |||||
}} | |||||
> | |||||
关闭所有 | |||||
</li> | |||||
</ul> | |||||
) : null} | |||||
</div> | |||||
); | |||||
}; | |||||
export default Tags; |
@@ -0,0 +1,88 @@ | |||||
@primary: #FA541C; | |||||
span{ | |||||
-moz-user-select: none; | |||||
user-select: none | |||||
} | |||||
.tags_wrapper { | |||||
position: relative; | |||||
width: 100%; | |||||
height: 34px; | |||||
line-height: 34px; | |||||
background: #fff; | |||||
box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.12), 0 0 1px 0 rgba(0, 0, 0, 0.01); | |||||
.item { | |||||
position: relative; | |||||
display: inline-block; | |||||
height: 28px; | |||||
margin-top: 2px; | |||||
margin-left: 5px; | |||||
padding: 0 8px; | |||||
color: #495060; | |||||
font-size: 13px; | |||||
line-height: 26px; | |||||
background: #fff; | |||||
border: 1px solid #d8dce5; | |||||
cursor: pointer; | |||||
&:first-of-type { | |||||
margin-left: 15px; | |||||
} | |||||
&:last-of-type { | |||||
margin-right: 15px; | |||||
} | |||||
&.active { | |||||
color: #fff; | |||||
background-color: @primary; | |||||
border-color: @primary; | |||||
&::before { | |||||
position: relative; | |||||
display: inline-block; | |||||
width: 8px; | |||||
height: 8px; | |||||
margin-right: 2px; | |||||
background: #fff; | |||||
border-radius: 50%; | |||||
content: ''; | |||||
} | |||||
} | |||||
} | |||||
.icon_close { | |||||
position: relative; | |||||
top: -1px; | |||||
margin-left: 6px; | |||||
font-size: 10px; | |||||
&:hover { | |||||
color: red; | |||||
} | |||||
} | |||||
.contextmenu { | |||||
position: fixed; | |||||
z-index: 3000; | |||||
margin: 0; | |||||
padding: 5px 0; | |||||
color: #333; | |||||
font-weight: 400; | |||||
font-size: 12px; | |||||
list-style-type: none; | |||||
background: #fff; | |||||
border-radius: 4px; | |||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3); | |||||
li { | |||||
margin: 0; | |||||
padding: 2px 16px; | |||||
line-height: 24px; | |||||
cursor: pointer; | |||||
&:hover { | |||||
background: #eee; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,174 @@ | |||||
import React, { useState, useEffect, useRef } from 'react'; | |||||
import { RouteContext } from '@ant-design/pro-layout'; | |||||
import { history } from 'umi'; | |||||
import Tags from './Tags'; | |||||
import styles from './index.less'; | |||||
// export type TagsItemType = { | |||||
// title?: string; | |||||
// path?: string; | |||||
// active: boolean; | |||||
// query?: any; | |||||
// children: any; | |||||
// refresh: number; | |||||
// }; | |||||
// interface IProps { | |||||
// home: string; | |||||
// current: string; | |||||
// } | |||||
/** | |||||
* @component TagView 标签页组件 | |||||
*/ | |||||
const TagView = ({ children, home, current }) => { | |||||
// if (current=='/') { | |||||
// current = '/welcome' | |||||
// } | |||||
console.log('current',current); | |||||
const [tagList, setTagList] = useState([]); | |||||
const routeContextRef = useRef(); | |||||
useEffect(() => { | |||||
if (routeContextRef?.current) { | |||||
handleOnChange(routeContextRef.current); | |||||
} | |||||
}, [current]); | |||||
// 初始化 visitedViews,设置project为首页 | |||||
const initTags = (routeContext) => { | |||||
if (tagList.length === 0 && routeContext.menuData) { | |||||
const firstTag = routeContext.menuData.filter((el) => el.path === home)[0]; | |||||
if (!firstTag) { | |||||
firstTag = routeContext.menuData[0]; | |||||
} | |||||
const title = firstTag.name; | |||||
const path = firstTag.path; | |||||
history.push({ pathname: firstTag.path, query: firstTag.query }); | |||||
setTagList([ | |||||
{ | |||||
title, | |||||
path, | |||||
children, | |||||
refresh: 0, | |||||
active: true, | |||||
}, | |||||
]); | |||||
} | |||||
}; | |||||
// 监听路由改变 | |||||
const handleOnChange = (routeContext) => { | |||||
const { currentMenu } = routeContext; | |||||
// tags初始化 | |||||
if (tagList.length === 0) { | |||||
return initTags(routeContext); | |||||
} | |||||
// 判断是否已打开过该页面 | |||||
let hasOpen = false; | |||||
const tagsCopy = tagList.map((item) => { | |||||
if (currentMenu?.path === item.path) { | |||||
hasOpen = true; | |||||
// 刷新浏览器时,重新覆盖当前 path 的 children | |||||
return { ...item, active: true, children }; | |||||
} else { | |||||
return { ...item, active: false }; | |||||
} | |||||
}); | |||||
// 没有该tag时追加一个,并打开这个tag页面 | |||||
if (!hasOpen) { | |||||
const title = routeContext.title || ''; | |||||
const path = currentMenu?.path; | |||||
tagsCopy.push({ | |||||
title, | |||||
path, | |||||
children, | |||||
refresh: 0, | |||||
active: true, | |||||
}); | |||||
} | |||||
return setTagList(tagsCopy); | |||||
}; | |||||
// 关闭标签 | |||||
const handleCloseTag = (tag) => { | |||||
const tagsCopy = tagList.map((el, i) => ({ ...el })); | |||||
// 判断关闭标签是否处于打开状态 | |||||
tagList.forEach((el, i) => { | |||||
if (el.path === tag.path && tag.active) { | |||||
const next = tagList[i - 1]; | |||||
next.active = true; | |||||
history.push({ pathname: next?.path, query: next?.query }); | |||||
} | |||||
}); | |||||
setTagList(tagsCopy.filter((el) => el.path !== tag?.path)); | |||||
}; | |||||
// 关闭所有标签 | |||||
const handleCloseAll = () => { | |||||
const tagsCopy = tagList.filter((el) => el.path === home); | |||||
history.push(home); | |||||
setTagList(tagsCopy); | |||||
}; | |||||
// 关闭其他标签 | |||||
const handleCloseOther = (tag) => { | |||||
const tagsCopy = tagList.filter( | |||||
(el) => el.path === home || el.path === tag.path, | |||||
); | |||||
history.push({ pathname: tag?.path, query: tag?.query }); | |||||
setTagList(tagsCopy); | |||||
}; | |||||
// 刷新选择的标签 | |||||
const handleRefreshTag = (tag) => { | |||||
const tagsCopy = tagList.map((item) => { | |||||
if (item.path === tag.path) { | |||||
history.push({ pathname: tag?.path, query: tag?.query }); | |||||
return { ...item, refresh: item.refresh + 1, active: true }; | |||||
} | |||||
return { ...item, active: false }; | |||||
}); | |||||
setTagList(tagsCopy); | |||||
}; | |||||
return ( | |||||
<> | |||||
<RouteContext.Consumer> | |||||
{(value) => { | |||||
// console.log(value); | |||||
routeContextRef.current = value; | |||||
return null; | |||||
}} | |||||
</RouteContext.Consumer> | |||||
<div className={styles.tag_view}> | |||||
<div className={styles.tags_container}> | |||||
<Tags | |||||
tagList={tagList} | |||||
closeTag={handleCloseTag} | |||||
closeAllTag={handleCloseAll} | |||||
closeOtherTag={handleCloseOther} | |||||
refreshTag={handleRefreshTag} | |||||
/> | |||||
</div> | |||||
</div> | |||||
{tagList.map((item) => { | |||||
return ( | |||||
<div key={item.path} style={{ display: item.active ? 'block' : 'none' }}> | |||||
<div key={item.refresh}> | |||||
{item.children} | |||||
</div> | |||||
</div> | |||||
); | |||||
})} | |||||
</> | |||||
); | |||||
}; | |||||
export default TagView; |
@@ -0,0 +1,11 @@ | |||||
.tag_view { | |||||
.tags_container { | |||||
position: relative; | |||||
top: -22px; | |||||
right: 24px; | |||||
z-index: 199; | |||||
width: calc(100% + 48px); | |||||
height: 36px; | |||||
border: 0px dashed coral; | |||||
} | |||||
} |
@@ -0,0 +1,271 @@ | |||||
--- | |||||
title: 业务组件 | |||||
sidemenu: false | |||||
--- | |||||
> 此功能由[dumi](https://d.umijs.org/zh-CN/guide/advanced#umi-%E9%A1%B9%E7%9B%AE%E9%9B%86%E6%88%90%E6%A8%A1%E5%BC%8F)提供,dumi 是一个 📖 为组件开发场景而生的文档工具,用过的都说好。 | |||||
# 业务组件 | |||||
这里列举了 Pro 中所有用到的组件,这些组件不适合作为组件库,但是在业务中却真实需要。所以我们准备了这个文档,来指导大家是否需要使用这个组件。 | |||||
## Footer 页脚组件 | |||||
这个组件自带了一些 Pro 的配置,你一般都需要改掉它的信息。 | |||||
```tsx | |||||
/** | |||||
* background: '#f0f2f5' | |||||
*/ | |||||
import React from 'react'; | |||||
import Footer from '@/components/Footer'; | |||||
export default () => <Footer />; | |||||
``` | |||||
## HeaderDropdown 头部下拉列表 | |||||
HeaderDropdown 是 antd Dropdown 的封装,但是增加了移动端的特殊处理,用法也是相同的。 | |||||
```tsx | |||||
/** | |||||
* background: '#f0f2f5' | |||||
*/ | |||||
import { Button, Menu } from 'antd'; | |||||
import React from 'react'; | |||||
import HeaderDropdown from '@/components/HeaderDropdown'; | |||||
export default () => { | |||||
const menuHeaderDropdown = ( | |||||
<Menu selectedKeys={[]}> | |||||
<Menu.Item key="center">个人中心</Menu.Item> | |||||
<Menu.Item key="settings">个人设置</Menu.Item> | |||||
<Menu.Divider /> | |||||
<Menu.Item key="logout">退出登录</Menu.Item> | |||||
</Menu> | |||||
); | |||||
return ( | |||||
<HeaderDropdown overlay={menuHeaderDropdown}> | |||||
<Button>hover 展示菜单</Button> | |||||
</HeaderDropdown> | |||||
); | |||||
}; | |||||
``` | |||||
## HeaderSearch 头部搜索框 | |||||
一个带补全数据的输入框,支持收起和展开 Input | |||||
```tsx | |||||
/** | |||||
* background: '#f0f2f5' | |||||
*/ | |||||
import { Button, Menu } from 'antd'; | |||||
import React from 'react'; | |||||
import HeaderSearch from '@/components/HeaderSearch'; | |||||
export default () => { | |||||
return ( | |||||
<HeaderSearch | |||||
placeholder="站内搜索" | |||||
defaultValue="umi ui" | |||||
options={[ | |||||
{ label: 'Ant Design Pro', value: 'Ant Design Pro' }, | |||||
{ | |||||
label: 'Ant Design', | |||||
value: 'Ant Design', | |||||
}, | |||||
{ | |||||
label: 'Pro Table', | |||||
value: 'Pro Table', | |||||
}, | |||||
{ | |||||
label: 'Pro Layout', | |||||
value: 'Pro Layout', | |||||
}, | |||||
]} | |||||
onSearch={(value) => { | |||||
}} | |||||
/> | |||||
); | |||||
}; | |||||
``` | |||||
### API | |||||
| 参数 | 说明 | 类型 | 默认值 | | |||||
| --------------- | ---------------------------------- | ---------------------------- | ------ | | |||||
| value | 输入框的值 | `string` | - | | |||||
| onChange | 值修改后触发 | `(value?: string) => void` | - | | |||||
| onSearch | 查询后触发 | `(value?: string) => void` | - | | |||||
| options | 选项菜单的的列表 | `{label,value}[]` | - | | |||||
| defaultVisible | 输入框默认是否显示,只有第一次生效 | `boolean` | - | | |||||
| visible | 输入框是否显示 | `boolean` | - | | |||||
| onVisibleChange | 输入框显示隐藏的回调函数 | `(visible: boolean) => void` | - | | |||||
## NoticeIcon 通知工具 | |||||
通知工具提供一个展示多种通知信息的界面。 | |||||
```tsx | |||||
/** | |||||
* background: '#f0f2f5' | |||||
*/ | |||||
import { message } from 'antd'; | |||||
import React from 'react'; | |||||
import NoticeIcon from '@/components/NoticeIcon/NoticeIcon'; | |||||
export default () => { | |||||
const list = [ | |||||
{ | |||||
id: '000000001', | |||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', | |||||
title: '你收到了 14 份新周报', | |||||
datetime: '2017-08-09', | |||||
type: 'notification', | |||||
}, | |||||
{ | |||||
id: '000000002', | |||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png', | |||||
title: '你推荐的 曲妮妮 已通过第三轮面试', | |||||
datetime: '2017-08-08', | |||||
type: 'notification', | |||||
}, | |||||
]; | |||||
return ( | |||||
<NoticeIcon | |||||
count={10} | |||||
onItemClick={(item) => { | |||||
message.info(`${item.title} 被点击了`); | |||||
}} | |||||
onClear={(title: string, key: string) => message.info('点击了清空更多')} | |||||
loading={false} | |||||
clearText="清空" | |||||
viewMoreText="查看更多" | |||||
onViewMore={() => message.info('点击了查看更多')} | |||||
clearClose | |||||
> | |||||
<NoticeIcon.Tab | |||||
tabKey="notification" | |||||
count={2} | |||||
list={list} | |||||
title="通知" | |||||
emptyText="你已查看所有通知" | |||||
showViewMore | |||||
/> | |||||
<NoticeIcon.Tab | |||||
tabKey="message" | |||||
count={2} | |||||
list={list} | |||||
title="消息" | |||||
emptyText="您已读完所有消息" | |||||
showViewMore | |||||
/> | |||||
<NoticeIcon.Tab | |||||
tabKey="event" | |||||
title="待办" | |||||
emptyText="你已完成所有待办" | |||||
count={2} | |||||
list={list} | |||||
showViewMore | |||||
/> | |||||
</NoticeIcon> | |||||
); | |||||
}; | |||||
``` | |||||
### NoticeIcon API | |||||
| 参数 | 说明 | 类型 | 默认值 | | |||||
| --- | --- | --- | --- | | |||||
| count | 有多少未读通知 | `number` | - | | |||||
| bell | 铃铛的图表 | `ReactNode` | - | | |||||
| onClear | 点击清空数据按钮 | `(tabName: string, tabKey: string) => void` | - | | |||||
| onItemClick | 未读消息列被点击 | `(item: API.NoticeIconData, tabProps: NoticeIconTabProps) => void` | - | | |||||
| onViewMore | 查看更多的按钮点击 | `(tabProps: NoticeIconTabProps, e: MouseEvent) => void` | - | | |||||
| onTabChange | 通知 Tab 的切换 | `(tabTile: string) => void;` | - | | |||||
| popupVisible | 通知显示是否展示 | `boolean` | - | | |||||
| onPopupVisibleChange | 通知信息显示隐藏的回调函数 | `(visible: boolean) => void` | - | | |||||
| clearText | 清空按钮的文字 | `string` | - | | |||||
| viewMoreText | 查看更多的按钮文字 | `string` | - | | |||||
| clearClose | 展示清空按钮 | `boolean` | - | | |||||
| emptyImage | 列表为空时的兜底展示 | `ReactNode` | - | | |||||
### NoticeIcon.Tab API | |||||
| 参数 | 说明 | 类型 | 默认值 | | |||||
| ------------ | ------------------ | ------------------------------------ | ------ | | |||||
| count | 有多少未读通知 | `number` | - | | |||||
| title | 通知 Tab 的标题 | `ReactNode` | - | | |||||
| showClear | 展示清除按钮 | `boolean` | `true` | | |||||
| showViewMore | 展示加载更 | `boolean` | `true` | | |||||
| tabKey | Tab 的唯一 key | `string` | - | | |||||
| onClick | 子项的单击事件 | `(item: API.NoticeIconData) => void` | - | | |||||
| onClear | 清楚按钮的点击 | `()=>void` | - | | |||||
| emptyText | 为空的时候测试 | `()=>void` | - | | |||||
| viewMoreText | 查看更多的按钮文字 | `string` | - | | |||||
| onViewMore | 查看更多的按钮点击 | `( e: MouseEvent) => void` | - | | |||||
| list | 通知信息的列表 | `API.NoticeIconData` | - | | |||||
### NoticeIconData | |||||
```tsx | pure | |||||
export interface NoticeIconData { | |||||
id: string; | |||||
key: string; | |||||
avatar: string; | |||||
title: string; | |||||
datetime: string; | |||||
type: string; | |||||
read?: boolean; | |||||
description: string; | |||||
clickClose?: boolean; | |||||
extra: any; | |||||
status: string; | |||||
} | |||||
``` | |||||
## RightContent | |||||
RightContent 是以上几个组件的组合,同时新增了 plugins 的 `SelectLang` 插件。 | |||||
```tsx | pure | |||||
<Space> | |||||
<HeaderSearch | |||||
placeholder="站内搜索" | |||||
defaultValue="umi ui" | |||||
options={[ | |||||
{ label: <a href="https://umijs.org/zh/guide/umi-ui.html">umi ui</a>, value: 'umi ui' }, | |||||
{ | |||||
label: <a href="next.ant.design">Ant Design</a>, | |||||
value: 'Ant Design', | |||||
}, | |||||
{ | |||||
label: <a href="https://protable.ant.design/">Pro Table</a>, | |||||
value: 'Pro Table', | |||||
}, | |||||
{ | |||||
label: <a href="https://prolayout.ant.design/">Pro Layout</a>, | |||||
value: 'Pro Layout', | |||||
}, | |||||
]} | |||||
/> | |||||
<Tooltip title="使用文档"> | |||||
<span | |||||
className={styles.action} | |||||
onClick={() => { | |||||
window.location.href = 'https://pro.ant.design/docs/getting-started'; | |||||
}} | |||||
> | |||||
<QuestionCircleOutlined /> | |||||
</span> | |||||
</Tooltip> | |||||
<Avatar /> | |||||
{REACT_APP_ENV && ( | |||||
<span> | |||||
<Tag color={ENVTagColor[REACT_APP_ENV]}>{REACT_APP_ENV}</Tag> | |||||
</span> | |||||
)} | |||||
<SelectLang className={styles.action} /> | |||||
</Space> | |||||
``` |
@@ -0,0 +1,61 @@ | |||||
const { uniq } = require('lodash'); | |||||
const RouterConfig = require('../../config/config').default.routes; | |||||
const BASE_URL = `http://localhost:${process.env.PORT || 8000}`; | |||||
function formatter(routes, parentPath = '') { | |||||
const fixedParentPath = parentPath.replace(/\/{1,}/g, '/'); | |||||
let result = []; | |||||
routes.forEach((item) => { | |||||
if (item.path && !item.path.startsWith('/')) { | |||||
result.push(`${fixedParentPath}/${item.path}`.replace(/\/{1,}/g, '/')); | |||||
} | |||||
if (item.path && item.path.startsWith('/')) { | |||||
result.push(`${item.path}`.replace(/\/{1,}/g, '/')); | |||||
} | |||||
if (item.routes) { | |||||
result = result.concat( | |||||
formatter(item.routes, item.path ? `${fixedParentPath}/${item.path}` : parentPath), | |||||
); | |||||
} | |||||
}); | |||||
return uniq(result.filter((item) => !!item)); | |||||
} | |||||
beforeEach(async () => { | |||||
await page.goto(`${BASE_URL}`); | |||||
await page.evaluate(() => { | |||||
localStorage.setItem('antd-pro-authority', '["admin"]'); | |||||
}); | |||||
}); | |||||
describe('Ant Design Pro E2E test', () => { | |||||
const testPage = (path) => async () => { | |||||
await page.goto(`${BASE_URL}${path}`); | |||||
await page.waitForSelector('footer', { | |||||
timeout: 2000, | |||||
}); | |||||
const haveFooter = await page.evaluate( | |||||
() => document.getElementsByTagName('footer').length > 0, | |||||
); | |||||
expect(haveFooter).toBeTruthy(); | |||||
}; | |||||
const routers = formatter(RouterConfig); | |||||
routers.forEach((route) => { | |||||
it(`test pages ${route}`, testPage(route)); | |||||
}); | |||||
it('topmenu should have footer', async () => { | |||||
const params = '?navTheme=light&layout=topmenu'; | |||||
await page.goto(`${BASE_URL}${params}`); | |||||
await page.waitForSelector('footer', { | |||||
timeout: 2000, | |||||
}); | |||||
const haveFooter = await page.evaluate( | |||||
() => document.getElementsByTagName('footer').length > 0, | |||||
); | |||||
expect(haveFooter).toBeTruthy(); | |||||
}); | |||||
}); |
@@ -0,0 +1,101 @@ | |||||
import { Button, message, notification } from 'antd'; | |||||
import { useIntl } from 'umi'; | |||||
import defaultSettings from '../config/defaultSettings'; | |||||
const { pwa } = defaultSettings; | |||||
const isHttps = document.location.protocol === 'https:'; // if pwa is true | |||||
if (pwa) { | |||||
// Notify user if offline now | |||||
window.addEventListener('sw.offline', () => { | |||||
message.warning( | |||||
useIntl().formatMessage({ | |||||
id: 'app.pwa.offline', | |||||
}), | |||||
); | |||||
}); // Pop up a prompt on the page asking the user if they want to use the latest version | |||||
window.addEventListener('sw.updated', (event) => { | |||||
const e = event; | |||||
const reloadSW = async () => { | |||||
// Check if there is sw whose state is waiting in ServiceWorkerRegistration | |||||
// https://developer.mozilla.org/en-US/docs/Web/kitchen/api/ServiceWorkerRegistration | |||||
const worker = e.detail && e.detail.waiting; | |||||
if (!worker) { | |||||
return true; | |||||
} // Send skip-waiting event to waiting SW with MessageChannel | |||||
await new Promise((resolve, reject) => { | |||||
const channel = new MessageChannel(); | |||||
channel.port1.onmessage = (msgEvent) => { | |||||
if (msgEvent.data.error) { | |||||
reject(msgEvent.data.error); | |||||
} else { | |||||
resolve(msgEvent.data); | |||||
} | |||||
}; | |||||
worker.postMessage( | |||||
{ | |||||
type: 'skip-waiting', | |||||
}, | |||||
[channel.port2], | |||||
); | |||||
}); // Refresh current page to use the updated HTML and other assets after SW has skiped waiting | |||||
window.location.reload(true); | |||||
return true; | |||||
}; | |||||
const key = `open${Date.now()}`; | |||||
const btn = ( | |||||
<Button | |||||
type="primary" | |||||
onClick={() => { | |||||
notification.close(key); | |||||
reloadSW(); | |||||
}} | |||||
> | |||||
{useIntl().formatMessage({ | |||||
id: 'app.pwa.serviceworker.updated.ok', | |||||
})} | |||||
</Button> | |||||
); | |||||
notification.open({ | |||||
message: useIntl().formatMessage({ | |||||
id: 'app.pwa.serviceworker.updated', | |||||
}), | |||||
description: useIntl().formatMessage({ | |||||
id: 'app.pwa.serviceworker.updated.hint', | |||||
}), | |||||
btn, | |||||
key, | |||||
onClose: async () => null, | |||||
}); | |||||
}); | |||||
} else if ('serviceWorker' in navigator && isHttps) { | |||||
// unregister service worker | |||||
const { serviceWorker } = navigator; | |||||
if (serviceWorker.getRegistrations) { | |||||
serviceWorker.getRegistrations().then((sws) => { | |||||
sws.forEach((sw) => { | |||||
sw.unregister(); | |||||
}); | |||||
}); | |||||
} | |||||
serviceWorker.getRegistration().then((sw) => { | |||||
if (sw) sw.unregister(); | |||||
}); // remove all caches | |||||
if (window.caches && window.caches.keys()) { | |||||
caches.keys().then((keys) => { | |||||
keys.forEach((key) => { | |||||
caches.delete(key); | |||||
}); | |||||
}); | |||||
} | |||||
} |
@@ -0,0 +1,57 @@ | |||||
@import '~antd/es/style/themes/default.less'; | |||||
html, | |||||
body, | |||||
#root { | |||||
height: 100%; | |||||
} | |||||
.colorWeak { | |||||
filter: invert(80%); | |||||
} | |||||
.ant-layout { | |||||
min-height: 100vh; | |||||
} | |||||
.ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed { | |||||
left: unset; | |||||
} | |||||
canvas { | |||||
display: block; | |||||
} | |||||
body { | |||||
text-rendering: optimizeLegibility; | |||||
-webkit-font-smoothing: antialiased; | |||||
-moz-osx-font-smoothing: grayscale; | |||||
} | |||||
ul, | |||||
ol { | |||||
list-style: none; | |||||
} | |||||
@media (max-width: @screen-xs) { | |||||
.ant-table { | |||||
width: 100%; | |||||
overflow-x: auto; | |||||
&-thead > tr, | |||||
&-tbody > tr { | |||||
> th, | |||||
> td { | |||||
white-space: pre; | |||||
> span { | |||||
display: block; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
// Compatible with IE11 | |||||
@media screen and(-ms-high-contrast: active), (-ms-high-contrast: none) { | |||||
body .ant-design-pro > .ant-layout { | |||||
min-height: 100vh; | |||||
} | |||||
} |
@@ -0,0 +1,7 @@ | |||||
export function getApiUrl() { | |||||
if (process.env.UMI_ENV === 'dev') { | |||||
return 'http://localhost:5006/'; | |||||
} else if (process.env.UMI_ENV === 'prod') { | |||||
return 'http://localhost:7002/'; | |||||
} | |||||
} |
@@ -0,0 +1,24 @@ | |||||
import component from './en-US/component'; | |||||
import globalHeader from './en-US/globalHeader'; | |||||
import menu from './en-US/menu'; | |||||
import pages from './en-US/pages'; | |||||
import pwa from './en-US/pwa'; | |||||
import settingDrawer from './en-US/settingDrawer'; | |||||
import settings from './en-US/settings'; | |||||
export default { | |||||
'navBar.lang': 'Languages', | |||||
'layout.user.link.help': 'Help', | |||||
'layout.user.link.privacy': 'Privacy', | |||||
'layout.user.link.terms': 'Terms', | |||||
'app.copyright.produced': 'Produced by Ant Financial Experience Department', | |||||
'app.preview.down.block': 'Download this page to your local project', | |||||
'app.welcome.link.fetch-blocks': 'Get all block', | |||||
'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development', | |||||
...globalHeader, | |||||
...menu, | |||||
...settingDrawer, | |||||
...settings, | |||||
...pwa, | |||||
...component, | |||||
...pages, | |||||
}; |
@@ -0,0 +1,5 @@ | |||||
export default { | |||||
'component.tagSelect.expand': 'Expand', | |||||
'component.tagSelect.collapse': 'Collapse', | |||||
'component.tagSelect.all': 'All', | |||||
}; |
@@ -0,0 +1,17 @@ | |||||
export default { | |||||
'component.globalHeader.search': 'Search', | |||||
'component.globalHeader.search.example1': 'Search example 1', | |||||
'component.globalHeader.search.example2': 'Search example 2', | |||||
'component.globalHeader.search.example3': 'Search example 3', | |||||
'component.globalHeader.help': 'Help', | |||||
'component.globalHeader.notification': 'Notification', | |||||
'component.globalHeader.notification.empty': 'You have viewed all notifications.', | |||||
'component.globalHeader.message': 'Message', | |||||
'component.globalHeader.message.empty': 'You have viewed all messsages.', | |||||
'component.globalHeader.event': 'Event', | |||||
'component.globalHeader.event.empty': 'You have viewed all events.', | |||||
'component.noticeIcon.clear': 'Clear', | |||||
'component.noticeIcon.cleared': 'Cleared', | |||||
'component.noticeIcon.empty': 'No notifications', | |||||
'component.noticeIcon.view-more': 'View more', | |||||
}; |
@@ -0,0 +1,52 @@ | |||||
export default { | |||||
'menu.welcome': 'Welcome', | |||||
'menu.more-blocks': 'More Blocks', | |||||
'menu.home': 'Home', | |||||
'menu.admin': 'Admin', | |||||
'menu.admin.sub-page': 'Sub-Page', | |||||
'menu.login': 'Login', | |||||
'menu.register': 'Register', | |||||
'menu.register.result': 'Register Result', | |||||
'menu.dashboard': 'Dashboard', | |||||
'menu.dashboard.analysis': 'Analysis', | |||||
'menu.dashboard.monitor': 'Monitor', | |||||
'menu.dashboard.workplace': 'Workplace', | |||||
'menu.exception.403': '403', | |||||
'menu.exception.404': '404', | |||||
'menu.exception.500': '500', | |||||
'menu.form': 'Form', | |||||
'menu.form.basic-form': 'Basic Form', | |||||
'menu.form.step-form': 'Step Form', | |||||
'menu.form.step-form.info': 'Step Form(write transfer information)', | |||||
'menu.form.step-form.confirm': 'Step Form(confirm transfer information)', | |||||
'menu.form.step-form.result': 'Step Form(finished)', | |||||
'menu.form.advanced-form': 'Advanced Form', | |||||
'menu.list': 'List', | |||||
'menu.list.table-list': 'Search Table', | |||||
'menu.list.basic-list': 'Basic List', | |||||
'menu.list.card-list': 'Card List', | |||||
'menu.list.search-list': 'Search List', | |||||
'menu.list.search-list.articles': 'Search List(articles)', | |||||
'menu.list.search-list.projects': 'Search List(projects)', | |||||
'menu.list.search-list.applications': 'Search List(applications)', | |||||
'menu.profile': 'Profile', | |||||
'menu.profile.basic': 'Basic Profile', | |||||
'menu.profile.advanced': 'Advanced Profile', | |||||
'menu.result': 'Result', | |||||
'menu.result.success': 'Success', | |||||
'menu.result.fail': 'Fail', | |||||
'menu.exception': 'Exception', | |||||
'menu.exception.not-permission': '403', | |||||
'menu.exception.not-find': '404', | |||||
'menu.exception.server-error': '500', | |||||
'menu.exception.trigger': 'Trigger', | |||||
'menu.account': 'Account', | |||||
'menu.account.center': 'Account Center', | |||||
'menu.account.settings': 'Account Settings', | |||||
'menu.account.trigger': 'Trigger Error', | |||||
'menu.account.logout': 'Logout', | |||||
'menu.editor': 'Graphic Editor', | |||||
'menu.editor.flow': 'Flow Editor', | |||||
'menu.editor.mind': 'Mind Editor', | |||||
'menu.editor.koni': 'Koni Editor', | |||||
}; |
@@ -0,0 +1,70 @@ | |||||
export default { | |||||
'pages.layouts.userLayout.title': | |||||
'Ant Design is the most influential web design specification in Xihu district', | |||||
'pages.login.accountLogin.tab': 'Account Login', | |||||
'pages.login.accountLogin.errorMessage': 'Incorrect username/password(admin/ant.design)', | |||||
'pages.login.failure': 'Login failed, please try again!', | |||||
'pages.login.success': 'Login successful!', | |||||
'pages.login.username.placeholder': 'Username: admin or user', | |||||
'pages.login.username.required': 'Please input your username!', | |||||
'pages.login.password.placeholder': 'Password: ant.design', | |||||
'pages.login.password.required': 'Please input your password!', | |||||
'pages.login.phoneLogin.tab': 'Phone Login', | |||||
'pages.login.phoneLogin.errorMessage': 'Verification Code Error', | |||||
'pages.login.phoneNumber.placeholder': 'Phone Number', | |||||
'pages.login.phoneNumber.required': 'Please input your phone number!', | |||||
'pages.login.phoneNumber.invalid': 'Phone number is invalid!', | |||||
'pages.login.captcha.placeholder': 'Verification Code', | |||||
'pages.login.captcha.required': 'Please input verification code!', | |||||
'pages.login.phoneLogin.getVerificationCode': 'Get Code', | |||||
'pages.getCaptchaSecondText': 'sec(s)', | |||||
'pages.login.rememberMe': 'Remember me', | |||||
'pages.login.forgotPassword': 'Forgot Password ?', | |||||
'pages.login.submit': 'Login', | |||||
'pages.login.loginWith': 'Login with :', | |||||
'pages.login.registerAccount': 'Register Account', | |||||
'pages.welcome.advancedComponent': 'Advanced Component', | |||||
'pages.welcome.link': 'Welcome', | |||||
'pages.welcome.advancedLayout': 'Advanced Layout', | |||||
'pages.welcome.alertMessage': 'Faster and stronger heavy-duty components have been released.', | |||||
'pages.admin.subPage.title': 'This page can only be viewed by Admin', | |||||
'pages.admin.subPage.alertMessage': | |||||
'Umi ui is now released, welcome to use npm run ui to start the experience.', | |||||
'pages.searchTable.createForm.newRule': 'New Rule', | |||||
'pages.searchTable.updateForm.ruleConfig': 'Rule configuration', | |||||
'pages.searchTable.updateForm.basicConfig': 'Basic Information', | |||||
'pages.searchTable.updateForm.ruleName.nameLabel': 'Rule Name', | |||||
'pages.searchTable.updateForm.ruleName.nameRules': 'Please enter the rule name!', | |||||
'pages.searchTable.updateForm.ruleDesc.descLabel': 'Rule Description', | |||||
'pages.searchTable.updateForm.ruleDesc.descPlaceholder': 'Please enter at least five characters', | |||||
'pages.searchTable.updateForm.ruleDesc.descRules': | |||||
'Please enter a rule description of at least five characters!', | |||||
'pages.searchTable.updateForm.ruleProps.title': 'Configure Properties', | |||||
'pages.searchTable.updateForm.object': 'Monitoring Object', | |||||
'pages.searchTable.updateForm.ruleProps.templateLabel': 'Rule Template', | |||||
'pages.searchTable.updateForm.ruleProps.typeLabel': 'Rule Type', | |||||
'pages.searchTable.updateForm.schedulingPeriod.title': 'Set Scheduling Period', | |||||
'pages.searchTable.updateForm.schedulingPeriod.timeLabel': 'Starting Time', | |||||
'pages.searchTable.updateForm.schedulingPeriod.timeRules': 'Please choose a start time!', | |||||
'pages.searchTable.titleDesc': 'Description', | |||||
'pages.searchTable.ruleName': 'Rule name is required', | |||||
'pages.searchTable.titleCallNo': 'Number of Service Calls', | |||||
'pages.searchTable.titleStatus': 'Status', | |||||
'pages.searchTable.nameStatus.default': 'default', | |||||
'pages.searchTable.nameStatus.running': 'running', | |||||
'pages.searchTable.nameStatus.online': 'online', | |||||
'pages.searchTable.nameStatus.abnormal': 'abnormal', | |||||
'pages.searchTable.titleUpdatedAt': 'Last Scheduled at', | |||||
'pages.searchTable.exception': 'Please enter the reason for the exception!', | |||||
'pages.searchTable.titleOption': 'Option', | |||||
'pages.searchTable.config': 'Configuration', | |||||
'pages.searchTable.subscribeAlert': 'Subscribe to alerts', | |||||
'pages.searchTable.title': 'Enquiry Form', | |||||
'pages.searchTable.new': 'New', | |||||
'pages.searchTable.chosen': 'chosen', | |||||
'pages.searchTable.item': 'item', | |||||
'pages.searchTable.totalServiceCalls': 'Total Number of Service Calls', | |||||
'pages.searchTable.tenThousand': '0000', | |||||
'pages.searchTable.batchDeletion': 'bacth deletion', | |||||
'pages.searchTable.batchApproval': 'batch approval', | |||||
}; |
@@ -0,0 +1,6 @@ | |||||
export default { | |||||
'app.pwa.offline': 'You are offline now', | |||||
'app.pwa.serviceworker.updated': 'New content is available', | |||||
'app.pwa.serviceworker.updated.hint': 'Please press the "Refresh" button to reload current page', | |||||
'app.pwa.serviceworker.updated.ok': 'Refresh', | |||||
}; |
@@ -0,0 +1,31 @@ | |||||
export default { | |||||
'app.setting.pagestyle': 'Page style setting', | |||||
'app.setting.pagestyle.dark': 'Dark style', | |||||
'app.setting.pagestyle.light': 'Light style', | |||||
'app.setting.content-width': 'Content Width', | |||||
'app.setting.content-width.fixed': 'Fixed', | |||||
'app.setting.content-width.fluid': 'Fluid', | |||||
'app.setting.themecolor': 'Theme Color', | |||||
'app.setting.themecolor.dust': 'Dust Red', | |||||
'app.setting.themecolor.volcano': 'Volcano', | |||||
'app.setting.themecolor.sunset': 'Sunset Orange', | |||||
'app.setting.themecolor.cyan': 'Cyan', | |||||
'app.setting.themecolor.green': 'Polar Green', | |||||
'app.setting.themecolor.daybreak': 'Daybreak Blue (default)', | |||||
'app.setting.themecolor.geekblue': 'Geek Glue', | |||||
'app.setting.themecolor.purple': 'Golden Purple', | |||||
'app.setting.navigationmode': 'Navigation Mode', | |||||
'app.setting.sidemenu': 'Side Menu Layout', | |||||
'app.setting.topmenu': 'Top Menu Layout', | |||||
'app.setting.fixedheader': 'Fixed Header', | |||||
'app.setting.fixedsidebar': 'Fixed Sidebar', | |||||
'app.setting.fixedsidebar.hint': 'Works on Side Menu Layout', | |||||
'app.setting.hideheader': 'Hidden Header when scrolling', | |||||
'app.setting.hideheader.hint': 'Works when Hidden Header is enabled', | |||||
'app.setting.othersettings': 'Other Settings', | |||||
'app.setting.weakmode': 'Weak Mode', | |||||
'app.setting.copy': 'Copy Setting', | |||||
'app.setting.copyinfo': 'copy success,please replace defaultSettings in src/models/setting.js', | |||||
'app.setting.production.hint': | |||||
'Setting panel shows in development environment only, please manually modify', | |||||
}; |
@@ -0,0 +1,60 @@ | |||||
export default { | |||||
'app.settings.menuMap.basic': 'Basic Settings', | |||||
'app.settings.menuMap.security': 'Security Settings', | |||||
'app.settings.menuMap.binding': 'Account Binding', | |||||
'app.settings.menuMap.notification': 'New Message Notification', | |||||
'app.settings.basic.avatar': 'Avatar', | |||||
'app.settings.basic.change-avatar': 'Change avatar', | |||||
'app.settings.basic.email': 'Email', | |||||
'app.settings.basic.email-message': 'Please input your email!', | |||||
'app.settings.basic.nickname': 'Nickname', | |||||
'app.settings.basic.nickname-message': 'Please input your Nickname!', | |||||
'app.settings.basic.profile': 'Personal profile', | |||||
'app.settings.basic.profile-message': 'Please input your personal profile!', | |||||
'app.settings.basic.profile-placeholder': 'Brief introduction to yourself', | |||||
'app.settings.basic.country': 'Country/Region', | |||||
'app.settings.basic.country-message': 'Please input your country!', | |||||
'app.settings.basic.geographic': 'Province or city', | |||||
'app.settings.basic.geographic-message': 'Please input your geographic info!', | |||||
'app.settings.basic.address': 'Street Address', | |||||
'app.settings.basic.address-message': 'Please input your address!', | |||||
'app.settings.basic.phone': 'Phone Number', | |||||
'app.settings.basic.phone-message': 'Please input your phone!', | |||||
'app.settings.basic.update': 'Update Information', | |||||
'app.settings.security.strong': 'Strong', | |||||
'app.settings.security.medium': 'Medium', | |||||
'app.settings.security.weak': 'Weak', | |||||
'app.settings.security.password': 'Account Password', | |||||
'app.settings.security.password-description': 'Current password strength', | |||||
'app.settings.security.phone': 'Security Phone', | |||||
'app.settings.security.phone-description': 'Bound phone', | |||||
'app.settings.security.question': 'Security Question', | |||||
'app.settings.security.question-description': | |||||
'The security question is not set, and the security policy can effectively protect the account security', | |||||
'app.settings.security.email': 'Backup Email', | |||||
'app.settings.security.email-description': 'Bound Email', | |||||
'app.settings.security.mfa': 'MFA Device', | |||||
'app.settings.security.mfa-description': | |||||
'Unbound MFA device, after binding, can be confirmed twice', | |||||
'app.settings.security.modify': 'Modify', | |||||
'app.settings.security.set': 'Set', | |||||
'app.settings.security.bind': 'Bind', | |||||
'app.settings.binding.taobao': 'Binding Taobao', | |||||
'app.settings.binding.taobao-description': 'Currently unbound Taobao account', | |||||
'app.settings.binding.alipay': 'Binding Alipay', | |||||
'app.settings.binding.alipay-description': 'Currently unbound Alipay account', | |||||
'app.settings.binding.dingding': 'Binding DingTalk', | |||||
'app.settings.binding.dingding-description': 'Currently unbound DingTalk account', | |||||
'app.settings.binding.bind': 'Bind', | |||||
'app.settings.notification.password': 'Account Password', | |||||
'app.settings.notification.password-description': | |||||
'Messages from other users will be notified in the form of a station letter', | |||||
'app.settings.notification.messages': 'System Messages', | |||||
'app.settings.notification.messages-description': | |||||
'System messages will be notified in the form of a station letter', | |||||
'app.settings.notification.todo': 'To-do Notification', | |||||
'app.settings.notification.todo-description': | |||||
'The to-do list will be notified in the form of a letter from the station', | |||||
'app.settings.open': 'Open', | |||||
'app.settings.close': 'Close', | |||||
}; |
@@ -0,0 +1,24 @@ | |||||
import component from './zh-CN/component'; | |||||
import globalHeader from './zh-CN/globalHeader'; | |||||
import menu from './zh-CN/menu'; | |||||
import pwa from './zh-CN/pwa'; | |||||
import settingDrawer from './zh-CN/settingDrawer'; | |||||
import settings from './zh-CN/settings'; | |||||
import pages from './zh-CN/pages'; | |||||
export default { | |||||
'navBar.lang': '语言', | |||||
'layout.user.link.help': '帮助', | |||||
'layout.user.link.privacy': '隐私', | |||||
'layout.user.link.terms': '条款', | |||||
'app.copyright.produced': '2021 四川黑菠萝科技有限公司', | |||||
'app.preview.down.block': '下载此页面到本地项目', | |||||
'app.welcome.link.fetch-blocks': '获取全部区块', | |||||
'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面', | |||||
...pages, | |||||
...globalHeader, | |||||
...menu, | |||||
...settingDrawer, | |||||
...settings, | |||||
...pwa, | |||||
...component, | |||||
}; |
@@ -0,0 +1,5 @@ | |||||
export default { | |||||
'component.tagSelect.expand': '展开', | |||||
'component.tagSelect.collapse': '收起', | |||||
'component.tagSelect.all': '全部', | |||||
}; |
@@ -0,0 +1,17 @@ | |||||
export default { | |||||
'component.globalHeader.search': '站内搜索', | |||||
'component.globalHeader.search.example1': '搜索提示一', | |||||
'component.globalHeader.search.example2': '搜索提示二', | |||||
'component.globalHeader.search.example3': '搜索提示三', | |||||
'component.globalHeader.help': '使用文档', | |||||
'component.globalHeader.notification': '通知', | |||||
'component.globalHeader.notification.empty': '你已查看所有通知', | |||||
'component.globalHeader.message': '消息', | |||||
'component.globalHeader.message.empty': '您已读完所有消息', | |||||
'component.globalHeader.event': '待办', | |||||
'component.globalHeader.event.empty': '你已完成所有待办', | |||||
'component.noticeIcon.clear': '清空', | |||||
'component.noticeIcon.cleared': '清空了', | |||||
'component.noticeIcon.empty': '暂无数据', | |||||
'component.noticeIcon.view-more': '查看更多', | |||||
}; |
@@ -0,0 +1,55 @@ | |||||
export default { | |||||
'menu.welcome': '欢迎', | |||||
'menu.usercenter': '个人管理', | |||||
'menu.franchiseecenter': '加盟商管理', | |||||
'menu.more-blocks': '更多区块', | |||||
'menu.home': '首页', | |||||
'menu.admin': '管理页', | |||||
'menu.admin.sub-page': '哈哈哈哈', | |||||
'menu.login': '登录', | |||||
'menu.register': '注册', | |||||
'menu.register.result': '注册结果', | |||||
'menu.dashboard': 'Dashboard', | |||||
'menu.dashboard.analysis': '分析页', | |||||
'menu.dashboard.monitor': '监控页', | |||||
'menu.dashboard.workplace': '工作台', | |||||
'menu.exception.403': '403', | |||||
'menu.exception.404': '404', | |||||
'menu.exception.500': '500', | |||||
'menu.form': '表单页', | |||||
'menu.form.basic-form': '基础表单', | |||||
'menu.form.step-form': '分步表单', | |||||
'menu.form.step-form.info': '分步表单(填写转账信息)', | |||||
'menu.form.step-form.confirm': '分步表单(确认转账信息)', | |||||
'menu.form.step-form.result': '分步表单(完成)', | |||||
'menu.form.advanced-form': '高级表单', | |||||
'menu.list': '列表页', | |||||
'menu.list.table-list': '查询表格', | |||||
'menu.list.basic-list': '标准列表', | |||||
'menu.list.card-list': '卡片列表', | |||||
'menu.list.search-list': '搜索列表', | |||||
'menu.list.search-list.articles': '搜索列表(文章)', | |||||
'menu.list.search-list.projects': '搜索列表(项目)', | |||||
'menu.list.search-list.applications': '搜索列表(应用)', | |||||
'menu.profile': '详情页', | |||||
'menu.profile.basic': '基础详情页', | |||||
'menu.profile.advanced': '高级详情页', | |||||
'menu.result': '结果页', | |||||
'menu.result.success': '成功页', | |||||
'menu.result.fail': '失败页', | |||||
'menu.exception': '异常页', | |||||
'menu.exception.not-permission': '403', | |||||
'menu.exception.not-find': '404', | |||||
'menu.exception.server-error': '500', | |||||
'menu.exception.trigger': '触发错误', | |||||
'menu.account': '个人页', | |||||
'menu.account.center': '个人中心', | |||||
'menu.account.settings': '个人设置', | |||||
'menu.account.trigger': '触发报错', | |||||
'menu.account.logout': '退出登录', | |||||
'menu.editor': '图形编辑器', | |||||
'menu.editor.flow': '流程编辑器', | |||||
'menu.editor.mind': '脑图编辑器', | |||||
'menu.editor.koni': '拓扑编辑器', | |||||
'menu.DynamicSettings': 'asda', | |||||
}; |
@@ -0,0 +1,67 @@ | |||||
export default { | |||||
'pages.layouts.userLayout.title': '黑菠萝网络科技', | |||||
'pages.login.accountLogin.tab': '账户密码登录', | |||||
'pages.login.accountLogin.errorMessage': '错误的用户名和密码(admin/ant.design)', | |||||
'pages.login.failure': '登录失败,请重试!', | |||||
'pages.login.success': '登录成功!', | |||||
'pages.login.username.placeholder': '请输入用户名', | |||||
'pages.login.username.required': '用户名是必填项!', | |||||
'pages.login.password.placeholder': '请输入密码', | |||||
'pages.login.password.required': '密码是必填项!', | |||||
'pages.login.phoneLogin.tab': '手机号登录', | |||||
'pages.login.phoneLogin.errorMessage': '验证码错误', | |||||
'pages.login.phoneNumber.placeholder': '请输入手机号!', | |||||
'pages.login.phoneNumber.required': '手机号是必填项!', | |||||
'pages.login.phoneNumber.invalid': '不合法的手机号!', | |||||
'pages.login.captcha.placeholder': '请输入验证码!', | |||||
'pages.login.captcha.required': '验证码是必填项!', | |||||
'pages.login.phoneLogin.getVerificationCode': '获取验证码', | |||||
'pages.getCaptchaSecondText': '秒后重新获取', | |||||
'pages.login.rememberMe': '自动登录', | |||||
'pages.login.forgotPassword': '忘记密码 ?', | |||||
'pages.login.submit': '登录', | |||||
'pages.login.loginWith': '其他登录方式 :', | |||||
'pages.login.registerAccount': '注册账户', | |||||
'pages.welcome.advancedComponent': '高级表格', | |||||
'pages.welcome.link': '欢迎使用', | |||||
'pages.welcome.advancedLayout': '高级布局', | |||||
'pages.welcome.alertMessage': '黑菠萝ERP系统', | |||||
'pages.admin.subPage.title': ' 这个页面只有 admin 权限才能查看', | |||||
'pages.admin.subPage.alertMessage': 'umi ui 现已发布,欢迎使用 npm run ui 启动体验。', | |||||
'pages.searchTable.createForm.newRule': '新建规则', | |||||
'pages.searchTable.updateForm.ruleConfig': '规则配置', | |||||
'pages.searchTable.updateForm.basicConfig': '基本信息', | |||||
'pages.searchTable.updateForm.ruleName.nameLabel': '规则名称', | |||||
'pages.searchTable.updateForm.ruleName.nameRules': '请输入规则名称!', | |||||
'pages.searchTable.updateForm.ruleDesc.descLabel': '规则描述', | |||||
'pages.searchTable.updateForm.ruleDesc.descPlaceholder': '请输入至少五个字符', | |||||
'pages.searchTable.updateForm.ruleDesc.descRules': '请输入至少五个字符的规则描述!', | |||||
'pages.searchTable.updateForm.ruleProps.title': '配置规则属性', | |||||
'pages.searchTable.updateForm.object': '监控对象', | |||||
'pages.searchTable.updateForm.ruleProps.templateLabel': '规则模板', | |||||
'pages.searchTable.updateForm.ruleProps.typeLabel': '规则类型', | |||||
'pages.searchTable.updateForm.schedulingPeriod.title': '设定调度周期', | |||||
'pages.searchTable.updateForm.schedulingPeriod.timeLabel': '开始时间', | |||||
'pages.searchTable.updateForm.schedulingPeriod.timeRules': '请选择开始时间!', | |||||
'pages.searchTable.titleDesc': '描述', | |||||
'pages.searchTable.ruleName': '规则名称为必填项', | |||||
'pages.searchTable.titleCallNo': '服务调用次数', | |||||
'pages.searchTable.titleStatus': '状态', | |||||
'pages.searchTable.nameStatus.default': '关闭', | |||||
'pages.searchTable.nameStatus.running': '运行中', | |||||
'pages.searchTable.nameStatus.online': '已上线', | |||||
'pages.searchTable.nameStatus.abnormal': '异常', | |||||
'pages.searchTable.titleUpdatedAt': '上次调度时间', | |||||
'pages.searchTable.exception': '请输入异常原因!', | |||||
'pages.searchTable.titleOption': '操作', | |||||
'pages.searchTable.config': '配置', | |||||
'pages.searchTable.subscribeAlert': '订阅警报', | |||||
'pages.searchTable.title': '查询表格', | |||||
'pages.searchTable.new': '新建', | |||||
'pages.searchTable.chosen': '已选择', | |||||
'pages.searchTable.item': '项', | |||||
'pages.searchTable.totalServiceCalls': '服务调用次数总计', | |||||
'pages.searchTable.tenThousand': '万', | |||||
'pages.searchTable.batchDeletion': '批量删除', | |||||
'pages.searchTable.batchApproval': '批量审批', | |||||
}; |
@@ -0,0 +1,6 @@ | |||||
export default { | |||||
'app.pwa.offline': '当前处于离线状态', | |||||
'app.pwa.serviceworker.updated': '有新内容', | |||||
'app.pwa.serviceworker.updated.hint': '请点击“刷新”按钮或者手动刷新页面', | |||||
'app.pwa.serviceworker.updated.ok': '刷新', | |||||
}; |
@@ -0,0 +1,31 @@ | |||||
export default { | |||||
'app.setting.pagestyle': '整体风格设置', | |||||
'app.setting.pagestyle.dark': '暗色菜单风格', | |||||
'app.setting.pagestyle.light': '亮色菜单风格', | |||||
'app.setting.content-width': '内容区域宽度', | |||||
'app.setting.content-width.fixed': '定宽', | |||||
'app.setting.content-width.fluid': '流式', | |||||
'app.setting.themecolor': '主题色', | |||||
'app.setting.themecolor.dust': '薄暮', | |||||
'app.setting.themecolor.volcano': '火山', | |||||
'app.setting.themecolor.sunset': '日暮', | |||||
'app.setting.themecolor.cyan': '明青', | |||||
'app.setting.themecolor.green': '极光绿', | |||||
'app.setting.themecolor.daybreak': '拂晓蓝(默认)', | |||||
'app.setting.themecolor.geekblue': '极客蓝', | |||||
'app.setting.themecolor.purple': '酱紫', | |||||
'app.setting.navigationmode': '导航模式', | |||||
'app.setting.sidemenu': '侧边菜单布局', | |||||
'app.setting.topmenu': '顶部菜单布局', | |||||
'app.setting.fixedheader': '固定 Header', | |||||
'app.setting.fixedsidebar': '固定侧边菜单', | |||||
'app.setting.fixedsidebar.hint': '侧边菜单布局时可配置', | |||||
'app.setting.hideheader': '下滑时隐藏 Header', | |||||
'app.setting.hideheader.hint': '固定 Header 时可配置', | |||||
'app.setting.othersettings': '其他设置', | |||||
'app.setting.weakmode': '色弱模式', | |||||
'app.setting.copy': '拷贝设置', | |||||
'app.setting.copyinfo': '拷贝成功,请到 config/defaultSettings.js 中替换默认配置', | |||||
'app.setting.production.hint': | |||||
'配置栏只在开发环境用于预览,生产环境不会展现,请拷贝后手动修改配置文件', | |||||
}; |
@@ -0,0 +1,55 @@ | |||||
export default { | |||||
'app.settings.menuMap.basic': '基本设置', | |||||
'app.settings.menuMap.security': '安全设置', | |||||
'app.settings.menuMap.binding': '账号绑定', | |||||
'app.settings.menuMap.notification': '新消息通知', | |||||
'app.settings.basic.avatar': '头像', | |||||
'app.settings.basic.change-avatar': '更换头像', | |||||
'app.settings.basic.email': '邮箱', | |||||
'app.settings.basic.email-message': '请输入您的邮箱!', | |||||
'app.settings.basic.nickname': '昵称', | |||||
'app.settings.basic.nickname-message': '请输入您的昵称!', | |||||
'app.settings.basic.profile': '个人简介', | |||||
'app.settings.basic.profile-message': '请输入个人简介!', | |||||
'app.settings.basic.profile-placeholder': '个人简介', | |||||
'app.settings.basic.country': '国家/地区', | |||||
'app.settings.basic.country-message': '请输入您的国家或地区!', | |||||
'app.settings.basic.geographic': '所在省市', | |||||
'app.settings.basic.geographic-message': '请输入您的所在省市!', | |||||
'app.settings.basic.address': '街道地址', | |||||
'app.settings.basic.address-message': '请输入您的街道地址!', | |||||
'app.settings.basic.phone': '联系电话', | |||||
'app.settings.basic.phone-message': '请输入您的联系电话!', | |||||
'app.settings.basic.update': '更新基本信息', | |||||
'app.settings.security.strong': '强', | |||||
'app.settings.security.medium': '中', | |||||
'app.settings.security.weak': '弱', | |||||
'app.settings.security.password': '账户密码', | |||||
'app.settings.security.password-description': '当前密码强度', | |||||
'app.settings.security.phone': '密保手机', | |||||
'app.settings.security.phone-description': '已绑定手机', | |||||
'app.settings.security.question': '密保问题', | |||||
'app.settings.security.question-description': '未设置密保问题,密保问题可有效保护账户安全', | |||||
'app.settings.security.email': '备用邮箱', | |||||
'app.settings.security.email-description': '已绑定邮箱', | |||||
'app.settings.security.mfa': 'MFA 店铺', | |||||
'app.settings.security.mfa-description': '未绑定 MFA 店铺,绑定后,可以进行二次确认', | |||||
'app.settings.security.modify': '修改', | |||||
'app.settings.security.set': '设置', | |||||
'app.settings.security.bind': '绑定', | |||||
'app.settings.binding.taobao': '绑定淘宝', | |||||
'app.settings.binding.taobao-description': '当前未绑定淘宝账号', | |||||
'app.settings.binding.alipay': '绑定支付宝', | |||||
'app.settings.binding.alipay-description': '当前未绑定支付宝账号', | |||||
'app.settings.binding.dingding': '绑定钉钉', | |||||
'app.settings.binding.dingding-description': '当前未绑定钉钉账号', | |||||
'app.settings.binding.bind': '绑定', | |||||
'app.settings.notification.password': '账户密码', | |||||
'app.settings.notification.password-description': '其他用户的消息将以站内信的形式通知', | |||||
'app.settings.notification.messages': '系统消息', | |||||
'app.settings.notification.messages-description': '系统消息将以站内信的形式通知', | |||||
'app.settings.notification.todo': '待办任务', | |||||
'app.settings.notification.todo-description': '待办任务将以站内信的形式通知', | |||||
'app.settings.open': '开', | |||||
'app.settings.close': '关', | |||||
}; |
@@ -0,0 +1,19 @@ | |||||
import component from './zh-TW/component'; | |||||
import globalHeader from './zh-TW/globalHeader'; | |||||
import menu from './zh-TW/menu'; | |||||
import pwa from './zh-TW/pwa'; | |||||
import settingDrawer from './zh-TW/settingDrawer'; | |||||
import settings from './zh-TW/settings'; | |||||
export default { | |||||
'navBar.lang': '語言', | |||||
'layout.user.link.help': '幫助', | |||||
'layout.user.link.privacy': '隱私', | |||||
'layout.user.link.terms': '條款', | |||||
'app.preview.down.block': '下載此頁面到本地項目', | |||||
...globalHeader, | |||||
...menu, | |||||
...settingDrawer, | |||||
...settings, | |||||
...pwa, | |||||
...component, | |||||
}; |
@@ -0,0 +1,5 @@ | |||||
export default { | |||||
'component.tagSelect.expand': '展開', | |||||
'component.tagSelect.collapse': '收起', | |||||
'component.tagSelect.all': '全部', | |||||
}; |
@@ -0,0 +1,17 @@ | |||||
export default { | |||||
'component.globalHeader.search': '站內搜索', | |||||
'component.globalHeader.search.example1': '搜索提示壹', | |||||
'component.globalHeader.search.example2': '搜索提示二', | |||||
'component.globalHeader.search.example3': '搜索提示三', | |||||
'component.globalHeader.help': '使用手冊', | |||||
'component.globalHeader.notification': '通知', | |||||
'component.globalHeader.notification.empty': '妳已查看所有通知', | |||||
'component.globalHeader.message': '消息', | |||||
'component.globalHeader.message.empty': '您已讀完所有消息', | |||||
'component.globalHeader.event': '待辦', | |||||
'component.globalHeader.event.empty': '妳已完成所有待辦', | |||||
'component.noticeIcon.clear': '清空', | |||||
'component.noticeIcon.cleared': '清空了', | |||||
'component.noticeIcon.empty': '暫無資料', | |||||
'component.noticeIcon.view-more': '查看更多', | |||||
}; |
@@ -0,0 +1,52 @@ | |||||
export default { | |||||
'menu.welcome': '歡迎', | |||||
'menu.more-blocks': '更多區塊', | |||||
'menu.home': '首頁', | |||||
'menu.login': '登錄', | |||||
'menu.admin': '权限', | |||||
'menu.admin.sub-page': '二级管理页', | |||||
'menu.exception.403': '403', | |||||
'menu.exception.404': '404', | |||||
'menu.exception.500': '500', | |||||
'menu.register': '註冊', | |||||
'menu.register.result': '註冊結果', | |||||
'menu.dashboard': 'Dashboard', | |||||
'menu.dashboard.analysis': '分析頁', | |||||
'menu.dashboard.monitor': '監控頁', | |||||
'menu.dashboard.workplace': '工作臺', | |||||
'menu.form': '表單頁', | |||||
'menu.form.basic-form': '基礎表單', | |||||
'menu.form.step-form': '分步表單', | |||||
'menu.form.step-form.info': '分步表單(填寫轉賬信息)', | |||||
'menu.form.step-form.confirm': '分步表單(確認轉賬信息)', | |||||
'menu.form.step-form.result': '分步表單(完成)', | |||||
'menu.form.advanced-form': '高級表單', | |||||
'menu.list': '列表頁', | |||||
'menu.list.table-list': '查詢表格', | |||||
'menu.list.basic-list': '標淮列表', | |||||
'menu.list.card-list': '卡片列表', | |||||
'menu.list.search-list': '搜索列表', | |||||
'menu.list.search-list.articles': '搜索列表(文章)', | |||||
'menu.list.search-list.projects': '搜索列表(項目)', | |||||
'menu.list.search-list.applications': '搜索列表(應用)', | |||||
'menu.profile': '詳情頁', | |||||
'menu.profile.basic': '基礎詳情頁', | |||||
'menu.profile.advanced': '高級詳情頁', | |||||
'menu.result': '結果頁', | |||||
'menu.result.success': '成功頁', | |||||
'menu.result.fail': '失敗頁', | |||||
'menu.account': '個人頁', | |||||
'menu.account.center': '個人中心', | |||||
'menu.account.settings': '個人設置', | |||||
'menu.account.trigger': '觸發報錯', | |||||
'menu.account.logout': '退出登錄', | |||||
'menu.exception': '异常页', | |||||
'menu.exception.not-permission': '403', | |||||
'menu.exception.not-find': '404', | |||||
'menu.exception.server-error': '500', | |||||
'menu.exception.trigger': '触发错误', | |||||
'menu.editor': '圖形編輯器', | |||||
'menu.editor.flow': '流程編輯器', | |||||
'menu.editor.mind': '腦圖編輯器', | |||||
'menu.editor.koni': '拓撲編輯器', | |||||
}; |
@@ -0,0 +1,6 @@ | |||||
export default { | |||||
'app.pwa.offline': '當前處於離線狀態', | |||||
'app.pwa.serviceworker.updated': '有新內容', | |||||
'app.pwa.serviceworker.updated.hint': '請點擊“刷新”按鈕或者手動刷新頁面', | |||||
'app.pwa.serviceworker.updated.ok': '刷新', | |||||
}; |
@@ -0,0 +1,31 @@ | |||||
export default { | |||||
'app.setting.pagestyle': '整體風格設置', | |||||
'app.setting.pagestyle.dark': '暗色菜單風格', | |||||
'app.setting.pagestyle.light': '亮色菜單風格', | |||||
'app.setting.content-width': '內容區域寬度', | |||||
'app.setting.content-width.fixed': '定寬', | |||||
'app.setting.content-width.fluid': '流式', | |||||
'app.setting.themecolor': '主題色', | |||||
'app.setting.themecolor.dust': '薄暮', | |||||
'app.setting.themecolor.volcano': '火山', | |||||
'app.setting.themecolor.sunset': '日暮', | |||||
'app.setting.themecolor.cyan': '明青', | |||||
'app.setting.themecolor.green': '極光綠', | |||||
'app.setting.themecolor.daybreak': '拂曉藍(默認)', | |||||
'app.setting.themecolor.geekblue': '極客藍', | |||||
'app.setting.themecolor.purple': '醬紫', | |||||
'app.setting.navigationmode': '導航模式', | |||||
'app.setting.sidemenu': '側邊菜單布局', | |||||
'app.setting.topmenu': '頂部菜單布局', | |||||
'app.setting.fixedheader': '固定 Header', | |||||
'app.setting.fixedsidebar': '固定側邊菜單', | |||||
'app.setting.fixedsidebar.hint': '側邊菜單布局時可配置', | |||||
'app.setting.hideheader': '下滑時隱藏 Header', | |||||
'app.setting.hideheader.hint': '固定 Header 時可配置', | |||||
'app.setting.othersettings': '其他設置', | |||||
'app.setting.weakmode': '色弱模式', | |||||
'app.setting.copy': '拷貝設置', | |||||
'app.setting.copyinfo': '拷貝成功,請到 config/defaultSettings.js 中替換默認配置', | |||||
'app.setting.production.hint': | |||||
'配置欄只在開發環境用於預覽,生產環境不會展現,請拷貝後手動修改配置文件', | |||||
}; |
@@ -0,0 +1,55 @@ | |||||
export default { | |||||
'app.settings.menuMap.basic': '基本設置', | |||||
'app.settings.menuMap.security': '安全設置', | |||||
'app.settings.menuMap.binding': '賬號綁定', | |||||
'app.settings.menuMap.notification': '新消息通知', | |||||
'app.settings.basic.avatar': '頭像', | |||||
'app.settings.basic.change-avatar': '更換頭像', | |||||
'app.settings.basic.email': '郵箱', | |||||
'app.settings.basic.email-message': '請輸入您的郵箱!', | |||||
'app.settings.basic.nickname': '昵稱', | |||||
'app.settings.basic.nickname-message': '請輸入您的昵稱!', | |||||
'app.settings.basic.profile': '個人簡介', | |||||
'app.settings.basic.profile-message': '請輸入個人簡介!', | |||||
'app.settings.basic.profile-placeholder': '個人簡介', | |||||
'app.settings.basic.country': '國家/地區', | |||||
'app.settings.basic.country-message': '請輸入您的國家或地區!', | |||||
'app.settings.basic.geographic': '所在省市', | |||||
'app.settings.basic.geographic-message': '請輸入您的所在省市!', | |||||
'app.settings.basic.address': '街道地址', | |||||
'app.settings.basic.address-message': '請輸入您的街道地址!', | |||||
'app.settings.basic.phone': '聯系電話', | |||||
'app.settings.basic.phone-message': '請輸入您的聯系電話!', | |||||
'app.settings.basic.update': '更新基本信息', | |||||
'app.settings.security.strong': '強', | |||||
'app.settings.security.medium': '中', | |||||
'app.settings.security.weak': '弱', | |||||
'app.settings.security.password': '賬戶密碼', | |||||
'app.settings.security.password-description': '當前密碼強度', | |||||
'app.settings.security.phone': '密保手機', | |||||
'app.settings.security.phone-description': '已綁定手機', | |||||
'app.settings.security.question': '密保問題', | |||||
'app.settings.security.question-description': '未設置密保問題,密保問題可有效保護賬戶安全', | |||||
'app.settings.security.email': '備用郵箱', | |||||
'app.settings.security.email-description': '已綁定郵箱', | |||||
'app.settings.security.mfa': 'MFA 設備', | |||||
'app.settings.security.mfa-description': '未綁定 MFA 設備,綁定後,可以進行二次確認', | |||||
'app.settings.security.modify': '修改', | |||||
'app.settings.security.set': '設置', | |||||
'app.settings.security.bind': '綁定', | |||||
'app.settings.binding.taobao': '綁定淘寶', | |||||
'app.settings.binding.taobao-description': '當前未綁定淘寶賬號', | |||||
'app.settings.binding.alipay': '綁定支付寶', | |||||
'app.settings.binding.alipay-description': '當前未綁定支付寶賬號', | |||||
'app.settings.binding.dingding': '綁定釘釘', | |||||
'app.settings.binding.dingding-description': '當前未綁定釘釘賬號', | |||||
'app.settings.binding.bind': '綁定', | |||||
'app.settings.notification.password': '賬戶密碼', | |||||
'app.settings.notification.password-description': '其他用戶的消息將以站內信的形式通知', | |||||
'app.settings.notification.messages': '系統消息', | |||||
'app.settings.notification.messages-description': '系統消息將以站內信的形式通知', | |||||
'app.settings.notification.todo': '待辦任務', | |||||
'app.settings.notification.todo-description': '待辦任務將以站內信的形式通知', | |||||
'app.settings.open': '開', | |||||
'app.settings.close': '關', | |||||
}; |
@@ -0,0 +1,22 @@ | |||||
{ | |||||
"name": "Ant Design Pro", | |||||
"short_name": "Ant Design Pro", | |||||
"display": "standalone", | |||||
"start_url": "./?utm_source=homescreen", | |||||
"theme_color": "#002140", | |||||
"background_color": "#001529", | |||||
"icons": [ | |||||
{ | |||||
"src": "icons/icon-192x192.png", | |||||
"sizes": "192x192" | |||||
}, | |||||
{ | |||||
"src": "icons/icon-128x128.png", | |||||
"sizes": "128x128" | |||||
}, | |||||
{ | |||||
"src": "icons/icon-512x512.png", | |||||
"sizes": "512x512" | |||||
} | |||||
] | |||||
} |
@@ -0,0 +1,18 @@ | |||||
import { Button, Result } from 'antd'; | |||||
import React from 'react'; | |||||
import { history } from 'umi'; | |||||
const NoFoundPage = () => ( | |||||
<Result | |||||
status="404" | |||||
title="404" | |||||
subTitle="Sorry, the page you visited does not exist." | |||||
extra={ | |||||
<Button type="primary" onClick={() => history.push('/')}> | |||||
Back Home | |||||
</Button> | |||||
} | |||||
/> | |||||
); | |||||
export default NoFoundPage; |
@@ -0,0 +1,52 @@ | |||||
import React from 'react'; | |||||
import { HeartTwoTone, SmileTwoTone } from '@ant-design/icons'; | |||||
import { Card, Typography, Alert } from 'antd'; | |||||
import { PageHeaderWrapper } from '@ant-design/pro-layout'; | |||||
import { useIntl } from 'umi'; | |||||
export default () => { | |||||
const intl = useIntl(); | |||||
return ( | |||||
<PageHeaderWrapper | |||||
content={intl.formatMessage({ | |||||
id: 'pages.admin.subPage.title', | |||||
defaultMessage: 'This page can only be viewed by admin', | |||||
})} | |||||
> | |||||
<Card> | |||||
<Alert | |||||
message={intl.formatMessage({ | |||||
id: 'pages.welcome.alertMessage', | |||||
defaultMessage: 'Faster and stronger heavy-duty components have been released.', | |||||
})} | |||||
type="success" | |||||
showIcon | |||||
banner | |||||
style={{ | |||||
margin: -12, | |||||
marginBottom: 48, | |||||
}} | |||||
/> | |||||
<Typography.Title | |||||
level={2} | |||||
style={{ | |||||
textAlign: 'center', | |||||
}} | |||||
> | |||||
<SmileTwoTone /> Ant Design Pro <HeartTwoTone twoToneColor="#eb2f96" /> You | |||||
</Typography.Title> | |||||
</Card> | |||||
<p | |||||
style={{ | |||||
textAlign: 'center', | |||||
marginTop: 24, | |||||
}} | |||||
> | |||||
Want to add more pages? Please refer to{' '} | |||||
<a href="https://pro.ant.design/docs/block-cn" target="_blank" rel="noopener noreferrer"> | |||||
use block | |||||
</a> | |||||
。 | |||||
</p> | |||||
</PageHeaderWrapper> | |||||
); | |||||
}; |
@@ -0,0 +1,47 @@ | |||||
import React from 'react'; | |||||
import { PageContainer } from '@ant-design/pro-layout'; | |||||
import { HeartTwoTone, SmileTwoTone } from '@ant-design/icons'; | |||||
import { Card, Alert, Typography } from 'antd'; | |||||
import { useIntl, FormattedMessage } from 'umi'; | |||||
import styles from './Welcome.less'; | |||||
// import MembershipAnalysis from './member/memberAnalysis'; | |||||
const CodePreview = ({ children }) => ( | |||||
<pre className={styles.pre}> | |||||
<code> | |||||
<Typography.Text copyable>{children}</Typography.Text> | |||||
</code> | |||||
</pre> | |||||
); | |||||
export default () => { | |||||
const intl = useIntl(); | |||||
return ( | |||||
<PageContainer> | |||||
{/* <MembershipAnalysis /> */} | |||||
{/* <Card> | |||||
<Alert | |||||
message={intl.formatMessage({ | |||||
id: 'pages.welcome.alertMessage', | |||||
defaultMessage: 'Faster and stronger heavy-duty components have been released.', | |||||
})} | |||||
type="success" | |||||
showIcon | |||||
banner | |||||
style={{ | |||||
margin: -12, | |||||
marginBottom: 48, | |||||
}} | |||||
/> | |||||
<Typography.Title | |||||
level={2} | |||||
style={{ | |||||
textAlign: 'center', | |||||
}} | |||||
> | |||||
<SmileTwoTone /> Black-Pa <HeartTwoTone twoToneColor="#eb2f96" /> You | |||||
</Typography.Title> | |||||
</Card> */} | |||||
</PageContainer> | |||||
); | |||||
}; |
@@ -0,0 +1,8 @@ | |||||
@import '~antd/lib/style/themes/default.less'; | |||||
.pre { | |||||
margin: 12px 0; | |||||
padding: 12px 20px; | |||||
background: @input-bg; | |||||
box-shadow: @card-shadow; | |||||
} |
@@ -0,0 +1,69 @@ | |||||
import React, { useState } from 'react'; | |||||
import { Modal, Form, Input, Button, Select, TreeSelect } from 'antd'; | |||||
const CreateForm = (props) => { | |||||
const { TextArea } = Input; | |||||
return ( | |||||
<Modal | |||||
title={props.values.id ? '用户编辑' : '用户新增'} | |||||
width={640} | |||||
visible={props.modalVisible} | |||||
bodyStyle={{ padding: '32px 40px 48px' }} | |||||
footer={null} | |||||
onCancel={() => { | |||||
props.onCancel(); | |||||
}} | |||||
destroyOnClose | |||||
> | |||||
<Form | |||||
layout="horizontal" | |||||
labelCol={{ | |||||
span: 4, | |||||
}} | |||||
preserve={false} | |||||
initialValues={props.values} | |||||
onFinish={props.onFinish} | |||||
> | |||||
<Form.Item name="id" hidden={true}> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item name="name" label="姓名" rules={[{ required: true, max: 64 }]}> | |||||
<Input placeholder="请输姓名" /> | |||||
</Form.Item> | |||||
<Form.Item name="account" label="账号" rules={[{ required: true, max: 64 }]}> | |||||
<Input placeholder="请输入账号" /> | |||||
</Form.Item> | |||||
{props.values.id ? ( | |||||
'' | |||||
) : ( | |||||
<Form.Item | |||||
hidden={props.values.id ? true : false} | |||||
name="password" | |||||
label="密码" | |||||
rules={[{ required: true, max: 64 }]} | |||||
> | |||||
<Input.Password placeholder="请输入密码" /> | |||||
</Form.Item> | |||||
)} | |||||
<Form.Item name="orgId" label="上级机构" rules={[{ required: true, max: 64 }]}> | |||||
<TreeSelect | |||||
style={{ width: '100%' }} | |||||
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} | |||||
treeData={props.treeDatas} | |||||
placeholder="请选择机构" | |||||
treeDefaultExpandAll | |||||
/> | |||||
</Form.Item> | |||||
<Form.Item name="remark" label="备注"> | |||||
<TextArea rows={4} /> | |||||
</Form.Item> | |||||
<Form.Item> | |||||
<Button type="primary" htmlType="submit"> | |||||
保存 | |||||
</Button> | |||||
</Form.Item> | |||||
</Form> | |||||
</Modal> | |||||
); | |||||
}; | |||||
export default CreateForm; |
@@ -0,0 +1,64 @@ | |||||
import React, { useEffect, useState } from 'react'; | |||||
import { Modal, Tree, Checkbox, Spin, Button, Input } from 'antd'; | |||||
import ProTable, { ProColumns, ActionType, TableDropdown } from '@ant-design/pro-table'; | |||||
import { Page } from '../../../org/roles/service'; | |||||
const columns = [ | |||||
{ | |||||
title: '角色名称', | |||||
dataIndex: 'name', | |||||
hideInSearch: true, | |||||
}, | |||||
{ | |||||
title: '备注', | |||||
dataIndex: 'remark', | |||||
valueType: 'textarea', | |||||
hideInSearch: true, | |||||
}, | |||||
]; | |||||
const OrgForm = (props) => { | |||||
const [options, setOptions] = useState(); | |||||
const [loading, setLoading] = useState(undefined); | |||||
const [keys, setSelectedRows] = useState(); | |||||
// // componentDidMount | |||||
// useEffect(() => { | |||||
// setLoading(true); | |||||
// async function getRoles() { | |||||
// if (loading) { | |||||
// return; | |||||
// } | |||||
// } | |||||
// getRoles(); | |||||
// }, []); | |||||
const renderContent = () => { | |||||
return ( | |||||
<Tree | |||||
defaultExpandAll={true} | |||||
checkable | |||||
onCheck={(e) => setSelectedRows(e)} | |||||
treeData={props.treeDatas} | |||||
/> | |||||
); | |||||
}; | |||||
return ( | |||||
<Modal | |||||
width={640} | |||||
bodyStyle={{ padding: '32px 40px 48px' }} | |||||
destroyOnClose | |||||
title={`给 ${props.values.name} 授权数据`} | |||||
visible={props.updateModalVisible} | |||||
onOk={() => props.onSubmit(keys)} | |||||
onCancel={() => props.onCancel()} | |||||
maskClosable={false} | |||||
> | |||||
{loading ? <Spin /> : renderContent()} | |||||
</Modal> | |||||
); | |||||
}; | |||||
export default OrgForm; |
@@ -0,0 +1,85 @@ | |||||
import React, { useEffect, useState } from 'react'; | |||||
import { Modal, Table, Checkbox, Spin, Button, Input } from 'antd'; | |||||
import ProTable, { ProColumns, ActionType, TableDropdown } from '@ant-design/pro-table'; | |||||
import { Page } from '../../../org/roles/service'; | |||||
const columns = [ | |||||
{ | |||||
title: '角色名称', | |||||
dataIndex: 'name', | |||||
hideInSearch: true, | |||||
}, | |||||
{ | |||||
title: '备注', | |||||
dataIndex: 'remark', | |||||
valueType: 'textarea', | |||||
hideInSearch: true, | |||||
}, | |||||
]; | |||||
const RoleForm = (props) => { | |||||
const [options, setOptions] = useState(); | |||||
const [loading, setLoading] = useState(undefined); | |||||
const [selectedRowsState, setSelectedRows] = useState(); | |||||
// // componentDidMount | |||||
// useEffect(() => { | |||||
// setLoading(true); | |||||
// async function getRoles() { | |||||
// if (loading) { | |||||
// return; | |||||
// } | |||||
// } | |||||
// getRoles(); | |||||
// }, []); | |||||
const renderContent = () => { | |||||
return ( | |||||
<ProTable | |||||
rowKey="id" | |||||
toolBarRender={false} | |||||
search={false} | |||||
pagination={false} | |||||
request={async (params) => { | |||||
let UserData = []; | |||||
var total = 0; | |||||
params.pageSize = 100; | |||||
//添加机构组织 | |||||
await Page(params).then((r) => { | |||||
UserData = r.data.data; | |||||
total = r.data.total; | |||||
}); | |||||
return { | |||||
data: UserData, | |||||
success: true, | |||||
total: total, | |||||
}; | |||||
}} | |||||
columns={columns} | |||||
rowSelection={{ | |||||
onChange: (_, selectedRows) => setSelectedRows(selectedRows), | |||||
defaultSelectedRowKeys: ['596916ab-300a-464f-b825-4bc5c624b06e'], | |||||
}} | |||||
/> | |||||
); | |||||
}; | |||||
return ( | |||||
<Modal | |||||
width={640} | |||||
bodyStyle={{ padding: '32px 40px 48px' }} | |||||
destroyOnClose | |||||
title={`给 ${props.values.name} 分配角色`} | |||||
visible={props.updateModalVisible} | |||||
onOk={() => props.onSubmit(selectedRowsState)} | |||||
onCancel={() => props.onCancel()} | |||||
maskClosable={false} | |||||
> | |||||
{loading ? <Spin /> : renderContent()} | |||||
</Modal> | |||||
); | |||||
}; | |||||
export default RoleForm; |
@@ -0,0 +1,437 @@ | |||||
import React, { useState, useRef, useEffect } from 'react'; | |||||
import { Modal, Card, Tree, Button, message, Drawer, Divider, Menu, Dropdown } from 'antd'; | |||||
import { PlusOutlined, DownOutlined, ExclamationCircleOutlined } from '@ant-design/icons'; | |||||
import { PageContainer, FooterToolbar } from '@ant-design/pro-layout'; | |||||
import ProTable, { ProColumns, ActionType, TableDropdown } from '@ant-design/pro-table'; | |||||
import ProDescriptions from '@ant-design/pro-descriptions'; | |||||
import CreateForm from './components/CreateForm'; | |||||
import { queryUser, addUser, removeRule, updateUser, setRoles, sysUserGrantData } from './service'; | |||||
import { gettree } from '../../org/orgamange/service'; | |||||
import moment from 'moment'; | |||||
import { useAccess } from 'umi'; | |||||
import RoleForm from './components/RoleForm'; | |||||
import OrgForm from './components/OrgForm'; | |||||
//import { TableListItem as RoleData } from '../roles/data.d'; | |||||
/** | |||||
* 添加节点 | |||||
* @param fields | |||||
*/ | |||||
const handleAdd = async (fields) => { | |||||
const hide = message.loading('正在添加'); | |||||
try { | |||||
await addUser({ ...fields }); | |||||
hide(); | |||||
message.success('添加成功'); | |||||
return true; | |||||
} catch (error) { | |||||
hide(); | |||||
message.error('添加失败请重试!'); | |||||
return false; | |||||
} | |||||
}; | |||||
/** | |||||
* 更新节点 | |||||
* @param fields | |||||
*/ | |||||
const handleUpdate = async (fields) => { | |||||
const hide = message.loading('正在更新'); | |||||
try { | |||||
await updateUser({ ...fields }); | |||||
hide(); | |||||
message.success('更新成功'); | |||||
return true; | |||||
} catch (error) { | |||||
hide(); | |||||
message.error('更新失败请重试!'); | |||||
return false; | |||||
} | |||||
}; | |||||
/** | |||||
* 分配角色 | |||||
* @param fields | |||||
*/ | |||||
const handleRoles = async (fields) => { | |||||
const hide = message.loading('正在分配角色'); | |||||
try { | |||||
await setRoles({ ...fields }); | |||||
hide(); | |||||
message.success('分配角色成功'); | |||||
return true; | |||||
} catch (error) { | |||||
hide(); | |||||
message.error('分配角色失败请重试!'); | |||||
return false; | |||||
} | |||||
}; | |||||
/** | |||||
* 正在分配数据权限 | |||||
* @param fields | |||||
*/ | |||||
const handleGrantData = async (fields) => { | |||||
const hide = message.loading('正在分配数据权限'); | |||||
try { | |||||
await sysUserGrantData({ ...fields }); | |||||
hide(); | |||||
message.success('分配数据权限成功'); | |||||
return true; | |||||
} catch (error) { | |||||
hide(); | |||||
message.error('分配数据权限失败请重试!'); | |||||
return false; | |||||
} | |||||
}; | |||||
/** | |||||
* 删除节点 | |||||
* @param selectedRows | |||||
*/ | |||||
const handleRemove = async (selectedRows) => { | |||||
const hide = message.loading('正在删除'); | |||||
if (!selectedRows) return true; | |||||
try { | |||||
await removeRule({ | |||||
ids: selectedRows.map((row) => row._id), | |||||
}); | |||||
hide(); | |||||
message.success('删除成功,即将刷新'); | |||||
return true; | |||||
} catch (error) { | |||||
hide(); | |||||
message.error('删除失败,请重试'); | |||||
return false; | |||||
} | |||||
}; | |||||
const TableList = () => { | |||||
const [createModalVisible, handleModalVisible] = useState(); | |||||
const [updateRoleModalVisible, handleUpdateRoleModalVisible] = useState(); | |||||
const [updateOrgModalVisible, handleUpdateOrgModalVisible] = useState(); | |||||
const [stepFormValues, setStepFormValues] = useState({}); | |||||
const actionRef = useRef(); | |||||
const [row, setRow] = useState(); | |||||
const [selectedRowsState, setSelectedRows] = useState(); | |||||
const [treeData, setTreeData] = useState(); | |||||
const [Pid, setKey] = useState(); | |||||
const access = useAccess(); | |||||
const columns = [ | |||||
{ | |||||
title: '关键词', | |||||
dataIndex: 'SearchValue', | |||||
hideInForm: true, | |||||
hideInTable: true, | |||||
tip: '可以输入账号,姓名', | |||||
placeholder: '', | |||||
}, | |||||
{ | |||||
title: '机构', | |||||
dataIndex: 'orgId', | |||||
hideInForm: true, | |||||
hideInTable: true, | |||||
hideInSearch: true, | |||||
}, | |||||
{ | |||||
title: '登录账号', | |||||
dataIndex: 'account', | |||||
tip: '登录账号是唯一的 key', | |||||
hideInSearch: true, | |||||
formItemProps: { | |||||
rules: [ | |||||
{ | |||||
required: true, | |||||
message: '登录账号必填!', | |||||
}, | |||||
], | |||||
}, | |||||
renderText: (dom, entity) => { | |||||
return <a onClick={() => setRow(entity)}>{dom}</a>; | |||||
}, | |||||
}, | |||||
{ | |||||
title: '姓名', | |||||
dataIndex: 'name', | |||||
hideInSearch: true, | |||||
formItemProps: { | |||||
rules: [ | |||||
{ | |||||
required: true, | |||||
message: '用户名为必填项', | |||||
}, | |||||
], | |||||
}, | |||||
}, | |||||
{ | |||||
title: '创建时间', | |||||
search: false, | |||||
dataIndex: 'createAt', | |||||
hideInForm: true, | |||||
sorter: true, | |||||
renderText: (val) => moment(val).fromNow(), | |||||
}, | |||||
{ | |||||
title: '备注', | |||||
dataIndex: 'remark', | |||||
valueType: 'textarea', | |||||
hideInSearch: true, | |||||
}, | |||||
{ | |||||
title: '操作', | |||||
dataIndex: 'option', | |||||
valueType: 'option', | |||||
render: (_, record) => ( | |||||
<> | |||||
<a | |||||
onClick={() => { | |||||
handleModalVisible(true); | |||||
setStepFormValues(record); | |||||
}} | |||||
> | |||||
修改 | |||||
</a> | |||||
<Divider type="vertical" /> | |||||
<TableDropdown | |||||
key="actionGroup" | |||||
onSelect={(option) => { | |||||
if (option === 'resetPwd') { | |||||
Modal.confirm({ | |||||
title: '重置密码', | |||||
icon: <ExclamationCircleOutlined />, | |||||
content: '是否确认重置密码', | |||||
okText: '确认', | |||||
cancelText: '取消', | |||||
onOk() { | |||||
}, | |||||
}); | |||||
} else if (option === 'grantRole') { | |||||
handleUpdateRoleModalVisible(true); | |||||
setStepFormValues(record); | |||||
} else if (option === 'grantData') { | |||||
handleUpdateOrgModalVisible(true); | |||||
setStepFormValues(record); | |||||
} else if (option === 'delete') { | |||||
Modal.confirm({ | |||||
title: '删除用户', | |||||
icon: <ExclamationCircleOutlined />, | |||||
content: '是否确认删除用户', | |||||
okText: '确认', | |||||
cancelText: '取消', | |||||
}); | |||||
} | |||||
}} | |||||
menus={[ | |||||
{ key: 'resetPwd', name: '重置密码' }, | |||||
{ key: 'grantRole', name: '授权角色' }, | |||||
{ key: 'grantData', name: '授权数据' }, | |||||
{ key: 'delete', name: '删除' }, | |||||
]} | |||||
/> | |||||
</> | |||||
), | |||||
}, | |||||
]; | |||||
//初始化数据 | |||||
useEffect(() => { | |||||
function initfranchiseeType() { | |||||
gettree().then((r) => { | |||||
setTreeData(r.data); | |||||
}); | |||||
} | |||||
initfranchiseeType(); | |||||
}, []); | |||||
return ( | |||||
<PageContainer> | |||||
<ProTable | |||||
headerTitle="员工列表" | |||||
actionRef={actionRef} | |||||
rowKey="id" | |||||
pagination={{ defaultPageSize: 20 }} | |||||
search={{ | |||||
labelWidth: 120, | |||||
}} | |||||
tableRender={(_, dom) => ( | |||||
<div | |||||
style={{ | |||||
display: 'flex', | |||||
width: '100%', | |||||
}} | |||||
> | |||||
<Card title="机构部门"> | |||||
<Tree defaultExpandAll={true} onSelect={(e) => setKey(e[0])} treeData={treeData} /> | |||||
</Card> | |||||
<div | |||||
style={{ | |||||
flex: 1, | |||||
}} | |||||
> | |||||
{dom} | |||||
</div> | |||||
</div> | |||||
)} | |||||
toolBarRender={() => [ | |||||
<Button type="primary" key="create" onClick={() => handleModalVisible(true)}> | |||||
<PlusOutlined /> 新建 | |||||
</Button>, | |||||
]} | |||||
params={{ | |||||
Pid, | |||||
}} | |||||
request={async (params) => { | |||||
let UserData = []; | |||||
var total = 0; | |||||
//添加机构组织 | |||||
await queryUser(params).then((r) => { | |||||
console.log(r); | |||||
UserData = r.data.data; | |||||
total = r.data.total; | |||||
}); | |||||
return { | |||||
data: UserData, | |||||
success: true, | |||||
total: total, | |||||
}; | |||||
}} | |||||
columns={columns} | |||||
rowSelection={{ | |||||
onChange: (_, selectedRows) => setSelectedRows(selectedRows), | |||||
}} | |||||
/> | |||||
{selectedRowsState?.length > 0 && ( | |||||
<FooterToolbar | |||||
extra={ | |||||
<div> | |||||
已选择 <a style={{ fontWeight: 600 }}>{selectedRowsState.length}</a> 项 | |||||
</div> | |||||
} | |||||
> | |||||
<Button | |||||
type="primary" | |||||
onClick={async () => { | |||||
await handleRemove(selectedRowsState); | |||||
setSelectedRows([]); | |||||
actionRef.current?.reloadAndRest?.(); | |||||
}} | |||||
> | |||||
批量删除 | |||||
</Button> | |||||
</FooterToolbar> | |||||
)} | |||||
<CreateForm | |||||
treeDatas={treeData} | |||||
onFinish={async (value) => { | |||||
var success = false; | |||||
if (value.id) { | |||||
success = await handleUpdate(value); | |||||
} else { | |||||
success = await handleAdd(value); | |||||
} | |||||
if (success) { | |||||
handleModalVisible(false); | |||||
if (actionRef.current) { | |||||
actionRef.current.reload(); | |||||
} | |||||
} | |||||
}} | |||||
onCancel={() => { | |||||
handleModalVisible(false); | |||||
setStepFormValues({}); | |||||
}} | |||||
modalVisible={createModalVisible} | |||||
values={stepFormValues || {}} | |||||
/> | |||||
{stepFormValues && Object.keys(stepFormValues).length ? ( | |||||
<RoleForm | |||||
onSubmit={async (value) => { | |||||
if (value == null || value == undefined) { | |||||
handleUpdateRoleModalVisible(false); | |||||
setStepFormValues({}); | |||||
return true; | |||||
} | |||||
const values = { | |||||
id: stepFormValues.id, | |||||
grantRoleIdList: value.map((key) => key.id), | |||||
}; | |||||
const success = await handleRoles(values); | |||||
if (success) { | |||||
handleUpdateRoleModalVisible(false); | |||||
setStepFormValues({}); | |||||
if (actionRef.current) { | |||||
actionRef.current.reload(); | |||||
} | |||||
} | |||||
}} | |||||
onCancel={() => { | |||||
handleUpdateRoleModalVisible(false); | |||||
setStepFormValues({}); | |||||
}} | |||||
updateModalVisible={updateRoleModalVisible} | |||||
values={stepFormValues} | |||||
/> | |||||
) : null} | |||||
{stepFormValues && Object.keys(stepFormValues).length ? ( | |||||
<OrgForm | |||||
treeDatas={treeData} | |||||
onSubmit={async (value) => { | |||||
if (value == null || value == undefined) { | |||||
handleUpdateOrgModalVisible(false); | |||||
setStepFormValues({}); | |||||
return true; | |||||
} | |||||
const values = { | |||||
id: stepFormValues.id, | |||||
grantRoleIdList: value, | |||||
}; | |||||
const success = await handleGrantData(values); | |||||
if (success) { | |||||
handleUpdateOrgModalVisible(false); | |||||
setStepFormValues({}); | |||||
if (actionRef.current) { | |||||
actionRef.current.reload(); | |||||
} | |||||
} | |||||
}} | |||||
onCancel={() => { | |||||
handleUpdateOrgModalVisible(false); | |||||
setStepFormValues({}); | |||||
}} | |||||
updateModalVisible={updateOrgModalVisible} | |||||
values={stepFormValues} | |||||
/> | |||||
) : null} | |||||
<Drawer | |||||
width={600} | |||||
visible={!!row} | |||||
onClose={() => { | |||||
setRow(undefined); | |||||
}} | |||||
closable={false} | |||||
> | |||||
{row?.name && ( | |||||
<ProDescriptions | |||||
column={2} | |||||
title={`${row?.name} 的详情`} | |||||
request={async () => ({ | |||||
data: row || {}, | |||||
})} | |||||
params={{ | |||||
id: row?._id, | |||||
}} | |||||
columns={columns} | |||||
/> | |||||
)} | |||||
</Drawer> | |||||
</PageContainer> | |||||
); | |||||
}; | |||||
export default TableList; |
@@ -0,0 +1,55 @@ | |||||
import { request } from 'umi'; | |||||
export async function queryUser(params) { | |||||
return request('/saasbase/api/sysUser/page', { | |||||
method: 'POST', | |||||
data: { | |||||
...params, | |||||
}, | |||||
}); | |||||
} | |||||
export async function removeRule(params) { | |||||
return request('/saasbase/api/rule', { | |||||
method: 'POST', | |||||
data: { | |||||
...params, | |||||
method: 'delete', | |||||
}, | |||||
}); | |||||
} | |||||
export async function addUser(params) { | |||||
return request('/saasbase/api/sysUser/add', { | |||||
method: 'POST', | |||||
data: { | |||||
...params, | |||||
}, | |||||
}); | |||||
} | |||||
export async function updateUser(params) { | |||||
return request('/saasbase/api/sysUser/add', { | |||||
method: 'POST', | |||||
data: { | |||||
...params, | |||||
}, | |||||
}); | |||||
} | |||||
export async function setRoles(params) { | |||||
return request(`/saasbase/api/sysUser/grantRole`, { | |||||
method: 'POST', | |||||
data: { | |||||
...params, | |||||
}, | |||||
}); | |||||
} | |||||
export async function sysUserGrantData(params) { | |||||
return request(`/saasbase/api/sysUser/grantData`, { | |||||
method: 'POST', | |||||
data: { | |||||
...params, | |||||
}, | |||||
}); | |||||
} |
@@ -0,0 +1,55 @@ | |||||
import React, { useState, useEffect } from 'react'; | |||||
import { Modal, Form, Input, Button, Select, InputNumber } from 'antd'; | |||||
const CreateForm = (props) => { | |||||
const { Option, OptGroup } = Select; | |||||
return ( | |||||
<Modal | |||||
title={props.values.id ? '编辑' : '新建'} | |||||
width={640} | |||||
visible={props.createModalVisible} | |||||
bodyStyle={{ padding: '32px 40px 48px' }} | |||||
footer={null} | |||||
onCancel={() => { | |||||
props.onCancel(); | |||||
}} | |||||
destroyOnClose | |||||
> | |||||
<Form | |||||
layout="vertical" | |||||
preserve={false} | |||||
initialValues={props.values} | |||||
onFinish={props.onFinish} | |||||
> | |||||
<Form.Item name="id" hidden={true}> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item | |||||
name="itemId" | |||||
label="原料名称" | |||||
rules={[{ required: true, message: '请选择原料名称' }]} | |||||
> | |||||
<Select disabled={props.values.id ? true : false} placeholder="请选择原料名称"> | |||||
{props.stockGoodsData.map((item, index) => { | |||||
return ( | |||||
<Select.Option index={index} value={item.id} key={item.id}> | |||||
{item.name} | |||||
</Select.Option> | |||||
); | |||||
})} | |||||
</Select> | |||||
</Form.Item> | |||||
<Form.Item name="price" label="价格" defaultValue={props.values.price}> | |||||
<InputNumber placeholder="价格" min={0} /> | |||||
</Form.Item> | |||||
<Form.Item> | |||||
<Button type="primary" htmlType="submit"> | |||||
保存 | |||||
</Button> | |||||
</Form.Item> | |||||
</Form> | |||||
</Modal> | |||||
); | |||||
}; | |||||
export default CreateForm; |
@@ -0,0 +1,291 @@ | |||||
import { PlusOutlined } from '@ant-design/icons'; | |||||
import { Button, message, Input, Modal } from 'antd'; | |||||
import React, { useState, useRef, useEffect } from 'react'; | |||||
import ProTable from '@ant-design/pro-table'; | |||||
import CreateForm from './components/CreateForm'; | |||||
import api from './services'; | |||||
const BomReplace = (props) => { | |||||
/** 新建/更新窗口的弹窗 */ | |||||
const [createModalVisible, handleModalVisible] = useState(false); | |||||
const actionRef = useRef(); | |||||
const [currentRow, setCurrentRow] = useState(); | |||||
const [selectedRowsState, setSelectedRows] = useState([]); | |||||
// //获取商品数据 | |||||
const [goods, setgoods] = useState([]); | |||||
//获取原料名称 | |||||
const [stockGoods, setstockGoods] = useState([]); | |||||
// 监控数据变化 | |||||
useEffect(() => { | |||||
/** 获取原料名称*/ | |||||
function initStockGoods() { | |||||
api.postStockGoods().then((r) => { | |||||
var arr = r.data; | |||||
arr.forEach((element) => { | |||||
element.text = element.name; | |||||
element.stockGoodsId = element.id; | |||||
}); | |||||
setstockGoods(arr); | |||||
}); | |||||
} | |||||
initStockGoods(); //回调原料信息 | |||||
}, []); | |||||
/** | |||||
* 添加节点 | |||||
* | |||||
* @param fields | |||||
*/ | |||||
function handleAdd(fields) { | |||||
try { | |||||
api | |||||
.addGoodsBom({ | |||||
itemId: fields.itemId, | |||||
price: fields.price, | |||||
goodsbomId: props.values.id, | |||||
replaceId: props.values.batchingId, | |||||
}) | |||||
.then((r) => { | |||||
if (r.data) { | |||||
message.success('添加成功'); | |||||
//刷新数据 | |||||
actionRef.current.reload(); | |||||
} else { | |||||
message.error('添加失败请重试!'); | |||||
} | |||||
}); | |||||
return true; | |||||
} catch (error) { | |||||
message.error('添加失败请重试!'); | |||||
return false; | |||||
} | |||||
} | |||||
/** | |||||
* 更新节点 | |||||
* | |||||
* @param fields | |||||
*/ | |||||
function handleUpdate(fields) { | |||||
console.log('传参数据', fields); | |||||
try { | |||||
api | |||||
.updateGoodsBom({ | |||||
id: fields.id, | |||||
status: fields.status, | |||||
itemId: fields.itemId, | |||||
price: fields.price, | |||||
goodsbomId: props.values.id, | |||||
replaceId: props.values.stockGoodsCode, | |||||
}) | |||||
.then((r) => { | |||||
if (r.data) { | |||||
message.success('配置成功'); | |||||
} else { | |||||
message.error('配置失败请重试!'); | |||||
} | |||||
//刷新数据 | |||||
actionRef.current.reload(); | |||||
}); | |||||
return true; | |||||
} catch (error) { | |||||
message.error('配置失败请重试!'); | |||||
return false; | |||||
} | |||||
} | |||||
/** | |||||
* 删除节点 | |||||
* | |||||
* | |||||
*/ | |||||
function handleRemove() { | |||||
if (!selectedRowsState) return true; | |||||
try { | |||||
api.removeGoodsBom(selectedRowsState.map((row) => row.id)).then((r) => { | |||||
if (r.data) { | |||||
message.success('删除成功,即将刷新'); | |||||
} else { | |||||
message.error('删除失败,请重试'); | |||||
} | |||||
//刷新数据 | |||||
actionRef.current.reload(); | |||||
}); | |||||
return true; | |||||
} catch (error) { | |||||
message.error('删除失败,请重试'); | |||||
return false; | |||||
} | |||||
} | |||||
/** 国际化配置 */ | |||||
const columns = [ | |||||
{ | |||||
title: '主键', | |||||
dataIndex: 'id', | |||||
hideInTable: true, | |||||
hideInSearch: true, | |||||
tip: '规则名称是唯一的 key', | |||||
render: (dom, entity) => { | |||||
return ( | |||||
<a | |||||
onClick={() => { | |||||
setCurrentRow(entity); | |||||
setShowDetail(true); | |||||
}} | |||||
> | |||||
{dom} | |||||
</a> | |||||
); | |||||
}, | |||||
}, | |||||
{ | |||||
title: '原料名称', | |||||
dataIndex: 'name', | |||||
hideInForm: true, | |||||
hideInSearch: true, | |||||
}, | |||||
{ | |||||
title: '商品价格', | |||||
dataIndex: 'price', | |||||
valueType: 'money', | |||||
}, | |||||
{ | |||||
title: '状态', | |||||
dataIndex: 'status', | |||||
hideInForm: true, | |||||
valueEnum: { | |||||
0: { | |||||
text: '正常', | |||||
status: 'Processing', | |||||
}, | |||||
1: { | |||||
text: '停用', | |||||
status: 'Error', | |||||
}, | |||||
2: { | |||||
text: '删除', | |||||
status: 'Error', | |||||
}, | |||||
}, | |||||
}, | |||||
{ | |||||
title: '操作', | |||||
dataIndex: 'option', | |||||
valueType: 'option', | |||||
render: (_, record) => [ | |||||
record.status === 0 && ( | |||||
<a | |||||
key="link" | |||||
onClick={() => { | |||||
record.status = '1'; | |||||
handleUpdate(record); | |||||
}} | |||||
> | |||||
停用 | |||||
</a> | |||||
), | |||||
(record.status === 1 || record.status === 2) && ( | |||||
<a | |||||
key="warn" | |||||
onClick={() => { | |||||
record.status = '0'; | |||||
handleUpdate(record); | |||||
}} | |||||
> | |||||
启用 | |||||
</a> | |||||
), | |||||
<a | |||||
key="primary" | |||||
type="primary" | |||||
onClick={() => { | |||||
setCurrentRow(record); | |||||
handleModalVisible(true); | |||||
}} | |||||
> | |||||
更新 | |||||
</a>, | |||||
], | |||||
}, | |||||
]; | |||||
return ( | |||||
<Modal | |||||
width={1200} | |||||
bodyStyle={{ padding: '32px 40px 48px' }} | |||||
destroyOnClose | |||||
title="配方物料替换" | |||||
visible={props.createModalVisible} | |||||
footer={false} | |||||
onCancel={() => props.onCancel()} | |||||
maskClosable={false} | |||||
> | |||||
<ProTable | |||||
headerTitle="替换物料列表" | |||||
actionRef={actionRef} | |||||
rowKey="id" | |||||
pagination={{ defaultPageSize: 20 }} | |||||
search={false} | |||||
toolBarRender={() => [ | |||||
<Button type="primary" key="create" onClick={() => handleModalVisible(true)}> | |||||
<PlusOutlined /> 新增配方替换物料 | |||||
</Button>, | |||||
]} | |||||
//数据绑定 | |||||
request={async (params) => { | |||||
var goodsBomsData = []; | |||||
var total = 0; | |||||
//商品编号 | |||||
if (props.values.id != undefined) { | |||||
params['goodsbomId'] = props.values.id; | |||||
params['replaceId'] = props.values.stockGoodsCode; | |||||
} | |||||
await api.goodsBoms(params).then((r) => { | |||||
goodsBomsData = r.data.data; | |||||
total = r.data.total; | |||||
}); | |||||
return { | |||||
data: goodsBomsData, | |||||
success: true, | |||||
total: total, | |||||
}; | |||||
}} | |||||
columns={columns} | |||||
/> | |||||
{/*商品配方(新增,修改) */} | |||||
<CreateForm | |||||
stockGoodsData={stockGoods} | |||||
onFinish={async (value) => { | |||||
var success = false; | |||||
if (value.id) { | |||||
success = await handleUpdate(value); | |||||
} else { | |||||
success = await handleAdd(value); | |||||
} | |||||
if (success) { | |||||
handleModalVisible(false); | |||||
setCurrentRow(undefined); | |||||
if (actionRef.current) { | |||||
actionRef.current.reload(); | |||||
} | |||||
} | |||||
}} | |||||
onCancel={() => { | |||||
handleModalVisible(false); | |||||
setCurrentRow(undefined); | |||||
}} | |||||
createModalVisible={createModalVisible} | |||||
values={currentRow || {}} | |||||
/> | |||||
</Modal> | |||||
); | |||||
}; | |||||
export default BomReplace; |
@@ -0,0 +1,49 @@ | |||||
// @ts-ignore | |||||
/* eslint-disable */ | |||||
import { request } from 'umi'; | |||||
export default { | |||||
/** 获取商品物料 sdsa GET /kitchen/api/rule */ | |||||
goodsBoms(data) { | |||||
return request(`/saasbase/api/GoodsBom/GetBomReplac`, { | |||||
method: 'Post', | |||||
data: data, | |||||
// params: { ...params }, | |||||
// ...(options || {}), | |||||
}); | |||||
}, | |||||
updateGoodsBom(data) { | |||||
return request('/saasbase/api/GoodsBom/EditReplac', { | |||||
method: 'PUT', | |||||
data: data, | |||||
// ...(options || {}), | |||||
}); | |||||
}, | |||||
addGoodsBom(data) { | |||||
// http://localhost:7002 | |||||
return request('/saasbase/api/GoodsBom/AddReplac', { | |||||
method: 'POST', | |||||
// type:'json', | |||||
data: data, | |||||
// ...(options || {}), | |||||
}); | |||||
}, | |||||
/** 获取商品商品 sdsa GET /kitchen/api/rule */ | |||||
goodList(data) { | |||||
return request(`/saasbase/api/goods/goodss`, { | |||||
method: 'Post', | |||||
data: data, | |||||
// params: { ...params }, | |||||
// ...(options || {}), | |||||
}); | |||||
}, | |||||
/** 获取原料信息 POST /kitchen/api/rule */ | |||||
postStockGoods() { | |||||
return request(`/saasbase/api/frachisee-stock-adjust/product-list`, { | |||||
method: 'Get', | |||||
// ...(options || {}), | |||||
}); | |||||
}, | |||||
}; |
@@ -0,0 +1,61 @@ | |||||
import React, { useState } from 'react'; | |||||
import { Modal, Form, Input, Button, Select } from 'antd'; | |||||
const CreateForm = (props) => { | |||||
const { Option, OptGroup } = Select; | |||||
return ( | |||||
<Modal | |||||
title={props?.values?.id ? '编辑' : '新建'} | |||||
width={640} | |||||
visible={props?.createModalVisible} | |||||
bodyStyle={{ padding: '32px 40px 48px' }} | |||||
footer={null} | |||||
onCancel={() => { | |||||
props?.onCancel(); | |||||
}} | |||||
destroyOnClose | |||||
maskClosable={false} | |||||
> | |||||
<Form | |||||
layout="vertical" | |||||
preserve={false} | |||||
initialValues={props?.values} | |||||
onFinish={props?.onFinish} | |||||
> | |||||
<Form.Item name="id" hidden={true}> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item name="name" label="配方类型名称" rules={[{ required: true, max: 64, whitespace: true }]}> | |||||
<Input placeholder="请输入配方类型名称" /> | |||||
</Form.Item> | |||||
<Form.Item | |||||
name="pertain" | |||||
label="归属" | |||||
defaultValue={props?.values?.pertain} | |||||
rules={[{ required: true, message: '请选择类型归属' }]} > | |||||
<Select placeholder="请选择类型归属"> | |||||
<Option value="1">无</Option> | |||||
<Option value="2">TMC </Option> | |||||
</Select> | |||||
</Form.Item> | |||||
<Form.Item | |||||
name="status" | |||||
label="状态" | |||||
defaultValue={props?.values?.status} | |||||
rules={[{ required: true, message: '请选择状态' }]} | |||||
> | |||||
<Select placeholder="请选择状态"> | |||||
<Option value="0">正常</Option> | |||||
<Option value="1">停用</Option> | |||||
</Select> | |||||
</Form.Item> | |||||
<Form.Item> | |||||
<Button type="primary" htmlType="submit"> | |||||
保存 | |||||
</Button> | |||||
</Form.Item> | |||||
</Form> | |||||
</Modal> | |||||
); | |||||
}; | |||||
export default CreateForm; |
@@ -0,0 +1,314 @@ | |||||
import { DropboxOutlined, PlusOutlined } from '@ant-design/icons'; | |||||
import { Button, message, Input, Drawer, Modal, Popconfirm } from 'antd'; | |||||
import React, { useState, useRef, useEffect } from 'react'; | |||||
import { PageContainer, FooterToolbar } from '@ant-design/pro-layout'; | |||||
import ProTable from '@ant-design/pro-table'; | |||||
import ProDescriptions from '@ant-design/pro-descriptions'; | |||||
import CreateForm from './components/CreateForm'; | |||||
import { getbomtypepage, deletebomtype, updatebomtype, addbomtype } from './services'; | |||||
//页面 相当于 class | |||||
/** | |||||
* 添加节点 | |||||
* | |||||
* @param fields | |||||
*/ | |||||
const handleAdd = async (fields) => { | |||||
try { | |||||
await addbomtype(JSON.stringify(fields)).then((r) => { | |||||
if (r.data) { | |||||
message.success('添加成功'); | |||||
} else { | |||||
message.error('添加失败请重试!'); | |||||
} | |||||
}); | |||||
return true; | |||||
} catch (error) { | |||||
message.error('添加失败请重试!'); | |||||
return false; | |||||
} | |||||
}; | |||||
/** | |||||
* 批量删除节点 | |||||
* | |||||
* @param selectedRows | |||||
*/ | |||||
const handleRemove = async (ids) => { | |||||
try { | |||||
deletebomtype(ids).then((r) => { | |||||
if (r.data) { | |||||
message.success('删除成功,即将刷新'); | |||||
} else { | |||||
message.error('删除失败,请重试'); | |||||
} | |||||
}); | |||||
return true; | |||||
} catch (error) { | |||||
message.error('删除失败,请重试'); | |||||
} | |||||
}; | |||||
/** | |||||
* 更新节点 | |||||
* | |||||
* @param fields | |||||
*/ | |||||
const handleUpdate = async (fields) => { | |||||
try { | |||||
updatebomtype(fields).then((r) => { | |||||
if (r.data) { | |||||
message.success('修改成功'); | |||||
} else { | |||||
message.error('修改失败请重试!'); | |||||
} | |||||
}); | |||||
return true; | |||||
} catch (error) { | |||||
message.error('修改失败请重试!'); | |||||
return false; | |||||
} | |||||
}; | |||||
//页面 相当于 class | |||||
const FranchiessSrdTypeManage = () => { | |||||
/** 新建/更新窗口的弹窗 */ | |||||
const [createModalVisible, handleModalVisible] = useState(false); | |||||
/** 分布更新窗口的弹窗 */ | |||||
const [showDetail, setShowDetail] = useState(false); | |||||
//绑定 | |||||
const actionRef = useRef(); | |||||
const [currentRow, setCurrentRow] = useState(); | |||||
//选中的行 | |||||
const [selectedRowsState, setSelectedRows] = useState([]); | |||||
/** 国际化配置 */ | |||||
const columns = [ | |||||
{ | |||||
title: '主键', | |||||
dataIndex: 'id', | |||||
tip: '规则名称是唯一的 key', | |||||
hideInSearch: true, | |||||
hideInTable: true, | |||||
render: (dom, entity) => { | |||||
return ( | |||||
<a | |||||
onClick={() => { | |||||
setCurrentRow(entity); | |||||
setShowDetail(true); | |||||
}} | |||||
> | |||||
{dom} | |||||
</a> | |||||
); | |||||
}, | |||||
}, | |||||
{ | |||||
title: '名称', | |||||
dataIndex: 'name', | |||||
valueType: 'textarea', | |||||
ellipsis: true, | |||||
search: true, | |||||
}, | |||||
{ | |||||
title: '归属', | |||||
dataIndex: 'pertain', | |||||
search: true, | |||||
valueEnum: { | |||||
1: { | |||||
text: '无', | |||||
status: 'Processing', | |||||
}, | |||||
2: { | |||||
text: 'TMC', | |||||
status: 'Success', | |||||
}, | |||||
}, | |||||
}, | |||||
{ | |||||
title: '状态', | |||||
dataIndex: 'status', | |||||
hideInForm: true, | |||||
search: false, | |||||
valueEnum: { | |||||
0: { | |||||
text: '正常', | |||||
status: 'Processing', | |||||
}, | |||||
1: { | |||||
text: '停用', | |||||
status: 'Success', | |||||
}, | |||||
}, | |||||
}, | |||||
{ | |||||
title: '创建时间', | |||||
dataIndex: 'createAt', | |||||
valueType: 'date', | |||||
hideInSearch: true, | |||||
}, | |||||
{ | |||||
title: '操作', | |||||
dataIndex: 'option', | |||||
valueType: 'option', | |||||
render: (_, record) => [ | |||||
<a | |||||
key="state" | |||||
onClick={async () => { | |||||
let a="0" | |||||
if(record.status=="0"){ | |||||
a="1" | |||||
} | |||||
let updateMassge = { id: record.id, name: record.name, pertain: record.pertain, status: a } | |||||
handleUpdate(updateMassge).then((r)=>{ | |||||
if(r){ | |||||
if (actionRef.current) { | |||||
actionRef.current.reload(); | |||||
} | |||||
} | |||||
}) | |||||
} | |||||
} | |||||
> | |||||
{record.status == 0 ? '停用' : '使用'} | |||||
</a>, | |||||
<a | |||||
key="config" | |||||
onClick={() => { | |||||
handleModalVisible(true); | |||||
setCurrentRow(record); | |||||
actionRef.current?.reloadAndRest?.(); | |||||
}} | |||||
> | |||||
更新 | |||||
</a>, | |||||
<Popconfirm | |||||
type="primary" | |||||
key="primary" | |||||
title="确认删除吗?" | |||||
okText="是" | |||||
cancelText="否" | |||||
onConfirm={() => { | |||||
handleRemove([record.id]); | |||||
actionRef.current?.reloadAndRest(); | |||||
}} | |||||
onCancel={() => { }} | |||||
> | |||||
<a href="#">删除</a> | |||||
</Popconfirm>, | |||||
], | |||||
}, | |||||
]; | |||||
return ( | |||||
<PageContainer> | |||||
<ProTable | |||||
headerTitle="物料分类" | |||||
actionRef={actionRef} | |||||
rowKey="id" | |||||
search={{ | |||||
labelWidth: 120, | |||||
}} | |||||
columns={columns} | |||||
toolBarRender={() => [ | |||||
<Button | |||||
type="primary" | |||||
key="primary" | |||||
onClick={() => { | |||||
handleModalVisible(true); | |||||
setCurrentRow({}); | |||||
}} | |||||
> | |||||
<PlusOutlined /> 新建 | |||||
</Button>, | |||||
]} | |||||
//数据绑定 | |||||
request={async (params) => { | |||||
console.log('fsa', 'gfdsgfd'); | |||||
var data = []; | |||||
var total = 0; | |||||
await getbomtypepage(params).then((r) => { | |||||
data = r.data.data; | |||||
data.forEach((x) => { | |||||
x.pertain = x.pertain.toString(); | |||||
x.status = x.status.toString(); | |||||
}); | |||||
total = r.data.total; | |||||
}); | |||||
return { | |||||
data: data, | |||||
success: true, | |||||
total: total, | |||||
}; | |||||
}} | |||||
// 每行选择点击事件 | |||||
rowSelection={{ | |||||
onChange: (_, selectedRows) => { | |||||
setSelectedRows(selectedRows); | |||||
}, | |||||
}} | |||||
></ProTable> | |||||
{/* {selectedRowsState?.length > 0 && ( | |||||
<FooterToolbar | |||||
extra={ | |||||
<div> | |||||
已选择{' '} | |||||
<a | |||||
style={{ | |||||
fontWeight: 600, | |||||
}} | |||||
> | |||||
{selectedRowsState.length} | |||||
</a>{' '} | |||||
项 | |||||
</div> | |||||
} | |||||
> | |||||
<Button | |||||
onClick={() => { | |||||
handleRemove(selectedRowsState); | |||||
setSelectedRows([]); | |||||
actionRef.current?.reloadAndRest?.(); | |||||
}} | |||||
type="primary" | |||||
key="primary" | |||||
> | |||||
批量删除 | |||||
</Button> | |||||
</FooterToolbar> | |||||
)} */} | |||||
{/* 加盟商管理(新增,修改) */} | |||||
<CreateForm | |||||
onFinish={async (value) => { | |||||
var success = false; | |||||
if (value.id) { | |||||
console.log(value) | |||||
success = await handleUpdate(value); | |||||
} else { | |||||
success = await handleAdd(value); | |||||
} | |||||
if (success) { | |||||
handleModalVisible(false); | |||||
actionRef.current.reload(); | |||||
// if (actionRef.current) { | |||||
// actionRef.current.reload(); | |||||
// } | |||||
} | |||||
}} | |||||
onCancel={() => { | |||||
setCurrentRow(undefined); | |||||
handleModalVisible(false); | |||||
}} | |||||
createModalVisible={createModalVisible} | |||||
values={currentRow || {}} | |||||
/> | |||||
</PageContainer> | |||||
); | |||||
}; | |||||
export default FranchiessSrdTypeManage; |
@@ -0,0 +1,37 @@ | |||||
// @ts-ignore | |||||
/* eslint-disable */ | |||||
import { request } from 'umi'; | |||||
//获取菜谱分类 | |||||
export async function getbomtypepage(data) { | |||||
return request(`/saasbase/api/bom/getbomtypepage`, { | |||||
method: 'Post', | |||||
data: data, | |||||
}); | |||||
} | |||||
//删除菜谱分类 | |||||
export async function deletebomtype(data) { | |||||
return request(`/saasbase/api/bom/deletebomtype`, { | |||||
method: 'Post', | |||||
data: data, | |||||
}); | |||||
} | |||||
//修改菜谱分类 | |||||
export async function updatebomtype(data) { | |||||
return request(`/saasbase/api/bom/updatebomtype`, { | |||||
method: 'Post', | |||||
data: data, | |||||
}); | |||||
} | |||||
//添加菜谱分类 | |||||
export async function addbomtype(data) { | |||||
return request(`/saasbase/api/bom/addbomtype`, { | |||||
method: 'Post', | |||||
data: data, | |||||
}); | |||||
} |
@@ -0,0 +1,54 @@ | |||||
import React, { useState, useEffect } from 'react'; | |||||
import { Modal, Form, Input, Button, Select, Switch, InputNumber } from 'antd'; | |||||
const AddBomInfo = (props) => { | |||||
const { Option, OptGroup } = Select; | |||||
return ( | |||||
<Modal | |||||
title={'添加配方原料'} | |||||
width={640} | |||||
visible={props.createModalVisible} | |||||
bodyStyle={{ padding: '32px 40px 48px' }} | |||||
footer={null} | |||||
onCancel={() => { | |||||
props.onCancel(); | |||||
}} | |||||
destroyOnClose | |||||
> | |||||
<Form | |||||
layout="vertical" | |||||
preserve={false} | |||||
initialValues={props.values} | |||||
onFinish={props.onFinish} | |||||
> | |||||
<Form.Item | |||||
name="BatchingId" | |||||
label="原料名称" | |||||
rules={[{ required: true, message: '请选择原料名称' }]} | |||||
> | |||||
<Select placeholder="请选择原料名称" | |||||
showSearch | |||||
optionFilterProp="children" | |||||
filterOption={(input, option) => | |||||
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |||||
}> | |||||
{props.stockGoodsData.map((item, index) => { | |||||
return ( | |||||
<Option index={index} value={item.id} key={item.id}> | |||||
{item.name} | |||||
</Option> | |||||
); | |||||
})} | |||||
</Select> | |||||
</Form.Item> | |||||
<Form.Item> | |||||
<Button type="primary" htmlType="submit"> | |||||
保存 | |||||
</Button> | |||||
</Form.Item> | |||||
</Form> | |||||
</Modal> | |||||
); | |||||
}; | |||||
export default AddBomInfo; |
@@ -0,0 +1,188 @@ | |||||
import { PlusOutlined } from '@ant-design/icons'; | |||||
import { Button, message, Input, Drawer, Modal } from 'antd'; | |||||
import React, { useState, useRef, useEffect } from 'react'; | |||||
import { PageContainer, FooterToolbar } from '@ant-design/pro-layout'; | |||||
import ProTable from '@ant-design/pro-table'; | |||||
import ProDescriptions from '@ant-design/pro-descriptions'; | |||||
import api from '../services'; | |||||
import AddBomInfo from './AddBomInfo'; | |||||
const BomList = (props) => { | |||||
/** 新建/更新窗口的弹窗 */ | |||||
const actionRef = useRef(); | |||||
const [AddBomModalVisible, handleAddBomModalVisible] = useState(false); | |||||
const [currentRow, setCurrentRow] = useState(); | |||||
const [stockGoods, setstockGoods] = useState([]); | |||||
useEffect(() => { | |||||
/** 获取原料名称*/ | |||||
function initStockGoods() { | |||||
api.getbygoods({IsMain: props.IsMain,goodsId: props.values.goodsId}).then((r) => { | |||||
var arr = r.data; | |||||
arr.forEach((element) => { | |||||
element.text = element.name; | |||||
element.stockGoodsId = element.value; | |||||
}); | |||||
setstockGoods(arr); | |||||
}); | |||||
} | |||||
initStockGoods(); //回调原料信息 | |||||
}, []); | |||||
/** | |||||
* 更新节点 | |||||
* | |||||
* @param fields | |||||
*/ | |||||
function handleUpdate(fields) { | |||||
try { | |||||
api | |||||
.DeleteEntry(fields.id) | |||||
.then((r) => { | |||||
if (r.data) { | |||||
message.success('配置成功'); | |||||
} else { | |||||
message.error('配置失败请重试!'); | |||||
} | |||||
//刷新数据 | |||||
actionRef.current.reload(); | |||||
}); | |||||
return true; | |||||
} catch (error) { | |||||
message.error('配置失败请重试!'); | |||||
return false; | |||||
} | |||||
} | |||||
/** | |||||
* 添加节点 | |||||
* | |||||
* @param fields | |||||
*/ | |||||
function handleAddEntry(fields) { | |||||
try { | |||||
api.AddUpdateEntry(JSON.stringify(fields)).then((r) => { | |||||
if (r.data) { | |||||
message.success('添加成功'); | |||||
//刷新数据 | |||||
actionRef.current.reload(); | |||||
} else { | |||||
message.error('添加失败请重试!'); | |||||
} | |||||
}); | |||||
return true; | |||||
} catch (error) { | |||||
message.error('添加失败请重试!'); | |||||
return false; | |||||
} | |||||
} | |||||
/** 国际化配置 */ | |||||
const columns = [ | |||||
{ | |||||
title: '主键', | |||||
dataIndex: 'id', | |||||
hideInTable: true, | |||||
hideInSearch: true, | |||||
tip: '规则名称是唯一的 key', | |||||
render: (dom, entity) => { | |||||
return ( | |||||
<a | |||||
onClick={() => { | |||||
setCurrentRow(entity); | |||||
setShowDetail(true); | |||||
}} | |||||
> | |||||
{dom} | |||||
</a> | |||||
); | |||||
}, | |||||
}, | |||||
{ | |||||
title: '原料名称', | |||||
dataIndex: 'batchingName', | |||||
hideInForm: true, | |||||
hideInSearch: true, | |||||
}, | |||||
{ | |||||
title: '操作', | |||||
dataIndex: 'option', | |||||
valueType: 'option', | |||||
render: (_, record) => [ | |||||
<a | |||||
key="link4" | |||||
onClick={() => { | |||||
handleUpdate(record); | |||||
}} | |||||
> | |||||
删除 | |||||
</a> | |||||
], | |||||
}, | |||||
]; | |||||
return ( | |||||
<Modal | |||||
title={props.IsMain?'主料配置':'辅料配置'} | |||||
width={640} | |||||
visible={props.createModalVisible} | |||||
bodyStyle={{ padding: '32px 40px 48px' }} | |||||
footer={null} | |||||
onCancel={() => { | |||||
props.onCancel(); | |||||
}} | |||||
destroyOnClose | |||||
> | |||||
<ProTable | |||||
headerTitle="" | |||||
actionRef={actionRef} | |||||
rowKey="id" | |||||
pagination={{ defaultPageSize: 20 }} | |||||
search={false} | |||||
toolBarRender={() => [ | |||||
<Button type="primary" key="create" onClick={() => handleAddBomModalVisible(true)}> | |||||
<PlusOutlined /> 添加原料 | |||||
</Button>, | |||||
]} | |||||
//数据绑定 | |||||
request={async (params) => { | |||||
var goodsBomsData = []; | |||||
var total = 0; | |||||
if (props.values.id != undefined) { | |||||
params['IsMain'] = props.IsMain; | |||||
params['StirFryTimeId'] = props.values.id; | |||||
} | |||||
await api.GetEntryPage(params).then((r) => { | |||||
goodsBomsData = r.data.data; | |||||
total = r.data.total; | |||||
}); | |||||
return { | |||||
data: goodsBomsData, | |||||
success: true, | |||||
total: total, | |||||
}; | |||||
}} | |||||
columns={columns} | |||||
/> | |||||
{/*商品配方(新增,修改) */} | |||||
<AddBomInfo | |||||
onFinish={async (value) => { | |||||
value['StirFryTimeId'] = props.values.id; | |||||
value['IsMain'] = props.IsMain; | |||||
var success = false; | |||||
success = await handleAddEntry(value); | |||||
if (success) { | |||||
handleAddBomModalVisible(false); | |||||
} | |||||
actionRef.current.reload(); | |||||
}} | |||||
onCancel={() => { | |||||
handleAddBomModalVisible(false); | |||||
}} | |||||
createModalVisible={AddBomModalVisible} | |||||
stockGoodsData={stockGoods} | |||||
/> | |||||
</Modal> | |||||
); | |||||
}; | |||||
export default BomList; |
@@ -0,0 +1,87 @@ | |||||
import React, { useState, useEffect } from 'react'; | |||||
import { Modal, Form, Input, Button, Select, InputNumber } from 'antd'; | |||||
import api from '../services'; | |||||
const CreateForm = (props) => { | |||||
const { Option, OptGroup } = Select; | |||||
const [options, setoptions] = useState(); | |||||
//每次触发 | |||||
useEffect(() => { | |||||
const initoptions = async (value) => { | |||||
var data = await api.getStirFryArray("stirfrypotaction"); | |||||
if (data != undefined) { | |||||
setoptions(data.data); | |||||
} | |||||
}; | |||||
initoptions(); | |||||
}, []); | |||||
return ( | |||||
<Modal | |||||
title={props.values.id ? '编辑' : '新建'} | |||||
width={640} | |||||
visible={props.createModalVisible} | |||||
bodyStyle={{ padding: '32px 40px 48px' }} | |||||
footer={null} | |||||
onCancel={() => { | |||||
props.onCancel(); | |||||
}} | |||||
destroyOnClose | |||||
> | |||||
<Form | |||||
layout="horizontal" | |||||
preserve={false} | |||||
initialValues={props.values} | |||||
onFinish={props.onFinish} | |||||
> | |||||
<Form.Item name="id" hidden={true}> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item name="status" hidden={true}> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item name="fryTime" label="步骤名称" rules={[{ required: true, max: 64 }]}> | |||||
<Input /> | |||||
</Form.Item> | |||||
<Form.Item name="actions" label="炒锅动作" rules={[{ required: true, max: 64 }]}> | |||||
<Select style={{ width: 200 }} | |||||
showSearch | |||||
optionFilterProp="children" | |||||
filterOption={(input, option) => | |||||
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |||||
} > | |||||
{options?.map((item, index) => { | |||||
return ( | |||||
<Option | |||||
index={item} | |||||
value={item} | |||||
key={index} | |||||
> | |||||
{item} | |||||
</Option> | |||||
); | |||||
})} | |||||
</Select> | |||||
</Form.Item> | |||||
<Form.Item name="during" label="持续时间" rules={[{ required: true }]}> | |||||
<InputNumber min={0} /> | |||||
</Form.Item> | |||||
<span style={{ color: '#FFCC52' }}> | |||||
注: 持续时间(s),若为0,则等待该流程走完结束 | |||||
</span> | |||||
<Form.Item name="sort" label="步骤排序" rules={[{ required: true }]}> | |||||
<InputNumber min={0} /> | |||||
</Form.Item> | |||||
<span style={{ color: '#FFCC52' }}> | |||||
注: 步骤排序不能重复 | |||||
</span> | |||||
<Form.Item> | |||||
<Button type="primary" htmlType="submit"> | |||||
保存 | |||||
</Button> | |||||
</Form.Item> | |||||
</Form> | |||||
</Modal> | |||||
); | |||||
}; | |||||
export default CreateForm; |