9b990809 by charles

feat: 项目初始化

0 parents
Showing 150 changed files with 4676 additions and 0 deletions
# 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
/lambda/
/scripts
/config
.history
\ No newline at end of file
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
}
};
# 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
/dist
# production
/.vscode
# misc
.DS_Store
npm-debug.log*
yarn-error.log
/coverage
.idea
yarn.lock
package-lock.json
*bak
.vscode
# visual studio code
.history
*.log
functions/*
.temp/**
# umi
.umi
.umi-production
# screenshot
screenshot
.firebase
.eslintcache
build
**/*.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
\ No newline at end of file
const fabric = require("@umijs/fabric");
module.exports = {
...fabric.prettier
};
const fabric = require('@umijs/fabric');
module.exports = {
...fabric.stylelint,
};
# 项目名称
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).
import defaultSettings from './defaultSettings'; // https://umijs.org/config/
import slash from 'slash2';
import routes from './routerConfig';
import proxy from './proxy';
const { pwa, primaryColor } = defaultSettings; // preview.pro.ant.design only do not use in your production ;
// preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION, REACT_APP_ENV } = process.env;
import { defineConfig, utils } from 'umi';
const { winPath } = utils;
export default defineConfig({
antd: {},
dva: {
hmr: true,
},
locale: {
default: 'zh-CN',
baseNavigator: true,
},
dynamicImport: {
// 无需 level, webpackChunkName 配置
// loadingComponent: './components/PageLoading/index'
loading: '@/components/PageLoading/index',
},
hash: true,
targets: {
ie: 11,
},
title: false,
// umi routes: https://umijs.org/zh/guide/router.html
routes: routes,
// Theme for antd: https://ant.design/docs/react/customize-theme-cn
theme: {
// ...darkTheme,
'primary-color': primaryColor,
},
define: {
REACT_APP_ENV: REACT_APP_ENV || false,
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION:
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION || '', // preview.pro.ant.design only do not use in your production ; preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
},
ignoreMomentLocale: true,
lessLoader: {
javascriptEnabled: true,
},
cssLoader: {
modules: {
getLocalIdent: (context, _, localName) => {
if (
context.resourcePath.includes('node_modules') ||
context.resourcePath.includes('ant.design.pro.less') ||
context.resourcePath.includes('global.less')
) {
return localName;
}
const match = context.resourcePath.match(/src(.*)/);
if (match && match[1]) {
const antdProPath = match[1].replace('.less', '');
const arr = slash(antdProPath)
.split('/')
.map(a => a.replace(/([A-Z])/g, '-$1'))
.map(a => a.toLowerCase());
return `antd-pro${arr.join('-')}-${localName}`.replace(/--/g, '-');
}
return localName;
},
},
},
manifest: {
basePath: '/',
},
proxy: proxy[REACT_APP_ENV || 'dev'],
externals: {
react: 'React',
'react-dom': 'ReactDOM',
bizcharts: 'BizCharts',
mockjs: 'Mock',
'@antv/data-set': 'DataSet',
},
});
import { routeMaps } from './routerConfig';
export default {
navTheme: 'dark',
primaryColor: '#185DA2',
layout: 'sidemenu',
contentWidth: 'Fluid',
fixedHeader: false,
autoHideHeader: false,
fixSiderbar: false,
colorWeak: false,
menu: {
locale: true,
},
title: '一网打“净”数智在线',
pwa: false,
iconfontUrl: '',
//缓存token的key
tokenKey: 'yh_token',
//md5的key
md5Key: 'QHZT',
// 路由对象
routeMaps,
};
import path from 'path';
function getModulePackageName(module) {
if (!module.context) return null;
const nodeModulesPath = path.join(__dirname, '../node_modules/');
if (module.context.substring(0, nodeModulesPath.length) !== nodeModulesPath) {
return null;
}
const moduleRelativePath = module.context.substring(nodeModulesPath.length);
const [moduleDirName] = moduleRelativePath.split(path.sep);
let packageName = moduleDirName; // handle tree shaking
if (packageName && packageName.match('^_')) {
// eslint-disable-next-line prefer-destructuring
packageName = packageName.match(/^_(@?[^@]+)/)[1];
}
return packageName;
}
export const webpackPlugin = config => {
// optimize chunks
config.optimization // share the same chunks across different modules
.runtimeChunk(false)
.splitChunks({
chunks: 'async',
name: 'vendors',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendors: {
test: module => {
const packageName = getModulePackageName(module) || '';
if (packageName) {
return [
'bizcharts',
'gg-editor',
'g6',
'@antv',
'gg-editor-core',
'bizcharts-plugin-slider',
].includes(packageName);
}
return false;
},
name(module) {
const packageName = getModulePackageName(module);
if (packageName) {
if (['bizcharts', '@antv_data-set'].indexOf(packageName) >= 0) {
return 'viz'; // visualization package
}
}
return 'misc';
},
},
},
});
};
/**
* 在生产环境 代理是无法生效的,所以这里没有生产环境的配置
* 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: {
'/ql/api/': {
target: 'https://yh2.jimilicai.com',
changeOrigin: true,
pathRewrite: {
'^': '',
},
},
},
};
export default {
path: '/',
name: 'appliction',
component: '../layouts/BasicLayout',
routes: [
{ path: '/', redirect: '/screen' },
{
path: '/screen',
name: 'dataScreen',
component: './Screen',
icons: 'screen',
resourceCode: 'Home',
},
{
path: '/risk',
name: 'risk',
component: './Risk',
icons: 'risk',
resourceCode: 'RiskManage',
},
{
path: '/event',
name: 'event',
component: './Incident',
icons: 'event',
resourceCode: 'EventManage',
},
{
path: '/data',
name: 'data',
component: './Data',
icons: 'data',
resourceCode: 'DataManage',
},
{
path: '/system',
name: 'system',
icons: 'system',
resourceCode: 'SystemManage',
routes: [
{
path: '/system/account',
name: 'account',
component: './System/Account',
icons: '',
resourceCode: 'AccountManage',
},
{
path: '/system/role',
name: 'role',
component: './System/Role',
icons: '',
resourceCode: 'RoleManage',
},
{
path: '/system/menu',
name: 'menu',
component: './System/Menu',
icons: '',
resourceCode: 'MenuManage',
},
],
},
{
component: './404',
resourceCode: '404',
},
],
};
import user from './user';
import app from './app';
import { cloneDeep } from 'lodash';
const getRoutes = (routes, maps = {}) => {
routes = routes.map(item => {
if (item.auths) {
maps[item.path] = item.auths;
}
if (item.routes) {
item.routes = getRoutes(item.routes, maps).routes;
}
return item;
});
routes.push({
component: '../pages/404',
});
return {
routes,
maps,
};
};
const { routes, maps } = getRoutes([user, app]);
export const routeMaps = maps;
export const codeMappingForResource = (() => {
let menu = cloneDeep([app]);
let codeMapping = new Map();
const getItem = arr => {
arr.forEach(item => {
codeMapping.set(item?.resourceCode, item);
if (item.routes) {
getItem(item.routes);
}
});
};
getItem(menu);
return codeMapping;
})();
export default routes;
export default {
path: '/user',
component: '../layouts/UserLayout',
routes: [
{ path: '/user', name: 'login', redirect: '/user/login' },
{ path: '/user/login', name: 'login', component: './User/Login' },
{ path: '/user/risk', name: 'login', component: './User/Risk' },
],
};
export default {
theme: [
{
key: 'dark',
fileName: 'dark.css',
theme: 'dark',
},
{
key: 'dust',
fileName: 'dust.css',
modifyVars: {
'@primary-color': '#F5222D',
},
},
{
key: 'volcano',
fileName: 'volcano.css',
modifyVars: {
'@primary-color': '#FA541C',
},
},
{
key: 'sunset',
fileName: 'sunset.css',
modifyVars: {
'@primary-color': '#FAAD14',
},
},
{
key: 'cyan',
fileName: 'cyan.css',
modifyVars: {
'@primary-color': '#13C2C2',
},
},
{
key: 'green',
fileName: 'green.css',
modifyVars: {
'@primary-color': '#52C41A',
},
},
{
key: 'geekblue',
fileName: 'geekblue.css',
modifyVars: {
'@primary-color': '#2F54EB',
},
},
{
key: 'purple',
fileName: 'purple.css',
modifyVars: {
'@primary-color': '#722ED1',
},
},
{
key: 'dust',
theme: 'dark',
fileName: 'dark-dust.css',
modifyVars: {
'@primary-color': '#F5222D',
},
},
{
key: 'volcano',
theme: 'dark',
fileName: 'dark-volcano.css',
modifyVars: {
'@primary-color': '#FA541C',
},
},
{
key: 'sunset',
theme: 'dark',
fileName: 'dark-sunset.css',
modifyVars: {
'@primary-color': '#FAAD14',
},
},
{
key: 'cyan',
theme: 'dark',
fileName: 'dark-cyan.css',
modifyVars: {
'@primary-color': '#13C2C2',
},
},
{
key: 'green',
theme: 'dark',
fileName: 'dark-green.css',
modifyVars: {
'@primary-color': '#52C41A',
},
},
{
key: 'geekblue',
theme: 'dark',
fileName: 'dark-geekblue.css',
modifyVars: {
'@primary-color': '#2F54EB',
},
},
{
key: 'purple',
theme: 'dark',
fileName: 'dark-purple.css',
modifyVars: {
'@primary-color': '#722ED1',
},
},
],
};
// ps https://github.com/GoogleChrome/puppeteer/issues/3120
module.exports = {
launch: {
args: [
'--disable-gpu',
'--disable-dev-shm-usage',
'--no-first-run',
'--no-zygote',
'--no-sandbox',
],
},
};
module.exports = {
testURL: 'http://localhost:8000',
preset: 'jest-puppeteer',
extraSetupFiles: ['./tests/setupTests.js'],
globals: {
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false,
localStorage: null,
},
};
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
{
"name": "ant-design-pro",
"version": "1.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 site && npm run gh-pages",
"dev": "npm run start:dev",
"fetch:blocks": "pro fetch-blocks --branch antd@4 && npm run prettier",
"format-imports": "cross-env import-sort --write '**/*.{js,jsx,ts,tsx}'",
"gh-pages": "cp CNAME ./dist/ && gh-pages -d dist",
"i18n-remove": "pro i18n-remove --locale=zh-CN --write",
"lint": "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": "check-prettier lint",
"lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
"prettier": "prettier -c --write \"**/*\"",
"start": "umi dev",
"start:dev": "cross-env REACT_APP_ENV=dev MOCK=none umi dev",
"start:no-mock": "cross-env MOCK=none umi dev",
"start:no-ui": "cross-env UMI_UI=none umi dev",
"start:pre": "cross-env REACT_APP_ENV=pre umi dev",
"start:test": "cross-env REACT_APP_ENV=test MOCK=none umi dev",
"test": "umi test",
"test:all": "node ./tests/run-tests.js",
"test:component": "umi test ./src/components",
"tsc": "tsc",
"ui": "umi ui"
},
"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/icons": "^4.0.0-rc.0",
"@ant-design/pro-layout": "^5.0.3",
"@antv/data-set": "^0.10.2",
"@umijs/preset-react": "^1.2.2",
"antd": "4.23.1",
"bizcharts": "^3.5.7",
"blueimp-md5": "^2.16.0",
"classnames": "^2.2.6",
"crypto-js": "^4.1.1",
"lodash": "^4.17.21",
"moment": "^2.24.0",
"omit.js": "^1.0.2",
"path-to-regexp": "2.4.0",
"qs": "^6.11.0",
"react": "^16.8.6",
"react-copy-to-clipboard": "^5.0.2",
"react-dom": "^16.8.6",
"react-helmet": "^5.2.1",
"react-redux": "^7.2.0",
"redux": "^4.0.1",
"styled-components": "^5.3.5",
"umi": "^3.0.0",
"umi-request": "^1.2.19",
"use-merge-value": "^1.0.1"
},
"devDependencies": {
"@ant-design/pro-cli": "^1.0.18",
"@types/classnames": "^2.2.7",
"@types/express": "^4.17.0",
"@types/history": "^4.7.2",
"@types/jest": "^25.1.0",
"@types/lodash": "^4.14.144",
"@types/qs": "^6.5.3",
"@types/react": "^16.9.17",
"@types/react-dom": "^16.8.4",
"@types/react-helmet": "^5.0.13",
"@umijs/fabric": "^2.0.2",
"chalk": "^3.0.0",
"check-prettier": "^1.0.3",
"cross-env": "^7.0.0",
"cross-port-killer": "^1.1.1",
"enzyme": "^3.11.0",
"express": "^4.17.1",
"gh-pages": "^2.0.1",
"husky": "^4.0.7",
"import-sort-cli": "^6.0.0",
"import-sort-parser-babylon": "^6.0.0",
"import-sort-parser-typescript": "^6.0.0",
"import-sort-style-module": "^6.0.0",
"jest-puppeteer": "^4.4.0",
"jsdom-global": "^3.0.2",
"lint-staged": "^10.0.0",
"mockjs": "^1.0.1-beta3",
"node-fetch": "^2.6.0",
"prettier": "^1.19.1",
"pro-download": "1.0.1",
"stylelint": "^13.0.0"
},
"optionalDependencies": {
"puppeteer": "^2.0.0"
},
"engines": {
"node": ">=10.0.0"
},
"checkFiles": [
"src/**/*.js*",
"src/**/*.ts*",
"src/**/*.less",
"config/**/*.js*",
"scripts/**/*.js"
]
}
No preview for this file type
import { createFromIconfontCN } from '@ant-design/icons';
/* 接口域名 */
const domain = window.location.hostname.toLowerCase();
export const urlConfig = {
'middletest.lipinclub.com': {
URL_API: 'http://middletest.lipinclub.com/api/',
},
'middle.lipinclub.com': {
URL_API: 'http://middle.lipinclub.com/api/',
},
}[domain] || {
URL_API: '/ql/api',
};
// 七牛云图片域名
export const IMG_URL = 'http://qn.qinwell.com/';
/* 自定义icon的地址 */
export const IconFontConfig = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_1948876_xye4vujgq2j.js',
});
/**
* Auther: APIS
*/
import React, { useEffect, useState } from 'react';
import { connect } from 'umi';
const AuthBlock = props => {
const { auth, userAuths, children } = props;
if (userAuths.includes(auth) || !auth) {
return <>{children}</>;
} else {
return null;
}
};
export default connect(({ user }) => {
return {
userAuths: user.userAuths,
};
})(AuthBlock);
/**
* Auther: APIS
*/
import React, { useEffect, useState } from 'react';
import { connect } from 'umi';
import NoFoundPage from '@/pages/404';
/**
* 路由权限控制
* 路由权限控制需要在路由配置里面对应的路由设置'auths'字段即可,支持字符串和数组,不需要加权限的路由不设置即可。
* 原则上一个页面允许存在多个权限code,但是不允许一个code对应多个页面
* @param {*} props
*/
const AuthRouter = props=> {
const { userAuths, routeMaps, path, children } = props;
const currentAuths = routeMaps[path];
let hasAuth = true;
if (currentAuths) {
if (Array.isArray(currentAuths)) {
hasAuth = currentAuths.some(item=> userAuths.includes(item));
} else {
hasAuth = userAuths.includes(currentAuths);
}
}
return <>{hasAuth ? children : <NoFoundPage />}</>;
};
export default connect(({user, settings})=> {
return {
userAuths: user.userAuths,
routeMaps: settings.routeMaps
}
})(AuthRouter);
\ No newline at end of file
/**
* Auther: APIS
*/
import React, { useEffect, useState } from 'react';
import { message} from 'antd';
import { CopyToClipboard } from 'react-copy-to-clipboard';
const style = {
cursor: 'pointer'
};
const Template = props=> {
const { text, children } = props;
useEffect(()=> {
}, [text]);
const onCopy = (test, result)=> {
if (result) {
message.success('已复制到剪切板~');
} else {
message.error('复制失败,请手动进行复制~');
}
}
return (
<CopyToClipboard text={text} onCopy={onCopy} style={style} title='点击可复制'>
{children}
</CopyToClipboard>
);
};
export default Template;
\ No newline at end of file
/**
* Auther: APIS
*/
import React, { useEffect, useState } from 'react';
const FileExport = props=> {
const { url, dispatch } = props;
useEffect(()=> {
const linkExport = document.getElementById('J_HandleExport');
if (url && linkExport) {
linkExport.click();
setTimeout(()=> {
dispatch({type: 'global/changeState', payload: { urlFileExport: '' }});
}, 100);
}
}, [url]);
return <a href={url} download id="J_HandleExport"></a>
};
export default FileExport;
\ No newline at end of file
/**
* 省市区formItem组件
* @param {*省市区的标签的文本} label
* @param {*省市区的标签的字段名} name
* @param {*详细地址的标签的文本} alabel
* @param {*详细地址的字段名} aname
*/
import React, { useState, useEffect, forwardRef } from 'react';
import { connect } from 'dva';
import { Card, Button, Row, Col, Input, Popover, Form, Cascader, Select } from 'antd';
import InputFormItem from '@/components/FormItems/InputFormItem';
const { TextArea } = Input;
const { Option } = Select;
const CitySelects = forwardRef((props, ref) => {
const { value = {}, onChange, options = [], disabled } = props;
const { province, city, district } = value;
const [provinceData, setprovinceData] = useState([]);
const [cityData, setcityData] = useState([]);
const [districtData, setdistrictData] = useState([]);
useEffect(() => {
if (options.length > 0) {
if (province) {
const plists = provinceData.filter(_ => _.label === province);
setcityData(plists[0].children);
}
}
}, [province]);
useEffect(() => {
if (city && cityData.length > 0) {
const cityLists = cityData.filter(_ => _.label === city);
setdistrictData(cityLists[0].children);
}
}, [city]);
useEffect(() => {
if (cityData.length > 0) {
const cityLists = cityData.filter(_ => _.label === city);
setdistrictData(cityLists.length > 0 ? cityLists[0].children : []);
}
}, [cityData]);
useEffect(() => {
setprovinceData(options);
}, [options]);
const handleProvinceChange = province => {
const provinceLists = provinceData.filter(_ => _.label === province);
setcityData(provinceLists[0].children);
onChange({
...value,
province,
city: '',
});
};
const onSecondCityChange = city => {
const cityLists = cityData.filter(_ => _.label === city);
setdistrictData(cityLists[0].children);
onChange({
...value,
city,
district: '',
});
};
const onDistrictChange = district => {
onChange({
...value,
district,
});
};
return (
<>
<Select
className="mr-10"
value={province}
style={{ width: 150 }}
onChange={handleProvinceChange}
placeholder="请选择省份"
disabled={disabled}
>
{provinceData.map(province => (
<Option key={province.label}>{province.label}</Option>
))}
</Select>
<Select
className="mr-10"
value={city}
style={{ width: 150 }}
onChange={onSecondCityChange}
placeholder="请选择城市"
disabled={disabled}
>
{cityData.map(city => (
<Option key={city.label}>{city.label}</Option>
))}
</Select>
<Select
value={district}
style={{ width: 150 }}
onChange={onDistrictChange}
placeholder="请选择区县"
disabled={disabled}
>
{districtData.map(district => (
<Option key={district.label}>{district.label}</Option>
))}
</Select>
</>
);
});
const FormItem = props => {
const {
name,
label,
alabel,
aname,
wrapperCol = { span: 10 },
BaseProvinceList,
dispatch,
form,
disabled,
} = props;
const validator = (rule, value = {}) => {
const { province, city, district } = value;
if (!province && !city && !district) {
return Promise.reject('请选择省市区!');
} else if (!province) {
return Promise.reject('请选择省份!');
} else if (!city) {
return Promise.reject('请选择城市!');
} else if (!district) {
return Promise.reject('请选择区县!');
} else {
return Promise.resolve();
}
};
return (
<>
<Form.Item
label={label}
name={name}
required
rules={[
{
validator,
},
]}
>
<CitySelects disabled={disabled} options={BaseProvinceList} />
</Form.Item>
<InputFormItem disabled={disabled} label={alabel} name={aname} max={25} />
</>
);
};
const mapStateToProps = ({ global }) => {
return {
BaseProvinceList: global.BaseProvinceList,
};
};
export default connect(mapStateToProps)(FormItem);
/**
* Author: wjw
* Date:
* Description:
*/
import React, { useState, useEffect, forwardRef } from 'react';
import { connect } from 'dva';
import { Card, Button, Row, Col, Input } from 'antd';
import styles from './index.less';
const { TextArea } = Input;
const InputCom = forwardRef((props, ref) => {
const { value, onChange, placeholder, max, type = 'input', style, disabled, defaultLen = 0 } = props;
const onIptChange = ({ target: { value } }) => {
if (onChange) {
onChange(value);
}
};
const suffix = (
<span style={{ lineHeight: '20px' }}>
{(value) ? (value.length + defaultLen) : ((!value && defaultLen) ? defaultLen : 0) }/{!defaultLen ? max : 500}
</span>
);
const params = {
autoComplete: 'off',
maxLength: max,
placeholder,
value,
style,
onChange: onIptChange,
disabled,
};
return (
<div>
{type === 'input' && <Input {...params} suffix={suffix} />}
{type === 'textArea' && (
<div style={{ position: 'relative' }}>
<TextArea {...params} rows={4} />
<div className={styles.suffixttextarea}>{suffix}</div>
</div>
)}
</div>
);
});
export default InputCom;
.suffixttextarea {
position: absolute;
bottom : 5px;
right : 10px;
}
\ No newline at end of file
/**
* 带有长度统计formItem组件
* @param {*标签的文本} label
* @param {*字段名} name
* @param {*占位符} placeholder
* @param {*最小长度} min
* @param {*最大长度} max
* @param {*input框类型input、textArea} type
* @param {*规则} rules
* @param {*是否必填} required
* @param {*必填提示} message
*/
import React, { useState, useEffect, forwardRef } from 'react';
import { connect } from 'dva';
import { Card, Button, Row, Col, Input, Popover, Form, Cascader, Upload } from 'antd';
import InputCom from './InputCom';
const FormItem = props => {
const { label, name, placeholder, max, type, rules, required = true, min = 0, message, extra } = props;
const placeholderText = placeholder ? placeholder : `请输入${label}`;
const rlueMessage = message ? message : placeholderText;
return (
<Form.Item
label={label}
name={name}
extra={extra}
rules={rules ? rules : [{ required: required, message: rlueMessage, min }]}
>
<InputCom {...props} placeholder={placeholderText} />
</Form.Item>
);
};
export default FormItem;
/**
* Author: wjw
* Date:
* Description:
*/
import React, { useState, useEffect, forwardRef } from 'react';
import { connect } from 'umi';
import { Card, Button, Row, Col, Input, Popover, Form } from 'antd';
import { CheckCircleOutlined } from '@ant-design/icons';
const hasLength = value => {
return value.length >= 6 && value.length <= 16;
};
const hasletter = value => {
for (var i in value) {
var asc = value.charCodeAt(i);
if ((asc >= 65 && asc <= 90) || (asc >= 97 && asc <= 122)) {
return true;
}
}
return false;
};
const hasnum = value => {
var str = value;
var reg = new RegExp(/\d+/gi);
if (reg.test(str)) {
return true;
} else {
return false;
}
};
const PasswordFormItem = forwardRef((props, ref) => {
const {
value = '',
onChange,
placeholder = '6 - 16位密码,包含字母和数字',
form,
visible,
prefix,
} = props;
const onIptChange = ({ target: { value } }) => {
onChange(value);
};
const [isObj, setIsObj] = useState({
count: false,
letter: false,
num: false,
});
useEffect(() => {
const nobj = {
count: hasLength(value),
letter: hasletter(value),
num: hasnum(value),
};
setIsObj(nobj);
}, [value]);
const isTure = bool => {
return (
<div className="mr-5">
{bool ? (
<CheckCircleOutlined style={{ color: '#52c41a' }} />
) : (
<div
style={{
width: '14px',
height: '14px',
border: '1px solid #ccc',
borderRadius: '100%',
}}
></div>
)}
</div>
);
};
const styles = {
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center',
};
const Content = props => {
return (
<div>
<div style={styles}>
{isTure(isObj.count)}输入6 - 16 位字符,特殊字符包括下划线,@,#等等
</div>
<div style={styles}>{isTure(isObj.letter)}包含字母</div>
<div style={styles}>{isTure(isObj.num)}包含数字</div>
</div>
);
};
return (
<Popover placement="right" content={<Content />} visible={visible}>
<Input.Password
prefix={prefix}
autoComplete="new-password"
placeholder={placeholder}
value={value}
onChange={onIptChange}
visibilityToggle={false}
/>
</Popover>
);
});
const FormItem = props => {
const { prefix, label } = props;
const [visible, setvisible] = useState(false);
const checkpassword = (rule, value) => {
if (!value || !(hasLength(value) && hasletter(value) && hasnum(value))) {
setvisible(true);
return Promise.reject('');
} else {
setvisible(false);
return Promise.resolve();
}
};
return (
<Form.Item required label={label} name="password" rules={[{ validator: checkpassword }]}>
<PasswordFormItem prefix={prefix} visible={visible} />
</Form.Item>
);
};
export default FormItem;
/**
* Author: wjw
* Date:
* Description:
*/
import React, { useState, useEffect, forwardRef } from 'react';
import { connect } from 'umi';
import { Card, Button, Icon, Row, Col, Input, Popover, Form, Select } from 'antd';
import { isPhone } from '@/utils/utils';
const { Option } = Select;
const FormItem = props => {
const { label = null, name, isPrefixSelector = false, cd, hasFeedback = true } = props;
const prefixSelector = (
<Form.Item name="prefix" noStyle>
<Select style={{ width: 70 }}>
<Option value="86">+86</Option>
</Select>
</Form.Item>
);
const validateTocheckMobile = (rule, value) => {
if (value) {
if (!isPhone(value)) {
return Promise.reject('请输入正确手机格式');
return;
} else {
if (cd) {
cd();
} else {
return Promise.resolve();
}
}
} else {
return Promise.resolve();
}
};
return (
<Form.Item
label={label}
hasFeedback={hasFeedback}
name={name}
rules={[
{
required: true,
message: '输入手机号',
},
{
validator: validateTocheckMobile,
},
]}
validateTrigger="onBlur"
>
<Input
addonBefore={isPrefixSelector ? prefixSelector : null}
style={{ width: '100%' }}
placeholder="11位手机号"
/>
</Form.Item>
);
};
export default FormItem;
/**
* Author: wjw
* Date:
* Description:
*/
import React, { useState, useEffect, forwardRef } from 'react';
import { connect } from 'dva';
import { Card, Button, Form, Radio } from 'antd';
const RadioFormItem = props => {
const { name, label, list = [], disabled } = props;
return (
<div>
<Form.Item label={label} name={name} rules={[{ required: true, message: '请选择' }]}>
<Radio.Group disabled={disabled}>
{list.map((item, index) => {
return (
<Radio key={index} value={item.value}>
{item.name}
</Radio>
);
})}
</Radio.Group>
</Form.Item>
</div>
);
};
export default RadioFormItem;
import React, { useState, useEffect, forwardRef } from 'react';
import { connect } from 'dva';
import { Card, Button, Upload, message } from 'antd';
import { urlConfig } from '@/common';
import styles from '../index.less';
import { getFileName, getFileType } from '@/utils/utils';
import { PlusOutlined, DownloadOutlined } from '@ant-design/icons';
// 上传图片的配置
const token = window.localStorage.getItem('qintaoyouxuan_token') || '';
const settings = {
name: 'file',
withCredentials: true,
headers: {
token,
},
};
const IMGUploadButton = ({ text }) => {
return (
<div>
<PlusOutlined />
<div className="ant-upload-text">{text}</div>
</div>
);
};
const UploadButton = ({ text }) => {
return (
<Button className={styles.textbtn}>
<DownloadOutlined />
{text}
</Button>
);
};
const UploadItem = forwardRef((props, ref) => {
const {
value,
onChange,
listType = 'text',
length = 1,
defaultFileList = [],
accept = 'image/jpg,image/jpeg,image/png',
maxSize = 2,
filemaxSize = 25,
dispatch,
style,
className,
text = '上传文件',
disabled,
children,
isError = false,
action = 'imgs/uploadImg',
} = props;
const serverURL = `${urlConfig.URL_API}${action}`;
const [fileList, setfileList] = useState(defaultFileList);
const [len, setlen] = useState(-1);
const [isupload, setIsupload] = useState(false);
useEffect(() => {
if (Array.isArray(value) && !isupload) {
let files = value.map((item, index) => {
let name = getFileName(item);
return {
uid: index,
name,
url: item,
thumbUrl: item,
status: 'done',
response: { status: 1, data: item },
percent: 100,
size: 0,
};
});
setfileList(files);
}
}, [value]);
useEffect(() => {
if (fileList.length >= len && len !== -1) {
let values = fileList.filter(item => item.url).map(i => i.url);
if (isError) {
values = fileList.map(i => {
const { status, response, url } = i;
return {
status,
url,
response,
};
});
}
if (onChange) {
onChange(values);
}
}
}, [fileList]);
const getisLtM = file => {
const { name, size } = file;
const type = getFileType(name);
const is = !!(type === '.pdf' || type === '.rar');
const MAX_SIZE = is ? filemaxSize : maxSize;
return {
MAX_SIZE,
isLtM: size / 1024 / 1024 < MAX_SIZE,
};
};
const getStatus = item => {
const { name, size, status, response, type } = item;
const MAX_SIZE = getisLtM(item).MAX_SIZE;
const isLtM = getisLtM(item).isLtM;
if (!isLtM) {
return {
...item,
status: status === 'removed' ? 'removed' : 'error',
response: `上传的文件大小不能超过${MAX_SIZE}M`,
url: item.url,
};
}
if (status === 'done') {
if (response && response.status !== 1) {
return {
...item,
status: 'error',
response: response.msg ? response.msg : response.data,
url: item.url,
};
}
}
return {
...item,
status: status,
response: response,
url:
item.status === 'done'
? item.response.data
: item.status === 'uploading'
? 'uploading'
: item.url,
};
};
const onFilechange = info => {
setIsupload(true);
let fileone = info.file;
let fileList = info.fileList;
fileone = getStatus(fileone);
if (fileone.status === 'done' && fileone.response) {
message.success('上传成功');
// onChange();
} else if (fileone.status === 'error' && fileone.response) {
message.error(`${fileone.response}`);
//不符合的文件删除
fileList = fileList.filter(_ => _.uid !== fileone.uid);
}
//长度限制
fileList = fileList
.filter((i, idx) => idx < length)
.map(item => {
return getStatus(item);
});
setfileList(fileList);
setlen(fileList.length);
};
const onRemove = file => {
setlen(fileList.length - 1);
};
const onPreview = file => {
const img = file.url;
if (img) {
handleAdd('PREVIEWIMG', { imgUrl: img });
}
};
const handleAdd = (modalType, r) => {
const payload = {
preImgDataModal: {
modalType,
modalShow: true,
modalData: r ? r : {},
},
};
dispatch({ type: 'global/changeState', payload });
};
//上传前判断文件大小是否符合
const imgupload = (file, fileList) => {
const isLtM = getisLtM(file).isLtM;
if (!isLtM) {
return false;
}
return isLtM;
};
return (
<Upload
action={serverURL}
// accept={accept}
multiple={!!(length > 1)}
onChange={onFilechange}
fileList={fileList}
{...settings}
beforeUpload={imgupload}
listType={listType}
onRemove={onRemove}
onPreview={listType === 'picture-card' ? onPreview : null}
style={style}
className={className}
disabled={disabled}
>
{!disabled && fileList.length < length && (
<div>
{listType === 'picture-card' ? (
<IMGUploadButton text={text} />
) : children ? (
children
) : (
<UploadButton text={text} />
)}
</div>
)}
</Upload>
);
});
const mapStateToProps = ({ global }) => {
return {
preImgDataModal: global.preImgDataModal,
};
};
export default connect(mapStateToProps)(UploadItem);
/**
* 上传图片组件
* @param {*标签的文本} label
* @param {*标签的字段名} name
* @param {*额外的提示信息} extra
* @param {*插槽} children
* @param {*value是否含有上传文件的当前状态{status:'done',url:'',response:''}} isError
*/
import React, { useState, useEffect, forwardRef } from 'react';
import { connect } from 'dva';
import { Card, Button, Icon, Row, Col, Input, Popover, Form, Cascader, Upload } from 'antd';
import UploadItem from './UploadItem';
const FormItem = props => {
const { name, label, extra, children, isError } = props;
const validator = (rule, value) => {
if (value && value.some(i => i.status === 'error')) {
return Promise.reject('您有文件不符合要求,请删除!');
} else if (value && value.some(i => i.status === 'uploading' || i === 'uploading')) {
return Promise.reject('您有文件正在上传!');
} else {
return Promise.resolve();
}
};
let rules = [
{ required: true, message: '请上传文件' },
{
validator: validator,
},
];
return (
<Form.Item label={label} extra={extra} name={name} rules={rules}>
<UploadItem {...props}>{children}</UploadItem>
</Form.Item>
);
};
export default FormItem;
.upfilebtn {
width: 70px;
height: 70px;
background: rgba(251, 251, 251, 1);
border: 1px dashed rgba(218, 218, 218, 1);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
p {
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 400;
color: rgba(153, 153, 153, 1);
margin-top: 11px;
}
}
.textbtn:hover,
.textbtn:focus {
border: 1px solid #1890ff !important;
color: #1890ff !important;
}
import React from 'react';
import { connect } from 'umi';
import { LogoutOutlined, InfoCircleOutlined } from '@ant-design/icons';
import { Avatar, Menu, Spin } from 'antd';
import { IconFontConfig } from '@/common';
import HeaderDropdown from '../HeaderDropdown';
import styles from './index.less';
import ModalUpdatePassword from '../ModalUpdatePassword';
class AvatarDropdown extends React.Component {
onMenuClick = event => {
const { key } = event;
if (key === 'logout') {
const { dispatch } = this.props;
if (dispatch) {
dispatch({
type: 'login/logout',
});
}
return;
} else if (key === 'password') {
const { dispatch } = this.props;
dispatch({
type: 'user/changeState',
payload: {
dataModal: {
modalType: 'PASSWORD_UPDATE_MODAL',
modalShow: true,
modalData: {},
},
},
});
}
};
render() {
const {
currentUser = {
avatar: '',
name: '',
},
menu,
} = this.props;
const menuHeaderDropdown = (
<Menu className={styles.menu} selectedKeys={[]} onClick={this.onMenuClick}>
<Menu.Item key="password">
<InfoCircleOutlined />
修改密码
</Menu.Item>
<Menu.Item key="logout">
<LogoutOutlined />
退出登录
</Menu.Item>
</Menu>
);
return currentUser && currentUser.name ? (
<>
<HeaderDropdown overlay={menuHeaderDropdown}>
<span className={`${styles.action} ${styles.account}`}>
<IconFontConfig type="icon-header" style={{ fontSize: '28px' }} className="mr-10" />
<span className={styles.name}>{currentUser.name}</span>
</span>
</HeaderDropdown>
<ModalUpdatePassword />
</>
) : (
<Spin
size="small"
style={{
marginLeft: 8,
marginRight: 8,
lineHeight: '64px',
}}
/>
);
}
}
export default connect(({ user }) => ({
currentUser: user.currentUser,
}))(AvatarDropdown);
import { Tooltip, Tag } from 'antd';
import { QuestionCircleOutlined } from '@ant-design/icons';
import React from 'react';
import { connect } from 'umi';
import Avatar from './AvatarDropdown';
import styles from './index.less';
const ENVTagColor = {
dev: 'orange',
test: 'green',
pre: '#87d068',
};
const GlobalHeaderRight = props => {
const { theme, layout } = props;
let className = styles.right;
if (theme === 'dark' && layout === 'topmenu') {
className = `${styles.right} ${styles.dark}`;
}
return (
<div className={className}>
{/* <Tooltip title="使用文档">
<a
target="_blank"
href="https://pro.ant.design/docs/getting-started"
rel="noopener noreferrer"
className={styles.action}
>
<QuestionCircleOutlined />
</a>
</Tooltip> */}
<Avatar />
{REACT_APP_ENV && (
<span>
<Tag color={ENVTagColor[REACT_APP_ENV]}>{REACT_APP_ENV}</Tag>
</span>
)}
</div>
);
};
export default connect(({ settings }) => ({
theme: settings.navTheme,
layout: settings.layout,
}))(GlobalHeaderRight);
@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: @layout-header-height;
margin-left: auto;
overflow: hidden;
.action {
display: flex;
align-items: center;
height: 100%;
padding: 0 12px;
cursor: pointer;
transition: all 0.3s;
> span {
color: @text-color;
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: ~'calc((@{layout-header-height} - 24px) / 2)' 0;
margin-right: 8px;
color: @primary-color;
vertical-align: top;
background: rgba(255, 255, 255, 0.85);
}
}
}
.dark {
.action {
color: rgba(255, 255, 255, 0.85);
> span {
color: rgba(255, 255, 255, 0.85);
}
// &:hover,
// &:global(.opened) {
// background: @primary-color;
// }
}
}
:global(.ant-pro-global-header) {
img {
height: 50px;
}
.dark {
.action {
color: @text-color;
> span {
color: @text-color;
}
&:hover {
color: rgba(255, 255, 255, 0.85);
> span {
color: rgba(255, 255, 255, 0.85);
}
}
}
}
}
@media only screen and (max-width: @screen-md) {
:global(.ant-divider-vertical) {
vertical-align: unset;
}
.name {
display: none;
}
.right {
position: absolute;
top: 0;
right: 12px;
.account {
.avatar {
margin-right: 0;
}
}
}
}
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;
@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;
}
}
/**
* Auther: APIS
*/
import React, { useEffect, useState } from 'react';
import { Spin } from 'antd';
import styles from './index.less';
export const customLoadingParams = {
indicator: (
<img
className="loading-img"
src="https://youxuan-prod.oss-cn-zhangjiakou.aliyuncs.com/qintaoyouxuan/loading.gif"
/>
),
delay: 50,
};
const Loading = props => {
const { loading } = props;
return <Spin spinning={loading} {...customLoadingParams} className="wrap-loading"></Spin>;
};
export default Loading;
:global {
.ant-pro-basicLayout .ant-pro-basicLayout-is-children.ant-pro-basicLayout-fix-siderbar{
.wrap-loading{
left: -100px !important;
}
}
.wrap-loading{
position: fixed !important;
top: 0 !important;
left: 0px !important;
right: 0 !important;
bottom: 0 !important;
z-index: 999 !important;
&:before{
content: '';
display: block;
width: 100%;
height: 100%;
background: #FFF;
opacity: .6;
}
.ant-spin-dot.ant-spin-dot-spin{
position: absolute;
top: 40%;
left: 50%;
}
.loading-img{
position: absolute;
top: 40%;
left: 50%;
width: 30px;
height: auto;
opacity: .6;
}
}
}
\ No newline at end of file
/**
* Author: Charles
* Date: 2022.9.13
* Description: [修改密码]
*/
import React, { useEffect } from 'react';
import { connect } from 'umi';
import { Modal, Form, Input, Button } from 'antd';
const formItemLayout = { labelCol: { span: 4 }, wrapperCol: { span: 20 } };
const ModalUpdatePassword = props => {
const [form] = Form.useForm();
const {
dispatch,
dataModal: { modalType, modalShow },
} = props;
useEffect(() => {
if (modalType === 'PASSWORD_UPDATE_MODAL' && modalShow) {
form.resetFields();
}
}, [modalType, modalShow]);
/* 点击保存 */
const handleSave = () => {
form.validateFields().then(values => {
dispatch({ type: 'user/updatePassword', payload: values });
});
};
return (
<Modal
title="修改密码"
placement="right"
width={700}
maskClosable={false}
onCancel={() => {
dispatch({ type: 'user/cancelModal' });
}}
visible={modalType === 'PASSWORD_UPDATE_MODAL' && modalShow}
footer={
<div
style={{
textAlign: 'right',
}}
>
<Button
onClick={() => {
dispatch({ type: 'user/cancelModal' });
}}
className="mr-10"
>
取消
</Button>
<Button onClick={handleSave} type="primary">
保存
</Button>
</div>
}
>
<Form form={form} {...formItemLayout} name="password_set_modal">
<Form.Item
name="oldPassword"
label="原密码"
rules={[{ required: true, message: '请输入原密码' }]}
>
<Input.Password placeholder="请输入原密码" />
</Form.Item>
<Form.Item
name="newPassword"
label="新密码"
rules={[
{ required: true, message: '请输入新密码' },
{
pattern: /^(?![\d]+$)(?![a-zA-Z]+$)(?![!#$%^&*]+$)[\da-zA-Z!#$%^&@*]{6,16}$/,
message: '密码至少包含字母、数字、特殊符号的两种组合,限制6~16个字符~',
},
]}
>
<Input.Password placeholder="请输入新密码" />
</Form.Item>
<Form.Item
name="checkPassword"
label="确认新密码"
rules={[
{ required: true, message: '请输入确认新密码' },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('newPassword') === value) {
return Promise.resolve();
}
return Promise.reject(new Error('两次密码不一致,请重新输入'));
},
}),
]}
>
<Input.Password placeholder="请输入确认新密码" />
</Form.Item>
</Form>
</Modal>
);
};
export default connect(({ user }) => ({
...user,
}))(ModalUpdatePassword);
/**
* Auther: APIS
*/
import ModalPreImg from '@/pages/Modals/ModalPreImg';
import FileExport from '@/components/FileExport';
import Loading from '@/components/Loading';
const PageCommons = props=> {
const { urlFileExport, loading, dispatch } = props;
return (
<>
{/* 图片预览 */}
<ModalPreImg />
{/* 文件下载 */}
<FileExport url={urlFileExport} dispatch={dispatch} />
{/* 全局loading */}
<Loading loading={loading} />
</>
)
};
export default PageCommons;
\ No newline at end of file
import { PageLoading } from '@ant-design/pro-layout'; // loading components from code split
// https://umijs.org/plugin/umi-plugin-react.html#dynamicimport
export default PageLoading;
import React, { useEffect } from 'react';
import { Row, Col, Card, Typography, Tooltip, Spin } from 'antd';
import styles from './index.less';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import Loading from '@/components/Loading';
const { Title } = Typography;
const QCard = props => {
const { title, des, children, extra, loading = false, ...otherProps } = props;
return (
<Card {...otherProps}>
<Loading loading={loading} />
<Row className={styles.main} type="flex" justify="space-between" align="middle">
<Col>
<Row type="flex" align="middle">
<Col>
<div className={styles.fk} />
</Col>
{title && (
<Col>
<div className={styles.title}>{title}</div>
</Col>
)}
{des && (
<Col>
<Tooltip title={des}>
<ExclamationCircleOutlined />
</Tooltip>
</Col>
)}
</Row>
</Col>
{extra && <Col>{extra}</Col>}
</Row>
{children}
</Card>
);
};
export default QCard;
.main {
margin-bottom: 20px;
.title {
font-size: 14px;
font-family: PingFang SC;
font-weight: 500;
color: rgba(51, 51, 51, 1);
margin: 0 8px;
}
.fk {
width: 3px;
height: 16px;
background: rgba(230, 15, 26, 1);
}
}
import styled, { css } from 'styled-components';
export const StyledPageContainer = styled.div`
width: 100%;
height: 100%;
`;
export const StyledPageHeader = styled.div<{ border: Boolean }>`
display: flex;
align-items: center;
justify-content: flex-end;
background-color: #fff;
padding: 8px 16px;
border-radius: 2px;
border-top: ${props => (props.border ? '1px solid rgba(0, 0, 0, 0.06)' : 'none')};
.ant-btn {
margin-left: 10px;
}
.ant-upload-list {
display: none;
}
`;
export const StyledPageContent = styled.div`
margin: 16px 16px 0;
.ant-form {
.ant-form-item {
margin-bottom: 16px;
}
}
`;
export const StyledEllipsisWrap = styled.div<{ maxLine: number }>`
overflow: hidden;
text-overflow: ellipsis;
${p =>
p.maxLine === 1
? css`
white-space: nowrap;
`
: css`
display: -webkit-box;
white-space: normal;
-webkit-line-clamp: ${p.maxLine};
-webkit-box-orient: vertical;
word-break: break-all;
`}
`;
export const StyledWapperTab = styled.div`
.ant-tabs {
background-color: #fff;
padding: 0px 16px;
}
.ant-tabs-nav {
margin: 0;
}
.ant-tabs-top > .ant-tabs-nav::before,
.ant-tabs-bottom > .ant-tabs-nav::before,
.ant-tabs-top > div > .ant-tabs-nav::before,
.ant-tabs-bottom > div > .ant-tabs-nav::before {
border-bottom: none;
}
`;
export const StyledPageFlex = styled.div`
display: flex;
`;
export const StyledPageLeft = styled.div`
width: 300px;
margin-right: 16px;
border-radius: 2px;
padding: 22px 16px;
background-color: #fff;
min-height: calc(100vh - 144px);
max-height: calc(100vh - 144px);
overflow-y: scroll;
.ant-tree .ant-tree-node-content-wrapper {
padding: 0 6px;
.ant-tree-title {
font-size: 15px;
}
}
`;
export const StyledPageRight = styled.div`
flex: 1;
border-radius: 2px;
`;
export const StyledWapperIframe = styled.div`
height: calc(100vh - 62px);
width: 100%;
background-color: #fff;
`;
export const StyledTitle = styled.div`
h1 {
font-size: 16px;
}
`;
export const StyledText = styled.div`
display: flex;
flex-wrap: wrap;
.item-text {
width: calc(50% - 10px);
margin-bottom: 10px;
margin-right: 10px;
.title {
color: #797e8f;
font-size: 14px;
margin-bottom: 4px;
}
.desc {
color: #000;
font-size: 14px;
}
}
`;
import moment from 'moment';
// paginationDefault
export const paginations = {
position: ['bottomCenter'],
defaultCurrent: 1,
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: ['5', '10', '20', '50'],
};
// staticModal
export const staticModal = {
modalType: '',
modalShow: false,
modalData: {},
};
// 处理状态
export const mapStatus = {
1: {
label: '待办理',
value: '1',
},
2: {
label: '待整改',
value: '2',
},
3: {
label: '已办结',
value: '3',
},
4: {
label: '已整改',
value: '4',
},
};
// 风险类型
export const mapRiskType = {
1: {
label: '涉黄',
value: '1',
},
2: {
label: '涉非',
value: '2',
},
3: {
label: '涉政',
value: '3',
},
};
// 风险源类型
export const mapRiskSourceType = {
1: {
label: '文本',
value: '1',
},
2: {
label: '图片',
value: '2',
},
3: {
label: '音频',
value: '3',
},
4: {
label: '视频',
value: '4',
},
};
// 事件类型
export const mapEventType = {
1: {
label: '网吧',
value: '1',
},
2: {
label: '出版物',
value: '2',
},
3: {
label: '网络文化',
value: '3',
},
4: {
label: '印刷',
value: '4',
},
5: {
label: '电影',
value: '5',
},
6: {
label: '广电',
value: '6',
},
7: {
label: '互联网视听',
value: '7',
},
};
// 事件等级
export const mapEventLevel = {
1: {
label: '轻微',
value: '1',
},
2: {
label: '一般',
value: '2',
},
3: {
label: '严重',
value: '3',
},
};
// 风险标签
export const enumRiskLabel = [
{
label: '涉黄',
value: '1',
},
{
label: '涉非',
value: '2',
},
{
label: '涉政',
value: '3',
},
];
// 事件状态
export const mapEventStatus = {
1: {
label: '发起中',
value: '1',
},
2: {
label: '处理中',
value: '2',
},
3: {
label: '协同中',
value: '3',
},
4: {
label: '已办结',
value: '4',
},
5: {
label: '已关闭',
value: '5',
},
};
// 区域
export const enumArea = [
'百丈镇',
'黄湖镇',
'鸬鸟镇',
'径山镇',
'瓶窑镇',
'良渚街道',
'仁和街道',
'仓前街道',
'余杭街道',
'中泰街道',
'闲林街道',
'五常街道',
];
// 案件原由
export const mapCause = {
1: {
label: '行政案件办理',
value: '1',
},
2: {
label: '网络行政案件',
value: '2',
},
3: {
label: '刑事案件',
value: '3',
},
4: {
label: '保护未成年人重要案件',
value: '4',
},
0: {
label: '其他',
value: '0',
},
};
export const enumYear = (startYear = 1950, endYear = +moment().format('YYYY')) => {
let year = [];
for (let i = startYear; i <= endYear; i++) {
year.push({
label: `${i} 年`,
value: i + '',
});
}
return year;
};
// 手机号正则
export const phoneReg = /^[1][3,4,5,7,8][0-9]{9}$/;
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) {
result.push(`${fixedParentPath}/${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));
}
beforeAll(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));
});
});
const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
describe('Homepage', () => {
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();
});
});
import { Button, message, notification } from 'antd';
import React from 'react';
import { formatMessage } from 'umi';
import defaultSettings from '../config/defaultSettings';
const { pwa } = defaultSettings; // if pwa is true
if (pwa) {
// Notify user if offline now
window.addEventListener('sw.offline', () => {
message.warning(
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/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();
}}
>
{formatMessage({
id: 'app.pwa.serviceworker.updated.ok',
})}
</Button>
);
notification.open({
message: formatMessage({
id: 'app.pwa.serviceworker.updated',
}),
description: formatMessage({
id: 'app.pwa.serviceworker.updated.hint',
}),
btn,
key,
onClose: async () => {},
});
});
} else if ('serviceWorker' in navigator) {
// 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);
});
});
}
}
@import '~antd/es/style/themes/default.less';
html,
body,
#root {
height: 100%;
overflow: hidden;
}
.colorWeak {
filter: invert(80%);
}
.ant-layout {
min-height: 100vh;
}
canvas {
display: block;
}
body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* 浏览器滚动条相关 */
::-webkit-scrollbar-thumb {
height: 50px;
background: rgba(160, 160, 160, 0.8);
border-radius: 3px;
transition: background 1s;
&:hover {
background: rgba(160, 160, 160, 1);
}
}
::-webkit-scrollbar {
width: 4px;
height: 4px;
}
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;
}
}
}
}
}
// 兼容IE11
@media screen and(-ms-high-contrast: active), (-ms-high-contrast: none) {
body .ant-design-pro > .ant-layout {
min-height: 100vh;
}
}
.mt-5 {
margin-top: 5px !important;
}
.mt-10 {
margin-top: 10px !important;
}
.mt-15 {
margin-top: 15px !important;
}
.mt-20 {
margin-top: 20px !important;
}
.mt-24 {
margin-top: 24px !important;
}
.ml-5 {
margin-left: 5px !important;
}
.ml-10 {
margin-left: 10px !important;
}
.ml-15 {
margin-left: 15px !important;
}
.ml-20 {
margin-left: 20px !important;
}
.mr-5 {
margin-right: 5px !important;
}
.mr-10 {
margin-right: 10px !important;
}
.mr-15 {
margin-right: 15px !important;
}
.mr-20 {
margin-right: 20px !important;
}
.mr-30 {
margin-right: 30px !important;
}
.mb-5 {
margin-bottom: 5px !important;
}
.mb-10 {
margin-bottom: 10px !important;
}
.mb-15 {
margin-bottom: 15px !important;
}
.mb-20 {
margin-bottom: 20px !important;
}
.tn-l {
text-align: left;
}
.tn-c {
text-align: center;
}
.tn-r {
text-align: right;
}
.ft-24 {
font-size: 24px;
}
.color-gray {
color: rgba(0, 0, 0, 0.35);
}
.color-primary {
color: @primary-color !important;
}
// 清除上下浮动
.clearfix {
&::before,
&::after {
content: ' ';
display: table;
}
&::after {
clear: both;
}
}
.left {
float: left;
}
.right {
float: right;
}
.fn-hide {
display: none !important;
}
// 单行字体溢出时显示省略号
.ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.pointer {
cursor: pointer;
&:hover {
color: #40a9ff;
}
}
.wflex_r_s {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.c9 {
color: #999 !important;
}
:global {
.ant-table-wrapper {
.ant-table-pagination.ant-pagination {
margin-top: 24px;
margin-bottom: 0px;
justify-content: flex-end;
}
}
.ant-pro-basicLayout-content {
margin: 0px;
}
.ant-tooltip-inner {
.anticon {
margin-right: 6px;
}
}
}
/* 权限相关样式 */
.view-tree-auth {
:global {
.ant-tree {
position: relative;
top: 3px;
}
.info-tree {
.ant-tree-list {
.ant-tree-treenode {
.ant-tree-node-content-wrapper {
.ant-tree-title {
span {
display: none;
&:hover {
opacity: 0.8;
}
&:first-child {
color: #185da2;
}
&:last-child {
color: #666;
}
}
&:hover {
span {
display: inline-block;
}
}
}
}
}
}
}
}
}
.box-info {
min-height: calc(~'100vh - 112px');
:global {
.ant-card {
min-height: calc(~'100vh - 195px');
margin-bottom: 70px;
.footer-btn {
position: fixed;
bottom: 0;
left: 0;
z-index: 10;
width: 100%;
height: 56px;
line-height: 56px;
margin-bottom: 0;
text-align: center;
background-color: #fff;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.03);
}
}
}
}
/**
* Ant Design Pro v4 use `@ant-design/pro-layout` to handle Layout.
* You can view component api by:
* https://github.com/ant-design/ant-design-pro-layout
*/
import ProLayout from '@ant-design/pro-layout';
import { Link, connect, useIntl, history } from 'umi';
import React from 'react';
import RightContent from '@/components/GlobalHeader/RightContent';
import AuthRouter from '@/components/Auth/AuthRouter';
import PageCommons from '@/components/PageCommons';
import YH from '@/assets/yh-logo.png';
import {
DesktopOutlined,
WarningOutlined,
SelectOutlined,
BarsOutlined,
SettingOutlined,
} from '@ant-design/icons';
import { StyledTitle } from '@/components/style';
import { codeMappingForResource } from '../../config/routerConfig';
const IconMap = {
screen: <DesktopOutlined />,
risk: <WarningOutlined />,
event: <SelectOutlined />,
data: <BarsOutlined />,
system: <SettingOutlined />,
};
const BasicLayout = props => {
const intl = useIntl();
const { formatMessage } = intl;
const {
dispatch,
children,
settings,
location: { pathname },
loading,
urlFileExport,
currentUser: { menus },
} = props;
const handleMenuCollapse = payload => {
if (dispatch) {
dispatch({
type: 'global/changeLayoutCollapsed',
payload,
});
}
};
// 重定向处理
// const isRedirect = pathname === '/';
// if (isRedirect && dataMenus.length) {
// history.replace({ pathname: dataMenus[0].path });
// };
// 菜单 loop
const loopMenuItem = menus => {
// console.log(menus, 'menus', codeMappingForResource);
return menus?.map(({ icon, children, ...item }) => ({
...(codeMappingForResource.get(item.resourceCode) || {}),
icon: icon && IconMap[icon],
routes: children && loopMenuItem(children),
}));
};
return (
<ProLayout
formatMessage={formatMessage}
menuHeaderRender={(logoDom, titleDom) => (
<Link to="/">
<img src={YH} />
<StyledTitle>{titleDom}</StyledTitle>
</Link>
)}
onCollapse={handleMenuCollapse}
menuItemRender={(menuItemProps, defaultDom) => {
if (menuItemProps.isUrl || menuItemProps.children || !menuItemProps.path) {
return defaultDom;
}
return (
<Link to={menuItemProps.path}>
{IconMap[menuItemProps.icons]}
{defaultDom}
</Link>
);
}}
subMenuItemRender={subProps => {
return (
<span className="ant-pro-menu-item">
{IconMap[subProps.icons]}
<span>{subProps.name}</span>
</span>
);
}}
menuDataRender={() => loopMenuItem(menus)}
rightContentRender={() => <RightContent />}
{...props}
{...settings}
>
{/* 路由权限 */}
<AuthRouter path={pathname}>{children}</AuthRouter>
<PageCommons loading={loading} urlFileExport={urlFileExport} dispatch={dispatch} />
</ProLayout>
);
};
export default connect(({ global, settings, user }) => ({
settings,
loading: global.loading,
collapsed: global.collapsed,
urlFileExport: global.urlFileExport,
currentUser: user.currentUser,
}))(BasicLayout);
import React from 'react';
const Layout = ({ children }) => <>{children}</>;
export default Layout;
import { connect } from 'umi';
import React from 'react';
import styles from './UserLayout.less';
const UserLayout = props => {
const { children } = props;
return (
<>
<div className={styles.container}>
<div className={styles.bgTop}>
<div>一网打“净”数智在线</div>
</div>
<div className={styles.content}>
<div className={styles.login}>{children}</div>
</div>
</div>
</>
);
};
export default connect(({ settings, user }) => ({
...settings,
currentUser: user.currentUser,
}))(UserLayout);
@import '~antd/es/style/themes/default.less';
.container {
width: 100%;
display: flex;
flex-direction: column;
height: 100vh;
overflow: auto;
position: relative;
background-color: #F5F5F5;
.bgTop {
background-color: @primary-color;
height: 400px;
div {
color: #fff;
text-align: center;
font-size: 22px;
position: absolute;
top: 170px;
left: 50%;
letter-spacing: 2px;
transform: translateX(-50%);
}
}
.content {
position: absolute;
background-color: #fff;
border-radius: 2px;
top: 220px;
left: 50%;
z-index: 2;
transform: translateX(-50%);
.login {
width: 400px;
box-shadow: 0 4px 50px 0 rgba(121,173,201,.17);
}
}
}
import component from './en-US/component';
import globalHeader from './en-US/globalHeader';
import menu from './en-US/menu';
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.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,
};
export default {
'component.tagSelect.expand': 'Expand',
'component.tagSelect.collapse': 'Collapse',
'component.tagSelect.all': 'All',
};
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',
};
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',
};
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',
};
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',
};
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',
};
import component from './pt-BR/component';
import globalHeader from './pt-BR/globalHeader';
import menu from './pt-BR/menu';
import pwa from './pt-BR/pwa';
import settingDrawer from './pt-BR/settingDrawer';
import settings from './pt-BR/settings';
export default {
'navBar.lang': 'Idiomas',
'layout.user.link.help': 'ajuda',
'layout.user.link.privacy': 'política de privacidade',
'layout.user.link.terms': 'termos de serviços',
'app.preview.down.block': 'Download this page to your local project',
...globalHeader,
...menu,
...settingDrawer,
...settings,
...pwa,
...component,
};
export default {
'component.tagSelect.expand': 'Expandir',
'component.tagSelect.collapse': 'Diminuir',
'component.tagSelect.all': 'Todas',
};
export default {
'component.globalHeader.search': 'Busca',
'component.globalHeader.search.example1': 'Exemplo de busca 1',
'component.globalHeader.search.example2': 'Exemplo de busca 2',
'component.globalHeader.search.example3': 'Exemplo de busca 3',
'component.globalHeader.help': 'Ajuda',
'component.globalHeader.notification': 'Notificação',
'component.globalHeader.notification.empty': 'Você visualizou todas as notificações.',
'component.globalHeader.message': 'Mensagem',
'component.globalHeader.message.empty': 'Você visualizou todas as mensagens.',
'component.globalHeader.event': 'Evento',
'component.globalHeader.event.empty': 'Você visualizou todos os eventos.',
'component.noticeIcon.clear': 'Limpar',
'component.noticeIcon.cleared': 'Limpo',
'component.noticeIcon.empty': 'Sem notificações',
'component.noticeIcon.loaded': 'Carregado',
'component.noticeIcon.view-more': 'Veja mais',
};
export default {
'menu.welcome': 'Welcome',
'menu.more-blocks': 'More Blocks',
'menu.home': 'Início',
'menu.login': 'Login',
'menu.admin': 'Admin',
'menu.admin.sub-page': 'Sub-Page',
'menu.register': 'Registro',
'menu.register.result': 'Resultado de registro',
'menu.dashboard': 'Dashboard',
'menu.dashboard.analysis': 'Análise',
'menu.dashboard.monitor': 'Monitor',
'menu.dashboard.workplace': 'Ambiente de Trabalho',
'menu.exception.403': '403',
'menu.exception.404': '404',
'menu.exception.500': '500',
'menu.form': 'Formulário',
'menu.form.basic-form': 'Formulário Básico',
'menu.form.step-form': 'Formulário Assistido',
'menu.form.step-form.info': 'Formulário Assistido(gravar informações de transferência)',
'menu.form.step-form.confirm': 'Formulário Assistido(confirmar informações de transferência)',
'menu.form.step-form.result': 'Formulário Assistido(finalizado)',
'menu.form.advanced-form': 'Formulário Avançado',
'menu.list': 'Lista',
'menu.list.table-list': 'Tabela de Busca',
'menu.list.basic-list': 'Lista Básica',
'menu.list.card-list': 'Lista de Card',
'menu.list.search-list': 'Lista de Busca',
'menu.list.search-list.articles': 'Lista de Busca(artigos)',
'menu.list.search-list.projects': 'Lista de Busca(projetos)',
'menu.list.search-list.applications': 'Lista de Busca(aplicações)',
'menu.profile': 'Perfil',
'menu.profile.basic': 'Perfil Básico',
'menu.profile.advanced': 'Perfil Avançado',
'menu.result': 'Resultado',
'menu.result.success': 'Sucesso',
'menu.result.fail': 'Falha',
'menu.exception': 'Exceção',
'menu.exception.not-permission': '403',
'menu.exception.not-find': '404',
'menu.exception.server-error': '500',
'menu.exception.trigger': 'Disparar',
'menu.account': 'Conta',
'menu.account.center': 'Central da Conta',
'menu.account.settings': 'Configurar Conta',
'menu.account.trigger': 'Disparar Erro',
'menu.account.logout': 'Sair',
'menu.editor': 'Graphic Editor',
'menu.editor.flow': 'Flow Editor',
'menu.editor.mind': 'Mind Editor',
'menu.editor.koni': 'Koni Editor',
};
export default {
'app.pwa.offline': 'Você está offline agora',
'app.pwa.serviceworker.updated': 'Novo conteúdo está disponível',
'app.pwa.serviceworker.updated.hint':
'Por favor, pressione o botão "Atualizar" para recarregar a página atual',
'app.pwa.serviceworker.updated.ok': 'Atualizar',
};
export default {
'app.setting.pagestyle': 'Configuração de estilo da página',
'app.setting.pagestyle.dark': 'Dark style',
'app.setting.pagestyle.light': 'Light style',
'app.setting.content-width': 'Largura do conteúdo',
'app.setting.content-width.fixed': 'Fixo',
'app.setting.content-width.fluid': 'Fluido',
'app.setting.themecolor': 'Cor do Tema',
'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': 'Modo de Navegação',
'app.setting.sidemenu': 'Layout do Menu Lateral',
'app.setting.topmenu': 'Layout do Menu Superior',
'app.setting.fixedheader': 'Cabeçalho fixo',
'app.setting.fixedsidebar': 'Barra lateral fixa',
'app.setting.fixedsidebar.hint': 'Funciona no layout do menu lateral',
'app.setting.hideheader': 'Esconder o cabeçalho quando rolar',
'app.setting.hideheader.hint': 'Funciona quando o esconder cabeçalho está abilitado',
'app.setting.othersettings': 'Outras configurações',
'app.setting.weakmode': 'Weak Mode',
'app.setting.copy': 'Copiar Configuração',
'app.setting.copyinfo':
'copiado com sucesso,por favor trocar o defaultSettings em src/models/setting.js',
'app.setting.production.hint':
'O painel de configuração apenas é exibido no ambiente de desenvolvimento, por favor modifique manualmente o',
};
export default {
'app.settings.menuMap.basic': 'Configurações Básicas',
'app.settings.menuMap.security': 'Configurações de Segurança',
'app.settings.menuMap.binding': 'Vinculação de Conta',
'app.settings.menuMap.notification': 'Mensagens de Notificação',
'app.settings.basic.avatar': 'Avatar',
'app.settings.basic.change-avatar': 'Alterar avatar',
'app.settings.basic.email': 'Email',
'app.settings.basic.email-message': 'Por favor insira seu email!',
'app.settings.basic.nickname': 'Nome de usuário',
'app.settings.basic.nickname-message': 'Por favor insira seu nome de usuário!',
'app.settings.basic.profile': 'Perfil pessoal',
'app.settings.basic.profile-message': 'Por favor insira seu perfil pessoal!',
'app.settings.basic.profile-placeholder': 'Breve introdução sua',
'app.settings.basic.country': 'País/Região',
'app.settings.basic.country-message': 'Por favor insira país!',
'app.settings.basic.geographic': 'Província, estado ou cidade',
'app.settings.basic.geographic-message': 'Por favor insira suas informações geográficas!',
'app.settings.basic.address': 'Endereço',
'app.settings.basic.address-message': 'Por favor insira seu endereço!',
'app.settings.basic.phone': 'Número de telefone',
'app.settings.basic.phone-message': 'Por favor insira seu número de telefone!',
'app.settings.basic.update': 'Atualizar Informações',
'app.settings.security.strong': 'Forte',
'app.settings.security.medium': 'Média',
'app.settings.security.weak': 'Fraca',
'app.settings.security.password': 'Senha da Conta',
'app.settings.security.password-description': 'Força da senha',
'app.settings.security.phone': 'Telefone de Seguraça',
'app.settings.security.phone-description': 'Telefone vinculado',
'app.settings.security.question': 'Pergunta de Segurança',
'app.settings.security.question-description':
'A pergunta de segurança não está definida e a política de segurança pode proteger efetivamente a segurança da conta',
'app.settings.security.email': 'Email de Backup',
'app.settings.security.email-description': 'Email vinculado',
'app.settings.security.mfa': 'Dispositivo MFA',
'app.settings.security.mfa-description':
'O dispositivo MFA não vinculado, após a vinculação, pode ser confirmado duas vezes',
'app.settings.security.modify': 'Modificar',
'app.settings.security.set': 'Atribuir',
'app.settings.security.bind': 'Vincular',
'app.settings.binding.taobao': 'Vincular Taobao',
'app.settings.binding.taobao-description': 'Atualmente não vinculado à conta Taobao',
'app.settings.binding.alipay': 'Vincular Alipay',
'app.settings.binding.alipay-description': 'Atualmente não vinculado à conta Alipay',
'app.settings.binding.dingding': 'Vincular DingTalk',
'app.settings.binding.dingding-description': 'Atualmente não vinculado à conta DingTalk',
'app.settings.binding.bind': 'Vincular',
'app.settings.notification.password': 'Senha da Conta',
'app.settings.notification.password-description':
'Mensagens de outros usuários serão notificadas na forma de uma estação de letra',
'app.settings.notification.messages': 'Mensagens de Sistema',
'app.settings.notification.messages-description':
'Mensagens de sistema serão notificadas na forma de uma estação de letra',
'app.settings.notification.todo': 'Notificação de To-do',
'app.settings.notification.todo-description':
'A lista de to-do será notificada na forma de uma estação de letra',
'app.settings.open': 'Aberto',
'app.settings.close': 'Fechado',
};
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';
export default {
'navBar.lang': '语言',
'layout.user.link.help': '帮助',
'layout.user.link.privacy': '隐私',
'layout.user.link.terms': '条款',
'app.preview.down.block': '下载此页面到本地项目',
'app.welcome.link.fetch-blocks': '获取全部区块',
'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面',
...globalHeader,
...menu,
...settingDrawer,
...settings,
...pwa,
...component,
};
export default {
'component.tagSelect.expand': '展开',
'component.tagSelect.collapse': '收起',
'component.tagSelect.all': '全部',
};
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': '查看更多',
};
export default {
'menu.appliction': '应用列表',
'menu.info': '应用详情',
'menu.login': '登录',
'menu.risk': '风险管理',
'menu.event': '事件管理',
'menu.data': '数据管理',
'menu.system': '系统管理',
'menu.system.account': '账号管理',
'menu.system.role': '角色管理',
'menu.system.menu': '菜单管理',
'menu.dataScreen': '数智大屏',
};
export default {
'app.pwa.offline': '当前处于离线状态',
'app.pwa.serviceworker.updated': '有新内容',
'app.pwa.serviceworker.updated.hint': '请点击“刷新”按钮或者手动刷新页面',
'app.pwa.serviceworker.updated.ok': '刷新',
};
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': '拷贝成功,请到 src/defaultSettings.js 中替换默认配置',
'app.setting.production.hint':
'配置栏只在开发环境用于预览,生产环境不会展现,请拷贝后手动修改配置文件',
};
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': '关',
};
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,
};
export default {
'component.tagSelect.expand': '展開',
'component.tagSelect.collapse': '收起',
'component.tagSelect.all': '全部',
};
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': '查看更多',
};
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': '拓撲編輯器',
};
export default {
'app.pwa.offline': '當前處於離線狀態',
'app.pwa.serviceworker.updated': '有新內容',
'app.pwa.serviceworker.updated.hint': '請點擊“刷新”按鈕或者手動刷新頁面',
'app.pwa.serviceworker.updated.ok': '刷新',
};
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': '拷貝成功,請到 src/defaultSettings.js 中替換默認配置',
'app.setting.production.hint':
'配置欄只在開發環境用於預覽,生產環境不會展現,請拷貝後手動修改配置文件',
};
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': '關',
};
{
"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"
}
]
}
import { queryNotices } from '@/services/user';
const GlobalModel = {
namespace: 'global',
state: {
collapsed: false,
loading: false,
urlFileExport: '',
dataModal: {
modalType: '',
modalShow: false,
modalData: {},
},
preImgDataModal: {
modalType: '',
modalShow: false,
modalData: {},
},
},
effects: {},
reducers: {
changeLayoutCollapsed(
state = {
notices: [],
collapsed: true,
},
{ payload },
) {
return { ...state, collapsed: payload };
},
},
subscriptions: {
setup({ history, dispatch }) {
// Subscribe history(url) change, trigger `load` action if pathname is `/`
history.listen(({ pathname, search }) => {
// 路由发生变化的时候切换菜单
const whiteList = ['/user/login'];
if (!whiteList.includes(pathname)) {
// 获取用户信息
dispatch({ type: 'user/fetchCurrent' });
}
});
},
},
};
export default GlobalModel;
import { queryLogin, loginOut } from '@/services/login';
import { getUserInfo, getBosUserList } from '@/services/user';
import { message } from 'antd';
import { history } from 'umi';
import md5 from 'blueimp-md5';
import defaultSettings from '../../config/defaultSettings';
import { getDataMenus } from '@/utils/menu';
const { tokenKey, md5Key } = defaultSettings;
const Model = {
namespace: 'login',
state: {
status: undefined,
},
effects: {
//退出登录
*logout({ payload }, { call, put, select }) {
try {
const res = yield call(loginOut);
if (res.code === 0 && res.data) {
if (!payload) {
message.success('退出成功');
}
localStorage.removeItem(tokenKey);
setTimeout(() => {
window.location.href = '/user/login';
}, 500);
}
} catch (err) {
console.error(err);
}
},
//登录
*login({ payload }, { call, put }) {
try {
const res = yield call(queryLogin, payload);
if (res.code === 0) {
localStorage.setItem(tokenKey, res.data);
// const userinfo = yield call(getUserInfo);
// const { permissionVos = [] } = userinfo.data;
// const dataMenus = getDataMenus(permissionVos);
// console.log(dataMenus);
// if (dataMenus.length) {
// history.replace(dataMenus[0].path);
// } else {
// history.replace('/');
// }
history.replace('/');
message.success('登录成功');
}
} catch (err) {
console.error(err);
}
},
},
reducers: {
changeLoginStatus(state, { payload }) {
return { ...state, status: payload.status, type: payload.type };
},
},
};
export default Model;
import defaultSettings from '../../config/defaultSettings';
const updateColorWeak = colorWeak => {
const root = document.getElementById('root');
if (root) {
root.className = colorWeak ? 'colorWeak' : '';
}
};
const SettingModel = {
namespace: 'settings',
state: defaultSettings,
reducers: {
changeSetting(state = defaultSettings, { payload }) {
const { colorWeak, contentWidth } = payload;
if (state.contentWidth !== contentWidth && window.dispatchEvent) {
window.dispatchEvent(new Event('resize'));
}
updateColorWeak(!!colorWeak);
return { ...state, ...payload };
},
},
};
export default SettingModel;
import { getUserInfo, updatePassword } from '@/services/user';
import { getDataMenus } from '@/utils/menu';
import { message } from 'antd';
const UserModel = {
namespace: 'user',
state: {
currentUser: {
admin: {},
dataMenus: [],
username: '',
},
userAuths: [], // 权限
dataModal: {
modalType: '',
modalShow: false,
modalData: {},
},
},
effects: {
*fetchCurrent(_, { call, put, select }) {
const {
currentUser,
currentUser: { username },
} = yield select(state => state.user);
if (!currentUser || !username) {
try {
const res = yield call(getUserInfo);
// data.dataMenus = getDataMenus(data.permissionVos);
if (res.code === 0) {
res.data.menus = [
{
children: [],
id: 999999,
parentId: 0,
resourceCode: 'Home',
resourceNodeType: '1',
title: '数智大屏',
},
...(res.data.menus || []),
{
resourceCode: '404',
},
];
yield put({ type: 'saveCurrentUser', payload: res.data });
}
} catch (err) {
console.error(err, 'err');
}
}
},
/* 修改密码 */
*updatePassword({ payload }, { call, put, select }) {
try {
const res = yield call(updatePassword, payload);
if (res.code === 0) {
message.success('修改成功');
yield put({ type: 'cancelModal' });
yield put({ type: 'login/logout', payload: { message: 1 } });
}
} catch (err) {
console.error(err);
}
},
},
reducers: {
changeState(state, { payload }) {
return {
...state,
...payload,
};
},
cancelModal(state, { payload }) {
return {
...state,
dataModal: {
modalType: '',
modalShow: false,
modalData: {},
},
};
},
saveCurrentUser(state, { payload }) {
return {
...state,
currentUser: payload || {},
// userAuths: payload.authorities.map(_ => _.code),
};
},
},
};
export default UserModel;
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;
/**
* Author: llw
* Date: 2020.7.20
* Description: [设置权限弹框]
*/
import React, { useEffect, useState } from 'react';
import { Drawer, Radio, Form, Select, Input, Button } from 'antd';
const { Option } = Select;
const formItemLayout = {labelCol: { span: 4 }, wrapperCol: { span: 20 }};
const ModalSetAuth = props => {
const [menuType, setType] = useState(1);
const [form] = Form.useForm();
let {
handleCancelModal,
handleAuthSetOk,
dataMenuList = [],
dataModal: {
modalType,
modalShow,
modalData,
modalData: { name, code, type, url, icon, parentCode, isEdit }
}
} = props;
dataMenuList = code ? dataMenuList.filter(item => code !== item.code) : dataMenuList;
/* 重置type */
useEffect(() => {
if (modalType === "AUTH_SET_MODAL" && modalShow) {
setType(type || 1);
setTimeout(() => {
form.resetFields();
form.setFieldsValue({
name,
code,
url,
icon,
parentCode,
type: type || 1,
});
}, 100)
}
}, [modalType, modalShow])
/* 点击保存 */
const handleSave = () => {
form.validateFields().then(values => {
handleAuthSetOk({
...modalData,
...values,
isEdit
});
})
};
/* 选择菜单类型 */
const handleChangeRadio = (value) => {
form.resetFields();
setType(value);
form.setFieldsValue({type: value});
};
return (
<Drawer
title="权限设置"
placement="right"
width={600}
maskClosable={false}
onClose={handleCancelModal}
visible={modalType === 'AUTH_SET_MODAL' && modalShow}
footer={
<div
style={{
textAlign: 'right',
}}
>
<Button onClick={handleCancelModal} className="mr-10">取消</Button>
<Button onClick={handleSave} type="primary">保存</Button>
</div>
}
>
<Form
form={form}
{...formItemLayout}
name="auth_set_modal"
initialValues={{ type: 1 }}
>
<Form.Item
name="type"
label="菜单类型"
>
<Radio.Group buttonStyle="solid" onChange={(e) => handleChangeRadio(e.target.value)}>
<Radio.Button disabled={(isEdit && type !== 1) ? true : false} value={1}>一级菜单</Radio.Button>
<Radio.Button disabled={(isEdit && type !== 2) ? true : false} value={2}>子菜单</Radio.Button>
<Radio.Button disabled={(isEdit && type !== 3) ? true : false} value={3}>按钮</Radio.Button>
</Radio.Group>
</Form.Item>
{
menuType !== 1 && <Form.Item
name="parentCode"
label={menuType === 2 ? "上级菜单" : "包含菜单"}
rules={
[{required: true, message: menuType === 2 ? "请选择上级菜单" : "请选择包含该按钮的菜单"}]
}
>
<Select
showSearch
placeholder={menuType === 2 ? '请选择上级菜单' : '请选择包含该按钮的菜单'}
optionFilterProp="children"
filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{
dataMenuList.map(item => {
return (
<Option key={item.code} value={item.code}>{item.name}</Option>
)
})
}
</Select>
</Form.Item>
}
<Form.Item
name="name"
label={menuType === 3 ? "按钮名称" : "菜单名称"}
rules={
[
{required: true, message: menuType === 3 ? '请输入按钮名称' : '请输入菜单名称'},
{min: 2, max: 15, message: '名称长度2~15个字符'}
]
}
>
<Input maxLength={15} placeholder={menuType === 3 ? "请输入按钮名称" : "请输入菜单名称"} />
</Form.Item>
<Form.Item
name="code"
label={menuType === 3 ? "按钮code" : "菜单code"}
rules={
[{required: true, message: menuType === 3 ? '请输入按钮code' : '请输入菜单code'}]
}
>
<Input maxLength={20} placeholder={menuType === 3 ? "请输入按钮code" : "请输入菜单code"} />
</Form.Item>
{
menuType !== 3 && <>
<Form.Item
name="url"
label="菜单URL"
rules={
[{required: true, message: '请输入菜单URL'}]
}
>
<Input placeholder="请输入菜单URL" />
</Form.Item>
<Form.Item
name="icon"
label="菜单Icon"
>
<Input placeholder="请输入菜单Icon" />
</Form.Item>
</>
}
</Form>
</Drawer>
)
};
export default ModalSetAuth;
\ No newline at end of file
/**
* Author: llw
* Date: 2020.7.16
* Description: [应用列表]
*/
import React, { useEffect } from 'react';
import { connect, Link } from 'umi';
import { Card, Form, DatePicker, Input, Button, Table, Divider, Modal } from 'antd';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { paginations } from '@/constants';
import moment from 'moment';
const { RangePicker } = DatePicker;
const FormItem = Form.Item;
/* SearchForm */
const SearchForm = props => {
const [form] = Form.useForm();
const {
dataSearch,
handleReset,
handleFinish,
dataSearch: { createTimeBegin, createTimeEnd }
} = props;
useEffect(() => {
form.setFieldsValue({
...dataSearch,
createTime: [createTimeBegin ? moment(createTimeBegin) : undefined, createTimeEnd ? moment(createTimeEnd) : undefined]
});
}, [dataSearch]);
/* 点击搜索 */
const onFinish = (values) => {
handleFinish(values);
};
/* 点击重置 */
const onReset = () => {
handleReset();
};
return (
<Form
name="Form_Application"
layout="inline"
form={form}
onFinish={onFinish}
>
<FormItem label="应用名称" name="name">
<Input placeholder="请输入应用名称" style={{width: "220px"}} />
</FormItem>
<FormItem label="创建时间" name="createTime">
<RangePicker allowClear={false} />
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" className="mr-15">搜索</Button>
<Button className="mr-15" htmlType="button" onClick={onReset}>重置</Button>
<Link to="/appliction/info"><Button>创建应用</Button></Link>
</FormItem>
</Form>
)
};
/* DataTable */
const DataTable = props => {
const {
loading,
handleDel,
handleGetList,
dataApplication: { data = [], page, size, totalItem }
} = props;
/* 点击分页 */
const handlePageChange = (page, size) => {
handleGetList(page, size);
};
const columns = [
{
title: '应用名称',
dataIndex: 'name',
align: 'center'
},
{
title: '应用标识码',
dataIndex: 'code',
align: 'center',
render(t, r) {
return t || '--'
}
},
{
title: '应用链接地址',
dataIndex: 'url',
align: 'center',
render(t, r) {
return t || '--'
}
},
{
title: '创建时间',
dataIndex: 'createTime',
align: 'center',
render(t, r) {
return t ? moment(t).format("YYYY-MM-DD HH:mm:ss") : '--'
}
},
{
title: '操作',
align: 'center',
render(t, r) {
return (
<>
<Link to={`/appliction/info?id=${r.id}`}>编辑</Link>
<Divider type="vertical" />
<a onClick={() => handleDel(r)}>删除</a>
</>
)
}
}
];
const pagination = {
...paginations,
total: totalItem,
pageSize: size,
current: page,
showSizeChanger: totalItem > 20,
onChange: (page, pageSize) => {
handlePageChange(page, pageSize);
},
onShowSizeChange: (page, pageSize) => {
handlePageChange(1, pageSize);
},
showTotal(total) {
return `总共 ${total} 条数据`;
},
};
return (
<Table
rowKey="id"
loading={loading}
dataSource={data}
columns={columns}
pagination={pagination}
/>
)
};
/* Main */
const Application = props => {
const { dispatch, loading, dataSearch, dataApplication, dataApplication: { size } } = props;
useEffect(() => {
handleGetList(1, 10);
}, [])
/* 应用列表 */
const handleGetList = (page, size) => {
dispatch({type: 'Application/getApplicationList', payload: {
page: page || 1,
size: size || 10
}});
};
/* 点击搜索 */
const handleFinish = (values) => {
const { createTime, name } = values;
dispatch({type: 'Application/changeState', payload: {
dataSearch: {
name,
createTimeBegin: createTime[0] ? createTime[0].startOf('day').valueOf() : undefined,
createTimeEnd: createTime[1] ? createTime[1].endOf('day').valueOf() : undefined,
}
}});
handleGetList(0, size);
};
/* 点击重置 */
const handleReset = () => {
dispatch({type: 'Application/resetSearch'});
handleGetList(0, 10);
};
/* 点击删除 */
const handleDel = ({ id }) => {
Modal.confirm({
title: '确定删除该应用吗?',
icon: <ExclamationCircleOutlined />,
okText: '确定',
cancelText: '取消',
onOk() {
dispatch({type: 'Application/delApplication', payload: {id}});
},
});
};
return (
<Card bordered={false}>
<SearchForm
dataSearch={dataSearch}
handleReset={handleReset}
handleFinish={handleFinish}
/>
<div className="mt-24">
<DataTable
loading={loading}
handleDel={handleDel}
handleGetList={handleGetList}
dataApplication={dataApplication}
/>
</div>
</Card>
)
};
export default connect(({ Application, loading }) => ({
...Application,
loading: loading.effects["Application/getApplicationList"]
}))(Application)
\ No newline at end of file
import { routerRedux } from 'dva';
import { message } from 'antd';
import { getPermissionList } from '@/utils/utils';
import * as services from '@/services/application';
/* SerachParams */
const staticSearch = {
name: undefined,
createTimeBegin: undefined,
createTimeEnd: undefined
};
export default {
namespace: 'Application',
state: {
dataSearch: {
...staticSearch
},
dataApplication: {
data: [],
page: 1,
size: 10,
totalItem: 0,
totalPage: 0,
},
dataModal: {
modalType: '',
modalShow: false,
modalData: {}
},
dataMenuList: [], // 获取所有的菜单
dataAuth: [], // 权限模块提交给后端的数据
dataExpandedKeys: [], // tree展开的数据
dataApplicationInfo: {}, // 应用相关信息
oldInfo: {}, // 上一次权限信息
},
effects: {
/* 获取应用列表 */
*getApplicationList({ payload }, { call, put, select }) {
const { dataSearch } = yield select(state => state.Application);
try {
const res = yield call(services.getApplicationList, {
page: 1,
size: 10,
...dataSearch,
...payload
});
if (res.status === 1) {
res.data.data = res.data.data || [];
yield put({type: 'changeState', payload: {
dataApplication: res.data
}})
}
} catch (err) {
console.error(err)
}
},
/* 删除应用 */
*delApplication({ payload }, { call, put, select }) {
const { dataApplication: { data = [], page, size } } = yield select(state => state.Application);
try {
const res = yield call(services.delApplication, payload);
if (res.status === 1) {
message.success("删除成功~");
yield put({type: 'getApplicationList', payload: {
size,
page: (data.length === 1 ? (page === 1 ? 1 : page - 1) : page)
}})
}
} catch (err) {
console.error(err)
}
},
/* 新增、修改应用 */
*updateApplication({ payload }, { call, put, select }) {
const { id } = payload;
try {
const res = yield call(services[!id ? "addApplication" : "updateApplication"], payload);
if (res.status === 1) {
message.success(!id ? "新增成功~" : "修改成功~");
yield put(routerRedux.push('/appliction'));
}
} catch (err) {
console.error(err)
}
},
/* 获取应用详情 */
*getApplicationInfo({ payload }, { call, put, select }) {
try {
const res = yield call(services.getApplicationInfo, payload);
if (res.status === 1) {
const { data, dataMenu, dataExpandedKeys } = getPermissionList(res.data.permissionList, true);
yield put({type: 'changeState', payload: {
dataApplicationInfo: res.data || {},
dataAuth: data,
dataMenuList: dataMenu,
dataExpandedKeys
}})
};
return res;
} catch (err) {
console.error(err)
}
}
},
reducers: {
changeState(state, { payload }) {
return {
...state,
...payload
}
},
cancelModal(state, { payload }) {
return {
...state,
dataModal: {
modalType: '',
modalShow: false,
modalData: {}
}
}
},
resetSearch(state, { payload }) {
return {
...state,
dataSearch: {
...staticSearch
}
}
}
}
};
/**
* Author: llw
* Date: 2022.9.14
* Description: [行政执法案件详情]
*/
import React, { useEffect } from 'react';
import { connect } from 'umi';
import { Drawer } from 'antd';
import { StyledText } from '@/components/style';
import { mapCause } from '@/constants';
const ModalEnforceMent = props => {
let {
dispatch,
dataModal: {
modalType,
modalShow,
modalData: { id },
},
enforcementInfo,
} = props;
useEffect(() => {
if (modalType === 'Enforce_Ment_Modal' && modalShow) {
dispatch({ type: 'Enforcement/getEventIllegalDetail', payload: { id } });
}
}, [modalType, modalShow]);
return (
<Drawer
title="详情"
placement="right"
width={900}
maskClosable={false}
onClose={() => {
dispatch({ type: 'Enforcement/cancelModal' });
}}
visible={modalType === 'Enforce_Ment_Modal' && modalShow}
footer={null}
>
<StyledText>
<div className="item-text">
<div className="title">立案单位</div>
<div className="desc">{enforcementInfo.company || '-'}</div>
</div>
<div className="item-text">
<div className="title">类型</div>
<div className="desc">
{(mapCause[enforcementInfo.type] && mapCause[enforcementInfo.type].label) || '-'}
</div>
</div>
<div className="item-text">
<div className="title">案件编号</div>
<div className="desc">{enforcementInfo.num || '-'}</div>
</div>
<div className="item-text">
<div className="title">呈批时间</div>
<div className="desc">{enforcementInfo.submitDate || '-'}</div>
</div>
<div className="item-text">
<div className="title">法定代表人及负责人</div>
<div className="desc">{enforcementInfo.placeUserName || '-'}</div>
</div>
<div className="item-text">
<div className="title">电话</div>
<div className="desc">{enforcementInfo.placeUserTel || '-'}</div>
</div>
<div className="item-text">
<div className="title">案件来源</div>
<div className="desc">{enforcementInfo.source || '-'}</div>
</div>
<div className="item-text">
<div className="title">执法人员及执法编号</div>
<div className="desc">{enforcementInfo.nameCode || '-'}</div>
</div>
<div className="item-text">
<div className="title">案由</div>
<div className="desc">{enforcementInfo.cause || '-'}</div>
</div>
<div className="item-text">
<div className="title">案发区域</div>
<div className="desc">{enforcementInfo.area || '-'}</div>
</div>
<div className="item-text" style={{ width: '100%' }}>
<div className="title">违法依据</div>
<div className="desc">{enforcementInfo.illegalBasis || '-'}</div>
</div>
<div className="item-text" style={{ width: '100%' }}>
<div className="title">处罚依据</div>
<div className="desc">{enforcementInfo.punishBasis || '-'}</div>
</div>
<div className="item-text" style={{ width: '100%' }}>
<div className="title">地址</div>
<div className="desc">{enforcementInfo.placeName || '-'}</div>
</div>
<div className="item-text" style={{ width: '100%' }}>
<div className="title">处罚内容</div>
<div className="desc">{enforcementInfo.contents || '-'}</div>
</div>
<div className="item-text" style={{ width: '100%' }}>
<div className="title">案卷相关文书</div>
<div className="desc">{enforcementInfo.instruments || '-'}</div>
</div>
</StyledText>
</Drawer>
);
};
export default connect(({ Enforcement }) => ({
...Enforcement,
}))(ModalEnforceMent);
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!