@@ -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,63 @@ | |||
############################################################################### | |||
# Set default behavior to automatically normalize line endings. | |||
############################################################################### | |||
* text=auto | |||
############################################################################### | |||
# Set default behavior for command prompt diff. | |||
# | |||
# This is need for earlier builds of msysgit that does not have it on by | |||
# default for csharp files. | |||
# Note: This is only used by command line | |||
############################################################################### | |||
#*.cs diff=csharp | |||
############################################################################### | |||
# Set the merge driver for project and solution files | |||
# | |||
# Merging from the command prompt will add diff markers to the files if there | |||
# are conflicts (Merging from VS is not affected by the settings below, in VS | |||
# the diff markers are never inserted). Diff markers may cause the following | |||
# file extensions to fail to load in VS. An alternative would be to treat | |||
# these files as binary and thus will always conflict and require user | |||
# intervention with every merge. To do so, just uncomment the entries below | |||
############################################################################### | |||
#*.sln merge=binary | |||
#*.csproj merge=binary | |||
#*.vbproj merge=binary | |||
#*.vcxproj merge=binary | |||
#*.vcproj merge=binary | |||
#*.dbproj merge=binary | |||
#*.fsproj merge=binary | |||
#*.lsproj merge=binary | |||
#*.wixproj merge=binary | |||
#*.modelproj merge=binary | |||
#*.sqlproj merge=binary | |||
#*.wwaproj merge=binary | |||
############################################################################### | |||
# behavior for image files | |||
# | |||
# image files are treated as binary by default. | |||
############################################################################### | |||
#*.jpg binary | |||
#*.png binary | |||
#*.gif binary | |||
############################################################################### | |||
# diff behavior for common document formats | |||
# | |||
# Convert binary document formats to text before diffing them. This feature | |||
# is only available from the command line. Turn it on by uncommenting the | |||
# entries below. | |||
############################################################################### | |||
#*.doc diff=astextplain | |||
#*.DOC diff=astextplain | |||
#*.docx diff=astextplain | |||
#*.DOCX diff=astextplain | |||
#*.dot diff=astextplain | |||
#*.DOT diff=astextplain | |||
#*.pdf diff=astextplain | |||
#*.PDF diff=astextplain | |||
#*.rtf diff=astextplain | |||
#*.RTF diff=astextplain |
@@ -0,0 +1,41 @@ | |||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | |||
# dependencies | |||
**/node_modules | |||
# roadhog-api-doc ignore | |||
/src/utils/request-temp.js | |||
_roadhog-api-doc | |||
# production | |||
/dist | |||
/.vscode | |||
# misc | |||
.DS_Store | |||
npm-debug.log* | |||
yarn-error.log | |||
/coverage | |||
.idea | |||
yarn.lock | |||
package-lock.json | |||
*bak | |||
.vscode | |||
.vs | |||
.git | |||
# visual studio code | |||
.history | |||
*.log | |||
functions/* | |||
.temp/** | |||
# umi | |||
.umi | |||
.umi-production | |||
# screenshot | |||
screenshot | |||
.firebase | |||
.eslintcache | |||
build |
@@ -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,139 @@ | |||
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: 'branchName', | |||
choices: ['master'] | |||
) | |||
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") { | |||
} | |||
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,57 @@ | |||
# Ant Design Pro | |||
This project is initialized with [Ant Design Pro](https://pro.ant.design). Follow is the quick guide for how to use. | |||
## Environment Prepare | |||
Install `node_modules`: | |||
```bash | |||
npm install | |||
``` | |||
or | |||
```bash | |||
yarn | |||
``` | |||
## Provided Scripts | |||
Ant Design Pro provides some useful script to help you quick start and build with web project, code style check and test. | |||
Scripts provided in `package.json`. It's safe to modify or add additional script: | |||
### Start project | |||
```bash | |||
npm start | |||
``` | |||
### Build project | |||
```bash | |||
npm run build | |||
``` | |||
### Check code style | |||
```bash | |||
npm run lint | |||
``` | |||
You can also use script to auto fix some lint error: | |||
```bash | |||
npm run lint:fix | |||
``` | |||
### Test code | |||
```bash | |||
npm test | |||
``` | |||
## More | |||
You can view full document on our [official website](https://pro.ant.design). And welcome any feedback in our [github](https://github.com/ant-design/ant-design-pro). |
@@ -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: { | |||
'/kitchen/api/': { | |||
target: 'http://10.2.1.26:21527', | |||
changeOrigin: true, | |||
secure: false, //关闭证书验证 | |||
pathRewrite: { | |||
'^': '', | |||
}, | |||
}, | |||
// Nginx发布的时候需要配置 | |||
'/cos/':{ | |||
target: 'https://hbl-test-1305371387.cos.ap-chengdu.myqcloud.com', | |||
changeOrigin: true, | |||
secure: false, //关闭证书验证 | |||
pathRewrite: { | |||
'/cos/': '/', | |||
}, | |||
} | |||
}, | |||
test: { | |||
'/api/': { | |||
target: 'http://localhost:7002', | |||
changeOrigin: true, | |||
secure: false, | |||
pathRewrite: { | |||
'^': '', | |||
}, | |||
}, | |||
}, | |||
pre: { | |||
'/api/': { | |||
target: 'http://localhost:7002', | |||
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,614 @@ | |||
/** | |||
* 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/company', | |||
component: './sys/company', | |||
access: 'k2', | |||
}, | |||
{ | |||
name: '组织管理', | |||
icon: 'smile', | |||
path: '/sys/Org', | |||
component: './sys/Org', | |||
access: 'k2', | |||
}, | |||
{ | |||
name: '操作日志', | |||
icon: 'smile', | |||
path: '/sys/Log', | |||
component: './sys/Log', | |||
access: 'k3', | |||
}, | |||
{ | |||
name: '系统用户', | |||
icon: 'smile', | |||
path: '/sys/user', | |||
component: './sys/user', | |||
access: 'k4', | |||
}, | |||
{ | |||
name: '角色权限', | |||
icon: 'smile', | |||
path: '/sys/Roles', | |||
component: './sys/Roles', | |||
access: 'k5', | |||
}, | |||
{ | |||
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/sysSettings', | |||
component: './sys/sysSettings', | |||
access: 'k6', | |||
}, | |||
{ | |||
name: '设备管理', | |||
icon: 'smile', | |||
path: '/sys/storemanage', | |||
component: './sys/storemanage', | |||
access: 'k6', | |||
}, | |||
], | |||
}, | |||
{ | |||
name: '物料管理', | |||
icon: 'DropboxSquareFilled', | |||
path: '/erp', | |||
routes: [ | |||
{ | |||
name: '物料信息', | |||
icon: 'smile', | |||
path: '/erp/basic/product', | |||
component: './erp/basic/product', | |||
access: 'k7', | |||
}, | |||
{ | |||
name: '物料单位', | |||
icon: 'smile', | |||
path: '/erp/basic/unit', | |||
component: './erp/basic/unit', | |||
access: 'k8', | |||
}, | |||
{ | |||
name: '物料分类', | |||
icon: 'smile', | |||
path: '/erp/basic/productType', | |||
component: './erp/basic/productType', | |||
access: 'k9', | |||
}, | |||
{ | |||
name: '验收入库', | |||
icon: 'smile', | |||
path: '/erp/Bill/ReceivingNote', | |||
component: './erp/Bill/ReceivingNote', | |||
access: 'k15', | |||
}, | |||
{ | |||
name: '采购管理', | |||
icon: 'smile', | |||
path: '/erp/Bill/purchaseOrder', | |||
component: './erp/Bill/purchaseOrder', | |||
}, | |||
{ | |||
name: '物料库存', | |||
icon: 'smile', | |||
path: '/erp/stock', | |||
component: './erp/stock', | |||
access: 'k16', | |||
}, | |||
], | |||
}, | |||
{ | |||
name: '物料管理', | |||
icon: 'DropboxSquareFilled', | |||
path: '/bom', | |||
routes: [ | |||
{ | |||
name: '配方管理', | |||
icon: 'smile', | |||
path: '/bom/manage', | |||
component: './bom/manage', | |||
access: 'k7', | |||
}, | |||
{ | |||
name: '配方下发', | |||
icon: 'smile', | |||
path: '/bom/push', | |||
component: './bom/push', | |||
access: 'k8', | |||
}, | |||
], | |||
}, | |||
{ | |||
name: '商品管理', | |||
icon: 'ProjectFilled', | |||
path: '/goods', | |||
routes: [ | |||
{ | |||
name: '商品管理', | |||
icon: 'smile', | |||
path: '/goods/goods-manage', | |||
component: './goods/goods-manage', | |||
access: 'k10', | |||
}, | |||
{ | |||
name: '商品管理新', | |||
icon: 'smile', | |||
path: '/goods/newgoods', | |||
component: './goods/newgoods', | |||
access: 'k10', | |||
}, | |||
{ | |||
name: '商品口味', | |||
icon: 'smile', | |||
path: '/goods/goods-taste', | |||
component: './goods/goods-taste', | |||
access: 'k11', | |||
}, | |||
{ | |||
name: '商品小类', | |||
icon: 'smile', | |||
path: '/goods/goods-type-manage', | |||
component: './goods/goods-type-manage', | |||
access: 'k11', | |||
}, | |||
{ | |||
name: '商品大类', | |||
icon: 'smile', | |||
path: '/goods/goodsParentTypemanage', | |||
component: './goods/goodsParentTypemanage', | |||
access: 'k11', | |||
}, | |||
{ | |||
name: '商品单位', | |||
icon: 'smile', | |||
path: '/goods/unit', | |||
component: './goods/unit', | |||
access: 'k11', | |||
}, | |||
{ | |||
name: '商品详情', | |||
icon: 'smile', | |||
path: '/goods/goodsInfo', | |||
component: './goods/goodsInfo', | |||
access: 'k11', | |||
}, | |||
], | |||
}, | |||
{ | |||
name: '店铺管理', | |||
icon: 'BankFilled', | |||
path: '/store', | |||
routes: [ | |||
{ | |||
name: '店铺类型', | |||
icon: 'smile', | |||
path: '/store/storeType', | |||
component: './store/storeType', | |||
access: 'k12', | |||
}, | |||
{ | |||
name: '店铺小票打印机', | |||
icon: 'smile', | |||
path: '/store/printer/Printer', | |||
component: './store/printer/Printer', | |||
access: 'k12', | |||
}, | |||
{ | |||
name: '打印机模板', | |||
icon: 'smile', | |||
path: '/store/printer/printerTemplate', | |||
component: './store/printer/printerTemplate', | |||
access: 'k12', | |||
}, | |||
{ | |||
name: '店铺桌面码', | |||
icon: 'smile', | |||
path: '/store/storeDesktopNumber', | |||
component: './store/storeDesktopNumber', | |||
access: 'k12', | |||
}, | |||
{ | |||
name: '店铺等级', | |||
icon: 'smile', | |||
path: '/store/storeLevel', | |||
component: './store/storeLevel', | |||
access: 'k13', | |||
}, | |||
{ | |||
name: '店铺信息', | |||
icon: 'smile', | |||
path: '/store/storeInfo', | |||
component: './store/storeInfo', | |||
access: 'k14', | |||
}, | |||
{ | |||
name: '加购商品', | |||
icon: 'smile', | |||
path: '/store/storeAddGoodsInfo', | |||
component: './store/storeAddGoodsInfo', | |||
access: 'k14', | |||
}, | |||
{ | |||
name: '店铺商品', | |||
icon: 'smile', | |||
path: '/store/storeGoodsInfo', | |||
component: './store/storeGoodsInfo', | |||
access: 'k14', | |||
}, | |||
{ | |||
name: '店铺广告', | |||
icon: 'smile', | |||
path: '/store/storeAdvertisement', | |||
component: './store/storeAdvertisement', | |||
access: 'k39', | |||
}, | |||
], | |||
}, | |||
{ | |||
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/deviceFood', | |||
component: './device/deviceFood', | |||
access: 'k14', | |||
}, | |||
{ | |||
name: '设备补货', | |||
icon: 'smile', | |||
path: '/device/deviceStock', | |||
component: './device/deviceStock', | |||
access: 'k14', | |||
} | |||
], | |||
}, | |||
{ | |||
name: '积分管理', | |||
icon: 'BankFilled', | |||
path: '/integral', | |||
routes: [ | |||
{ | |||
name: '积分规则', | |||
icon: 'smile', | |||
path: '/integral/integralConfigure', | |||
component: './integral/integralConfigure', | |||
access: 'k12', | |||
}, | |||
] | |||
}, | |||
{ | |||
name: '菜谱管理', | |||
icon: 'BankFilled', | |||
path: '/foodMenu', | |||
routes: [ | |||
{ | |||
name: '菜谱设置', | |||
icon: 'smile', | |||
path: '/foodMenu/foodMenuInfo', | |||
component: './foodMenu/foodMenuInfo', | |||
access: 'k12', | |||
}, | |||
{ | |||
name: '菜谱关联门店', | |||
icon: 'smile', | |||
path: '/foodMenu/storeFoodMenu', | |||
component: './foodMenu/storeFoodMenu', | |||
access: 'k14', | |||
}, | |||
{ | |||
name: '菜谱关联菜品', | |||
icon: 'smile', | |||
path: '/foodMenu/foodMenu', | |||
component: './foodMenu/foodMenu', | |||
access: 'k14', | |||
} | |||
] | |||
}, | |||
{ | |||
name: '会员管理', | |||
icon: 'UserSwitchOutlined', | |||
path: '/member', | |||
routes: [ | |||
{ | |||
name: '会员信息', | |||
icon: 'smile', | |||
path: '/member/memberInfo', | |||
component: './member/memberInfo', | |||
access: 'k17', | |||
}, | |||
{ | |||
name: '会员标签信息', | |||
icon: 'smile', | |||
path: '/member/membertag', | |||
component: './member/membertag', | |||
access: 'k18', | |||
}, | |||
{ | |||
name: '会员数据分析', | |||
icon: 'smile', | |||
path: '/member/memberAnalysis', | |||
component: './member/memberAnalysis', | |||
access: 'k19', | |||
}, | |||
], | |||
}, | |||
{ | |||
name: '活动管理', | |||
icon: 'CarryOutOutlined', | |||
path: '/activity', | |||
routes: [ | |||
{ | |||
name: '活动信息', | |||
icon: 'smile', | |||
path: '/activity/activityInfo', | |||
component: './activity/activityInfo', | |||
access: 'k20', | |||
}, | |||
{ | |||
name: '活动记录信息', | |||
icon: 'smile', | |||
path: '/activity/activityRecord', | |||
component: './activity/activityRecord', | |||
access: 'k21', | |||
}, | |||
], | |||
}, | |||
{ | |||
name: '优惠券管理', | |||
icon: 'WalletOutlined', | |||
path: '/crm', | |||
routes: [ | |||
{ | |||
name: '优惠卷管理', | |||
icon: 'smile', | |||
path: '/crm/coupon/couponInfo', | |||
component: './crm/coupon/couponInfo', | |||
access: 'k22', | |||
}, | |||
{ | |||
name: '优惠卷批次信息', | |||
icon: 'smile', | |||
path: '/crm/coupon/coupon-Batch', | |||
component: './crm/coupon/coupon-Batch', | |||
access: 'k23', | |||
}, | |||
{ | |||
name: '优惠卷领取信息', | |||
icon: 'smile', | |||
path: '/crm/coupon/customerCoupon', | |||
component: './crm/coupon/customerCoupon', | |||
access: 'k24', | |||
}, | |||
], | |||
}, | |||
{ | |||
name: '分账管理', | |||
icon: 'RedEnvelopeOutlined', | |||
path: '/split', | |||
routes: [ | |||
{ | |||
name: '分账账户管理', | |||
icon: 'smile', | |||
path: '/split/account', | |||
component: './split/account', | |||
access: 'k25', | |||
}, | |||
{ | |||
name: '加盟商分账方案', | |||
icon: 'smile', | |||
path: '/split/FranchiseePlan', | |||
component: './split/FranchiseePlan', | |||
access: 'k25', | |||
}, | |||
{ | |||
name: '分账方案管理', | |||
icon: 'smile', | |||
path: '/split/plan', | |||
component: './split/plan', | |||
access: 'k26', | |||
}, | |||
{ | |||
name: '分账订单管理', | |||
icon: 'smile', | |||
path: '/split/splitOrders', | |||
component: './split/splitOrders', | |||
access: 'k27', | |||
}, | |||
], | |||
}, | |||
{ | |||
name: '退款操作', | |||
icon: 'AccountBookOutlined', | |||
path: '/refundment', | |||
routes: [ | |||
{ | |||
name: '售后申请', | |||
icon: 'smile', | |||
path: '/refundment/OrderAfterSales', | |||
component: './refundment/OrderAfterSales', | |||
access: 'k28', | |||
}, | |||
], | |||
}, | |||
{ | |||
name: '报表统计', | |||
icon: 'SmileOutlined', | |||
path: '/order', | |||
routes: [ | |||
{ | |||
name: '订单汇总', | |||
path: '/order/order-summary-manage', | |||
component: './order/order-summary-manage', | |||
access: 'k29', | |||
}, | |||
{ | |||
name: '订单流水', | |||
path: '/order/order-flow-manage', | |||
component: './order/order-flow-manage', | |||
access: 'k30', | |||
}, | |||
{ | |||
name: '订单明细', | |||
path: '/order/orderDetail', | |||
component: './order/orderDetail', | |||
access: 'k31', | |||
}, | |||
{ | |||
name: '营业日报', | |||
path: '/order/ordersalescountbyday', | |||
component: './order/ordersalescountbyday', | |||
access: 'k32', | |||
}, | |||
{ | |||
name: '异常订单信息', | |||
path: '/order/ExOrder', | |||
component: './order/ExOrder', | |||
access: 'k31', | |||
}, | |||
], | |||
}, | |||
{ | |||
name: '库存管理', | |||
icon: 'HddOutlined', | |||
path: '/stockManager', | |||
routes: [ | |||
{ | |||
name: '其它物料入库', | |||
path: '/stockManager/stockItem', | |||
component: './stockManager/stockItem', | |||
access: 'k36', | |||
}, | |||
{ | |||
name: '库存盘点', | |||
path: '/stockManager/stockInventory', | |||
component: './stockManager/stockInventory', | |||
access: 'k37', | |||
}, | |||
{ | |||
name: '库存报表', | |||
path: '/stockManager/stockReports', | |||
component: './stockManager/stockReports', | |||
access: 'k38', | |||
}, | |||
], | |||
}, | |||
{ | |||
name: '财务报表', | |||
icon: 'HddOutlined', | |||
path: '/financereport', | |||
routes: [ | |||
{ | |||
name: '财务账单', | |||
path: '/financereport/finance', | |||
component: './financereport/finance', | |||
access: 'k37', | |||
}, | |||
], | |||
}, | |||
{ | |||
path: '/', | |||
redirect: '/welcome', | |||
}, | |||
{ | |||
path: '/welcome', | |||
name: 'welcome', | |||
icon: 'smile', | |||
component: './Welcome', | |||
}, | |||
{ | |||
name: 'test', | |||
icon: 'HddOutlined', | |||
path: '/test', | |||
routes: [ | |||
{ | |||
name: 'test', | |||
path: '/test/update', | |||
component: './test/update', | |||
access: 'k37', | |||
}, | |||
{ | |||
name: 'test', | |||
path: '/test', | |||
component: './test', | |||
access: 'k37', | |||
}, | |||
], | |||
}, | |||
//什么都不要想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,4 @@ | |||
FROM nginx:latest | |||
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,55 @@ | |||
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> | |||
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 { | |||
'/kitchen/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,120 @@ | |||
{ | |||
"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", | |||
"antd": "^4.17.0", | |||
"axios": "^0.26.1", | |||
"braft-editor": "^2.3.9", | |||
"classnames": "^2.2.6", | |||
"cos-js-sdk-v5": "^1.3.5", | |||
"js-export-excel": "^1.1.4", | |||
"linq": "^4.0.0", | |||
"lodash": "^4.17.11", | |||
"moment": "^2.25.3", | |||
"omit.js": "^2.0.2", | |||
"qrcode.react": "^1.0.1", | |||
"react": "^17.0.0", | |||
"react-dev-inspector": "^1.1.1", | |||
"react-dom": "^17.0.0", | |||
"react-helmet-async": "^1.0.4", | |||
"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", | |||
"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,782 @@ | |||
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 * 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.push(loginPath); | |||
} | |||
return currentUser; | |||
} catch (error) { | |||
const { location } = history; // 如果没有登录,重定向到 login | |||
console.log(location.pathname); | |||
if (location.pathname != loginPath) { | |||
history.push(loginPath); | |||
} | |||
} | |||
return undefined; | |||
}; | |||
const queryMenuData = async () => { | |||
try { | |||
const data = [ | |||
{ | |||
code:'device', | |||
name: '设备管理', | |||
icon: 'BankFilled', | |||
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:'deviceFood', | |||
name: '设备商品管理', | |||
icon: 'smile', | |||
path: '/device/deviceFood', | |||
component: './device/deviceFood', | |||
access: 'k14', | |||
} | |||
], | |||
}, | |||
{ | |||
code:'integral', | |||
name: '积分管理', | |||
icon: 'BankFilled', | |||
path: '/integral', | |||
routes: [ | |||
{ | |||
code:'integralConfigure', | |||
name: '积分规则', | |||
icon: 'smile', | |||
path: '/integral/integralConfigure', | |||
component: './integral/integralConfigure', | |||
access: 'k12', | |||
},] | |||
}, | |||
{ | |||
code:'foodMenu', | |||
name: '菜谱管理', | |||
icon: 'BankFilled', | |||
path: '/foodMenu', | |||
routes: [ | |||
{ | |||
code:'foodMenuInfo', | |||
name: '菜谱设置', | |||
icon: 'smile', | |||
path: '/foodMenu/foodMenuInfo', | |||
component: './foodMenu/foodMenuInfo', | |||
access: 'k12', | |||
}, | |||
{ | |||
code:'storeFoodMenu', | |||
name: '菜谱关联门店', | |||
icon: 'smile', | |||
path: '/foodMenu/storeFoodMenu', | |||
component: './foodMenu/storeFoodMenu', | |||
access: 'k14', | |||
}, | |||
{ | |||
code:'foodMenu', | |||
name: '菜谱关联菜品', | |||
icon: 'smile', | |||
path: '/foodMenu/foodMenu', | |||
component: './foodMenu/foodMenu', | |||
access: 'k14', | |||
} | |||
] | |||
}, | |||
{ | |||
code:'sys', | |||
name: '系统管理', | |||
icon: 'SettingOutlined', | |||
path: '/sys', | |||
routes: [ | |||
{ | |||
code:'company', | |||
name: '加盟商管理', | |||
icon: 'smile', | |||
path: '/sys/company', | |||
component: './sys/company', | |||
access: 'k2', | |||
}, | |||
{ | |||
code:'Org', | |||
name: '组织管理', | |||
icon: 'smile', | |||
path: '/sys/Org', | |||
component: './sys/Org', | |||
access: 'k2', | |||
}, | |||
{ | |||
code:'Log', | |||
name: '操作日志', | |||
icon: 'smile', | |||
path: '/sys/Log', | |||
component: './sys/Log', | |||
access: 'k3', | |||
}, | |||
{ | |||
code:'User', | |||
name: '系统用户', | |||
icon: 'smile', | |||
path: '/sys/User', | |||
component: './sys/User', | |||
access: 'k4', | |||
}, | |||
{ | |||
code:'Roles', | |||
name: '角色权限', | |||
icon: 'smile', | |||
path: '/sys/Roles', | |||
component: './sys/Roles', | |||
access: 'k5', | |||
}, | |||
{ | |||
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:'sysSettings', | |||
name: '系统配置', | |||
icon: 'smile', | |||
path: '/sys/sysSettings', | |||
component: './sys/sysSettings', | |||
access: 'k6', | |||
}, | |||
], | |||
}, | |||
{ | |||
code:'bom', | |||
name: '配方管理', | |||
icon: 'DropboxSquareFilled', | |||
path: '/bom', | |||
routes: [ | |||
{ | |||
code:'bommanage', | |||
name: '配方列表', | |||
icon: 'smile', | |||
path: '/bom/manage', | |||
component: './bom/manage', | |||
access: 'k7', | |||
}, | |||
], | |||
}, | |||
{ | |||
code:'erp', | |||
name: '物料管理', | |||
icon: 'DropboxSquareFilled', | |||
path: '/erp', | |||
routes: [ | |||
{ | |||
code:'product', | |||
name: '物料信息', | |||
icon: 'smile', | |||
path: '/erp/basic/product', | |||
component: './erp/basic/product', | |||
access: 'k7', | |||
}, | |||
{ | |||
code:'unit', | |||
name: '物料单位', | |||
icon: 'smile', | |||
path: '/erp/basic/unit', | |||
component: './erp/basic/unit', | |||
access: 'k8', | |||
}, | |||
{ | |||
code:'productType', | |||
name: '物料分类', | |||
icon: 'smile', | |||
path: '/erp/basic/productType', | |||
component: './erp/basic/productType', | |||
access: 'k9', | |||
}, | |||
], | |||
}, | |||
{ | |||
code:'goods', | |||
name: '商品管理', | |||
icon: 'ProjectFilled', | |||
path: '/goods', | |||
routes: [ | |||
{ | |||
code:'newgoods', | |||
name: '商品管理', | |||
icon: 'smile', | |||
path: '/goods/newgoods', | |||
component: './goods/newgoods', | |||
access: 'k10', | |||
}, | |||
{ | |||
code:'goods-taste', | |||
name: '商品口味', | |||
icon: 'smile', | |||
path: '/goods/goods-taste', | |||
component: './goods/goods-taste', | |||
access: 'k11', | |||
}, | |||
{ | |||
code:'goods-type', | |||
name: '商品小类', | |||
icon: 'smile', | |||
path: '/goods/goods-type-manage', | |||
component: './goods/goods-type-manage', | |||
access: 'k11', | |||
}, | |||
{ | |||
code:'goodsbigtype', | |||
name: '商品大类', | |||
icon: 'smile', | |||
path: '/goods/goodsParentTypemanage', | |||
component: './goods/goodsParentTypemanage', | |||
access: 'k11', | |||
}, | |||
{ | |||
code:'unit', | |||
name: '商品单位', | |||
icon: 'smile', | |||
path: '/goods/unit', | |||
component: './goods/unit', | |||
access: 'k11', | |||
}, | |||
], | |||
}, | |||
{ | |||
code:'store', | |||
name: '店铺管理', | |||
icon: 'BankFilled', | |||
path: '/store', | |||
routes: [ | |||
{ | |||
code:'storeDesktopNumber', | |||
name: '店铺桌面码', | |||
icon: 'smile', | |||
path: '/store/storeDesktopNumber', | |||
component: './store/storeDesktopNumber', | |||
access: 'k12', | |||
}, | |||
{ | |||
code:'Printer', | |||
name: '店铺小票打印机', | |||
icon: 'smile', | |||
path: '/store/printer/Printer', | |||
component: './store/printer/Printer', | |||
access: 'k12', | |||
}, | |||
{ | |||
code:'printerTemplate', | |||
name: '打印机模板', | |||
icon: 'smile', | |||
path: '/store/printer/printerTemplate', | |||
component: './store/printer/printerTemplate', | |||
access: 'k12', | |||
}, | |||
{ | |||
code:'storeGoodsInfo', | |||
name: '店铺商品', | |||
icon: 'smile', | |||
path: '/store/storeGoodsInfo', | |||
component: './store/storeGoodsInfo', | |||
access: 'k14', | |||
}, | |||
{ | |||
code:'storeAdvertisement', | |||
name: '店铺广告', | |||
icon: 'smile', | |||
path: '/store/storeAdvertisement', | |||
component: './store/storeAdvertisement', | |||
access: 'k39', | |||
}, | |||
], | |||
}, | |||
{ | |||
code:'member', | |||
name: '会员管理', | |||
icon: 'UserSwitchOutlined', | |||
path: '/member', | |||
routes: [ | |||
{ | |||
code:'memberInfo', | |||
name: '会员信息', | |||
icon: 'smile', | |||
path: '/member/memberInfo', | |||
component: './member/memberInfo', | |||
access: 'k17', | |||
}, | |||
{ | |||
code:'membertag', | |||
name: '会员标签信息', | |||
icon: 'smile', | |||
path: '/member/membertag', | |||
component: './member/membertag', | |||
access: 'k18', | |||
}, | |||
{ | |||
code:'memberAnalysis', | |||
name: '会员数据分析', | |||
icon: 'smile', | |||
path: '/member/memberAnalysis', | |||
component: './member/memberAnalysis', | |||
access: 'k19', | |||
}, | |||
], | |||
}, | |||
{ | |||
code:'activity', | |||
name: '活动管理', | |||
icon: 'CarryOutOutlined', | |||
path: '/activity', | |||
routes: [ | |||
{ | |||
code:'activityInfo', | |||
name: '活动信息', | |||
icon: 'smile', | |||
path: '/activity/activityInfo', | |||
component: './crm/activity/activityInfo', | |||
access: 'k20', | |||
}, | |||
{ | |||
code:'activityRecord', | |||
name: '活动记录信息', | |||
icon: 'smile', | |||
path: '/activity/activityRecord', | |||
component: './activity/activityRecord', | |||
access: 'k21', | |||
}, | |||
], | |||
}, | |||
{ | |||
code:'Coupon', | |||
name: '优惠券管理', | |||
icon: 'WalletOutlined', | |||
path: '/crm', | |||
routes: [ | |||
{ | |||
code:'couponInfo', | |||
name: '优惠卷管理', | |||
icon: 'smile', | |||
path: '/crm/Coupon/couponInfo', | |||
component: './crm/Coupon/couponInfo', | |||
access: 'k22', | |||
}, | |||
{ | |||
code:'coupon-Batch', | |||
name: '优惠卷批次信息', | |||
icon: 'smile', | |||
path: '/crm/Coupon/coupon-Batch', | |||
component: './crm/Coupon/coupon-Batch', | |||
access: 'k23', | |||
}, | |||
{ | |||
code:'customerCoupon', | |||
name: '优惠卷领取信息', | |||
icon: 'smile', | |||
path: '/crm/Coupon/customerCoupon', | |||
component: './crm/Coupon/customerCoupon', | |||
access: 'k24', | |||
}, | |||
], | |||
}, | |||
{ | |||
code:'split', | |||
name: '分账管理', | |||
icon: 'RedEnvelopeOutlined', | |||
path: '/split', | |||
routes: [ | |||
{ | |||
code:'account', | |||
name: '分账账户管理', | |||
icon: 'smile', | |||
path: '/split/account', | |||
component: './split/account', | |||
access: 'k25', | |||
}, | |||
{ | |||
code:'FranchiseePlan', | |||
name: '加盟商分账方案', | |||
icon: 'smile', | |||
path: '/split/FranchiseePlan', | |||
component: './split/FranchiseePlan', | |||
access: 'k25', | |||
}, | |||
{ | |||
code:'plan', | |||
name: '分账方案管理', | |||
icon: 'smile', | |||
path: '/split/plan', | |||
component: './split/plan', | |||
access: 'k26', | |||
}, | |||
{ | |||
code:'splitOrders', | |||
name: '分账订单管理', | |||
icon: 'smile', | |||
path: '/split/splitOrders', | |||
component: './split/splitOrders', | |||
access: 'k27', | |||
}, | |||
], | |||
}, | |||
{ | |||
code:'refundment', | |||
name: '退款操作', | |||
icon: 'AccountBookOutlined', | |||
path: '/refundment', | |||
routes: [ | |||
{ | |||
code:'OrderAfterSales', | |||
name: '售后申请', | |||
icon: 'smile', | |||
path: '/refundment/OrderAfterSales', | |||
component: './refundment/OrderAfterSales', | |||
access: 'k28', | |||
}, | |||
], | |||
}, | |||
{ | |||
code:'order', | |||
name: '报表统计', | |||
icon: 'SmileOutlined', | |||
path: '/order', | |||
routes: [ | |||
{ | |||
code:'ordersummarymanage', | |||
name: '订单汇总', | |||
path: '/order/order-summary-manage', | |||
component: './order/order-summary-manage', | |||
access: 'k29', | |||
}, | |||
{ | |||
code:'orderflowmanage', | |||
name: '订单流水', | |||
path: '/order/order-flow-manage', | |||
component: './order/order-flow-manage', | |||
access: 'k30', | |||
}, | |||
{ | |||
code:'orderDetail', | |||
name: '订单明细', | |||
path: '/order/orderDetail', | |||
component: './order/orderDetail', | |||
access: 'k31', | |||
}, | |||
{ | |||
code:'ordersalescountbyday', | |||
name: '营业日报', | |||
path: '/order/ordersalescountbyday', | |||
component: './order/ordersalescountbyday', | |||
access: 'k32', | |||
}, | |||
{ | |||
code:'ExOrder', | |||
name: '异常订单', | |||
path: '/order/ExOrder', | |||
component: './order/ExOrder', | |||
access: 'k32', | |||
}, | |||
], | |||
}, | |||
{ | |||
code:'stockManager', | |||
name: '库存管理', | |||
icon: 'HddOutlined', | |||
path: '/stockManager', | |||
routes: [ | |||
{ | |||
code:'stockItem', | |||
name: '其它物料入库', | |||
path: '/stockManager/stockItem', | |||
component: './stockManager/stockItem', | |||
access: 'k36', | |||
}, | |||
{ | |||
code:'stockInventory', | |||
name: '库存盘点', | |||
path: '/stockManager/stockInventory', | |||
component: './stockManager/stockInventory', | |||
access: 'k37', | |||
}, | |||
{ | |||
code:'stockReports', | |||
name: '库存报表', | |||
path: '/stockManager/stockReports', | |||
component: './stockManager/stockReports', | |||
access: 'k38', | |||
}, | |||
], | |||
}, | |||
{ | |||
code:'financereport', | |||
name: '财务报表', | |||
icon: 'HddOutlined', | |||
path: '/financereport', | |||
routes: [ | |||
{ | |||
code:'finance', | |||
name: '财务账单', | |||
path: '/financereport/finance', | |||
component: './financereport/finance', | |||
access: 'k37', | |||
}, | |||
], | |||
}, | |||
]; | |||
// 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; | |||
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.push(loginPath); | |||
} | |||
} else { | |||
notification.error({ | |||
message: `请求错误 ${status}: ${url}`, | |||
description: errorText, | |||
}); | |||
} | |||
} | |||
if (!response) { | |||
notification.error({ | |||
description: '您的网络发生异常,无法连接服务器', | |||
message: '网络异常', | |||
}); | |||
} | |||
throw error; | |||
}, | |||
//prefix: `${DOMAIN}`, | |||
requestInterceptors: [ | |||
(url, options) => { | |||
console.log(url); | |||
console.log(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 | |||
if (!initialState?.currentUser && !location.pathname.includes(loginPath)) { | |||
history.push(loginPath); | |||
} | |||
}, | |||
menuDataRender: () => loopMenuItem(initialState?.menuData), | |||
// 自定义 403 页面 | |||
// unAccessible: <div>unAccessible</div>, | |||
...initialState?.settings, | |||
}; | |||
}; |
@@ -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,10 @@ | |||
import { Dropdown } from 'antd'; | |||
import React from 'react'; | |||
import classNames from 'classnames'; | |||
import styles from './index.less'; | |||
const HeaderDropdown = ({ overlayClassName: cls, ...restProps }) => ( | |||
<Dropdown overlayClassName={classNames(styles.container, cls)} {...restProps} /> | |||
); | |||
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} src={currentUser.data.avatar} alt="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(255, 255, 255, 0.85); | |||
} | |||
} | |||
} | |||
.dark { | |||
.action { | |||
&:hover { | |||
background: #252a3d; | |||
} | |||
&:global(.opened) { | |||
background: #252a3d; | |||
} | |||
} | |||
} |
@@ -0,0 +1,272 @@ | |||
--- | |||
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) => { | |||
console.log('input', 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:7002/'; | |||
} 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,279 @@ | |||
import React, { useState, useEffect } from 'react'; | |||
import { | |||
Row, | |||
Col, | |||
Checkbox, | |||
Space, | |||
Steps, | |||
message, | |||
Modal, | |||
Form, | |||
Input, | |||
Button, | |||
Select, | |||
DatePicker, | |||
} from 'antd'; | |||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'; | |||
import { GetActivityConfiguration, GetCoupon, AddactivityConfiguration, goodsType, GetStoreInfos } from '../service'; | |||
const key = 'message'; | |||
const CreateConfigForm = (props) => { | |||
const [form] = Form.useForm(); | |||
const { Option, OptGroup } = Select; | |||
const [isFormShow, SetisFormShow] = useState(true); | |||
const [GoodsType, SetGoodsType] = useState([]); | |||
const [Shops, Setshops] = useState([]); | |||
//初始化数据 | |||
useEffect(() => { | |||
const initgoodsType = async (value) => { | |||
var data = await goodsType({ current: 1, pageSize: 100 }); | |||
if (data != undefined) { | |||
SetGoodsType(data.data); | |||
} | |||
}; | |||
const initShops = async (value) => { | |||
var data = await GetStoreInfos({ current: 1, pageSize: 100 }); | |||
if (data) { | |||
Setshops(data.data); | |||
} | |||
}; | |||
initgoodsType(); | |||
initShops(); | |||
}, []); | |||
const tailFormItemLayout = { | |||
wrapperCol: { | |||
sm: { | |||
span: 500, | |||
offset: 1, | |||
}, | |||
}, | |||
}; | |||
let numChar = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'] | |||
const GetSelectVal = (info) => { | |||
let value = []; | |||
if (info.configurationName == "星期限制") { | |||
for (let i = 0; i < 7; i++) { | |||
value.push({ | |||
label: `星期${i == 0 ? "日" : numChar[i]}`, | |||
value: i.toString(), | |||
}); | |||
} | |||
} | |||
if (info.configurationName == "商品种类限制") { | |||
GoodsType.data.forEach(element => { | |||
value.push({ | |||
label: element.goodsType_Name, | |||
value: element.id, | |||
}); | |||
}); | |||
} | |||
if (info.configurationName == "门店限制") { | |||
Shops.data.forEach(element => { | |||
value.push({ | |||
label: element.storeName, | |||
value: element.storeId, | |||
}); | |||
}); | |||
} | |||
return value; | |||
} | |||
//新增 | |||
const onFinish = async (values) => { | |||
message.loading('正在添加...', key); | |||
try { | |||
if (values.users) { | |||
values.users.forEach(element => { | |||
element.activity_Id = props.activityId; | |||
}); | |||
} | |||
await AddactivityConfiguration(JSON.stringify(values.users)).then((r) => { | |||
message.destroy(key); | |||
if (r.data) { | |||
props.onCancel(); | |||
message.success('添加成功'); | |||
} else { | |||
message.error('添加失败请重试!'); | |||
} | |||
}); | |||
return true; | |||
} catch (error) { | |||
message.destroy(key); | |||
message.error('添加失败请重试!'); | |||
return false; | |||
} | |||
}; | |||
return ( | |||
<Modal | |||
title={'活动配置'} | |||
width={700} | |||
visible={props.createModalVisible} | |||
footer={null} //底部内容 | |||
onCancel={() => { | |||
props.onCancel(); | |||
}} | |||
//关闭时销毁 Modal 里的子元素 | |||
destroyOnClose > | |||
{/* ----------------------------------------------------------------------------------------- */} | |||
<Form name="dynamic_form_nest_item" autoComplete="off" onFinish={onFinish} | |||
initialValues={{ | |||
"users": props.config | |||
}} | |||
> | |||
<Form.List name="users"> | |||
{(fields, { add, remove }) => ( | |||
<> | |||
{fields.map(({ key, name, fieldKey, ...restField }) => ( | |||
<Space key={key} style={{ display: 'flex', marginBottom: 8 }} align="baseline"> | |||
<Form.Item | |||
{...restField} | |||
name={[name, 'valueType']} | |||
fieldKey={[fieldKey, 'valueType']} | |||
hidden={true}> | |||
<Input style={{ width: 80 }} size='Small' /> | |||
</Form.Item> | |||
<Form.Item | |||
{...restField} | |||
name={[name, 'configId']} | |||
fieldKey={[fieldKey, 'configId']}> | |||
<Checkbox.Group > | |||
<Row> | |||
<Col span={32} > | |||
<Checkbox value={props.config[key].configuration_Id} style={{ lineHeight: '32px' }}> | |||
{props.config[key].configurationName} | |||
</Checkbox> | |||
</Col> | |||
</Row> | |||
</Checkbox.Group> | |||
</Form.Item> | |||
<span>条件:{props.config[key].configurationRule}</span> | |||
<Form.Item | |||
noStyle | |||
shouldUpdate={(prevValues, currentValues) => | |||
prevValues.activityType !== currentValues.activityType | |||
} > | |||
{({ getFieldValue }) => | |||
props.config[key].configurationValueCount != 0 ? ( | |||
<Form.Item | |||
{...restField} | |||
name={[name, 'operator']} | |||
fieldKey={[fieldKey, 'operator']} | |||
// rules={[{ required: true, message: '请输入判定符' }]} | |||
> | |||
<Select style={{ width: 80 }} size='Small' > | |||
<Option value=">">大于</Option> | |||
<Option value="<">小于</Option> | |||
<Option value="==">等于</Option> | |||
<Option value=">=and<=">包含</Option> | |||
</Select> | |||
</Form.Item> | |||
) : ( | |||
<span></span> | |||
) | |||
} | |||
</Form.Item> | |||
<Form.Item | |||
noStyle | |||
shouldUpdate={(prevValues, currentValues) => | |||
prevValues.activityType !== currentValues.activityType | |||
} > | |||
{({ getFieldValue }) => | |||
props.config[key].configurationValueCount == 1 ? ( | |||
<Form.Item | |||
{...restField} | |||
name={[name, 'value1']} | |||
fieldKey={[fieldKey, 'value1']} | |||
// rules={[{ required: true, message: '请输入判定符' }]} | |||
> | |||
{props.config[key].valueType == 1 ? ( | |||
<Input style={{ width: 80 }} size='Small' /> | |||
) : ( | |||
<Select {...{ | |||
mode: 'multiple', | |||
style: { width: '250px' }, | |||
options: GetSelectVal(props.config[key]), | |||
placeholder: 'Select Item...', | |||
maxTagCount: 'responsive', | |||
mode: 'multiple', | |||
}} /> | |||
)} | |||
</Form.Item> | |||
) : ( | |||
<span></span> | |||
) | |||
} | |||
</Form.Item> | |||
<Form.Item | |||
noStyle | |||
shouldUpdate={(prevValues, currentValues) => | |||
prevValues.activityType !== currentValues.activityType | |||
} > | |||
{({ getFieldValue }) => | |||
props.config[key].configurationValueCount == 2 ? ( | |||
<Form.Item | |||
{...restField} | |||
name={[name, 'value1']} | |||
fieldKey={[fieldKey, 'value1']} | |||
// rules={[{ required: true, message: '请输入判定符' }]} | |||
> | |||
<Input style={{ width: 80 }} size='Small' /> | |||
</Form.Item> | |||
) : ( | |||
<span></span> | |||
) | |||
} | |||
</Form.Item> | |||
<Form.Item | |||
noStyle | |||
shouldUpdate={(prevValues, currentValues) => | |||
prevValues.activityType !== currentValues.activityType | |||
} > | |||
{({ getFieldValue }) => | |||
props.config[key].configurationValueCount == 2 ? ( | |||
<Form.Item | |||
{...restField} | |||
name={[name, 'value2']} | |||
fieldKey={[fieldKey, 'value2']} | |||
// rules={[{ required: true, message: '请输入判定符' }]} | |||
> | |||
<Input style={{ width: 80 }} size='Small' /> | |||
</Form.Item> | |||
) : ( | |||
<span></span> | |||
) | |||
} | |||
</Form.Item> | |||
</Space> | |||
))} | |||
</> | |||
)} | |||
</Form.List> | |||
<Form.Item> | |||
<Button type="primary" htmlType="submit"> | |||
保存 | |||
</Button> | |||
</Form.Item> | |||
</Form> | |||
{/* ----------------------------------------------------------------------------------------- */} | |||
</Modal> | |||
); | |||
}; | |||
export default CreateConfigForm; |
@@ -0,0 +1,368 @@ | |||
import React, { useState, useEffect } from 'react'; | |||
import { | |||
InputNumber, | |||
Row, | |||
Col, | |||
Checkbox, | |||
Space, | |||
Steps, | |||
message, | |||
Modal, | |||
Form, | |||
Input, | |||
Button, | |||
Select, | |||
DatePicker, | |||
} from 'antd'; | |||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'; | |||
import { GetActivityConfiguration, GetCouponCanUseCountAndLimit, GetGoodsInfo } from '../service'; | |||
const CreateForm = (props) => { | |||
const [form] = Form.useForm(); | |||
const { Option, OptGroup } = Select; | |||
const [options, setoptions] = useState(); | |||
const [current, setCurrent] = React.useState(0); | |||
const [firstItem, SetFirstItem] = useState(); | |||
const [secondItem, SetSecondItem] = useState(); | |||
const [isFormShow, SetisFormShow] = useState(true); | |||
const [couponInfo, SetCouponInfo] = useState([]); | |||
const [goodsInfo, SetGoodsInfo] = useState([]); | |||
const [isCouponModalVisible, SetIsCouponModalVisible] = useState(false); | |||
const [selectCoupon, SetselectCoupon] = useState(false); | |||
const [quantityLimit, SetQuantityLimit] = useState([]); | |||
const [goodsPrice, SetGoodsPrice] = useState([]); | |||
const tailFormItemLayout = { | |||
wrapperCol: { | |||
sm: { | |||
span: 500, | |||
offset: 1, | |||
}, | |||
}, | |||
}; | |||
//初始化数据 | |||
useEffect(() => { | |||
const initcouponType = async (value) => { | |||
var data = (await GetCouponCanUseCountAndLimit()).data; | |||
if (data != undefined) { | |||
SetCouponInfo(data); | |||
} | |||
}; | |||
const initGoodsInfoType = async (value) => { | |||
var data = (await GetGoodsInfo()).data; | |||
if (data) { | |||
SetGoodsInfo(data); | |||
} | |||
}; | |||
initcouponType(); | |||
initGoodsInfoType(); | |||
}, []); | |||
const canUseCountOrlimit = (key, value, type) => { | |||
var result = 0; | |||
if (value) { | |||
if (type == 3) { | |||
if (goodsPrice[key]) { | |||
return goodsPrice[key].price; | |||
} else { | |||
const arr = [[...goodsPrice]]; //克隆的时候改变地址 | |||
goodsInfo.filter((item) => { | |||
if (item.id == value[key]?.rewardValue) { | |||
arr[key] = item; | |||
} | |||
}); | |||
var vv = arr; | |||
// SetGoodsPrice(arr??[]); | |||
return arr[key] ? arr[key].price : 0; | |||
} | |||
} else { | |||
var rewardValue = value[key]; | |||
var count = 0; | |||
couponInfo.filter((item) => { | |||
if (rewardValue && item.couponId == rewardValue?.rewardValue) { | |||
if (type == 1) { | |||
count = item.canUseCount; | |||
} else { | |||
count = item.limit; | |||
} | |||
} | |||
}); | |||
} | |||
if (type == 1) { | |||
result = parseInt( | |||
quantityLimit[key] ? JSON.stringify(quantityLimit[key].canUseCount) : count, | |||
); | |||
} else { | |||
result = parseInt(quantityLimit[key] ? JSON.stringify(quantityLimit[key].limit) : count); | |||
} | |||
} else if (quantityLimit[key]) { | |||
if (type == 1) { | |||
result = quantityLimit[key].canUseCount; | |||
} else { | |||
result = quantityLimit[key].limit; | |||
} | |||
} | |||
return result; | |||
}; | |||
return ( | |||
<Modal | |||
title={props.values.id ? '编辑优活动' : '新建活动'} | |||
width={700} | |||
visible={props.createModalVisible} | |||
footer={null} //底部内容 | |||
onCancel={() => { | |||
props.onCancel(); | |||
}} | |||
//关闭时销毁 Modal 里的子元素 | |||
destroyOnClose={true} | |||
> | |||
<Form | |||
preserve={false} | |||
// form={form} | |||
labelCol={{ span: 4 }} | |||
layout="Horizontal" | |||
initialValues={props.values} | |||
onFinish={props.onFinish} | |||
> | |||
<span style={{ display: isFormShow ? 'block' : 'none' }}> | |||
<Form.Item name="id" hidden={true}> | |||
<Input /> | |||
</Form.Item> | |||
<Form.Item name="activityName" label="活动名称" rules={[{ required: true, max: 64 }]}> | |||
<Input placeholder="请输入活动名称" /> | |||
</Form.Item> | |||
<Form.Item name="activityImg" label="活动图片" rules={[{ required: true, max: 500 }]}> | |||
<Input placeholder="请输入活动图片" /> | |||
</Form.Item> | |||
<Form.Item | |||
name="activityType" | |||
label="活动类型" | |||
defaultValue={props.values.activityType} | |||
rules={[{ required: true }]} | |||
> | |||
<Select | |||
onChange={() => { | |||
// form.setFieldsValue({ activityValue: undefined }); | |||
}} | |||
allowClear | |||
placeholder="请选择活动类型" | |||
> | |||
<OptGroup> | |||
<Select.Option key={1} value={1}> | |||
{'送券'} | |||
</Select.Option> | |||
<Select.Option key={2} value={2}> | |||
{' 其他'} | |||
</Select.Option> | |||
<Select.Option key={3} value={3}> | |||
{'商品促销'} | |||
</Select.Option> | |||
<Select.Option key={4} value={4}> | |||
{'商城优惠券'} | |||
</Select.Option> | |||
</OptGroup> | |||
</Select> | |||
</Form.Item> | |||
<Form.Item | |||
noStyle | |||
shouldUpdate={(prevValues, currentValues) => | |||
prevValues.activityType !== currentValues.activityType | |||
} | |||
> | |||
{({ getFieldValue }) => | |||
getFieldValue('activityType') == '2' ? ( | |||
<Form.Item name="activityValue" label="活动值" rules={[{ required: true }]}> | |||
<Form.Item name="activityValue" noStyle> | |||
<Input placeholder="请输入活动值" /> | |||
</Form.Item> | |||
</Form.Item> | |||
) : getFieldValue('activityType') == '3' ? ( | |||
<Form.Item label="商品信息"> | |||
<Form.List name="activityRewards"> | |||
{(fields, { add, remove }) => ( | |||
<> | |||
{fields.map(({ key, name, fieldKey, ...restField }) => ( | |||
<Space | |||
key={key} | |||
style={{ display: 'flex', marginBottom: 8 }} | |||
align="baseline" | |||
> | |||
<Form.Item | |||
{...restField} | |||
name={[name, 'rewardValue']} | |||
fieldKey={[fieldKey, 'rewardValue']} | |||
rules={[{ required: true, message: '请选择商品' }]} | |||
> | |||
<Select | |||
style={{ width: 200 }} | |||
onChange={(value) => { | |||
const arr = [...goodsPrice]; //克隆的时候改变地址 | |||
goodsInfo.filter((item) => { | |||
if (item.id == value) { | |||
arr[key] = item; | |||
} | |||
}); | |||
SetGoodsPrice(arr); | |||
}} | |||
> | |||
{goodsInfo.map((item, index) => { | |||
return ( | |||
<Select.Option index={index} value={item.id} key={item.id}> | |||
{item.name} | |||
</Select.Option> | |||
); | |||
})} | |||
</Select> | |||
</Form.Item> | |||
{/* {`原价:${goodsPrice[key]?.price}`} */} | |||
{`原价:${canUseCountOrlimit(key, props.values.activityRewards, 3)}`} | |||
| |||
<Form.Item | |||
{...restField} | |||
label="促销价" | |||
name={[name, 'sendNum']} | |||
fieldKey={[fieldKey, 'sendNum']} | |||
rules={[{ required: true, message: '请输入促销价' }]} | |||
> | |||
<InputNumber min={0} placeholder="促销价" /> | |||
</Form.Item> | |||
<MinusCircleOutlined onClick={() => remove(name)} /> | |||
</Space> | |||
))} | |||
<Form.Item> | |||
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}> | |||
添加 | |||
</Button> | |||
</Form.Item> | |||
</> | |||
)} | |||
</Form.List> | |||
</Form.Item> | |||
) : ( | |||
<Form.Item label="优惠券"> | |||
<Form.List name="activityRewards"> | |||
{(fields, { add, remove }) => ( | |||
<> | |||
{fields.map(({ key, name, fieldKey, ...restField }) => ( | |||
<Space | |||
key={key} | |||
style={{ display: 'flex', marginBottom: 8 }} | |||
align="baseline" | |||
> | |||
<Form.Item | |||
{...restField} | |||
name={[name, 'rewardValue']} | |||
fieldKey={[fieldKey, 'rewardValue']} | |||
rules={[{ required: true, message: '请选择优惠券' }]} | |||
> | |||
<Select | |||
style={{ width: 200 }} | |||
onChange={(value) => { | |||
const arr = [...quantityLimit]; //克隆的时候改变地址 | |||
couponInfo.filter((item) => { | |||
if (item.couponId == value) { | |||
arr[key] = item; | |||
} | |||
}); | |||
SetQuantityLimit(arr); | |||
}} | |||
> | |||
{couponInfo.map((item, index) => { | |||
return ( | |||
<Select.Option | |||
index={index} | |||
value={item.couponId} | |||
key={item.couponId} | |||
> | |||
{item.couponName} | |||
</Select.Option> | |||
); | |||
})} | |||
</Select> | |||
</Form.Item> | |||
{`可用数量:${canUseCountOrlimit(key, props.values.activityRewards, 1)}`} | |||
| |||
<Form.Item | |||
{...restField} | |||
label={getFieldValue('activityType')==4?"兑换值":"每次领取数量"} | |||
name={[name, 'sendNum']} | |||
fieldKey={[fieldKey, 'sendNum']} | |||
rules={[ | |||
{ required: true, message: getFieldValue('activityType')==4?"请输入兑换值":"请输入发送数量"}, | |||
{ | |||
type: 'number', | |||
min: 0, | |||
max: parseInt( | |||
getFieldValue('activityType')==4?999999999:canUseCountOrlimit(key, props.values.activityRewards, 2), | |||
), | |||
message: '超出领取限制', | |||
}, | |||
]} | |||
> | |||
<InputNumber | |||
min={0} | |||
max={getFieldValue('activityType')==4?999999999:canUseCountOrlimit(key, props.values.activityRewards, 2)} | |||
placeholder={getFieldValue('activityType')==4?"兑换值":"每次领取数量"} | |||
/> | |||
</Form.Item> | |||
<MinusCircleOutlined onClick={() => remove(name)} /> | |||
</Space> | |||
))} | |||
<Form.Item> | |||
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}> | |||
添加 | |||
</Button> | |||
</Form.Item> | |||
</> | |||
)} | |||
</Form.List> | |||
</Form.Item> | |||
) | |||
} | |||
</Form.Item> | |||
<Form.Item noStyle> | |||
<Form.Item {...tailFormItemLayout} style={{ height: 32 }}> | |||
<Form.Item | |||
name="activityStartTime" | |||
label="开始时间" | |||
style={{ float: 'left' }} | |||
rules={[{ required: true }]} | |||
> | |||
<DatePicker placeholder="开始时间" /> | |||
</Form.Item> | |||
<Form.Item | |||
name="activityEndTime" | |||
label="结束时间" | |||
style={{ float: 'left', marginLeft: 20 }} | |||
rules={[{ required: true }]} | |||
> | |||
<DatePicker placeholder="结束时间" /> | |||
</Form.Item> | |||
</Form.Item> | |||
</Form.Item> | |||
<Form.Item name="remark" label="备注" rules={[{ required: true, max: 200 }]}> | |||
<Input placeholder="请输入备注" /> | |||
</Form.Item> | |||
<Form.Item name="activityRule" label="规则说明" rules={[{ required: true, max: 500 }]}> | |||
<Input placeholder="请输入规则说明" /> | |||
</Form.Item> | |||
<Form.Item> | |||
<Button type="primary" htmlType="submit" style={{ float: 'right' }}> | |||
保存 | |||
</Button> | |||
</Form.Item> | |||
</span> | |||
</Form> | |||
</Modal> | |||
); | |||
}; | |||
export default CreateForm; |
@@ -0,0 +1,217 @@ | |||
import React, { useState, useEffect } from 'react'; | |||
import { | |||
Row, | |||
Col, | |||
Checkbox, | |||
Space, | |||
Steps, | |||
message, | |||
Modal, | |||
Form, | |||
Input, | |||
Button, | |||
Select, | |||
DatePicker, | |||
} from 'antd'; | |||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'; | |||
import { GetActivityConfiguration, GetCoupon } from '../service'; | |||
const CreateForm = (props) => { | |||
const { Option, OptGroup } = Select; | |||
const [options, setoptions] = useState(); | |||
const [current, setCurrent] = React.useState(0); | |||
const [firstItem, SetFirstItem] = useState(); | |||
const [secondItem, SetSecondItem] = useState(); | |||
const [isFormShow, SetisFormShow] = useState(true); | |||
const [activityConfiguration, SetactivityConfiguration] = useState([]); | |||
const { Step } = Steps; | |||
const tailFormItemLayout = { | |||
wrapperCol: { | |||
sm: { | |||
span: 500, | |||
offset: 1, | |||
}, | |||
}, | |||
}; | |||
const steps = [ | |||
{ | |||
title: '活动信息', | |||
content: 'First-content1', | |||
}, | |||
{ | |||
title: '活动配置', | |||
content: 'Second-content2', | |||
}, | |||
]; | |||
//初始化数据 | |||
useEffect(() => { | |||
const initcouponType = async (value) => { | |||
var data = (await GetActivityConfiguration()).data; | |||
if (data != undefined) { | |||
SetactivityConfiguration(data); | |||
} | |||
}; | |||
initcouponType(); | |||
}, []); | |||
const next = () => { | |||
SetisFormShow(false); | |||
setCurrent(current + 1); | |||
}; | |||
const prev = () => { | |||
SetisFormShow(true); | |||
setCurrent(current - 1); | |||
}; | |||
return ( | |||
<Modal | |||
title={props.values.id ? '编辑优活动' : '新建活动'} | |||
width={700} | |||
visible={props.createModalVisible} | |||
footer={null} //底部内容 | |||
onCancel={() => { | |||
props.onCancel(); | |||
}} | |||
//关闭时销毁 Modal 里的子元素 | |||
destroyOnClose | |||
> | |||
{/* //-------------------------------------------------------------------------- */} | |||
<Steps current={current}> | |||
{steps.map((item) => ( | |||
<Step key={item.title} title={item.title} /> | |||
))} | |||
</Steps> | |||
<div className="steps-content" style={{ marginTop: '20px' }}> | |||
<Form | |||
labelCol={{ span: 4 }} | |||
layout="Horizontal" | |||
preserve={false} | |||
initialValues={props.values} | |||
onFinish={props.onFinish} | |||
> | |||
<span style={{ display: isFormShow ? 'block' : 'none' }}> | |||
<Form.Item name="id" hidden={true}> | |||
<Input /> | |||
</Form.Item> | |||
<Form.Item name="activityName" label="活动名称" rules={[{ required: true, max: 50 }]}> | |||
<Input placeholder="请输入活动名称" /> | |||
</Form.Item> | |||
<Form.Item name="activityImg" label="活动图片"> | |||
<Input placeholder="请输入活动图片" /> | |||
</Form.Item> | |||
<Form.Item | |||
name="activityType" | |||
label="活动类型" | |||
defaultValue={props.values.activityType} | |||
rules={[{ required: true }]} | |||
> | |||
<Select placeholder="请选择活动类型"> | |||
<OptGroup> | |||
<Select.Option key={1} value={1}> | |||
{' '} | |||
送券 | |||
</Select.Option> | |||
<Select.Option key={2} value={2}> | |||
{' '} | |||
其他{' '} | |||
</Select.Option> | |||
</OptGroup> | |||
</Select> | |||
</Form.Item> | |||
<Form.Item noStyle> | |||
<Form.Item {...tailFormItemLayout} style={{ height: 32 }}> | |||
<Form.Item | |||
name="activityStartTime" | |||
label="开始时间" | |||
style={{ float: 'left' }} | |||
rules={[{ required: true }]} | |||
> | |||
<DatePicker placeholder="开始时间" /> | |||
</Form.Item> | |||
<Form.Item | |||
name="activityEndTime" | |||
label="结束时间" | |||
style={{ float: 'left', marginLeft: 20 }} | |||
rules={[{ required: true }]} | |||
> | |||
<DatePicker placeholder="结束时间" /> | |||
</Form.Item> | |||
</Form.Item> | |||
</Form.Item> | |||
<Form.Item name="remark" label="备注" rules={[{ required: true }]}> | |||
<Input placeholder="请输入备注" /> | |||
</Form.Item> | |||
<Form.Item name="activityRule" label="规则说明" rules={[{ required: true }]}> | |||
<Input placeholder="请输入规则说明" /> | |||
</Form.Item> | |||
</span> | |||
<span style={{ display: !isFormShow ? 'block' : 'none' }}> | |||
<Form.List name="activityConfiguration"> | |||
{(fields, { add, remove }) => ( | |||
<> | |||
{activityConfiguration.map(({ value, showName, type, ...restField }) => ( | |||
<Space key={value} style={{ marginBottom: 8 }}> | |||
<Form.Item | |||
{...restField} | |||
name={[showName, 'showName']} | |||
fieldKey={[value, 'value']} | |||
> | |||
<Checkbox onChange={value}>{showName}</Checkbox> | |||
</Form.Item> | |||
{/* <Form.Item | |||
{...restField} | |||
name={[name, 'showName']} | |||
fieldKey={[fieldKey, 'showName']} | |||
rules={[{ required: true, message: 'Missing first name' }]}> | |||
<Input placeholder="First Name" /> | |||
</Form.Item> */} | |||
{/* <Form.Item | |||
{...restField} | |||
name={[name, 'value']} | |||
fieldKey={[fieldKey, 'value']} | |||
rules={[{ required: true, message: 'Missing last name' }]} > | |||
<Input placeholder="Last Name" /> | |||
</Form.Item> */} | |||
</Space> | |||
))} | |||
{/* <Form.Item> | |||
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}> | |||
Add field | |||
</Button> | |||
</Form.Item> */} | |||
</> | |||
)} | |||
</Form.List> | |||
{/* <Form.Item> | |||
<Button type="primary" htmlType="submit" style={{ float: 'right' }}> | |||
保存 | |||
</Button> | |||
</Form.Item> */} | |||
</span> | |||
</Form> | |||
</div> | |||
<div className="steps-action"> | |||
{current < steps.length - 1 && <Button onClick={() => next()}>下一步</Button>} | |||
{current === steps.length - 1 && <Button onClick={() => prev()}>上一步</Button>} | |||
{current > 0 && ( | |||
<Button | |||
type="primary" | |||
style={{ margin: '0 8px' }} | |||
onClick={() => message.success('Processing complete!')} | |||
> | |||
保存 | |||
</Button> | |||
)} | |||
</div> | |||
{/* //-------------------------------------------------------------------------- */} | |||
</Modal> | |||
); | |||
}; | |||
export default CreateForm; |
@@ -0,0 +1,589 @@ | |||
import React, { useState, useEffect } from 'react'; | |||
import { | |||
InputNumber, | |||
Row, | |||
Col, | |||
Checkbox, | |||
Space, | |||
Steps, | |||
message, | |||
Modal, | |||
Form, | |||
Input, | |||
Button, | |||
Select, | |||
DatePicker, | |||
Upload | |||
} from 'antd'; | |||
import { MinusCircleOutlined, PlusOutlined ,UploadOutlined} from '@ant-design/icons'; | |||
import { GetActivityConfiguration, GetCouponCanUseCountAndLimit, GetGoodsInfo,GetCosRequestURL } from '../service'; | |||
import axios from 'axios'; | |||
const CreateForm = (props) => { | |||
const [form] = Form.useForm(); | |||
const { Option, OptGroup } = Select; | |||
const [options, setoptions] = useState(); | |||
const [current, setCurrent] = React.useState(0); | |||
const [firstItem, SetFirstItem] = useState(); | |||
const [secondItem, SetSecondItem] = useState(); | |||
const [isFormShow, SetisFormShow] = useState(true); | |||
const [couponInfo, SetCouponInfo] = useState([]); | |||
const [goodsInfo, SetGoodsInfo] = useState([]); | |||
const [isCouponModalVisible, SetIsCouponModalVisible] = useState(false); | |||
const [selectCoupon, SetselectCoupon] = useState(false); | |||
const [quantityLimit, SetQuantityLimit] = useState([]); | |||
const [imgurl, setimgUrl] = useState(props.values.activityImg ? props.values.activityImg : null); | |||
const [goodsPrice, SetGoodsPrice] = useState([]); | |||
const children = []; | |||
for (let i = 10; i < 36; i++) { | |||
children.push(<Option key={i.toString(36) + i}>{i.toString(36) + i}</Option>); | |||
} | |||
const tailFormItemLayout = { | |||
wrapperCol: { | |||
sm: { | |||
span: 500, | |||
offset: 1, | |||
}, | |||
}, | |||
}; | |||
//每次触发 | |||
useEffect(()=>{ | |||
return ()=>{ | |||
if (!props.createModalVisible) { | |||
setimgUrl(null); | |||
} | |||
} | |||
}) | |||
//初始化数据 | |||
useEffect(() => { | |||
const initcouponType = async (value) => { | |||
var data = (await GetCouponCanUseCountAndLimit()).data; | |||
if (data != undefined) { | |||
SetCouponInfo(data); | |||
} | |||
}; | |||
const initGoodsInfoType = async (value) => { | |||
var data = (await GetGoodsInfo()).data; | |||
if (data) { | |||
SetGoodsInfo(data); | |||
} | |||
}; | |||
initcouponType(); | |||
initGoodsInfoType(); | |||
}, []); | |||
const canUseCountOrlimit = (key, value, type) => { | |||
var result = 0; | |||
if (value) { | |||
if (type == 3) { | |||
if (goodsPrice[key]) { | |||
return goodsPrice[key].price; | |||
} else { | |||
const arr = [[...goodsPrice]]; //克隆的时候改变地址 | |||
goodsInfo.filter((item) => { | |||
if (item.id == value[key]?.rewardValue) { | |||
arr[key] = item; | |||
} | |||
}); | |||
// SetGoodsPrice(arr??[]); | |||
return arr[key] ? (arr[key].price ? arr[key].price : 0) : 0; | |||
} | |||
} else { | |||
var rewardValue = value[key]; | |||
var count = 0; | |||
couponInfo.filter((item) => { | |||
if (rewardValue && item.couponId == rewardValue?.rewardValue) { | |||
if (type == 1) { | |||
count = item.canUseCount; | |||
} else { | |||
count = item.limit; | |||
} | |||
} | |||
}); | |||
} | |||
if (type == 1) { | |||
result = parseInt( | |||
quantityLimit[key] ? JSON.stringify(quantityLimit[key].canUseCount) : count, | |||
); | |||
} else { | |||
result = parseInt(quantityLimit[key] ? JSON.stringify(quantityLimit[key].limit) : count); | |||
} | |||
} else if (quantityLimit[key]) { | |||
if (type == 1) { | |||
result = quantityLimit[key].canUseCount; | |||
} else { | |||
result = quantityLimit[key].limit; | |||
} | |||
} | |||
return result; | |||
}; | |||
const uploadProp = { | |||
name: 'file', | |||
showUploadList: false, | |||
multiple: false, | |||
accept: '.png, .jpg, .jpeg, .gif', | |||
// 这里需要指定文件上传的content-type | |||
headers: { | |||
'Content-Type': 'application/octet-stream', | |||
}, | |||
customRequest({ | |||
action, | |||
file, | |||
headers, | |||
onError, | |||
onProgress, | |||
onSuccess, | |||
withCredentials, | |||
}) { | |||
//覆盖action 上传之前获取上传地址 | |||
var index = file.name.lastIndexOf("."); | |||
var ext = file.name.substr(index + 1); | |||
let fileData = null; | |||
GetCosRequestURL({ directory: "activity", fileExtension: ext, method: "PUT" }).then((r) => { | |||
action = r.allUrl; | |||
const reader = new FileReader(); | |||
reader.readAsArrayBuffer(file); | |||
reader.onload = (e) => { | |||
fileData = e.target.result; | |||
// 使用 axios 进行文件上传的请求 | |||
axios.put(action, fileData, { | |||
withCredentials, | |||
headers, | |||
onUploadProgress: ({ total, loaded }) => { | |||
// 进行上传进度输出,更加直观 | |||
onProgress({ percent: Math.round(loaded / total * 100).toFixed(2) }, file); | |||
}, | |||
}).then(response => { | |||
form.setFieldsValue({ activityImg: r.seeUrl }); | |||
if (response.status == 200 || response.statusText == 'OK') { | |||
setimgUrl(r.seeUrl) | |||
} | |||
// onSuccess(response, file); | |||
}) | |||
.catch(onError); | |||
}; | |||
}); | |||
// return { | |||
// abort() { | |||
// console.log('upload progress is aborted.'); | |||
// }, | |||
// }; | |||
}, | |||
//defaultFileList: props.location.query.values ? props.location.query.values : null | |||
}; | |||
return ( | |||
<Modal | |||
title={props.values.id ? '编辑优活动' : '新建活动'} | |||
width={700} | |||
visible={props.createModalVisible} | |||
footer={null} //底部内容 | |||
onCancel={() => { | |||
props.onCancel(); | |||
}} | |||
//关闭时销毁 Modal 里的子元素 | |||
destroyOnClose={true} | |||
> | |||
<Form | |||
preserve={false} | |||
form={form} | |||
labelCol={{ span: 4 }} | |||
layout="Horizontal" | |||
initialValues={props.values} | |||
onFinish={props.onFinish} | |||
> | |||
<span style={{ display: isFormShow ? 'block' : 'none' }}> | |||
<Form.Item name="id" hidden={true}> | |||
<Input /> | |||
</Form.Item> | |||
<Form.Item name="activityName" label="活动名称" rules={[{ required: true, max: 64 }]}> | |||
<Input placeholder="请输入活动名称" /> | |||
</Form.Item> | |||
<Form.Item name="activityImg1" label="请输入活动图片" rules={[{ required: true }]} | |||
> | |||
<Upload | |||
{...uploadProp} | |||
> | |||
{imgurl ? <img src={imgurl} alt="avatar" style={{ width: '30px',height:'40px' }} /> : <Button icon={<UploadOutlined />}>上传</Button>} | |||
</Upload> | |||
</Form.Item> | |||
<Form.Item name="activityImg" label="请输入活动图片" hidden={true} > | |||
<Input placeholder="请输入活动图片" /> | |||
</Form.Item> | |||
<Form.Item | |||
name="activityType" | |||
label="活动类型" | |||
defaultValue={props.values.activityType} | |||
rules={[{ required: true }]} | |||
> | |||
<Select | |||
onChange={() => { | |||
// form.setFieldsValue({ activityValue: undefined }); | |||
}} | |||
allowClear | |||
placeholder="请选择活动类型" | |||
> | |||
<OptGroup> | |||
<Select.Option key={1} value={1}> | |||
{'送券'} | |||
</Select.Option> | |||
<Select.Option key={2} value={2}> | |||
{'阶梯抵扣金'} | |||
</Select.Option> | |||
<Select.Option key={3} value={3}> | |||
{'特价商品'} | |||
</Select.Option> | |||
<Select.Option key={4} value={4}> | |||
{'阶梯优惠券'} | |||
</Select.Option> | |||
<Select.Option key={5} value={5}> | |||
{'阶梯商品'} | |||
</Select.Option> | |||
<Select.Option key={6} value={6}> | |||
{'阶梯消费次数'} | |||
</Select.Option> | |||
<Select.Option key={7} value={7}> | |||
{'循环消费次数'} | |||
</Select.Option> | |||
{/* <Select.Option key={4} value={4}> | |||
{'商城优惠券'} | |||
</Select.Option> */} | |||
</OptGroup> | |||
</Select> | |||
</Form.Item> | |||
<Form.Item | |||
noStyle | |||
shouldUpdate={(prevValues, currentValues) => | |||
prevValues.activityType !== currentValues.activityType | |||
} | |||
> | |||
{({ getFieldValue }) => | |||
// (getFieldValue('activityType') == '6' || getFieldValue('activityType') == '7') ? ( | |||
// <Form.Item label="消费次数"> | |||
// <Form.List name="activityRewards"> | |||
// {(fields, { add, remove }) => ( | |||
// <> | |||
// {fields.map(({ key, name, fieldKey, ...restField }) => ( | |||
// <Space | |||
// key={key} | |||
// style={{ display: 'flex', marginBottom: 8 }} | |||
// align="baseline" | |||
// > | |||
// <Form.Item | |||
// {...restField} | |||
// name={[name, 'rewardValue']} | |||
// fieldKey={[fieldKey, 'rewardValue']} | |||
// rules={[{ required: true, message: '消费次数' }]} | |||
// > | |||
// <InputNumber /> | |||
// </Form.Item> | |||
// | |||
// {`根据店铺实际情况奖励`} | |||
// <MinusCircleOutlined onClick={() => remove(name)} /> | |||
// </Space> | |||
// ))} | |||
// <Form.Item> | |||
// <Button type="dashed" onClick={() => { | |||
// if (getFieldValue('activityType') == '7') { | |||
// if (!getFieldValue('activityRewards')||getFieldValue('activityRewards').length < 1) { | |||
// add(); | |||
// }else{ | |||
// message.warning('添加数量已达到上限'); | |||
// } | |||
// } else { | |||
// add(); | |||
// } | |||
// }} block icon={<PlusOutlined />}> | |||
// 添加 | |||
// </Button> | |||
// </Form.Item> | |||
// </> | |||
// )} | |||
// </Form.List> | |||
// </Form.Item> | |||
// ) : | |||
(getFieldValue('activityType') == '2' || | |||
getFieldValue('activityType') == '4' || | |||
getFieldValue('activityType') == '5'|| | |||
getFieldValue('activityType') == '6'|| | |||
getFieldValue('activityType') == '7') ? ( | |||
<Form.Item label={getFieldValue('activityType')=='6'||getFieldValue('activityType')=='7'?'消费次数':'消费金额'}> | |||
<Form.List name="activityRewards"> | |||
{(fields, { add, remove }) => ( | |||
<> | |||
{fields.map(({ key, name, fieldKey, ...restField }) => ( | |||
<Space | |||
key={key} | |||
style={{ display: 'flex', marginBottom: 8 }} | |||
align="baseline" | |||
> | |||
<Form.Item | |||
{...restField} | |||
name={[name, 'rewardValue']} | |||
fieldKey={[fieldKey, 'rewardValue']} | |||
rules={[{ required: true, message: '请输入消费金额' }]} | |||
> | |||
<InputNumber /> | |||
</Form.Item> | |||
| |||
<Form.Item | |||
{...restField} | |||
label={getFieldValue('activityType')=='6'||getFieldValue('activityType')=='7'||getFieldValue('activityType')=='4' ? "赠送优惠券" : getFieldValue('activityType') == '5' ? "赠送商品" : "赠送金额"} | |||
name={[name, 'value']} | |||
fieldKey={[fieldKey, 'value']} | |||
rules={[{ required: true, message: '请选择' }]} > | |||
{[4,6,7].includes(getFieldValue('activityType')) ? ( | |||
<Select style={{ width: 200 }} > | |||
{couponInfo.map((item, index) => { | |||
return ( | |||
<Select.Option | |||
index={index} | |||
value={item.couponId} | |||
key={item.couponId} | |||
> | |||
{item.couponName} | |||
</Select.Option> | |||
); | |||
})} | |||
</Select>) : | |||
getFieldValue('activityType') == '5' ? ( | |||
<Select style={{ width: 200 }} > | |||
{goodsInfo.map((item, index) => { | |||
return ( | |||
<Select.Option index={index} value={item.id} key={item.id}> | |||
{item.name} | |||
</Select.Option> | |||
); | |||
})} | |||
</Select> | |||
) : | |||
(<InputNumber />) | |||
} | |||
</Form.Item> | |||
<MinusCircleOutlined onClick={() => remove(name)} /> | |||
</Space> | |||
))} | |||
<Form.Item> | |||
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}> | |||
添加 | |||
</Button> | |||
</Form.Item> | |||
</> | |||
)} | |||
</Form.List> | |||
</Form.Item> | |||
) : getFieldValue('activityType') == '3' ? ( | |||
// --------------特价商品-------------- | |||
<> | |||
<Form.Item label="商品信息"> | |||
<Form.List name="activityRewards"> | |||
{(fields, { add, remove }) => ( | |||
<> | |||
{fields.map(({ key, name, fieldKey, ...restField }) => ( | |||
<Space | |||
key={key} | |||
style={{ display: 'flex', marginBottom: 8 }} | |||
align="baseline" | |||
> | |||
<Form.Item | |||
{...restField} | |||
name={[name, 'rewardValue']} | |||
fieldKey={[fieldKey, 'rewardValue']} | |||
rules={[{ required: true, message: '请选择商品' }]} | |||
> | |||
<Select | |||
style={{ width: 200 }} | |||
onChange={(value) => { | |||
const arr = [...goodsPrice]; //克隆的时候改变地址 | |||
goodsInfo.filter((item) => { | |||
if (item.id == value) { | |||
arr[key] = item; | |||
} | |||
}); | |||
SetGoodsPrice(arr); | |||
}} | |||
> | |||
{goodsInfo.map((item, index) => { | |||
return ( | |||
<Select.Option index={index} value={item.id} key={item.id}> | |||
{item.name} | |||
</Select.Option> | |||
); | |||
})} | |||
</Select> | |||
</Form.Item> | |||
{/* {`原价:${goodsPrice[key]?.price}`} */} | |||
{`原价:${canUseCountOrlimit(key, props.values.activityRewards, 3)}`} | |||
| |||
<Form.Item | |||
{...restField} | |||
label="促销价" | |||
name={[name, 'value']} | |||
fieldKey={[fieldKey, 'value']} | |||
rules={[{ required: true, message: '请输入促销价' }]} > | |||
<Select mode="tags" style={{ width: '160px' }} placeholder="请输入促销价" maxTagCount='responsive'> | |||
{[]} | |||
</Select> | |||
</Form.Item> | |||
<MinusCircleOutlined onClick={() => remove(name)} /> | |||
</Space> | |||
))} | |||
<Form.Item> | |||
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}> | |||
添加 | |||
</Button> | |||
</Form.Item> | |||
</> | |||
)} | |||
</Form.List> | |||
</Form.Item> | |||
</> | |||
// --------------end特价商品end-------------- | |||
) : ( | |||
// --------------优惠券-------------- | |||
<> | |||
<Form.Item label="优惠券"> | |||
<Form.List name="activityRewards"> | |||
{(fields, { add, remove }) => ( | |||
<> | |||
{fields.map(({ key, name, fieldKey, ...restField }) => ( | |||
<Space | |||
key={key} | |||
style={{ display: 'flex', marginBottom: 8 }} | |||
align="baseline" | |||
> | |||
<Form.Item | |||
{...restField} | |||
name={[name, 'rewardValue']} | |||
fieldKey={[fieldKey, 'rewardValue']} | |||
rules={[{ required: true, message: '请选择优惠券' }]} | |||
> | |||
<Select | |||
style={{ width: 200 }} | |||
onChange={(value) => { | |||
const arr = [...quantityLimit]; //克隆的时候改变地址 | |||
couponInfo.filter((item) => { | |||
if (item.couponId == value) { | |||
arr[key] = item; | |||
} | |||
}); | |||
SetQuantityLimit(arr); | |||
}} | |||
> | |||
{couponInfo.map((item, index) => { | |||
return ( | |||
<Select.Option | |||
index={index} | |||
value={item.couponId} | |||
key={item.couponId} | |||
> | |||
{item.couponName} | |||
</Select.Option> | |||
); | |||
})} | |||
</Select> | |||
</Form.Item> | |||
{`可用数量:${canUseCountOrlimit(key, props.values.activityRewards, 1)}`} | |||
| |||
<Form.Item | |||
{...restField} | |||
label={getFieldValue('activityType') == 4 ? "兑换值" : "每次领取数量"} | |||
name={[name, 'value']} | |||
fieldKey={[fieldKey, 'value']} | |||
rules={[ | |||
{ required: true, message: getFieldValue('activityType') == 4 ? "请输入兑换值" : "请输入发送数量" }, | |||
{ | |||
type: 'number', | |||
min: 0, | |||
max: parseInt( | |||
getFieldValue('activityType') == 4 ? 999999999 : canUseCountOrlimit(key, props.values.activityRewards, 2), | |||
), | |||
message: '超出领取限制', | |||
}, | |||
]} | |||
> | |||
<InputNumber | |||
min={0} | |||
max={getFieldValue('activityType') == 4 ? 999999999 : canUseCountOrlimit(key, props.values.activityRewards, 2)} | |||
placeholder={getFieldValue('activityType') == 4 ? "兑换值" : "每次领取数量"} | |||
/> | |||
</Form.Item> | |||
<MinusCircleOutlined onClick={() => remove(name)} /> | |||
</Space> | |||
))} | |||
<Form.Item> | |||
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}> | |||
添加 | |||
</Button> | |||
</Form.Item> | |||
</> | |||
)} | |||
</Form.List> | |||
</Form.Item> | |||
</> | |||
//--------------end优惠券end-------------- | |||
) | |||
} | |||
</Form.Item> | |||
<Form.Item noStyle> | |||
<Form.Item {...tailFormItemLayout} style={{ height: 32 }}> | |||
<Form.Item | |||
name="activityStartTime" | |||
label="开始时间" | |||
style={{ float: 'left' }} | |||
rules={[{ required: true }]} | |||
> | |||
<DatePicker placeholder="开始时间" /> | |||
</Form.Item> | |||
<Form.Item | |||
name="activityEndTime" | |||
label="结束时间" | |||
style={{ float: 'left', marginLeft: 20 }} | |||
rules={[{ required: true }]} | |||
> | |||
<DatePicker placeholder="结束时间" /> | |||
</Form.Item> | |||
</Form.Item> | |||
</Form.Item> | |||
<Form.Item name="remark" label="备注" rules={[{ required: true, max: 200 }]}> | |||
<Input placeholder="请输入备注" /> | |||
</Form.Item> | |||
<Form.Item name="activityRule" label="规则说明" rules={[{ required: true, max: 500 }]}> | |||
<Input placeholder="请输入规则说明" /> | |||
</Form.Item> | |||
<Form.Item> | |||
<Button type="primary" htmlType="submit" style={{ float: 'right' }}> | |||
保存 | |||
</Button> | |||
</Form.Item> | |||
</span> | |||
</Form> | |||
</Modal> | |||
); | |||
}; | |||
export default CreateForm; |
@@ -0,0 +1,427 @@ | |||
import { PlusOutlined } from '@ant-design/icons'; | |||
import { Switch, Popconfirm, Space, Tag, 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 moment from 'moment'; | |||
import CreateForm from './components/CreateForm'; | |||
import ConfigurationForm from './components/ConfigurationForm'; | |||
import { CloseOutlined, CheckOutlined } from '@ant-design/icons'; | |||
import QRCode from 'qrcode.react'; | |||
import { | |||
GetactivityInfo, | |||
AddActivityInfo, | |||
UpdateActivityInfo, | |||
DelActivityInfo, | |||
GetActivityConfiguration, | |||
UpdateActivityAllState, | |||
GetCanUseCoupon, | |||
} from './service'; | |||
const key = 'message'; | |||
const activityInfManage = () => { | |||
const actionRef = useRef(); | |||
const [currentRow, setCurrentRow] = useState(); | |||
const [createModalVisible, handleModalVisible] = useState(false); | |||
const [ConfigurationModalVisible, handleConfigurationModalVisible] = useState(false); | |||
const [activityConfiguration, SetactivityConfiguration] = useState([]); | |||
const [activityId, SetActivityId] = useState([]); | |||
//新增 | |||
const handleAdd = async (fields) => { | |||
message.loading('正在添加...', key); | |||
try { | |||
if(fields.activityType==3){ | |||
fields.activityRewards.forEach(element => { | |||
element.value=`[${element.value.join()}]` | |||
}); | |||
} | |||
await AddActivityInfo(JSON.stringify(fields)).then((r) => { | |||
message.destroy(key); | |||
if (r.data) { | |||
message.success('添加成功'); | |||
} else { | |||
message.error('添加失败请重试!'); | |||
} | |||
}); | |||
//刷新数据 | |||
actionRef.current.reload(); | |||
return true; | |||
} catch (error) { | |||
message.destroy(key); | |||
message.error('添加失败请重试!'); | |||
return false; | |||
} | |||
}; | |||
//删除(支持批量删除) | |||
const handleRemove = async (value) => { | |||
message.loading('正在删除...', key); | |||
try { | |||
let ids = []; | |||
value.forEach((item) => { | |||
ids.push(item.id); | |||
}); | |||
await DelActivityInfo(JSON.stringify(ids)).then((r) => { | |||
message.destroy(key); | |||
if (r.data) { | |||
message.destroy(key); | |||
message.success('删除成功'); | |||
} else { | |||
message.error('删除失败请重试!'); | |||
} | |||
}); | |||
//刷新数据 | |||
actionRef.current.reload(); | |||
return true; | |||
} catch (error) { | |||
message.destroy(key); | |||
message.error('删除失败请重试!'); | |||
return false; | |||
} | |||
}; | |||
//修改 | |||
const handleUpdate = async (fields) => { | |||
message.loading('正在修改...', key); | |||
try { | |||
debugger | |||
if(fields.activityType==3){ | |||
debugger | |||
fields.activityRewards.forEach(element => { | |||
element.value=`[${element.value.join()}]` | |||
}); | |||
} | |||
await UpdateActivityInfo(JSON.stringify(fields)).then((r) => { | |||
message.destroy(key); | |||
if (r.data) { | |||
message.success('修改成功'); | |||
} else { | |||
message.error(r.errors); | |||
} | |||
}); | |||
//刷新数据 | |||
actionRef.current.reload(); | |||
return true; | |||
} catch (error) { | |||
message.destroy(key); | |||
message.error('修改失败请重试!'); | |||
return false; | |||
} | |||
}; | |||
//修改活动状态 | |||
const updateActivityState = async (fields) => { | |||
message.loading('正在修改...', key); | |||
try { | |||
await UpdateActivityAllState(JSON.stringify(fields)).then((r) => { | |||
message.destroy(key); | |||
if (r.data) { | |||
message.success('修改成功'); | |||
} else { | |||
message.error(r.errors); | |||
} | |||
}); | |||
//刷新数据 | |||
actionRef.current.reload(); | |||
return true; | |||
} catch (error) { | |||
message.destroy(key); | |||
message.error('修改失败请重试!'); | |||
return false; | |||
} | |||
}; | |||
const columns = [ | |||
{ | |||
title: '主键', | |||
dataIndex: 'id', | |||
hideInSearch: true, | |||
hideInTable: true, | |||
tip: '规则名称是唯一的 key', | |||
}, | |||
{ | |||
title: '图片', | |||
dataIndex: 'activityImg', | |||
valueType: 'textarea', | |||
hideInSearch: true, | |||
render: (_, record) => ( | |||
<img src={record.activityImg} style={{ width: '45px', height: '45px' }}></img> | |||
), | |||
}, | |||
{ | |||
title: '活动名称', | |||
dataIndex: 'activityName', | |||
valueType: 'textarea', | |||
}, | |||
{ | |||
title: '活动类型', | |||
dataIndex: 'activityType', | |||
valueEnum: { | |||
1: { text: '送券', status: 'Processing' }, | |||
2: { text: '送现金', status: 'Success' }, | |||
3: { text: '商品促销', status: 'Success' }, | |||
4: { text: '商城优惠券', status: 'Success' }, | |||
}, | |||
}, | |||
{ | |||
title: '状态', | |||
dataIndex: 'status', | |||
valueEnum: { | |||
0: { text: '启用', status: 'Processing' }, | |||
1: { text: '禁用', status: 'Error' }, | |||
}, | |||
}, | |||
{ | |||
title: '是否显示', | |||
dataIndex: 'isShow', | |||
valueEnum: { | |||
0: { text: '不显示', status: 'Processing' }, | |||
1: { text: '显示', status: 'Error' }, | |||
}, | |||
render: (_, record) => ( | |||
<> | |||
<Switch | |||
checkedChildren="显示" | |||
unCheckedChildren="不显示" | |||
checked={record.isShow == 1} | |||
onChange={(checked) => { | |||
var data = { activityId: record.id, type: 2, value: record.isShow }; | |||
data.value = checked ? 1 : 0; | |||
updateActivityState(data); | |||
}} | |||
/> | |||
</> | |||
), | |||
}, | |||
{ | |||
title: '是否后台计算', | |||
dataIndex: 'isAutoSend', | |||
valueEnum: { | |||
0: { text: '不显示', status: 'Processing' }, | |||
1: { text: '显示', status: 'Error' }, | |||
}, | |||
render: (_, record) => ( | |||
<> | |||
<Switch | |||
checkedChildren="点击参与" | |||
unCheckedChildren="后台计算" | |||
checked={record.isAutoSend == 1} | |||
onChange={(checked) => { | |||
var data = { activityId: record.id, type: 3, value: record.isAutoSend }; | |||
data.value = checked ? 1 : 0; | |||
updateActivityState(data); | |||
}} | |||
/> | |||
</> | |||
), | |||
}, | |||
{ | |||
title: '活动时间', | |||
hideInSearch: true, | |||
render: (text, record, index) => { | |||
return ( | |||
<span> | |||
<div> | |||
{moment(record.activityStartTime).format('YYYY-MM-DD')}至 | |||
{moment(record.activityEndTime).format('YYYY-MM-DD')} | |||
</div> | |||
</span> | |||
); | |||
}, | |||
}, | |||
{ | |||
title: '创建时间', | |||
dataIndex: 'createAt', | |||
valueType: 'date', | |||
renderFormItem: (item, { defaultRender, ...rest }, form) => { | |||
const status = form.getFieldValue('status'); | |||
if (`${status}` === '0') { | |||
return false; | |||
} | |||
if (`${status}` === '3') { | |||
return <Input {...rest} placeholder="请输入异常原因!" />; | |||
} | |||
return defaultRender(item); | |||
}, | |||
}, | |||
{ | |||
title: '操作', | |||
dataIndex: 'option', | |||
valueType: 'option', | |||
render: (_, record) => { | |||
let template = [ | |||
<a | |||
key="update" | |||
type="primary" | |||
onClick={() => { | |||
GetCanUseCoupon(record.id).then((r) => { | |||
if (r.data) { | |||
handleModalVisible(true); | |||
setCurrentRow(() => { | |||
record.activityStartTime = moment(record.activityStartTime); | |||
record.activityEndTime = moment(record.activityEndTime); | |||
record.activityRewards = r.data; | |||
if (record.activityType==3) | |||
{ | |||
record.activityRewards.forEach((item)=>{ | |||
item.value= JSON.parse(item.value) | |||
}) | |||
} | |||
return record; | |||
}); | |||
} | |||
}); | |||
}} | |||
> | |||
更新 | |||
</a>, | |||
<a | |||
key="jy" | |||
type="primary" | |||
onClick={() => { | |||
var data = { activityId: record.id, type: 1, value: record.status }; | |||
data.value = record.status == 0 ? 1 : 0; | |||
updateActivityState(data); | |||
}} | |||
> | |||
{record.status == 0 ? '禁用' : '启用'} | |||
</a>, | |||
<a | |||
key="pz" | |||
type="primary" | |||
onClick={() => { | |||
GetActivityConfiguration(record.id).then((r) => { | |||
if (r.data) { | |||
SetActivityId(record.id); | |||
SetactivityConfiguration(r.data); | |||
handleConfigurationModalVisible(true); | |||
} | |||
}); | |||
}} | |||
> | |||
配置 | |||
</a>, | |||
<Popconfirm | |||
type="primary" | |||
key="del" | |||
title="确认删除吗?" | |||
okText="是" | |||
cancelText="否" | |||
onConfirm={() => { | |||
handleRemove([record]); | |||
}} | |||
onCancel={() => {}} | |||
> | |||
<a href="#">删除</a> | |||
</Popconfirm>, | |||
<a | |||
key="primary" | |||
onClick={() => { | |||
const config = { | |||
title: '二维码', | |||
content: ( | |||
<QRCode | |||
id="qrCode" | |||
value={`https://black-pa.com:5443/lead?activityId=${record.id}`} | |||
size={200} // 二维码的大小 | |||
fgColor="#000000" // 二维码的颜色 | |||
style={{ margin: 'auto' }} | |||
/> | |||
), | |||
}; | |||
Modal.info(config); | |||
}} | |||
> | |||
{' '} | |||
二维码 | |||
</a> | |||
]; | |||
return template; | |||
}, | |||
}, | |||
]; | |||
return ( | |||
<PageContainer> | |||
<ProTable | |||
headerTitle="活动信息" | |||
actionRef={actionRef} | |||
rowKey="id" | |||
search={{ | |||
labelWidth: 120, | |||
}} | |||
request={async (params) => { | |||
var data = []; | |||
var total = 0; | |||
await GetactivityInfo(params).then((r) => { | |||
data = r.data.data; | |||
total = r.data.total; | |||
}); | |||
return { | |||
data: data, | |||
success: true, | |||
total: total, | |||
}; | |||
}} | |||
toolBarRender={() => [ | |||
<Button | |||
key="primary" | |||
type="primary" | |||
onClick={() => { | |||
handleModalVisible(true); | |||
}} | |||
> | |||
新建 | |||
</Button>, | |||
]} | |||
columns={columns} | |||
// rowSelection={{}} | |||
/> | |||
<CreateForm | |||
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(); | |||
} | |||
} | |||
actionRef.current.reload(); | |||
}} | |||
onCancel={() => { | |||
setCurrentRow(undefined); | |||
handleModalVisible(false); | |||
}} | |||
createModalVisible={createModalVisible} | |||
values={currentRow || {}} | |||
/> | |||
<ConfigurationForm | |||
onCancel={() => { | |||
setCurrentRow(undefined); | |||
handleConfigurationModalVisible(false); | |||
}} | |||
activityId={activityId} | |||
config={activityConfiguration} | |||
createModalVisible={ConfigurationModalVisible} | |||
values={currentRow || {}} | |||
/> | |||
</PageContainer> | |||
); | |||
}; | |||
export default activityInfManage; |
@@ -0,0 +1,98 @@ | |||
import { request } from 'umi'; | |||
export function GetactivityInfo(data) { | |||
return request(`/kitchen/api/activity-info/activity-info`, { | |||
method: 'post', | |||
data: data, | |||
}); | |||
} | |||
export function AddActivityInfo(data) { | |||
return request(`/kitchen/api/activity-info`, { | |||
method: 'post', | |||
data: data, | |||
}); | |||
} | |||
export async function GetCosRequestURL(data) { | |||
return request(`/kitchen/api/systemconfig/GetCosRequestSignURL`, { | |||
method: 'POST', | |||
data: data, | |||
}); | |||
} | |||
export function UpdateActivityInfo(data) { | |||
return request(`/kitchen/api/activity-info`, { | |||
method: 'put', | |||
data: data, | |||
}); | |||
} | |||
export function DelActivityInfo(data) { | |||
return request(`/kitchen/api/activity-info/batch-del-activity-info`, { | |||
method: 'post', | |||
data: data, | |||
}); | |||
} | |||
export function GetActivityConfiguration(data) { | |||
return request(`/kitchen/api/activity-info/activity-configuration/${data}`, { | |||
method: 'get', | |||
}); | |||
} | |||
//添加活动配置 | |||
export function AddactivityConfiguration(data) { | |||
return request(`/kitchen/api/activity-info/addactivity-configuration`, { | |||
method: 'Post', | |||
data: data, | |||
}); | |||
} | |||
//修改活动状态 | |||
export function UpdateActivityAllState(data) { | |||
return request(`/kitchen/api/activity-info/activity-all-state`, { | |||
method: 'put', | |||
data: data, | |||
}); | |||
} | |||
//获取优惠券 | |||
export function GetCouponCanUseCountAndLimit(data) { | |||
return request(`/kitchen/api/coupon-helper/coupon-can-use-count-and-limit`, { | |||
method: 'get', | |||
}); | |||
} | |||
//获取活动优惠券的配置 | |||
export function GetCanUseCoupon(data) { | |||
return request(`/kitchen/api/activity-info/activity-reward-list/${data}`, { | |||
method: 'get', | |||
}); | |||
} | |||
//获取商品信息 | |||
export function GetGoodsInfo() { | |||
return request(`/kitchen/api/activity-info/goods-info`, { | |||
method: 'get', | |||
}); | |||
} | |||
/** 获取商品类型 GET /kitchen/api/rule */ | |||
export async function goodsType(data) { | |||
return request(`/kitchen/api/goods-type/goods-types`, { | |||
method: 'POST', | |||
data: data, | |||
// params: { ...params }, | |||
// ...(options || {}), | |||
}); | |||
} | |||
//获取店铺信息 | |||
export async function GetStoreInfos(data) { | |||
return request(`/kitchen/api/printer/store-infos`, { | |||
method: 'Post', | |||
data: data, | |||
}); | |||
} |
@@ -0,0 +1,135 @@ | |||
import { DropboxOutlined, PlusOutlined } from '@ant-design/icons'; | |||
import { Button, message, Input, Drawer, Popconfirm } from 'antd'; | |||
import React, { useState, useRef, useEffect } from 'react'; | |||
import moment from 'moment'; | |||
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'; | |||
//页面 相当于 class | |||
const activityRecordManage = () => { | |||
/** 新建/更新窗口的弹窗 */ | |||
const [createModalVisible, handleModalVisible] = useState(false); | |||
/** 分布更新窗口的弹窗 */ | |||
const [showDetail, setShowDetail] = useState(false); | |||
//绑定 | |||
const actionRef = useRef(); | |||
//活动信息 | |||
const [activityInfo, setActivityInfo] = useState(); | |||
//初始化数据 | |||
useEffect(() => { | |||
const initActivityInfo = async (value) => { | |||
var data = await api.GetActivityInfo(); | |||
var list = []; | |||
for (let k in data.data) { | |||
list.push({ activity_Id: k.toString(), text: data.data[k.toString()] }); | |||
} | |||
setActivityInfo(list); | |||
}; | |||
initActivityInfo(); | |||
}, []); | |||
/** 国际化配置 */ | |||
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: 'customerName', | |||
valueType: 'textarea', | |||
}, | |||
{ | |||
title: '客户电话', | |||
dataIndex: 'telePhone', | |||
valueType: 'textarea', | |||
}, | |||
{ | |||
title: '活动名称', | |||
dataIndex: 'activity_Id', | |||
hideInForm: true, | |||
hideInTable: true, | |||
valueType: 'select', | |||
valueEnum: activityInfo, | |||
}, | |||
{ | |||
title: '活动名称', | |||
dataIndex: 'activityName', | |||
hideInForm: true, | |||
hideInSearch: true, | |||
}, | |||
{ | |||
title: '活动时间范围', | |||
hideInSearch: true, | |||
width: 300, | |||
render: (text, record, index) => { | |||
return ( | |||
<span> | |||
<div> | |||
{moment(record.activityStartTime).format('YYYY-MM-DD')}至 | |||
{moment(record.activityEndTime).format('YYYY-MM-DD')} | |||
</div> | |||
</span> | |||
); | |||
}, | |||
}, | |||
{ | |||
title: '创建时间', | |||
dataIndex: 'createAt', | |||
valueType: 'date', | |||
}, | |||
]; | |||
return ( | |||
<PageContainer> | |||
<ProTable | |||
headerTitle="活动参与记录信息" | |||
actionRef={actionRef} | |||
rowKey="id" | |||
search={{ | |||
labelWidth: 120, | |||
}} | |||
columns={columns} | |||
//数据绑定 | |||
request={async (params) => { | |||
var activityRecordData = []; | |||
var total = 0; | |||
if (params.activity_Id != undefined) { | |||
params.activity_Id = activityInfo[params.activity_Id].activity_Id; | |||
} | |||
await api.activityRecordList(params).then((r) => { | |||
activityRecordData = r.data.data; | |||
total = r.data.total; | |||
}); | |||
return { | |||
data: activityRecordData, | |||
success: true, | |||
total: total, | |||
}; | |||
}} | |||
></ProTable> | |||
</PageContainer> | |||
); | |||
}; | |||
export default activityRecordManage; |
@@ -0,0 +1,60 @@ | |||
// @ts-ignore | |||
/* eslint-disable */ | |||
import { request } from 'umi'; | |||
export default { | |||
/** 获取活动记录信息 sdsa GET /kitchen/api/rule */ | |||
activityRecordList(data) { | |||
return request(`/kitchen/api/activity-participation-record/activity-record`, { | |||
method: 'POST', | |||
data: data, | |||
// params: { ...params }, | |||
// ...(options || {}), | |||
}); | |||
}, | |||
/** 新建活动记录信息 POST /kitchen/api/rule */ | |||
addActivityRecord(data) { | |||
// http://localhost:7002 | |||
return request(`/kitchen/api/goods`, { | |||
method: 'POST', | |||
// type:'json', | |||
data: data, | |||
// ...(options || {}), | |||
}); | |||
}, | |||
/** 修改活动记录信息 POST /kitchen/api/rule */ | |||
updateActivityRecord(data) { | |||
// http://localhost:7002 | |||
return request(`/kitchen/api/goods`, { | |||
method: 'POST', | |||
// type:'json', | |||
data: data, | |||
// ...(options || {}), | |||
}); | |||
}, | |||
/** 删除活动记录信息 POST /kitchen/api/rule */ | |||
removeActivityRecord(data) { | |||
return request(`/kitchen/api/goods`, { | |||
method: 'DELETE', | |||
data: data, | |||
// ...(options || {}), | |||
}); | |||
}, | |||
/** 删除单个活动记录信息 DELETE /kitchen/api/rule */ | |||
removeSingleActivityRecord(id) { | |||
return request(`/kitchen/api/franchisee/${id}`, { | |||
method: 'DELETE', | |||
// data: data, | |||
// ...(options || {}), | |||
}); | |||
}, | |||
/** 获取活动信息 DELETE /kitchen/api/rule */ | |||
GetActivityInfo() { | |||
return request(`/kitchen/api/select/activity`, { | |||
method: 'get', | |||
// params: { ...params }, | |||
// ...(options || {}), | |||
}); | |||
}, | |||
}; |
@@ -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 '../../../sys/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 '../../../sys/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,438 @@ | |||
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 '../../sys/Org/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() { | |||
console.log('OK', record); | |||
}, | |||
}); | |||
} 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,56 @@ | |||
import { request } from 'umi'; | |||
export async function queryUser(params) { | |||
console.log('查询', params); | |||
return request('/kitchen/api/sysUser/page', { | |||
method: 'POST', | |||
data: { | |||
...params, | |||
}, | |||
}); | |||
} | |||
export async function removeRule(params) { | |||
return request('/kitchen/api/rule', { | |||
method: 'POST', | |||
data: { | |||
...params, | |||
method: 'delete', | |||
}, | |||
}); | |||
} | |||
export async function addUser(params) { | |||
return request('/kitchen/api/sysUser/add', { | |||
method: 'POST', | |||
data: { | |||
...params, | |||
}, | |||
}); | |||
} | |||
export async function updateUser(params) { | |||
return request('/kitchen/api/sysUser/add', { | |||
method: 'POST', | |||
data: { | |||
...params, | |||
}, | |||
}); | |||
} | |||
export async function setRoles(params) { | |||
return request(`/kitchen/api/sysUser/grantRole`, { | |||
method: 'POST', | |||
data: { | |||
...params, | |||
}, | |||
}); | |||
} | |||
export async function sysUserGrantData(params) { | |||
return request(`/kitchen/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(`/kitchen/api/GoodsBom/GetBomReplac`, { | |||
method: 'Post', | |||
data: data, | |||
// params: { ...params }, | |||
// ...(options || {}), | |||
}); | |||
}, | |||
updateGoodsBom(data) { | |||
return request('/kitchen/api/GoodsBom/EditReplac', { | |||
method: 'PUT', | |||
data: data, | |||
// ...(options || {}), | |||
}); | |||
}, | |||
addGoodsBom(data) { | |||
// http://localhost:7002 | |||
return request('/kitchen/api/GoodsBom/AddReplac', { | |||
method: 'POST', | |||
// type:'json', | |||
data: data, | |||
// ...(options || {}), | |||
}); | |||
}, | |||
/** 获取商品商品 sdsa GET /kitchen/api/rule */ | |||
goodList(data) { | |||
return request(`/kitchen/api/goods/goodss`, { | |||
method: 'Post', | |||
data: data, | |||
// params: { ...params }, | |||
// ...(options || {}), | |||
}); | |||
}, | |||
/** 获取原料信息 POST /kitchen/api/rule */ | |||
postStockGoods() { | |||
return request(`/kitchen/api/frachisee-stock-adjust/product-list`, { | |||
method: 'Get', | |||
// ...(options || {}), | |||
}); | |||
}, | |||
}; |
@@ -0,0 +1,55 @@ | |||
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="请选择原料名称"> | |||
{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="bomQty" label="配方用量"> | |||
<InputNumber placeholder="配方用量" min={0} /> | |||
</Form.Item> | |||
<Form.Item name="isReplace" label="是否可以替换" > | |||
<Switch checkedChildren="否" unCheckedChildren="是" /> | |||
</Form.Item> | |||
<Form.Item> | |||
<Button type="primary" htmlType="submit"> | |||
保存 | |||
</Button> | |||
</Form.Item> | |||
</Form> | |||
</Modal> | |||
); | |||
}; | |||
export default AddBomInfo; |