@@ -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; |