Initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
/src
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# aikesheng-qc-administration
|
||||||
|
|
||||||
|
## Project setup
|
||||||
|
```
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
- 开发模式运行
|
||||||
|
```
|
||||||
|
yarn run serve
|
||||||
|
```
|
||||||
|
|
||||||
|
- 编译项目
|
||||||
|
```
|
||||||
|
yarn run build
|
||||||
|
```
|
||||||
|
|
||||||
|
- Lints and fixes files
|
||||||
|
```
|
||||||
|
yarn run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Customize configuration
|
||||||
|
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: ["@vue/cli-plugin-babel/preset"],
|
||||||
|
};
|
||||||
Generated
+16644
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"name": "aikesheng-qc-administration",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
"lint": "vue-cli-service lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ant-design-vue": "^1.7.6",
|
||||||
|
"axios": "^0.21.1",
|
||||||
|
"compression-webpack-plugin": "^8.0.1",
|
||||||
|
"core-js": "^3.6.5",
|
||||||
|
"dayjs": "^1.10.6",
|
||||||
|
"enquire.js": "^2.1.6",
|
||||||
|
"jsencrypt": "^3.2.1",
|
||||||
|
"lodash.pick": "^4.4.0",
|
||||||
|
"nprogress": "^0.2.0",
|
||||||
|
"vue": "^2.6.11",
|
||||||
|
"vue-ls": "^3.2.2",
|
||||||
|
"vue-photo-preview": "^1.1.3",
|
||||||
|
"vue-print-nb-jeecg": "^1.0.9",
|
||||||
|
"vue-router": "^3.2.0",
|
||||||
|
"vuedraggable": "^2.20.0",
|
||||||
|
"vuex": "^3.4.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-router": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-vuex": "~4.5.0",
|
||||||
|
"@vue/cli-service": "~4.5.0",
|
||||||
|
"@vue/eslint-config-prettier": "^6.0.0",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"eslint": "^6.7.2",
|
||||||
|
"eslint-plugin-prettier": "^3.3.1",
|
||||||
|
"eslint-plugin-vue": "^6.2.2",
|
||||||
|
"less": "^3.0.4",
|
||||||
|
"less-loader": "^5.0.0",
|
||||||
|
"prettier": "^2.2.1",
|
||||||
|
"vue-template-compiler": "^2.6.11"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:vue/essential",
|
||||||
|
"eslint:recommended",
|
||||||
|
"@vue/prettier"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"parser": "babel-eslint"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"no-debugger": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not dead"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* 防止打包部署时路径被写死
|
||||||
|
*/
|
||||||
|
window._CONFIG = {};
|
||||||
|
window._CONFIG["domianURL"] = "/jero-boot"; // 路由转发 devServer
|
||||||
|
window._CONFIG["domianWebSocketURL"] = "http://localhost:8080/jero-boot";
|
||||||
|
// 单点登录地址
|
||||||
|
window._CONFIG["staticDomainURL"] = window._CONFIG["domianURL"] + "/sys/common/download";
|
||||||
|
window._CONFIG["pdfDomainURL"] = window._CONFIG["domianURL"] + "/sys/common/pdf/pdfPreviewIframe";
|
||||||
|
window._CONFIG["casPrefixUrl"] = "http://cas.example.org:8443/cas";
|
||||||
|
// 预览文件地址
|
||||||
|
window._CONFIG["onlinePreviewDomainURL"] = "http://127.0.0.1:8012/onlinePreview";
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+7720
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,261 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
<script src="<%= BASE_URL %>cdn/babel-polyfill/polyfill_7_2_5.js"></script>
|
||||||
|
<script src="<%= BASE_URL %>api_address_config.js"></script>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#app {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
.chromeframe {
|
||||||
|
margin: 0.2em 0;
|
||||||
|
background: #ccc;
|
||||||
|
color: #000;
|
||||||
|
padding: 0.2em 0;
|
||||||
|
}
|
||||||
|
#loader-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 999999;
|
||||||
|
}
|
||||||
|
#loader {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
margin: -75px 0 0 -75px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 3px solid transparent;
|
||||||
|
/* COLOR 1 */
|
||||||
|
border-top-color: #FFF;
|
||||||
|
-webkit-animation: spin 2s linear infinite;
|
||||||
|
/* Chrome, Opera 15+, Safari 5+ */
|
||||||
|
-ms-animation: spin 2s linear infinite;
|
||||||
|
/* Chrome, Opera 15+, Safari 5+ */
|
||||||
|
-moz-animation: spin 2s linear infinite;
|
||||||
|
/* Chrome, Opera 15+, Safari 5+ */
|
||||||
|
-o-animation: spin 2s linear infinite;
|
||||||
|
/* Chrome, Opera 15+, Safari 5+ */
|
||||||
|
animation: spin 2s linear infinite;
|
||||||
|
/* Chrome, Firefox 16+, IE 10+, Opera */
|
||||||
|
z-index: 1001;
|
||||||
|
}
|
||||||
|
#loader:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
left: 5px;
|
||||||
|
right: 5px;
|
||||||
|
bottom: 5px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 3px solid transparent;
|
||||||
|
/* COLOR 2 */
|
||||||
|
border-top-color: #FFF;
|
||||||
|
-webkit-animation: spin 3s linear infinite;
|
||||||
|
/* Chrome, Opera 15+, Safari 5+ */
|
||||||
|
-moz-animation: spin 3s linear infinite;
|
||||||
|
/* Chrome, Opera 15+, Safari 5+ */
|
||||||
|
-o-animation: spin 3s linear infinite;
|
||||||
|
/* Chrome, Opera 15+, Safari 5+ */
|
||||||
|
-ms-animation: spin 3s linear infinite;
|
||||||
|
/* Chrome, Opera 15+, Safari 5+ */
|
||||||
|
animation: spin 3s linear infinite;
|
||||||
|
/* Chrome, Firefox 16+, IE 10+, Opera */
|
||||||
|
}
|
||||||
|
#loader:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
left: 15px;
|
||||||
|
right: 15px;
|
||||||
|
bottom: 15px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 3px solid transparent;
|
||||||
|
border-top-color: #FFF;
|
||||||
|
/* COLOR 3 */
|
||||||
|
-moz-animation: spin 1.5s linear infinite;
|
||||||
|
/* Chrome, Opera 15+, Safari 5+ */
|
||||||
|
-o-animation: spin 1.5s linear infinite;
|
||||||
|
/* Chrome, Opera 15+, Safari 5+ */
|
||||||
|
-ms-animation: spin 1.5s linear infinite;
|
||||||
|
/* Chrome, Opera 15+, Safari 5+ */
|
||||||
|
-webkit-animation: spin 1.5s linear infinite;
|
||||||
|
/* Chrome, Opera 15+, Safari 5+ */
|
||||||
|
animation: spin 1.5s linear infinite;
|
||||||
|
/* Chrome, Firefox 16+, IE 10+, Opera */
|
||||||
|
}
|
||||||
|
@-webkit-keyframes spin {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
/* Chrome, Opera 15+, Safari 3.1+ */
|
||||||
|
-ms-transform: rotate(0deg);
|
||||||
|
/* IE 9 */
|
||||||
|
transform: rotate(0deg);
|
||||||
|
/* Firefox 16+, IE 10+, Opera */
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
/* Chrome, Opera 15+, Safari 3.1+ */
|
||||||
|
-ms-transform: rotate(360deg);
|
||||||
|
/* IE 9 */
|
||||||
|
transform: rotate(360deg);
|
||||||
|
/* Firefox 16+, IE 10+, Opera */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
/* Chrome, Opera 15+, Safari 3.1+ */
|
||||||
|
-ms-transform: rotate(0deg);
|
||||||
|
/* IE 9 */
|
||||||
|
transform: rotate(0deg);
|
||||||
|
/* Firefox 16+, IE 10+, Opera */
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
/* Chrome, Opera 15+, Safari 3.1+ */
|
||||||
|
-ms-transform: rotate(360deg);
|
||||||
|
/* IE 9 */
|
||||||
|
transform: rotate(360deg);
|
||||||
|
/* Firefox 16+, IE 10+, Opera */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#loader-wrapper .loader-section {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
width: 51%;
|
||||||
|
height: 100%;
|
||||||
|
background: #49a9ee;
|
||||||
|
/* Old browsers */
|
||||||
|
z-index: 1000;
|
||||||
|
-webkit-transform: translateX(0);
|
||||||
|
/* Chrome, Opera 15+, Safari 3.1+ */
|
||||||
|
-ms-transform: translateX(0);
|
||||||
|
/* IE 9 */
|
||||||
|
transform: translateX(0);
|
||||||
|
/* Firefox 16+, IE 10+, Opera */
|
||||||
|
}
|
||||||
|
#loader-wrapper .loader-section.section-left {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
#loader-wrapper .loader-section.section-right {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
/* Loaded */
|
||||||
|
.loaded #loader-wrapper .loader-section.section-left {
|
||||||
|
-webkit-transform: translateX(-100%);
|
||||||
|
/* Chrome, Opera 15+, Safari 3.1+ */
|
||||||
|
-ms-transform: translateX(-100%);
|
||||||
|
/* IE 9 */
|
||||||
|
transform: translateX(-100%);
|
||||||
|
/* Firefox 16+, IE 10+, Opera */
|
||||||
|
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||||
|
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||||
|
}
|
||||||
|
.loaded #loader-wrapper .loader-section.section-right {
|
||||||
|
-webkit-transform: translateX(100%);
|
||||||
|
/* Chrome, Opera 15+, Safari 3.1+ */
|
||||||
|
-ms-transform: translateX(100%);
|
||||||
|
/* IE 9 */
|
||||||
|
transform: translateX(100%);
|
||||||
|
/* Firefox 16+, IE 10+, Opera */
|
||||||
|
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||||
|
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||||
|
}
|
||||||
|
.loaded #loader {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transition: all 0.3s ease-out;
|
||||||
|
transition: all 0.3s ease-out;
|
||||||
|
}
|
||||||
|
.loaded #loader-wrapper {
|
||||||
|
visibility: hidden;
|
||||||
|
-webkit-transform: translateY(-100%);
|
||||||
|
/* Chrome, Opera 15+, Safari 3.1+ */
|
||||||
|
-ms-transform: translateY(-100%);
|
||||||
|
/* IE 9 */
|
||||||
|
transform: translateY(-100%);
|
||||||
|
/* Firefox 16+, IE 10+, Opera */
|
||||||
|
-webkit-transition: all 0.3s 1s ease-out;
|
||||||
|
transition: all 0.3s 1s ease-out;
|
||||||
|
}
|
||||||
|
/* JavaScript Turned Off */
|
||||||
|
.no-js #loader-wrapper {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.no-js h1 {
|
||||||
|
color: #222222;
|
||||||
|
}
|
||||||
|
#loader-wrapper .load_title {
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
color: #FFF;
|
||||||
|
font-size: 14px;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 9999999999999;
|
||||||
|
position: absolute;
|
||||||
|
top: 60%;
|
||||||
|
opacity: 1;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
#loader-wrapper .load_title span {
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #FFF;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
/* 滚动条优化 start */
|
||||||
|
::-webkit-scrollbar{
|
||||||
|
width:8px;
|
||||||
|
height:8px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track{
|
||||||
|
background: #f6f6f6;
|
||||||
|
border-radius:2px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb{
|
||||||
|
background: #cdcdcd;
|
||||||
|
border-radius:2px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb:hover{
|
||||||
|
background: #747474;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-corner {
|
||||||
|
background: #f6f6f6;
|
||||||
|
}
|
||||||
|
/* 滚动条优化 end */
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
|
||||||
|
Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app">
|
||||||
|
<div id="loader-wrapper">
|
||||||
|
<div id="loader"></div>
|
||||||
|
<div class="loader-section section-left"></div>
|
||||||
|
<div class="loader-section section-right"></div>
|
||||||
|
<div class="load_title">正在加载,请耐心等待
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<a-config-provider :locale="locale">
|
||||||
|
<div id="app">
|
||||||
|
<router-view></router-view>
|
||||||
|
</div>
|
||||||
|
</a-config-provider>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import zhCN from "ant-design-vue/lib/locale-provider/zh_CN";
|
||||||
|
import enquireScreen from "@/utils/device";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
locale: zhCN
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
let that = this;
|
||||||
|
enquireScreen(deviceType => {
|
||||||
|
// tablet
|
||||||
|
if (deviceType === 0) {
|
||||||
|
that.$store.commit("TOGGLE_DEVICE", "mobile");
|
||||||
|
that.$store.dispatch("setSidebar", false);
|
||||||
|
}
|
||||||
|
// mobile
|
||||||
|
else if (deviceType === 1) {
|
||||||
|
that.$store.commit("TOGGLE_DEVICE", "mobile");
|
||||||
|
that.$store.dispatch("setSidebar", false);
|
||||||
|
} else {
|
||||||
|
that.$store.commit("TOGGLE_DEVICE", "desktop");
|
||||||
|
that.$store.dispatch("setSidebar", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
#app {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+160
@@ -0,0 +1,160 @@
|
|||||||
|
import { getAction, deleteAction, putAction, postAction, httpAction } from '@/api/manage'
|
||||||
|
import Vue from 'vue'
|
||||||
|
import {UI_CACHE_DB_DICT_DATA } from "@/store/mutation-types"
|
||||||
|
|
||||||
|
//角色管理
|
||||||
|
const addRole = (params)=>postAction("/sys/role/add",params);
|
||||||
|
const editRole = (params)=>putAction("/sys/role/edit",params);
|
||||||
|
const checkRoleCode = (params)=>getAction("/sys/role/checkRoleCode",params);
|
||||||
|
const queryall = (params)=>getAction("/sys/role/queryall",params);
|
||||||
|
|
||||||
|
//用户管理
|
||||||
|
const addUser = (params)=>postAction("/sys/user/add",params);
|
||||||
|
const editUser = (params)=>putAction("/sys/user/edit",params);
|
||||||
|
const queryUserRole = (params)=>getAction("/sys/user/queryUserRole",params);
|
||||||
|
const getUserList = (params)=>getAction("/sys/user/page",params);
|
||||||
|
const frozenBatch = (params)=>putAction("/sys/user/frozenBatch",params);
|
||||||
|
//验证用户是否存在
|
||||||
|
const checkOnlyUser = (params)=>getAction("/sys/user/checkOnlyUser",params);
|
||||||
|
//改变密码
|
||||||
|
const changePassword = (params)=>putAction("/sys/user/changePassword",params);
|
||||||
|
|
||||||
|
//权限管理
|
||||||
|
const addPermission= (params)=>postAction("/sys/permission/add",params);
|
||||||
|
const editPermission= (params)=>putAction("/sys/permission/edit",params);
|
||||||
|
const getPermissionList = (params)=>getAction("/sys/permission/page",params);
|
||||||
|
const getSystemMenuList = (params)=>getAction("/sys/permission/getSystemMenuList",params);
|
||||||
|
const getSystemSubmenu = (params)=>getAction("/sys/permission/getSystemSubmenu",params);
|
||||||
|
const getSystemSubmenuBatch = (params) => getAction('/sys/permission/getSystemSubmenuBatch', params)
|
||||||
|
const queryTreeList = (params)=>getAction("/sys/permission/queryTreeList",params);
|
||||||
|
const queryTreeListForRole = (params)=>getAction("/sys/role/queryTreeList",params);
|
||||||
|
const queryListAsync = (params)=>getAction("/sys/permission/queryListAsync",params);
|
||||||
|
const queryRolePermission = (params)=>getAction("/sys/permission/queryRolePermission",params);
|
||||||
|
const saveRolePermission = (params)=>postAction("/sys/permission/saveRolePermission",params);
|
||||||
|
const queryPermissionsByUser = ()=>getAction("/sys/permission/getUserPermissionByToken");
|
||||||
|
const loadAllRoleIds = (params)=>getAction("/sys/permission/loadAllRoleIds",params);
|
||||||
|
const getPermissionRuleList = (params)=>getAction("/sys/permission/getPermRuleListByPermId",params);
|
||||||
|
const queryPermissionRule = (params)=>getAction("/sys/permission/queryPermissionRule",params);
|
||||||
|
|
||||||
|
// 部门管理
|
||||||
|
const queryDepartTreeList = (params)=>getAction("/sys/sysDepart/queryTreeList",params);
|
||||||
|
const queryIdTree = (params)=>getAction("/sys/sysDepart/queryIdTree",params);
|
||||||
|
const queryParentName = (params)=>getAction("/sys/sysDepart/queryParentName",params);
|
||||||
|
const searchByKeywords = (params)=>getAction("/sys/sysDepart/searchBy",params);
|
||||||
|
const deleteByDepartId = (params)=>deleteAction("/sys/sysDepart/delete",params);
|
||||||
|
|
||||||
|
//二级部门管理
|
||||||
|
const queryDepartPermission = (params)=>getAction("/sys/permission/queryDepartPermission",params);
|
||||||
|
const saveDepartPermission = (params)=>postAction("/sys/permission/saveDepartPermission",params);
|
||||||
|
const queryTreeListForDeptRole = (params)=>getAction("/sys/sysDepartPermission/queryTreeListForDeptRole",params);
|
||||||
|
const queryDeptRolePermission = (params)=>getAction("/sys/sysDepartPermission/queryDeptRolePermission",params);
|
||||||
|
const saveDeptRolePermission = (params)=>postAction("/sys/sysDepartPermission/saveDeptRolePermission",params);
|
||||||
|
const queryMyDepartTreeList = (params)=>getAction("/sys/sysDepart/queryMyDeptTreeList",params);
|
||||||
|
|
||||||
|
//日志管理
|
||||||
|
//const getLogList = (params)=>getAction("/sys/log/page",params);
|
||||||
|
const deleteLog = (params)=>deleteAction("/sys/log/delete",params);
|
||||||
|
const deleteLogList = (params)=>deleteAction("/sys/log/deleteBatch",params);
|
||||||
|
|
||||||
|
//数据字典
|
||||||
|
const addDict = (params)=>postAction("/sys/dict/add",params);
|
||||||
|
const editDict = (params)=>putAction("/sys/dict/edit",params);
|
||||||
|
const treeList = (params)=>getAction("/sys/dict/treeList",params);
|
||||||
|
const addDictItem = (params)=>postAction("/sys/dictItem/add",params);
|
||||||
|
const editDictItem = (params)=>putAction("/sys/dictItem/edit",params);
|
||||||
|
|
||||||
|
//字典标签专用(通过code获取字典数组)
|
||||||
|
export const ajaxGetDictItems = (code, params)=>getAction(`/sys/dict/getDictItems/${code}`,params);
|
||||||
|
//从缓存中获取字典配置
|
||||||
|
function getDictItemsFromCache(dictCode) {
|
||||||
|
if (Vue.ls.get(UI_CACHE_DB_DICT_DATA) && Vue.ls.get(UI_CACHE_DB_DICT_DATA)[dictCode]) {
|
||||||
|
let dictItems = Vue.ls.get(UI_CACHE_DB_DICT_DATA)[dictCode];
|
||||||
|
return dictItems;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//系统通告
|
||||||
|
const doReleaseData = (params)=>getAction("/sys/annountCement/doReleaseData",params);
|
||||||
|
const doReovkeData = (params)=>getAction("/sys/annountCement/doReovkeData",params);
|
||||||
|
//获取系统访问量
|
||||||
|
const getLoginfo = (params)=>getAction("/sys/loginfo",params);
|
||||||
|
const getVisitInfo = (params)=>getAction("/sys/visitInfo",params);
|
||||||
|
|
||||||
|
// 根据部门主键查询用户信息
|
||||||
|
const queryUserByDepId = (params)=>getAction("/sys/user/queryUserByDepId",params);
|
||||||
|
|
||||||
|
// 重复校验
|
||||||
|
const duplicateCheck = (params)=>getAction("/sys/duplicate/check",params);
|
||||||
|
// 加载分类字典
|
||||||
|
const loadCategoryData = (params)=>getAction("/sys/category/loadAllData",params);
|
||||||
|
const checkRuleByCode = (params) => getAction('/sys/checkRule/checkByCode', params)
|
||||||
|
//加载我的通告信息
|
||||||
|
const getUserNoticeInfo= (params)=>getAction("/sys/sysAnnouncementSend/getMyAnnouncementSend",params);
|
||||||
|
const getTransitURL = url => `/sys/common/transitRESTful?url=${encodeURIComponent(url)}`
|
||||||
|
// 中转HTTP请求
|
||||||
|
export const transitRESTful = {
|
||||||
|
get: (url, parameter) => getAction(getTransitURL(url), parameter),
|
||||||
|
post: (url, parameter) => postAction(getTransitURL(url), parameter),
|
||||||
|
put: (url, parameter) => putAction(getTransitURL(url), parameter),
|
||||||
|
http: (url, parameter) => httpAction(getTransitURL(url), parameter),
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
addRole,
|
||||||
|
editRole,
|
||||||
|
checkRoleCode,
|
||||||
|
addUser,
|
||||||
|
editUser,
|
||||||
|
queryUserRole,
|
||||||
|
getUserList,
|
||||||
|
queryall,
|
||||||
|
frozenBatch,
|
||||||
|
checkOnlyUser,
|
||||||
|
changePassword,
|
||||||
|
getPermissionList,
|
||||||
|
addPermission,
|
||||||
|
editPermission,
|
||||||
|
queryTreeList,
|
||||||
|
queryListAsync,
|
||||||
|
queryRolePermission,
|
||||||
|
saveRolePermission,
|
||||||
|
queryPermissionsByUser,
|
||||||
|
loadAllRoleIds,
|
||||||
|
getPermissionRuleList,
|
||||||
|
queryPermissionRule,
|
||||||
|
queryDepartTreeList,
|
||||||
|
queryIdTree,
|
||||||
|
queryParentName,
|
||||||
|
searchByKeywords,
|
||||||
|
deleteByDepartId,
|
||||||
|
deleteLog,
|
||||||
|
deleteLogList,
|
||||||
|
addDict,
|
||||||
|
editDict,
|
||||||
|
treeList,
|
||||||
|
addDictItem,
|
||||||
|
editDictItem,
|
||||||
|
doReleaseData,
|
||||||
|
doReovkeData,
|
||||||
|
getLoginfo,
|
||||||
|
getVisitInfo,
|
||||||
|
queryUserByDepId,
|
||||||
|
duplicateCheck,
|
||||||
|
queryTreeListForRole,
|
||||||
|
getSystemMenuList,
|
||||||
|
getSystemSubmenu,
|
||||||
|
getSystemSubmenuBatch,
|
||||||
|
loadCategoryData,
|
||||||
|
checkRuleByCode,
|
||||||
|
queryDepartPermission,
|
||||||
|
saveDepartPermission,
|
||||||
|
queryTreeListForDeptRole,
|
||||||
|
queryDeptRolePermission,
|
||||||
|
saveDeptRolePermission,
|
||||||
|
queryMyDepartTreeList,
|
||||||
|
getUserNoticeInfo,
|
||||||
|
getDictItemsFromCache
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
const api = {
|
||||||
|
Login: '/sys/login',
|
||||||
|
Logout: '/sys/logout',
|
||||||
|
ForgePassword: '/auth/forge-password',
|
||||||
|
Register: '/auth/register',
|
||||||
|
SendSms: '/account/sms',
|
||||||
|
// get my info
|
||||||
|
UserInfo: '/user/info'
|
||||||
|
}
|
||||||
|
export default api
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import api from './index'
|
||||||
|
import { axios } from '@/utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* login func
|
||||||
|
* parameter: {
|
||||||
|
* username: '',
|
||||||
|
* password: '',
|
||||||
|
* remember_me: true,
|
||||||
|
* captcha: '12345'
|
||||||
|
* }
|
||||||
|
* @param parameter
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function login(parameter) {
|
||||||
|
return axios({
|
||||||
|
url: '/sys/login',
|
||||||
|
method: 'post',
|
||||||
|
data: parameter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function phoneLogin(parameter) {
|
||||||
|
return axios({
|
||||||
|
url: '/sys/phoneLogin',
|
||||||
|
method: 'post',
|
||||||
|
data: parameter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSmsCaptcha(parameter) {
|
||||||
|
return axios({
|
||||||
|
url: api.SendSms,
|
||||||
|
method: 'post',
|
||||||
|
data: parameter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// export function getInfo() {
|
||||||
|
// return axios({
|
||||||
|
// url: '/api/user/info',
|
||||||
|
// method: 'get',
|
||||||
|
// headers: {
|
||||||
|
// 'Content-Type': 'application/json;charset=UTF-8'
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
export function logout(logoutToken) {
|
||||||
|
return axios({
|
||||||
|
url: '/sys/logout',
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json;charset=UTF-8',
|
||||||
|
'X-Access-Token': logoutToken
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方登录
|
||||||
|
* @param token
|
||||||
|
* @param thirdType
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function thirdLogin(token,thirdType) {
|
||||||
|
return axios({
|
||||||
|
url: `/sys/thirdLogin/getLoginUser/${token}/${thirdType}`,
|
||||||
|
method: 'get',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json;charset=UTF-8'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取RSA公钥
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function getRSAPublicKey() {
|
||||||
|
return axios({
|
||||||
|
url: `/sys/getRSAPublicKey`,
|
||||||
|
method: 'get',
|
||||||
|
timeout: 5000,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json;charset=UTF-8'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import { axios } from '@/utils/request'
|
||||||
|
|
||||||
|
const api = {
|
||||||
|
user: '/mock/api/user',
|
||||||
|
role: '/mock/api/role',
|
||||||
|
service: '/mock/api/service',
|
||||||
|
permission: '/mock/api/permission',
|
||||||
|
permissionNoPager: '/mock/api/permission/no-pager'
|
||||||
|
}
|
||||||
|
|
||||||
|
export default api
|
||||||
|
|
||||||
|
//post
|
||||||
|
export function postAction(url,parameter) {
|
||||||
|
return axios({
|
||||||
|
url: url,
|
||||||
|
method:'post' ,
|
||||||
|
data: parameter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//post method= {post | put}
|
||||||
|
export function httpAction(url,parameter,method) {
|
||||||
|
return axios({
|
||||||
|
url: url,
|
||||||
|
method:method ,
|
||||||
|
data: parameter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//put
|
||||||
|
export function putAction(url,parameter) {
|
||||||
|
return axios({
|
||||||
|
url: url,
|
||||||
|
method:'put',
|
||||||
|
data: parameter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//get
|
||||||
|
export function getAction(url,parameter) {
|
||||||
|
return axios({
|
||||||
|
url: url,
|
||||||
|
method: 'get',
|
||||||
|
params: parameter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//deleteAction
|
||||||
|
export function deleteAction(url,parameter) {
|
||||||
|
return axios({
|
||||||
|
url: url,
|
||||||
|
method: 'delete',
|
||||||
|
params: parameter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserList(parameter) {
|
||||||
|
return axios({
|
||||||
|
url: api.user,
|
||||||
|
method: 'get',
|
||||||
|
params: parameter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRoleList(parameter) {
|
||||||
|
return axios({
|
||||||
|
url: api.role,
|
||||||
|
method: 'get',
|
||||||
|
params: parameter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getServiceList(parameter) {
|
||||||
|
return axios({
|
||||||
|
url: api.service,
|
||||||
|
method: 'get',
|
||||||
|
params: parameter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPermissions(parameter) {
|
||||||
|
return axios({
|
||||||
|
url: api.permissionNoPager,
|
||||||
|
method: 'get',
|
||||||
|
params: parameter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// id == 0 add post
|
||||||
|
// id != 0 update put
|
||||||
|
export function saveService(parameter) {
|
||||||
|
return axios({
|
||||||
|
url: api.service,
|
||||||
|
method: parameter.id == 0 ? 'post' : 'put',
|
||||||
|
data: parameter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件 用于excel导出
|
||||||
|
* @param url
|
||||||
|
* @param parameter
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function downFile(url,parameter){
|
||||||
|
return axios({
|
||||||
|
url: url,
|
||||||
|
params: parameter,
|
||||||
|
method:'get' ,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件
|
||||||
|
* @param url 文件路径
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @param parameter
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function downloadFile(url, fileName, parameter) {
|
||||||
|
return downFile(url, parameter).then((data) => {
|
||||||
|
if (!data || data.size === 0) {
|
||||||
|
Vue.prototype['$message'].warning('文件下载失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (typeof window.navigator.msSaveBlob !== 'undefined') {
|
||||||
|
window.navigator.msSaveBlob(new Blob([data]), fileName)
|
||||||
|
} else {
|
||||||
|
let url = window.URL.createObjectURL(new Blob([data]))
|
||||||
|
let link = document.createElement('a')
|
||||||
|
link.style.display = 'none'
|
||||||
|
link.href = url
|
||||||
|
link.setAttribute('download', fileName)
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link) //下载完成移除元素
|
||||||
|
window.URL.revokeObjectURL(url) //释放掉blob对象
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传 用于富文本上传图片
|
||||||
|
* @param url
|
||||||
|
* @param parameter
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function uploadAction(url,parameter){
|
||||||
|
return axios({
|
||||||
|
url: url,
|
||||||
|
data: parameter,
|
||||||
|
method:'post' ,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data', // 文件上传
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件服务访问路径
|
||||||
|
* @param avatar
|
||||||
|
* @param subStr
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function getFileAccessHttpUrl(avatar,subStr) {
|
||||||
|
if(!subStr) subStr = 'http'
|
||||||
|
try {
|
||||||
|
if(avatar && avatar.startsWith(subStr)){
|
||||||
|
return avatar;
|
||||||
|
}else{
|
||||||
|
if(avatar && avatar.length>0 && avatar.indexOf('[')==-1){
|
||||||
|
return window._CONFIG['staticDomainURL'] + "/" + avatar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(err){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/** [表格主题样式一] 表格强制列不换行 */
|
||||||
|
.j-table-force-nowrap {
|
||||||
|
td, th {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-selection-column {
|
||||||
|
padding: 12px 22px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列自适应,弊端会导致列宽失效 */
|
||||||
|
&.ant-table-wrapper .ant-table-content {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
|
||||||
|
/*列表上方操作按钮区域*/
|
||||||
|
.ant-card-body .table-operator {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
/** Button按钮间距 */
|
||||||
|
.table-operator .ant-btn {
|
||||||
|
margin: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
.table-operator .ant-btn-group .ant-btn {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-operator .ant-btn-group .ant-btn:last-child {
|
||||||
|
margin: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
/*列表td的padding设置 可以控制列表大小*/
|
||||||
|
.ant-table-tbody .ant-table-row td {
|
||||||
|
padding-top: 15px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*列表页面弹出modal*/
|
||||||
|
.ant-modal-cust-warp {
|
||||||
|
height: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
/*弹出modal Y轴滚动条*/
|
||||||
|
.ant-modal-cust-warp .ant-modal-body {
|
||||||
|
height: calc(100% - 110px) !important;
|
||||||
|
overflow-y: auto
|
||||||
|
}
|
||||||
|
|
||||||
|
/*弹出modal 先有content后有body 故滚动条控制在body上*/
|
||||||
|
.ant-modal-cust-warp .ant-modal-content {
|
||||||
|
height: 90% !important;
|
||||||
|
overflow-y: hidden
|
||||||
|
}
|
||||||
|
/*列表中有图片的加这个样式 参考用户管理*/
|
||||||
|
.anty-img-wrap {
|
||||||
|
height: 25px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.anty-img-wrap > img {
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
/*列表中范围查询样式*/
|
||||||
|
.query-group-cust{width: calc(50% - 10px)}
|
||||||
|
.query-group-split-cust:before{content:"~";width: 20px;display: inline-block;text-align: center}
|
||||||
|
|
||||||
|
|
||||||
|
/*erp风格子表外框padding设置*/
|
||||||
|
.ant-card-wider-padding.cust-erp-sub-tab>.ant-card-body{padding:5px 12px}
|
||||||
|
|
||||||
|
/* 内嵌子表背景颜色 */
|
||||||
|
.j-inner-table-wrapper /deep/ .ant-table-expanded-row .ant-table-wrapper .ant-table-tbody .ant-table-row {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**隐藏样式-modal确定按钮 */
|
||||||
|
.jee-hidden{display: none}
|
||||||
|
/*列表td的padding设置 可以控制列表大小*/
|
||||||
|
.ant-table-tbody .ant-table-row td .action-span-cell a + a{
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
<template>
|
||||||
|
<div class="hello">
|
||||||
|
<h1>{{ msg }}</h1>
|
||||||
|
<p>
|
||||||
|
For a guide and recipes on how to configure / customize this project,<br />
|
||||||
|
check out the
|
||||||
|
<a href="https://cli.vuejs.org" target="_blank" rel="noopener"
|
||||||
|
>vue-cli documentation</a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
<h3>Installed CLI Plugins</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>babel</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>router</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>vuex</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>eslint</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h3>Essential Links</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://forum.vuejs.org" target="_blank" rel="noopener"
|
||||||
|
>Forum</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://chat.vuejs.org" target="_blank" rel="noopener"
|
||||||
|
>Community Chat</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener"
|
||||||
|
>Twitter</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h3>Ecosystem</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="https://router.vuejs.org" target="_blank" rel="noopener"
|
||||||
|
>vue-router</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://github.com/vuejs/vue-devtools#vue-devtools"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>vue-devtools</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener"
|
||||||
|
>vue-loader</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://github.com/vuejs/awesome-vue"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>awesome-vue</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "HelloWorld",
|
||||||
|
props: {
|
||||||
|
msg: String,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
<style scoped lang="less">
|
||||||
|
h3 {
|
||||||
|
margin: 40px 0 0;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #42b983;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
<template>
|
||||||
|
<a-radio-group v-if="tagType=='radio'" @change="handleInput" :value="getValueSting" :disabled="disabled">
|
||||||
|
<a-radio v-for="(item, key) in dictOptions" :key="key" :value="item.value">{{ item.text }}</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
|
||||||
|
<a-radio-group v-else-if="tagType=='radioButton'" buttonStyle="solid" @change="handleInput" :value="getValueSting" :disabled="disabled">
|
||||||
|
<a-radio-button v-for="(item, key) in dictOptions" :key="key" :value="item.value">{{ item.text }}</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
|
||||||
|
<a-select v-else-if="tagType=='select'" :getPopupContainer = "getPopupContainer" :placeholder="placeholder" :disabled="disabled" :value="getValueSting" @change="handleInput">
|
||||||
|
<a-select-option :value="undefined">请选择</a-select-option>
|
||||||
|
<a-select-option v-for="(item, key) in dictOptions" :key="key" :value="item.value">
|
||||||
|
<span style="display: inline-block;width: 100%" :title=" item.text || item.label ">
|
||||||
|
{{ item.text || item.label }}
|
||||||
|
</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {ajaxGetDictItems,getDictItemsFromCache} from '@/api/api'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "JDictSelectTag",
|
||||||
|
props: {
|
||||||
|
dictCode: String,
|
||||||
|
placeholder: String,
|
||||||
|
triggerChange: Boolean,
|
||||||
|
disabled: Boolean,
|
||||||
|
value: [String, Number],
|
||||||
|
type: String,
|
||||||
|
getPopupContainer:{
|
||||||
|
type: Function,
|
||||||
|
default: (node) => node.parentNode
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dictOptions: [],
|
||||||
|
tagType:""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch:{
|
||||||
|
dictCode:{
|
||||||
|
immediate:true,
|
||||||
|
handler() {
|
||||||
|
this.initDictData()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// console.log(this.dictCode);
|
||||||
|
if(!this.type || this.type==="list"){
|
||||||
|
this.tagType = "select"
|
||||||
|
}else{
|
||||||
|
this.tagType = this.type
|
||||||
|
}
|
||||||
|
//获取字典数据
|
||||||
|
// this.initDictData();
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
getValueSting(){
|
||||||
|
// update-begin author:wangshuai date:20200601 for: 不显示placeholder的文字 ------
|
||||||
|
// 当有null或“” placeholder不显示
|
||||||
|
return this.value != null ? this.value.toString() : undefined;
|
||||||
|
// update-end author:wangshuai date:20200601 for: 不显示placeholder的文字 ------
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initDictData() {
|
||||||
|
//优先从缓存中读取字典配置
|
||||||
|
if(getDictItemsFromCache(this.dictCode)){
|
||||||
|
this.dictOptions = getDictItemsFromCache(this.dictCode);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据字典Code, 初始化字典数组
|
||||||
|
ajaxGetDictItems(this.dictCode, null).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
// console.log(res.result);
|
||||||
|
this.dictOptions = res.result;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleInput(e) {
|
||||||
|
let val;
|
||||||
|
if(this.tagType=="radio"){
|
||||||
|
val = e.target.value
|
||||||
|
}else{
|
||||||
|
val = e
|
||||||
|
}
|
||||||
|
console.log(val);
|
||||||
|
if(this.triggerChange){
|
||||||
|
this.$emit('change', val);
|
||||||
|
}else{
|
||||||
|
this.$emit('input', val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setCurrentDictOptions(dictOptions){
|
||||||
|
this.dictOptions = dictOptions
|
||||||
|
},
|
||||||
|
getCurrentDictOptions(){
|
||||||
|
return this.dictOptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
/**
|
||||||
|
* 字典 util
|
||||||
|
* author: scott
|
||||||
|
* date: 20190109
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {ajaxGetDictItems,getDictItemsFromCache} from '@/api/api'
|
||||||
|
import {getAction} from '@/api/manage'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字典数组
|
||||||
|
* @param dictCode 字典Code
|
||||||
|
* @return List<Map>
|
||||||
|
*/
|
||||||
|
export async function initDictOptions(dictCode) {
|
||||||
|
if (!dictCode) {
|
||||||
|
return '字典Code不能为空!';
|
||||||
|
}
|
||||||
|
//优先从缓存中读取字典配置
|
||||||
|
if(getDictItemsFromCache(dictCode)){
|
||||||
|
let res = {}
|
||||||
|
res.result = getDictItemsFromCache(dictCode);
|
||||||
|
res.success = true;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
//获取字典数组
|
||||||
|
let res = await ajaxGetDictItems(dictCode);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典值替换文本通用方法
|
||||||
|
* @param dictOptions 字典数组
|
||||||
|
* @param text 字典值
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
export function filterDictText(dictOptions, text) {
|
||||||
|
// --update-begin----author:sunjianlei---date:20200323------for: 字典翻译 text 允许逗号分隔 ---
|
||||||
|
if (text != null && Array.isArray(dictOptions)) {
|
||||||
|
let result = []
|
||||||
|
// 允许多个逗号分隔,允许传数组对象
|
||||||
|
let splitText
|
||||||
|
if (Array.isArray(text)) {
|
||||||
|
splitText = text
|
||||||
|
} else {
|
||||||
|
splitText = text.toString().trim().split(',')
|
||||||
|
}
|
||||||
|
for (let txt of splitText) {
|
||||||
|
let dictText = txt
|
||||||
|
for (let dictItem of dictOptions) {
|
||||||
|
if (txt.toString() === dictItem.value.toString()) {
|
||||||
|
dictText = (dictItem.text || dictItem.title || dictItem.label)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(dictText)
|
||||||
|
}
|
||||||
|
return result.join(',')
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
// --update-end----author:sunjianlei---date:20200323------for: 字典翻译 text 允许逗号分隔 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典值替换文本通用方法(多选)
|
||||||
|
* @param dictOptions 字典数组
|
||||||
|
* @param text 字典值
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
export function filterMultiDictText(dictOptions, text) {
|
||||||
|
//js “!text” 认为0为空,所以做提前处理
|
||||||
|
if(text === 0 || text === '0'){
|
||||||
|
if(dictOptions){
|
||||||
|
for (let dictItem of dictOptions) {
|
||||||
|
if (text == dictItem.value) {
|
||||||
|
return dictItem.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!text || text=='null' || !dictOptions || dictOptions.length==0){
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
let re = "";
|
||||||
|
text = text.toString()
|
||||||
|
let arr = text.split(",")
|
||||||
|
dictOptions.forEach(function (option) {
|
||||||
|
if(option){
|
||||||
|
for(let i=0;i<arr.length;i++){
|
||||||
|
if (arr[i] === option.value) {
|
||||||
|
re += option.text+",";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if(re==""){
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
return re.substring(0,re.length-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 翻译字段值对应的文本
|
||||||
|
* @param children
|
||||||
|
* @returns string
|
||||||
|
*/
|
||||||
|
export function filterDictTextByCache(dictCode, key) {
|
||||||
|
if(key==null ||key.length==0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!dictCode) {
|
||||||
|
return '字典Code不能为空!';
|
||||||
|
}
|
||||||
|
//优先从缓存中读取字典配置
|
||||||
|
if(getDictItemsFromCache(dictCode)){
|
||||||
|
let item = getDictItemsFromCache(dictCode).filter(t => t["value"] == key)
|
||||||
|
if(item && item.length>0){
|
||||||
|
return item[0]["text"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 通过code获取字典数组 */
|
||||||
|
export async function getDictItems(dictCode, params) {
|
||||||
|
//优先从缓存中读取字典配置
|
||||||
|
if(getDictItemsFromCache(dictCode)){
|
||||||
|
let desformDictItems = getDictItemsFromCache(dictCode).map(item => ({...item, label: item.text}))
|
||||||
|
return desformDictItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
//缓存中没有,就请求后台
|
||||||
|
return await ajaxGetDictItems(dictCode, params).then(({success, result}) => {
|
||||||
|
if (success) {
|
||||||
|
let res = result.map(item => ({...item, label: item.text}))
|
||||||
|
console.log('------- 从DB中获取到了字典-------dictCode : ', dictCode, res)
|
||||||
|
return Promise.resolve(res)
|
||||||
|
} else {
|
||||||
|
console.error('getDictItems error: : ', res)
|
||||||
|
return Promise.resolve([])
|
||||||
|
}
|
||||||
|
}).catch((res) => {
|
||||||
|
console.error('getDictItems error: ', res)
|
||||||
|
return Promise.resolve([])
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
<template>
|
||||||
|
<a-checkbox-group v-if="tagType=='checkbox'" @change="onChange" :value="arrayValue" :disabled="disabled">
|
||||||
|
<a-checkbox v-for="(item, key) in dictOptions" :key="key" :value="item.value">{{ item.text || item.label }}</a-checkbox>
|
||||||
|
</a-checkbox-group>
|
||||||
|
|
||||||
|
<a-select
|
||||||
|
v-else-if="tagType=='select'"
|
||||||
|
:value="arrayValue"
|
||||||
|
@change="onChange"
|
||||||
|
:disabled="disabled"
|
||||||
|
mode="multiple"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:getPopupContainer="getParentContainer"
|
||||||
|
optionFilterProp="children"
|
||||||
|
:filterOption="filterOption"
|
||||||
|
allowClear>
|
||||||
|
<a-select-option
|
||||||
|
v-for="(item,index) in dictOptions"
|
||||||
|
:key="index"
|
||||||
|
:value="item.value">
|
||||||
|
<span style="display: inline-block;width: 100%" :title=" item.text || item.label ">
|
||||||
|
{{ item.text || item.label }}
|
||||||
|
</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {ajaxGetDictItems,getDictItemsFromCache} from '@/api/api'
|
||||||
|
export default {
|
||||||
|
name: 'JMultiSelectTag',
|
||||||
|
props: {
|
||||||
|
dictCode: String,
|
||||||
|
placeholder: String,
|
||||||
|
disabled: Boolean,
|
||||||
|
value: String,
|
||||||
|
type: String,
|
||||||
|
options:Array,
|
||||||
|
spliter:{
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: ','
|
||||||
|
},
|
||||||
|
popContainer:{
|
||||||
|
type:String,
|
||||||
|
default:'',
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dictOptions: [],
|
||||||
|
tagType:"",
|
||||||
|
arrayValue:!this.value?[]:this.value.split(this.spliter)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
if(!this.type || this.type==="list_multi"){
|
||||||
|
this.tagType = "select"
|
||||||
|
}else{
|
||||||
|
this.tagType = this.type
|
||||||
|
}
|
||||||
|
//获取字典数据
|
||||||
|
//this.initDictData();
|
||||||
|
},
|
||||||
|
watch:{
|
||||||
|
options: function(val){
|
||||||
|
this.setCurrentDictOptions(val);
|
||||||
|
},
|
||||||
|
dictCode:{
|
||||||
|
immediate:true,
|
||||||
|
handler() {
|
||||||
|
this.initDictData()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
value (val) {
|
||||||
|
if(!val){
|
||||||
|
this.arrayValue = []
|
||||||
|
}else{
|
||||||
|
this.arrayValue = this.value.split(this.spliter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initDictData() {
|
||||||
|
if(this.options && this.options.length>0){
|
||||||
|
this.dictOptions = [...this.options]
|
||||||
|
}else{
|
||||||
|
//优先从缓存中读取字典配置
|
||||||
|
let cacheOption = getDictItemsFromCache(this.dictCode)
|
||||||
|
if(cacheOption && cacheOption.length>0){
|
||||||
|
this.dictOptions = cacheOption
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//根据字典Code, 初始化字典数组
|
||||||
|
ajaxGetDictItems(this.dictCode, null).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
this.dictOptions = res.result;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
onChange (selectedValue) {
|
||||||
|
this.$emit('change', selectedValue.join(this.spliter));
|
||||||
|
},
|
||||||
|
setCurrentDictOptions(dictOptions){
|
||||||
|
this.dictOptions = dictOptions
|
||||||
|
},
|
||||||
|
getCurrentDictOptions(){
|
||||||
|
return this.dictOptions
|
||||||
|
},
|
||||||
|
getParentContainer(node){
|
||||||
|
if(!this.popContainer){
|
||||||
|
return node.parentNode
|
||||||
|
}else{
|
||||||
|
return document.querySelector(this.popContainer)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// update--begin--autor:lvdandan-----date:20201120------for:LOWCOD-1086 下拉多选框,搜索时只字典code进行搜索不能通过字典text搜索
|
||||||
|
filterOption(input, option) {
|
||||||
|
return option.componentOptions.children[0].children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
// update--end--autor:lvdandan-----date:20201120------for:LOWCOD-1086 下拉多选框,搜索时只字典code进行搜索不能通过字典text搜索
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
prop: 'value',
|
||||||
|
event: 'change'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,246 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<a-select
|
||||||
|
v-if="async"
|
||||||
|
showSearch
|
||||||
|
labelInValue
|
||||||
|
:disabled="disabled"
|
||||||
|
:getPopupContainer="getParentContainer"
|
||||||
|
@search="loadData"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
v-model="selectedAsyncValue"
|
||||||
|
style="width: 100%"
|
||||||
|
:filterOption="false"
|
||||||
|
@change="handleAsyncChange"
|
||||||
|
allowClear
|
||||||
|
:notFoundContent="loading ? undefined : null"
|
||||||
|
>
|
||||||
|
<a-spin v-if="loading" slot="notFoundContent" size="small"/>
|
||||||
|
<a-select-option v-for="d in options" :key="d.value" :value="d.value">{{ d.text }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
|
||||||
|
<a-select
|
||||||
|
v-else
|
||||||
|
:getPopupContainer="getParentContainer"
|
||||||
|
showSearch
|
||||||
|
:disabled="disabled"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
optionFilterProp="children"
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleChange"
|
||||||
|
:filterOption="filterOption"
|
||||||
|
v-model="selectedValue"
|
||||||
|
allowClear
|
||||||
|
:notFoundContent="loading ? undefined : null">
|
||||||
|
<a-spin v-if="loading" slot="notFoundContent" size="small"/>
|
||||||
|
<a-select-option v-for="d in options" :key="d.value" :value="d.value">{{ d.text }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ajaxGetDictItems,getDictItemsFromCache } from '@/api/api'
|
||||||
|
import debounce from 'lodash/debounce';
|
||||||
|
import { getAction } from '../../api/manage'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'JSearchSelectTag',
|
||||||
|
props:{
|
||||||
|
disabled: Boolean,
|
||||||
|
value: [String, Number],
|
||||||
|
dict: String,
|
||||||
|
dictOptions: Array,
|
||||||
|
async: Boolean,
|
||||||
|
placeholder:{
|
||||||
|
type:String,
|
||||||
|
default:"请选择",
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
popContainer:{
|
||||||
|
type:String,
|
||||||
|
default:'',
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
pageSize:{
|
||||||
|
type: Number,
|
||||||
|
default: 10,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
this.loadData = debounce(this.loadData, 800);//消抖
|
||||||
|
this.lastLoad = 0;
|
||||||
|
return {
|
||||||
|
loading:false,
|
||||||
|
selectedValue:[],
|
||||||
|
selectedAsyncValue:[],
|
||||||
|
options: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created(){
|
||||||
|
this.initDictData();
|
||||||
|
},
|
||||||
|
watch:{
|
||||||
|
"value":{
|
||||||
|
immediate:true,
|
||||||
|
handler(val){
|
||||||
|
if(!val){
|
||||||
|
if(val==0){
|
||||||
|
this.initSelectValue()
|
||||||
|
}else{
|
||||||
|
this.selectedValue=[]
|
||||||
|
this.selectedAsyncValue=[]
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
this.initSelectValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dict":{
|
||||||
|
handler(){
|
||||||
|
this.initDictData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'dictOptions':{
|
||||||
|
deep: true,
|
||||||
|
handler(val){
|
||||||
|
if(val && val.length>0){
|
||||||
|
this.options = [...val]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
initSelectValue(){
|
||||||
|
if(this.async){
|
||||||
|
if(!this.selectedAsyncValue || !this.selectedAsyncValue.key || this.selectedAsyncValue.key!=this.value){
|
||||||
|
console.log("这才请求后台")
|
||||||
|
getAction(`/sys/dict/loadDictItem/${this.dict}`,{key:this.value}).then(res=>{
|
||||||
|
if(res.success){
|
||||||
|
let obj = {
|
||||||
|
key:this.value,
|
||||||
|
label:res.result
|
||||||
|
}
|
||||||
|
this.selectedAsyncValue = {...obj}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
this.selectedValue = this.value.toString()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loadData(value){
|
||||||
|
console.log("数据加载",value)
|
||||||
|
this.lastLoad +=1
|
||||||
|
const currentLoad = this.lastLoad
|
||||||
|
this.options = []
|
||||||
|
this.loading=true
|
||||||
|
// 字典code格式:table,text,code
|
||||||
|
getAction(`/sys/dict/loadDict/${this.dict}`,{keyword:value, pageSize: this.pageSize}).then(res=>{
|
||||||
|
this.loading=false
|
||||||
|
if(res.success){
|
||||||
|
if(currentLoad!=this.lastLoad){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.options = res.result
|
||||||
|
console.log("我是第一个",res)
|
||||||
|
}else{
|
||||||
|
this.$message.warning(res.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
},
|
||||||
|
initDictData(){
|
||||||
|
if(!this.async){
|
||||||
|
//如果字典项集合有数据
|
||||||
|
if(this.dictOptions && this.dictOptions.length>0){
|
||||||
|
this.options = [...this.dictOptions]
|
||||||
|
}else{
|
||||||
|
//根据字典Code, 初始化字典数组
|
||||||
|
let dictStr = ''
|
||||||
|
if(this.dict){
|
||||||
|
let arr = this.dict.split(',')
|
||||||
|
if(arr[0].indexOf('where')>0){
|
||||||
|
let tbInfo = arr[0].split('where')
|
||||||
|
dictStr = tbInfo[0].trim()+','+arr[1]+','+arr[2]+','+encodeURIComponent(tbInfo[1])
|
||||||
|
}else{
|
||||||
|
dictStr = this.dict
|
||||||
|
}
|
||||||
|
if (this.dict.indexOf(",") == -1) {
|
||||||
|
//优先从缓存中读取字典配置
|
||||||
|
if (getDictItemsFromCache(this.dictCode)) {
|
||||||
|
this.options = getDictItemsFromCache(this.dictCode);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ajaxGetDictItems(dictStr, null).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
this.options = res.result;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
//异步一开始也加载一点数据
|
||||||
|
this.loading=true
|
||||||
|
getAction(`/sys/dict/loadDict/${this.dict}`,{pageSize: this.pageSize, keyword:''}).then(res=>{
|
||||||
|
this.loading=false
|
||||||
|
if(res.success){
|
||||||
|
this.options = res.result
|
||||||
|
}else{
|
||||||
|
this.$message.warning(res.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filterOption(input, option) {
|
||||||
|
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
},
|
||||||
|
handleChange (selectedValue) {
|
||||||
|
console.log("selectedValue",selectedValue)
|
||||||
|
this.selectedValue = selectedValue
|
||||||
|
this.callback()
|
||||||
|
},
|
||||||
|
handleAsyncChange(selectedObj){
|
||||||
|
//update-begin-author:scott date:20201222 for:【搜索】搜索查询组件,删除条件,默认下拉还是上次的缓存数据,不好 JT-191
|
||||||
|
if(selectedObj){
|
||||||
|
this.selectedAsyncValue = selectedObj
|
||||||
|
this.selectedValue = selectedObj.key
|
||||||
|
}else{
|
||||||
|
this.selectedAsyncValue = null
|
||||||
|
this.selectedValue = null
|
||||||
|
this.options = null
|
||||||
|
this.loadData("")
|
||||||
|
}
|
||||||
|
this.callback()
|
||||||
|
//update-end-author:scott date:20201222 for:【搜索】搜索查询组件,删除条件,默认下拉还是上次的缓存数据,不好 JT-191
|
||||||
|
},
|
||||||
|
callback(){
|
||||||
|
this.$emit('change', this.selectedValue);
|
||||||
|
},
|
||||||
|
setCurrentDictOptions(dictOptions){
|
||||||
|
this.options = dictOptions
|
||||||
|
},
|
||||||
|
getCurrentDictOptions(){
|
||||||
|
return this.options
|
||||||
|
},
|
||||||
|
getParentContainer(node){
|
||||||
|
if(!this.popContainer){
|
||||||
|
return node.parentNode
|
||||||
|
}else{
|
||||||
|
return document.querySelector(this.popContainer)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
prop: 'value',
|
||||||
|
event: 'change'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
# JDictSelectTag 组件用法
|
||||||
|
----
|
||||||
|
- 从字典表获取数据,dictCode格式说明: 字典code
|
||||||
|
```html
|
||||||
|
<j-dict-select-tag v-model="queryParam.sex" placeholder="请输入用户性别"
|
||||||
|
dictCode="sex"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
v-decorator用法:
|
||||||
|
```html
|
||||||
|
<j-dict-select-tag v-decorator="['sex', {}]" :triggerChange="true" placeholder="请输入用户性别"
|
||||||
|
dictCode="sex"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
- 从数据库表获取字典数据,dictCode格式说明: 表名,文本字段,取值字段
|
||||||
|
```html
|
||||||
|
<j-dict-select-tag v-model="queryParam.username" placeholder="请选择用户名称"
|
||||||
|
dictCode="sys_user,realname,id"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# JDictSelectUtil.js 列表字典函数用法
|
||||||
|
----
|
||||||
|
|
||||||
|
- 第一步: 引入依赖方法
|
||||||
|
```html
|
||||||
|
import {initDictOptions, filterDictText} from '@/components/dict/JDictSelectUtil'
|
||||||
|
```
|
||||||
|
|
||||||
|
- 第二步: 在created()初始化方法执行字典配置方法
|
||||||
|
```html
|
||||||
|
//初始化字典配置
|
||||||
|
this.initDictConfig();
|
||||||
|
```
|
||||||
|
|
||||||
|
- 第三步: 实现initDictConfig方法,加载列表所需要的字典(列表上有多个字典项,就执行多次initDictOptions方法)
|
||||||
|
|
||||||
|
```html
|
||||||
|
initDictConfig() {
|
||||||
|
//初始化字典 - 性别
|
||||||
|
initDictOptions('sex').then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
this.sexDictOptions = res.result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
- 第四步: 实现字段的customRender方法
|
||||||
|
```html
|
||||||
|
customRender: (text, record, index) => {
|
||||||
|
//字典值替换通用方法
|
||||||
|
return filterDictText(this.sexDictOptions, text);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
# JMultiSelectTag 多选组件
|
||||||
|
下拉/checkbox
|
||||||
|
|
||||||
|
## 参数配置
|
||||||
|
| 参数 | 类型 | 必填 |说明|
|
||||||
|
|--------------|---------|----|---------|
|
||||||
|
| placeholder |string | | placeholder |
|
||||||
|
| disabled |Boolean | | 是否禁用 |
|
||||||
|
| type |string | | 多选类型 select/checkbox 默认是select |
|
||||||
|
| dictCode |string | | 数据字典编码或者表名,显示字段名,存储字段名拼接而成的字符串,如果提供了options参数 则此参数可不填|
|
||||||
|
| options |Array | | 多选项,如果dictCode参数未提供,可以设置此参数加载多选项 |
|
||||||
|
|
||||||
|
使用示例
|
||||||
|
----
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<a-form>
|
||||||
|
<a-form-item label="下拉多选" style="width: 300px">
|
||||||
|
<j-multi-select-tag
|
||||||
|
v-model="selectValue"
|
||||||
|
:options="dictOptions"
|
||||||
|
placeholder="请做出你的选择">
|
||||||
|
</j-multi-select-tag>
|
||||||
|
{{ selectValue }}
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="checkbox">
|
||||||
|
<j-multi-select-tag
|
||||||
|
v-model="checkboxValue"
|
||||||
|
:options="dictOptions"
|
||||||
|
type="checkbox">
|
||||||
|
</j-multi-select-tag>
|
||||||
|
{{ checkboxValue }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-form >
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import JMultiSelectTag from '@/components/dict/JMultiSelectTag'
|
||||||
|
export default {
|
||||||
|
components: {JMultiSelectTag},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectValue:"",
|
||||||
|
checkboxValue:"",
|
||||||
|
dictOptions:[{
|
||||||
|
label:"选项一",
|
||||||
|
value:"1"
|
||||||
|
},{
|
||||||
|
label:"选项二",
|
||||||
|
value:"2"
|
||||||
|
},{
|
||||||
|
label:"选项三",
|
||||||
|
value:"3"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
# JSearchSelectTag 字典表的搜索组件
|
||||||
|
下拉搜索组件,支持异步加载,异步加载用于大数据量的字典表
|
||||||
|
|
||||||
|
## 参数配置
|
||||||
|
| 参数 | 类型 | 必填 |说明|
|
||||||
|
|--------------|---------|----|---------|
|
||||||
|
| placeholder |string | | placeholder |
|
||||||
|
| disabled |Boolean | | 是否禁用 |
|
||||||
|
| dict |string | | 表名,显示字段名,存储字段名拼接而成的字符串,如果提供了dictOptions参数 则此参数可不填|
|
||||||
|
| dictOptions |Array | | 多选项,如果dict参数未提供,可以设置此参数加载多选项 |
|
||||||
|
| async |Boolean | | 是否支持异步加载,设置成true,则通过输入的内容加载远程数据,否则在本地过滤数据,默认false|
|
||||||
|
|
||||||
|
使用示例
|
||||||
|
----
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<a-form>
|
||||||
|
<a-form-item label="下拉搜索" style="width: 300px">
|
||||||
|
<j-search-select-tag
|
||||||
|
placeholder="请做出你的选择"
|
||||||
|
v-model="selectValue"
|
||||||
|
:dictOptions="dictOptions">
|
||||||
|
</j-search-select-tag>
|
||||||
|
{{ selectValue }}
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="异步加载" style="width: 300px">
|
||||||
|
<j-search-select-tag
|
||||||
|
placeholder="请做出你的选择"
|
||||||
|
v-model="asyncSelectValue"
|
||||||
|
dict="sys_depart,depart_name,id"
|
||||||
|
:async="true">
|
||||||
|
</j-search-select-tag>
|
||||||
|
{{ asyncSelectValue }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-form >
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import JSearchSelectTag from '@/components/dict/JSearchSelectTag'
|
||||||
|
export default {
|
||||||
|
components: {JSearchSelectTag},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectValue:"",
|
||||||
|
asyncSelectValue:"",
|
||||||
|
dictOptions:[{
|
||||||
|
text:"选项一",
|
||||||
|
value:"1"
|
||||||
|
},{
|
||||||
|
text:"选项二",
|
||||||
|
value:"2"
|
||||||
|
},{
|
||||||
|
text:"选项三",
|
||||||
|
value:"3"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import JDictSelectTag from './JDictSelectTag.vue'
|
||||||
|
import JMultiSelectTag from './JMultiSelectTag.vue'
|
||||||
|
import JSearchSelectTag from './JSearchSelectTag.vue'
|
||||||
|
import { filterMultiDictText,filterDictText,initDictOptions,filterDictTextByCache } from './JDictSelectUtil'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install: function (Vue) {
|
||||||
|
Vue.component('JDictSelectTag',JDictSelectTag);
|
||||||
|
Vue.component('JMultiSelectTag',JMultiSelectTag);
|
||||||
|
Vue.component('JSearchSelectTag',JSearchSelectTag);
|
||||||
|
Vue.prototype.$initDictOptions = (dictCode) => initDictOptions(dictCode)
|
||||||
|
Vue.prototype.$filterMultiDictText = (dictOptions, text) => filterMultiDictText(dictOptions, text)
|
||||||
|
Vue.prototype.$filterDictText = (dictOptions, text) => filterDictText(dictOptions, text)
|
||||||
|
Vue.prototype.$filterDictTextByCache = (...param) => filterDictTextByCache(...param)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<template>
|
||||||
|
<div class="components-input-demo-presuffix">
|
||||||
|
<a-input @click="openModal" placeholder="corn表达式" v-model="cron" @change="handleOK">
|
||||||
|
<a-icon slot="prefix" type="schedule" title="corn控件"/>
|
||||||
|
<a-icon v-if="cron" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
|
||||||
|
</a-input>
|
||||||
|
<JCronModal ref="innerVueCron" :data="cron" @ok="handleOK"></JCronModal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import JCronModal from "./modal/JCronModal";
|
||||||
|
export default {
|
||||||
|
name: 'JCron',
|
||||||
|
components: {
|
||||||
|
JCronModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
required: false,
|
||||||
|
type: String,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
cron: this.value,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch:{
|
||||||
|
value(val){
|
||||||
|
this.cron = val
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
openModal(){
|
||||||
|
this.$refs.innerVueCron.show();
|
||||||
|
},
|
||||||
|
handleOK(val){
|
||||||
|
this.cron = val;
|
||||||
|
this.$emit("change", this.cron);
|
||||||
|
//this.$emit("change", Object.assign({}, this.cron));
|
||||||
|
},
|
||||||
|
handleEmpty(){
|
||||||
|
this.handleOK('')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
prop: 'value',
|
||||||
|
event: 'change'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.components-input-demo-presuffix .anticon-close-circle {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #ccc;
|
||||||
|
transition: color 0.3s;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.components-input-demo-presuffix .anticon-close-circle:hover {
|
||||||
|
color: #f5222d;
|
||||||
|
}
|
||||||
|
.components-input-demo-presuffix .anticon-close-circle:active {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!--YYYY-MM-DD HH:mm:ss-->
|
||||||
|
<a-date-picker
|
||||||
|
v-if="showType === 'time'"
|
||||||
|
dropdownClassName="j-date-picker"
|
||||||
|
:disabled="disabled || readOnly"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
@change="handleDateChange"
|
||||||
|
:value="momVal"
|
||||||
|
:showTime="true"
|
||||||
|
:format="dateFormat"
|
||||||
|
v-bind="$attrs"
|
||||||
|
v-on="childListeners"
|
||||||
|
:getCalendarContainer="getCalendarContainer">
|
||||||
|
</a-date-picker>
|
||||||
|
<!--YYYY-MM-DD-->
|
||||||
|
<a-date-picker
|
||||||
|
v-if="showType === 'day'"
|
||||||
|
dropdownClassName="j-date-picker"
|
||||||
|
:disabled="disabled || readOnly"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
@change="handleDateChange"
|
||||||
|
:value="momVal"
|
||||||
|
:showTime="false"
|
||||||
|
:format="dateFormat"
|
||||||
|
v-bind="$attrs"
|
||||||
|
v-on="childListeners"
|
||||||
|
:getCalendarContainer="getCalendarContainer">
|
||||||
|
</a-date-picker>
|
||||||
|
<!--YYYY-MM-->
|
||||||
|
<a-month-picker
|
||||||
|
v-if="showType === 'month'"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:value="momVal"
|
||||||
|
:disabled="disabled || readOnly"
|
||||||
|
:format="dateFormat"
|
||||||
|
v-bind="$attrs"
|
||||||
|
v-on="childListeners"
|
||||||
|
@change="handleMonthChange">
|
||||||
|
</a-month-picker>
|
||||||
|
<!--YYYY-->
|
||||||
|
<a-date-picker
|
||||||
|
v-if="showType === 'year'"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
mode="year"
|
||||||
|
:format="dateFormat"
|
||||||
|
:value="year"
|
||||||
|
:disabled="disabled || readOnly"
|
||||||
|
:open="yearShowOne"
|
||||||
|
v-bind="$attrs"
|
||||||
|
v-on="childListeners"
|
||||||
|
:getCalendarContainer="getCalendarContainer"
|
||||||
|
@openChange="openChangeOne"
|
||||||
|
@panelChange="panelChangeOne">
|
||||||
|
</a-date-picker>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import moment from 'moment'
|
||||||
|
export default {
|
||||||
|
name: 'JDate',
|
||||||
|
props: {
|
||||||
|
placeholder:{
|
||||||
|
type: String,
|
||||||
|
default: '请选择',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
value:{
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
dateFormat:{
|
||||||
|
type: String,
|
||||||
|
default: 'YYYY-MM-DD',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
readOnly:{
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
disabled:{
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
//控制选择时分秒
|
||||||
|
showType:{
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default:'time'
|
||||||
|
},
|
||||||
|
getCalendarContainer: {
|
||||||
|
type: Function,
|
||||||
|
default: (node) => node.parentNode
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
let dateStr = this.value;
|
||||||
|
return {
|
||||||
|
yearShowOne:false, //控制年份模态框的显示与否
|
||||||
|
year:'', // mode==“year” 专用
|
||||||
|
momVal:!dateStr?null:moment(dateStr,this.dateFormat)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
//透传给下级组件的事件,需要排除本组件使用的change事件
|
||||||
|
childListeners() {
|
||||||
|
const vm = this;
|
||||||
|
let result = Object.assign({},
|
||||||
|
this.$listeners,
|
||||||
|
)
|
||||||
|
delete result.change;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value (val) {
|
||||||
|
let fm = this.dateFormat
|
||||||
|
moment.prototype.toJSON = function(){
|
||||||
|
return moment(this).format(fm)
|
||||||
|
}
|
||||||
|
if(!val){
|
||||||
|
this.momVal = null
|
||||||
|
}else{
|
||||||
|
this.momVal = moment(val,this.dateFormat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
moment,
|
||||||
|
handleDateChange(mom,dateStr){
|
||||||
|
this.$emit('change', dateStr);
|
||||||
|
},
|
||||||
|
handleMonthChange(mom,dateStr){
|
||||||
|
this.$emit('change',dateStr)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 弹出日历和关闭日历的回调
|
||||||
|
* status:打开或关闭的状态
|
||||||
|
* */
|
||||||
|
openChangeOne(status){
|
||||||
|
this.yearShowOne = status
|
||||||
|
},
|
||||||
|
// 得到年份选择器的值
|
||||||
|
panelChangeOne(value){
|
||||||
|
this.yearShowOne = false
|
||||||
|
let year = moment(value,this.dateFormat).year().toString() // 处理成字符串
|
||||||
|
this.year = value //组件显示的
|
||||||
|
this.$emit('change',year)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//2.2新增 在组件内定义 指定父组件调用时候的传值属性和事件类型
|
||||||
|
model: {
|
||||||
|
prop: 'value',
|
||||||
|
event: 'change'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
|||||||
|
<template>
|
||||||
|
<a-tooltip
|
||||||
|
placement="topLeft"
|
||||||
|
v-bind="$attrs"
|
||||||
|
v-on="$listeners">
|
||||||
|
<template slot="title">
|
||||||
|
<span>{{value}}</span>
|
||||||
|
</template>
|
||||||
|
{{ value | ellipsis(length) }}
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'JEllipsis',
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
length: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 25,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="disabled?'jero-form-container-disabled':''">
|
||||||
|
<fieldset :disabled="disabled">
|
||||||
|
<slot name="detail"></slot>
|
||||||
|
</fieldset>
|
||||||
|
<slot name="edit"></slot>
|
||||||
|
<fieldset disabled>
|
||||||
|
<slot></slot>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 使用方法
|
||||||
|
* 在form下直接写这个组件就行了,
|
||||||
|
*<a-form layout="inline" :form="form" >
|
||||||
|
* <j-form-container :disabled="true">
|
||||||
|
* <!-- 表单内容省略..... -->
|
||||||
|
* </j-form-container>
|
||||||
|
*</a-form>
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: 'JFormContainer',
|
||||||
|
props:{
|
||||||
|
disabled:{
|
||||||
|
type:Boolean,
|
||||||
|
default:false,
|
||||||
|
required:false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted(){
|
||||||
|
console.log("我是表单禁用专用组件,但是我并不支持表单中iframe的内容禁用")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.jero-form-container-disabled{
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.jero-form-container-disabled fieldset[disabled] {
|
||||||
|
-ms-pointer-events: none;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.jero-form-container-disabled .ant-select{
|
||||||
|
-ms-pointer-events: none;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jero-form-container-disabled .ant-upload-select{display:none}
|
||||||
|
.jero-form-container-disabled .ant-upload-list{cursor:grabbing}
|
||||||
|
.jero-form-container-disabled fieldset[disabled] .ant-upload-list{
|
||||||
|
-ms-pointer-events: auto !important;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jero-form-container-disabled .ant-upload-list-item-actions .anticon-delete,
|
||||||
|
.jero-form-container-disabled .ant-upload-list-item .anticon-close{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
<template>
|
||||||
|
<div class="img">
|
||||||
|
<a-upload
|
||||||
|
name="file"
|
||||||
|
listType="picture-card"
|
||||||
|
:multiple="isMultiple"
|
||||||
|
:action="uploadAction"
|
||||||
|
:headers="headers"
|
||||||
|
:data="{biz:bizPath}"
|
||||||
|
:fileList="fileList"
|
||||||
|
:beforeUpload="beforeUpload"
|
||||||
|
:disabled="disabled"
|
||||||
|
:isMultiple="isMultiple"
|
||||||
|
:showUploadList="isMultiple"
|
||||||
|
v-bind="$attrs"
|
||||||
|
v-on="childListeners"
|
||||||
|
@change="handleChange"
|
||||||
|
@preview="handlePreview"
|
||||||
|
:class="!isMultiple?'imgupload':''">
|
||||||
|
<div :style="{'width':(!isMultiple?'104px':'auto'),'height':(!isMultiple?'104px':'auto')}">
|
||||||
|
<img v-if="!isMultiple && picUrl" :src="getAvatarView()" style="width:100%;height:100%"/>
|
||||||
|
<!--<img v-if="!isMultiple && picUrl" src="http://localhost:3000/jero-boot/sys/common/download/1379989167782797313?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MTc4NTA1OTgsInVzZXJuYW1lIjoiYWRtaW4ifQ.1xRpivI0TzTqAnISJICNm-PoMxSDUITH5uaEcvwheIQ&fullfilename=3.jpeg" style="width:100%;height:100%"/>-->
|
||||||
|
<div v-else class="iconp">
|
||||||
|
<a-icon :type="uploadLoading ? 'loading' : 'plus'" />
|
||||||
|
<div class="ant-upload-text">{{ text }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-upload>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { ACCESS_TOKEN } from "@/store/mutation-types"
|
||||||
|
import { getFileAccessHttpUrl } from '@/api/manage'
|
||||||
|
const Base64 = require('js-base64').Base64
|
||||||
|
|
||||||
|
const uidGenerator=()=>{
|
||||||
|
return '-'+parseInt(Math.random()*10000+1,10);
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
name: 'JImageUpload',
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
uploadAction:window._CONFIG['domianURL']+"/sys/common/upload",
|
||||||
|
uploadLoading:false,
|
||||||
|
picUrl:false,
|
||||||
|
headers:{},
|
||||||
|
fileList: [],
|
||||||
|
previewImage:"",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props:{
|
||||||
|
text:{
|
||||||
|
type:String,
|
||||||
|
required:false,
|
||||||
|
default:"上传"
|
||||||
|
},
|
||||||
|
/*这个属性用于控制文件上传的业务路径*/
|
||||||
|
bizPath:{
|
||||||
|
type:String,
|
||||||
|
required:false,
|
||||||
|
default:"temp"
|
||||||
|
},
|
||||||
|
value:{
|
||||||
|
type:[String,Array],
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
disabled:{
|
||||||
|
type:Boolean,
|
||||||
|
required:false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
isMultiple:{
|
||||||
|
type:Boolean,
|
||||||
|
required:false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
//update-begin-author:wangshuai date:20201021 for:LOWCOD-969 新增number属性,用于判断上传数量
|
||||||
|
number:{
|
||||||
|
type:Number,
|
||||||
|
required:false,
|
||||||
|
default:0
|
||||||
|
}
|
||||||
|
//update-end-author:wangshuai date:20201021 for:LOWCOD-969 新增number属性,用于判断上传数量
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
//透传给下级组件的事件,需要排除本组件使用的change事件
|
||||||
|
childListeners() {
|
||||||
|
const vm = this;
|
||||||
|
let result = Object.assign({},
|
||||||
|
this.$listeners,
|
||||||
|
)
|
||||||
|
delete result.change;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch:{
|
||||||
|
value: {
|
||||||
|
handler(val,oldValue) {
|
||||||
|
if (val instanceof Array) {
|
||||||
|
this.initFileList(val.join(','))
|
||||||
|
} else {
|
||||||
|
this.initFileList(val)
|
||||||
|
}
|
||||||
|
if(!val || val.length==0){
|
||||||
|
this.picUrl = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//立刻执行handler
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created(){
|
||||||
|
const token = Vue.ls.get(ACCESS_TOKEN);
|
||||||
|
this.headers = {"X-Access-Token":token}
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
initFileList(paths){
|
||||||
|
if(!paths || paths.length === 0){
|
||||||
|
this.fileList = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.picUrl = true;
|
||||||
|
let arr = paths.split(",")
|
||||||
|
for(var a=0;a<arr.length;a++){
|
||||||
|
this.fileList.forEach((item)=>{
|
||||||
|
if (item.url === arr[a]){
|
||||||
|
item.uid = uidGenerator()
|
||||||
|
item.response.status = 'history'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeUpload: function(file){
|
||||||
|
var fileType = file.type;
|
||||||
|
if(fileType.indexOf('image')<0){
|
||||||
|
this.$message.warning('请上传图片');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleChange(info) {
|
||||||
|
this.picUrl = false;
|
||||||
|
let fileList = info.fileList
|
||||||
|
//update-begin-author:wangshuai date:20201022 for:LOWCOD-969 判断number是否大于0和是否多选,返回选定的元素。
|
||||||
|
if(this.number>0 && this.isMultiple){
|
||||||
|
fileList = fileList.slice(-this.number);
|
||||||
|
}
|
||||||
|
//update-end-author:wangshuai date:20201022 for:LOWCOD-969 判断number是否大于0和是否多选,返回选定的元素。
|
||||||
|
if(info.file.status==='done'){
|
||||||
|
if(info.file.response.success){
|
||||||
|
this.picUrl = true;
|
||||||
|
fileList = fileList.map((file) => {
|
||||||
|
if (file.response) {
|
||||||
|
file.url = `${file.response.result.id}?token=${Vue.ls.get(ACCESS_TOKEN)}&fullfilename=${file.response.result.fileName}`;
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//this.$message.success(`${info.file.name} 上传成功!`);
|
||||||
|
}else if (info.file.status === 'error') {
|
||||||
|
this.$message.error(`${info.file.name} 上传失败.`);
|
||||||
|
}else if(info.file.status === 'removed'){
|
||||||
|
this.handleDelete(info.file)
|
||||||
|
}
|
||||||
|
this.fileList = fileList
|
||||||
|
if(info.file.status==='done' || info.file.status === 'removed'){
|
||||||
|
this.handlePathChange()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 预览
|
||||||
|
handlePreview(file) {
|
||||||
|
if (file && file.url) {
|
||||||
|
var fileFullUrl = `${window._CONFIG['domianWebSocketURL']}/sys/common/download/${file.response.result.id}?token=${Vue.ls.get(ACCESS_TOKEN)}&fullfilename=${file.name}`
|
||||||
|
let url = `${window._CONFIG['onlinePreviewDomainURL']}?url=${encodeURIComponent(Base64.encode(fileFullUrl))}`
|
||||||
|
window.open(url)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getAvatarView(){
|
||||||
|
if(this.fileList.length>0){
|
||||||
|
let url = this.fileList[this.fileList.length-1].url
|
||||||
|
return getFileAccessHttpUrl(url)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handlePathChange(){
|
||||||
|
let uploadFiles = this.fileList
|
||||||
|
let path = ''
|
||||||
|
if(!uploadFiles || uploadFiles.length==0){
|
||||||
|
path = ''
|
||||||
|
}
|
||||||
|
let arr = [];
|
||||||
|
if(!this.isMultiple){
|
||||||
|
arr.push(uploadFiles[uploadFiles.length-1].url)
|
||||||
|
}else{
|
||||||
|
for(let a=0;a<uploadFiles.length;a++){
|
||||||
|
// update-begin-author:taoyan date:20200819 for:【开源问题z】上传图片组件 LOWCOD-783
|
||||||
|
if(uploadFiles[a].status === 'done' ) {
|
||||||
|
arr.push(uploadFiles[a].url)
|
||||||
|
}else{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// update-end-author:taoyan date:20200819 for:【开源问题z】上传图片组件 LOWCOD-783
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(arr.length>0){
|
||||||
|
path = arr.join(",")
|
||||||
|
}
|
||||||
|
this.$emit('change', path);
|
||||||
|
},
|
||||||
|
handleDelete(file){
|
||||||
|
//如有需要新增 删除逻辑
|
||||||
|
},
|
||||||
|
handleCancel() {
|
||||||
|
this.close();
|
||||||
|
this.previewVisible = false;
|
||||||
|
},
|
||||||
|
close () {
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
prop: 'value',
|
||||||
|
event: 'change'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/deep/ .imgupload .ant-upload-select{display:block}
|
||||||
|
/deep/ .imgupload .ant-upload.ant-upload-select-picture-card{ width:120px;height: 120px;}
|
||||||
|
/deep/ .imgupload .iconp{padding:32px;}
|
||||||
|
/* update--end--autor:lvdandan-----date:20201016------for:j-image-upload图片组件单张图片详情回显空白*/
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
<template>
|
||||||
|
<a-input :placeholder="placeholder"
|
||||||
|
:value="inputVal"
|
||||||
|
@input="backValue"
|
||||||
|
v-bind="$props"
|
||||||
|
v-on="childListeners"
|
||||||
|
></a-input>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import { Input } from 'ant-design-vue'
|
||||||
|
|
||||||
|
const JINPUT_QUERY_LIKE = 'like';
|
||||||
|
const JINPUT_QUERY_NE = 'ne';
|
||||||
|
const JINPUT_QUERY_GE = 'ge'; //大于等于
|
||||||
|
const JINPUT_QUERY_LE = 'le'; //小于等于
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'JInput',
|
||||||
|
props:{
|
||||||
|
...Input.props, // 拿到全部外部的参数
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: JINPUT_QUERY_LIKE
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
trim: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
inputVal:''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
//透传给下级组件的事件,需要排除本组件使用的change事件
|
||||||
|
childListeners() {
|
||||||
|
const vm = this;
|
||||||
|
let result = Object.assign({},
|
||||||
|
this.$listeners,
|
||||||
|
)
|
||||||
|
delete result.change;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch:{
|
||||||
|
value:{
|
||||||
|
immediate:true,
|
||||||
|
handler:function(){
|
||||||
|
this.initVal();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 当 type 变化的时候重新计算值
|
||||||
|
type() {
|
||||||
|
this.backValue({ target: { value: this.inputVal } })
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
initVal(){
|
||||||
|
if(!this.value){
|
||||||
|
this.inputVal = ''
|
||||||
|
}else{
|
||||||
|
let text = this.value
|
||||||
|
switch (this.type) {
|
||||||
|
case JINPUT_QUERY_LIKE:
|
||||||
|
//修复路由传参的值传送到jinput框被前后各截取了一位 #1336
|
||||||
|
if(text.indexOf("*") != -1){
|
||||||
|
text = text.substring(1,text.length-1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JINPUT_QUERY_NE:
|
||||||
|
text = text.substring(1);
|
||||||
|
break;
|
||||||
|
case JINPUT_QUERY_GE:
|
||||||
|
text = text.substring(2);
|
||||||
|
break;
|
||||||
|
case JINPUT_QUERY_LE:
|
||||||
|
text = text.substring(2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
this.inputVal = text
|
||||||
|
}
|
||||||
|
},
|
||||||
|
backValue(e){
|
||||||
|
let text = e.target.value
|
||||||
|
if(text && this.trim===true){
|
||||||
|
text = text.trim()
|
||||||
|
}
|
||||||
|
switch (this.type) {
|
||||||
|
case JINPUT_QUERY_LIKE:
|
||||||
|
text = "*"+text+"*";
|
||||||
|
break;
|
||||||
|
case JINPUT_QUERY_NE:
|
||||||
|
text = "!"+text;
|
||||||
|
break;
|
||||||
|
case JINPUT_QUERY_GE:
|
||||||
|
text = ">="+text;
|
||||||
|
break;
|
||||||
|
case JINPUT_QUERY_LE:
|
||||||
|
text = "<="+text;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
this.$emit('change', text);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
prop: 'value',
|
||||||
|
event: 'change'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
ref="modal"
|
||||||
|
:class="getClass(modalClass)"
|
||||||
|
:style="getStyle(modalStyle)"
|
||||||
|
:visible="visible"
|
||||||
|
v-bind="_attrs"
|
||||||
|
v-on="$listeners"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
|
||||||
|
<slot></slot>
|
||||||
|
|
||||||
|
<template v-if="!isNoTitle" slot="title">
|
||||||
|
<a-row class="j-modal-title-row" type="flex">
|
||||||
|
<a-col class="left">
|
||||||
|
<slot name="title">{{ title }}</slot>
|
||||||
|
</a-col>
|
||||||
|
<a-col v-if="switchFullscreen" class="right" @click="toggleFullscreen">
|
||||||
|
<a-button class="ant-modal-close ant-modal-close-x" ghost type="link" :icon="fullscreenButtonIcon"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 处理 scopedSlots -->
|
||||||
|
<template v-for="slotName of scopedSlotsKeys" :slot="slotName">
|
||||||
|
<slot :name="slotName"></slot>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 处理 slots -->
|
||||||
|
<template v-for="slotName of slotsKeys" v-slot:[slotName]>
|
||||||
|
<slot :name="slotName"></slot>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import { getClass, getStyle } from '@/utils/props-util'
|
||||||
|
import { triggerWindowResizeEvent } from '@/utils/util'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'JModal',
|
||||||
|
props: {
|
||||||
|
title: String,
|
||||||
|
// 可使用 .sync 修饰符
|
||||||
|
visible: Boolean,
|
||||||
|
// 是否全屏弹窗,当全屏时无论如何都会禁止 body 滚动。可使用 .sync 修饰符
|
||||||
|
fullscreen: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 是否允许切换全屏(允许后右上角会出现一个按钮)
|
||||||
|
switchFullscreen: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 点击确定按钮的时候是否关闭弹窗
|
||||||
|
okClose: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 内部使用的 slots ,不再处理
|
||||||
|
usedSlots: ['title'],
|
||||||
|
// 实际控制是否全屏的参数
|
||||||
|
innerFullscreen: this.fullscreen,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 一些未处理的参数或特殊处理的参数绑定到 a-modal 上
|
||||||
|
_attrs() {
|
||||||
|
let attrs = { ...this.$attrs }
|
||||||
|
// 如果全屏就将宽度设为 100%
|
||||||
|
if (this.innerFullscreen) {
|
||||||
|
attrs['width'] = '100%'
|
||||||
|
}
|
||||||
|
return attrs
|
||||||
|
},
|
||||||
|
modalClass() {
|
||||||
|
return {
|
||||||
|
'j-modal-box': true,
|
||||||
|
'fullscreen': this.innerFullscreen,
|
||||||
|
'no-title': this.isNoTitle,
|
||||||
|
'no-footer': this.isNoFooter,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modalStyle() {
|
||||||
|
let style = {}
|
||||||
|
// 如果全屏就将top设为 0
|
||||||
|
if (this.innerFullscreen) {
|
||||||
|
style['top'] = '0'
|
||||||
|
}
|
||||||
|
return style
|
||||||
|
},
|
||||||
|
isNoTitle() {
|
||||||
|
return !this.title && !this.allSlotsKeys.includes('title')
|
||||||
|
},
|
||||||
|
isNoFooter() {
|
||||||
|
return this._attrs['footer'] === null
|
||||||
|
},
|
||||||
|
slotsKeys() {
|
||||||
|
return Object.keys(this.$slots).filter(key => !this.usedSlots.includes(key))
|
||||||
|
},
|
||||||
|
scopedSlotsKeys() {
|
||||||
|
return Object.keys(this.$scopedSlots).filter(key => !this.usedSlots.includes(key))
|
||||||
|
},
|
||||||
|
allSlotsKeys() {
|
||||||
|
return Object.keys(this.$slots).concat(Object.keys(this.$scopedSlots))
|
||||||
|
},
|
||||||
|
// 切换全屏的按钮图标
|
||||||
|
fullscreenButtonIcon() {
|
||||||
|
return this.innerFullscreen ? 'fullscreen-exit' : 'fullscreen'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
visible() {
|
||||||
|
if (this.visible) {
|
||||||
|
this.innerFullscreen = this.fullscreen
|
||||||
|
}
|
||||||
|
},
|
||||||
|
innerFullscreen(val) {
|
||||||
|
this.$emit('update:fullscreen', val)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
getClass(clazz) {
|
||||||
|
return { ...getClass(this), ...clazz }
|
||||||
|
},
|
||||||
|
getStyle(style) {
|
||||||
|
return { ...getStyle(this), ...style }
|
||||||
|
},
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.$emit('update:visible', false)
|
||||||
|
},
|
||||||
|
|
||||||
|
handleOk() {
|
||||||
|
if (this.okClose) {
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleCancel() {
|
||||||
|
this.close()
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 切换全屏 */
|
||||||
|
toggleFullscreen() {
|
||||||
|
this.innerFullscreen = !this.innerFullscreen
|
||||||
|
triggerWindowResizeEvent()
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.j-modal-box {
|
||||||
|
|
||||||
|
&.fullscreen {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
// 兼容1.6.2版本的antdv
|
||||||
|
& .ant-modal {
|
||||||
|
top: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .ant-modal-content {
|
||||||
|
height: 100vh;
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
& .ant-modal-body {
|
||||||
|
/* title 和 footer 各占 55px */
|
||||||
|
height: calc(100% - 55px - 55px);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.no-title, &.no-footer {
|
||||||
|
.ant-modal-body {
|
||||||
|
height: calc(100% - 55px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.no-title.no-footer {
|
||||||
|
.ant-modal-body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-modal-title-row {
|
||||||
|
.left {
|
||||||
|
width: calc(100% - 56px - 56px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
width: 56px;
|
||||||
|
position: inherit;
|
||||||
|
|
||||||
|
.ant-modal-close {
|
||||||
|
right: 56px;
|
||||||
|
color: rgba(0, 0, 0, 0.45);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgba(0, 0, 0, 0.75);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.j-modal-box.fullscreen {
|
||||||
|
margin: 0;
|
||||||
|
max-width: 100vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,338 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-tree-select
|
||||||
|
v-if="isAsync"
|
||||||
|
allowClear
|
||||||
|
tree-node-filter-prop="title"
|
||||||
|
:getPopupContainer="(node) => node.parentNode"
|
||||||
|
style="width: 100%"
|
||||||
|
:disabled="disabled"
|
||||||
|
:dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:loadData="asyncLoadTreeData"
|
||||||
|
:value="treeValue"
|
||||||
|
:treeData="treeData"
|
||||||
|
:multiple="multiple"
|
||||||
|
:show-search="!multiple"
|
||||||
|
v-bind="_attrs"
|
||||||
|
v-on="childListeners"
|
||||||
|
@change="onChange">
|
||||||
|
</a-tree-select>
|
||||||
|
<a-tree-select
|
||||||
|
v-if="!isAsync"
|
||||||
|
allowClear
|
||||||
|
tree-node-filter-prop="title"
|
||||||
|
:getPopupContainer="(node) => node.parentNode"
|
||||||
|
style="width: 100%"
|
||||||
|
:disabled="disabled"
|
||||||
|
:dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:value="treeValue"
|
||||||
|
:treeData="treeData"
|
||||||
|
:multiple="multiple"
|
||||||
|
:show-search="!multiple"
|
||||||
|
v-bind="_attrs"
|
||||||
|
v-on="$listeners"
|
||||||
|
@change="onChange">
|
||||||
|
</a-tree-select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 异步树加载组件 通过传入表名 显示字段 存储字段 加载一个树控件
|
||||||
|
* <j-tree-select dict="aa_tree_test,aad,id" pid-field="pid" ></j-tree-select>
|
||||||
|
* */
|
||||||
|
import { getAction } from '@/api/manage'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'JTreeSelect',
|
||||||
|
props: {
|
||||||
|
isAsync:{ // 是否采用异步方式初始化树
|
||||||
|
type:Boolean,
|
||||||
|
required:false,
|
||||||
|
default:true
|
||||||
|
},
|
||||||
|
value:{
|
||||||
|
// type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
placeholder:{
|
||||||
|
type: String,
|
||||||
|
default: '请选择',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
dict:{
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
pidField:{
|
||||||
|
type: String,
|
||||||
|
default: 'pid',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
pidValue:{
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
disabled:{
|
||||||
|
type:Boolean,
|
||||||
|
default:false,
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
hasChildField:{
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
condition:{
|
||||||
|
type:String,
|
||||||
|
default:'',
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
// 是否支持多选
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
loadTriggleChange:{
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required:false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
treeValue: null, // 选中的数据
|
||||||
|
treeData:[],// 渲染树的数组
|
||||||
|
url:"/sys/dict/loadTreeData",
|
||||||
|
view:'/sys/dict/loadDictItem/',
|
||||||
|
tableName:"",
|
||||||
|
text:"",
|
||||||
|
code:"",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value () {
|
||||||
|
this.loadItemByCode()
|
||||||
|
},
|
||||||
|
dict(){
|
||||||
|
this.initDictInfo()
|
||||||
|
this.loadRoot()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed:{
|
||||||
|
_attrs() {
|
||||||
|
let attrs
|
||||||
|
attrs = { ...this.$attrs }
|
||||||
|
return attrs
|
||||||
|
},
|
||||||
|
//透传给下级组件的事件,需要排除本组件使用的change事件
|
||||||
|
childListeners() {
|
||||||
|
const vm = this;
|
||||||
|
let result = Object.assign({},
|
||||||
|
this.$listeners,
|
||||||
|
)
|
||||||
|
delete result.change;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created(){
|
||||||
|
this.validateProp().then(()=>{
|
||||||
|
this.initDictInfo()
|
||||||
|
this.loadRoot()
|
||||||
|
this.loadItemByCode()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
mounted(){
|
||||||
|
window.that = this
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadItemByCode(){
|
||||||
|
if( !this.value || this.value == "0" ){
|
||||||
|
this.treeValue = null
|
||||||
|
}else{
|
||||||
|
getAction(`${this.view}${this.dict}`,{key:this.value}).then(res=>{
|
||||||
|
if(res.success){
|
||||||
|
let values = this.value.split(',')
|
||||||
|
this.treeValue = values // v-model接收的是string || string[]
|
||||||
|
//将节点名称显示在选择框上
|
||||||
|
this.treeValue = res.result[0]
|
||||||
|
this.onLoadTriggleChange(res.result[0]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoadTriggleChange(text){
|
||||||
|
//只有单选才会触发
|
||||||
|
if(!this.multiple && this.loadTriggleChange){
|
||||||
|
this.$emit('change', this.value,text)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initDictInfo(){ //取出父组件传过来的code,text,tableName
|
||||||
|
let arr = this.dict.split(",")
|
||||||
|
this.tableName = arr[0]
|
||||||
|
this.text = arr[1]
|
||||||
|
this.code = arr[2]
|
||||||
|
},
|
||||||
|
//异步加载树节点
|
||||||
|
asyncLoadTreeData (treeNode) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (treeNode.$vnode.children) {
|
||||||
|
resolve()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let pid = treeNode.$vnode.key
|
||||||
|
let param = {
|
||||||
|
pid:pid,
|
||||||
|
tableName:this.tableName,
|
||||||
|
text:this.text,
|
||||||
|
code:this.code,
|
||||||
|
pidField:this.pidField,
|
||||||
|
hasChildField:this.hasChildField,
|
||||||
|
condition:this.condition
|
||||||
|
}
|
||||||
|
getAction(this.url,param).then(res=>{
|
||||||
|
if(res.success){
|
||||||
|
for(let i of res.result){
|
||||||
|
i.value = i.key
|
||||||
|
if(i.leaf==false){
|
||||||
|
i.isLeaf=false
|
||||||
|
}else if(i.leaf==true){
|
||||||
|
i.isLeaf=true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.addChildren(pid,res.result,this.treeData)
|
||||||
|
this.treeData = [...this.treeData]
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
addChildren(pid,children,treeArray){
|
||||||
|
if(treeArray && treeArray.length>0){
|
||||||
|
for(let item of treeArray){
|
||||||
|
if(item.key == pid){ //找到当前元素所在的父节点
|
||||||
|
if(!children || children.length==0){
|
||||||
|
item.isLeaf=true
|
||||||
|
}else{
|
||||||
|
item.children = children // 搜索出来的children添加到原树子children
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}else{
|
||||||
|
this.addChildren(pid,children,item.children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 初始化树
|
||||||
|
* isAsync:true===异步获取 false===同步获取数据 默认异步*/
|
||||||
|
loadRoot(){
|
||||||
|
if (this.isAsync){
|
||||||
|
let param = {
|
||||||
|
pid:this.pidValue,
|
||||||
|
tableName:this.tableName,
|
||||||
|
text:this.text,
|
||||||
|
code:this.code,
|
||||||
|
pidField:this.pidField,
|
||||||
|
hasChildField:this.hasChildField,
|
||||||
|
condition:this.condition
|
||||||
|
}
|
||||||
|
getAction(this.url,param).then(res=>{
|
||||||
|
if(res.success && res.result){
|
||||||
|
for(let i of res.result){
|
||||||
|
i.value = i.key
|
||||||
|
if(i.leaf==false){
|
||||||
|
i.isLeaf=false
|
||||||
|
}else if(i.leaf==true){
|
||||||
|
i.isLeaf=true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.treeData = [...res.result]
|
||||||
|
}else{
|
||||||
|
console.log("数根节点查询结果-else",res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
//同步
|
||||||
|
let param = {
|
||||||
|
code:this.code,
|
||||||
|
pidField:this.pidField,
|
||||||
|
tableName:this.tableName,
|
||||||
|
text:this.text
|
||||||
|
}
|
||||||
|
getAction('/sys/dict/queryAllTreeData',param).then((res)=>{
|
||||||
|
if (res.success){
|
||||||
|
this.treeData = deepFor(res.result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onChange(value){
|
||||||
|
if(!value){
|
||||||
|
/*
|
||||||
|
* 使用$listeners向上暴露事件---和$emit一起使用出现的问题:change事件会执行两遍
|
||||||
|
* 解决办法:改变选中时提交的事件名*/
|
||||||
|
this.$emit('change', '');
|
||||||
|
this.treeValue = null
|
||||||
|
} else if (value instanceof Array) {
|
||||||
|
//多选
|
||||||
|
this.$emit('change', value.join(',').toString()) //修改第一次选中时,选中为空的情况
|
||||||
|
this.treeValue = value
|
||||||
|
} else {
|
||||||
|
//单选
|
||||||
|
this.$emit('change', value)
|
||||||
|
this.treeValue = value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getCurrTreeData(){
|
||||||
|
return this.treeData
|
||||||
|
},
|
||||||
|
validateProp(){
|
||||||
|
let myCondition = this.condition
|
||||||
|
return new Promise((resolve,reject)=>{
|
||||||
|
if(!myCondition){
|
||||||
|
resolve();
|
||||||
|
}else{
|
||||||
|
try {
|
||||||
|
let test=JSON.parse(myCondition);
|
||||||
|
if(typeof test == 'object' && test){
|
||||||
|
resolve()
|
||||||
|
}else{
|
||||||
|
this.$message.error("组件JTreeSelect-condition传值有误,需要一个json字符串!")
|
||||||
|
reject()
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
this.$message.error("组件JTreeSelect-condition传值有误,需要一个json字符串!")
|
||||||
|
reject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//2.2新增 在组件内定义 指定父组件调用时候的传值属性和事件类型
|
||||||
|
model: {
|
||||||
|
prop: 'value',
|
||||||
|
event: 'change'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//递归整个树,判断是否是叶子节点----同步获取数据时用到
|
||||||
|
function deepFor(source){
|
||||||
|
for(let i of source){
|
||||||
|
i.value = i.key
|
||||||
|
|
||||||
|
if(i.leaf==false){
|
||||||
|
i.isLeaf=false
|
||||||
|
}else if(i.leaf==true){
|
||||||
|
i.isLeaf=true
|
||||||
|
}
|
||||||
|
if (i.children && i.children.length > 0){
|
||||||
|
deepFor(i.children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import JModal from "./JModal";
|
||||||
|
import JInput from "./JInput.vue";
|
||||||
|
import JEllipsis from './JEllipsis.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install(Vue) {
|
||||||
|
Vue.component(JModal.name, JModal);
|
||||||
|
Vue.component("JInput", JInput);
|
||||||
|
Vue.component('JEllipsis', JEllipsis)
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-modal
|
||||||
|
:title="fileType === 'image' ? '图片上传' : '文件上传'"
|
||||||
|
:width="width"
|
||||||
|
:visible="visible"
|
||||||
|
@ok="ok"
|
||||||
|
cancelText="取消"
|
||||||
|
@cancel="close">
|
||||||
|
<!--style="top: 20px;"-->
|
||||||
|
<j-upload :file-type="fileType" :value="filePath" @change="handleChange" :disabled="disabled" :number="number"></j-upload>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getFileAccessHttpUrl } from '@/api/manage';
|
||||||
|
|
||||||
|
const getFileName=(path)=>{
|
||||||
|
if(path.lastIndexOf("\\")>=0){
|
||||||
|
let reg=new RegExp("\\\\","g");
|
||||||
|
path = path.replace(reg,"/");
|
||||||
|
}
|
||||||
|
return path.substring(path.lastIndexOf("/")+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'JFilePop',
|
||||||
|
components: { },
|
||||||
|
props:{
|
||||||
|
title:{
|
||||||
|
type:String,
|
||||||
|
default:'',
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
position:{
|
||||||
|
type:String,
|
||||||
|
default:'right',
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
height:{
|
||||||
|
type:Number,
|
||||||
|
default:200,
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
width:{
|
||||||
|
type:Number,
|
||||||
|
default:520,
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
|
||||||
|
popContainer:{
|
||||||
|
type:String,
|
||||||
|
default:'',
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
disabled:{
|
||||||
|
type:Boolean,
|
||||||
|
default:false,
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
number:{
|
||||||
|
type:Number,
|
||||||
|
required:false,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
visible:false,
|
||||||
|
filePath:'',
|
||||||
|
id:'',
|
||||||
|
fileType:'file'
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
handleChange(value){
|
||||||
|
this.filePath = value;
|
||||||
|
},
|
||||||
|
show(id,value,flag){
|
||||||
|
this.id = id;
|
||||||
|
this.filePath = value;
|
||||||
|
this.visible=true
|
||||||
|
if(flag === 'img'){
|
||||||
|
this.fileType = 'image'
|
||||||
|
}else{
|
||||||
|
this.fileType = 'file'
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
ok(){
|
||||||
|
if(!this.filePath){
|
||||||
|
this.$message.error("未上传任何文件")
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let arr = this.filePath.split(",")
|
||||||
|
let obj = {
|
||||||
|
name:getFileName(arr[0]),
|
||||||
|
url:getFileAccessHttpUrl(arr[0]),
|
||||||
|
path:this.filePath,
|
||||||
|
status: 'done',
|
||||||
|
id:this.id
|
||||||
|
}
|
||||||
|
this.$emit('ok',obj)
|
||||||
|
this.visible=false
|
||||||
|
},
|
||||||
|
close(){
|
||||||
|
this.visible=false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
<template>
|
||||||
|
<a-popover trigger="contextmenu" v-model="visible" :placement="position" overlayClassName="j-input-pop">
|
||||||
|
<!--"(node) => node.parentNode.parentNode"-->
|
||||||
|
<div slot="title">
|
||||||
|
<span>{{ title }}</span>
|
||||||
|
<span style="float: right" title="关闭">
|
||||||
|
<a-icon type="close" @click="visible=false"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<a-input :value="inputContent" :disabled="disabled" @change="handleInputChange">
|
||||||
|
<a-icon slot="suffix" type="fullscreen" @click.stop="pop" />
|
||||||
|
</a-input>
|
||||||
|
<div slot="content">
|
||||||
|
<a-textarea ref="textarea" :value="inputContent" :disabled="disabled" @input="handleInputChange" :style="{ height: height + 'px', width: width + 'px' }"/>
|
||||||
|
</div>
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'JInputPop',
|
||||||
|
props:{
|
||||||
|
title:{
|
||||||
|
type:String,
|
||||||
|
default:'',
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
position:{
|
||||||
|
type:String,
|
||||||
|
default:'right',
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
height:{
|
||||||
|
type:Number,
|
||||||
|
default:200,
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
width:{
|
||||||
|
type:Number,
|
||||||
|
default:150,
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
value:{
|
||||||
|
type:String,
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
popContainer:{
|
||||||
|
type:String,
|
||||||
|
default:'',
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
visible:false,
|
||||||
|
inputContent:''
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch:{
|
||||||
|
value:{
|
||||||
|
immediate:true,
|
||||||
|
handler:function(){
|
||||||
|
if(this.value && this.value.length>0){
|
||||||
|
this.inputContent = this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
prop: 'value',
|
||||||
|
event: 'change'
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
handleInputChange(event){
|
||||||
|
this.inputContent = event.target.value
|
||||||
|
this.$emit('change',this.inputContent)
|
||||||
|
},
|
||||||
|
pop(){
|
||||||
|
this.visible=true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.textarea.focus()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getPopupContainer(node){
|
||||||
|
if(!this.popContainer){
|
||||||
|
return node.parentNode
|
||||||
|
}else{
|
||||||
|
return document.getElementById(this.popContainer)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,928 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
title="corn表达式"
|
||||||
|
:width="modalWidth"
|
||||||
|
:visible="visible"
|
||||||
|
:confirmLoading="confirmLoading"
|
||||||
|
@ok="handleSubmit"
|
||||||
|
@cancel="close"
|
||||||
|
cancelText="关闭">
|
||||||
|
<div class="card-container">
|
||||||
|
<a-tabs type="card">
|
||||||
|
<a-tab-pane key="1" type="card">
|
||||||
|
<span slot="tab"><a-icon type="schedule" /> 秒</span>
|
||||||
|
<a-radio-group v-model="result.second.cronEvery">
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="1">每一秒钟</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="2">每隔
|
||||||
|
<a-input-number size="small" v-model="result.second.incrementIncrement" :min="1" :max="59"></a-input-number>
|
||||||
|
秒执行 从
|
||||||
|
<a-input-number size="small" v-model="result.second.incrementStart" :min="0" :max="59"></a-input-number>
|
||||||
|
秒开始
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="3">具体秒数(可多选)</a-radio>
|
||||||
|
<a-select style="width:354px;" size="small" mode="multiple" v-model="result.second.specificSpecific">
|
||||||
|
<a-select-option v-for="(val,index) in 60" :key="index" :value="index">{{ index }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="4">周期从
|
||||||
|
<a-input-number size="small" v-model="result.second.rangeStart" :min="1" :max="59"></a-input-number>
|
||||||
|
到
|
||||||
|
<a-input-number size="small" v-model="result.second.rangeEnd" :min="0" :max="59"></a-input-number>
|
||||||
|
秒
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="2">
|
||||||
|
<span slot="tab"><a-icon type="schedule" />分</span>
|
||||||
|
<div class="tabBody">
|
||||||
|
<a-radio-group v-model="result.minute.cronEvery">
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="1">每一分钟</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="2">每隔
|
||||||
|
<a-input-number size="small" v-model="result.minute.incrementIncrement" :min="1" :max="60"></a-input-number>
|
||||||
|
分执行 从
|
||||||
|
<a-input-number size="small" v-model="result.minute.incrementStart" :min="0" :max="59"></a-input-number>
|
||||||
|
分开始
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="3">具体分钟数(可多选)</a-radio>
|
||||||
|
<a-select style="width:340px;" size="small" mode="multiple" v-model="result.minute.specificSpecific">
|
||||||
|
<a-select-option v-for="(val,index) in Array(60)" :key="index" :value="index"> {{ index }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="4">周期从
|
||||||
|
<a-input-number size="small" v-model="result.minute.rangeStart" :min="1" :max="60"></a-input-number>
|
||||||
|
到
|
||||||
|
<a-input-number size="small" v-model="result.minute.rangeEnd" :min="0" :max="59"></a-input-number>
|
||||||
|
分
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="3">
|
||||||
|
<span slot="tab"><a-icon type="schedule" /> 时</span>
|
||||||
|
<div class="tabBody">
|
||||||
|
<a-radio-group v-model="result.hour.cronEvery">
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="1">每一小时</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="2">每隔
|
||||||
|
<a-input-number size="small" v-model="result.hour.incrementIncrement" :min="0" :max="23"></a-input-number>
|
||||||
|
小时执行 从
|
||||||
|
<a-input-number size="small" v-model="result.hour.incrementStart" :min="0" :max="23"></a-input-number>
|
||||||
|
小时开始
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio class="long" value="3">具体小时数(可多选)</a-radio>
|
||||||
|
<a-select style="width:340px;" size="small" mode="multiple" v-model="result.hour.specificSpecific">
|
||||||
|
<a-select-option v-for="(val,index) in Array(24)" :key="index" >{{ index }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="4">周期从
|
||||||
|
<a-input-number size="small" v-model="result.hour.rangeStart" :min="0" :max="23"></a-input-number>
|
||||||
|
到
|
||||||
|
<a-input-number size="small" v-model="result.hour.rangeEnd" :min="0" :max="23"></a-input-number>
|
||||||
|
小时
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="4">
|
||||||
|
<span slot="tab"><a-icon type="schedule" /> 天</span>
|
||||||
|
<div class="tabBody">
|
||||||
|
<a-radio-group v-model="result.day.cronEvery">
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="1">每一天</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="2">每隔
|
||||||
|
<a-input-number size="small" v-model="result.week.incrementIncrement" :min="1" :max="7"></a-input-number>
|
||||||
|
周执行 从
|
||||||
|
<a-select size="small" v-model="result.week.incrementStart">
|
||||||
|
<a-select-option v-for="(val,index) in Array(7)" :key="index" :value="index+1">{{ weekDays[index] }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
开始
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="3">每隔
|
||||||
|
<a-input-number size="small" v-model="result.day.incrementIncrement" :min="1" :max="31"></a-input-number>
|
||||||
|
天执行 从
|
||||||
|
<a-input-number size="small" v-model="result.day.incrementStart" :min="1" :max="31"></a-input-number>
|
||||||
|
天开始
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio class="long" value="4">具体星期几(可多选)</a-radio>
|
||||||
|
<a-select style="width:340px;" size="small" mode="multiple" v-model="result.week.specificSpecific">
|
||||||
|
<a-select-option v-for="(val,index) in Array(7)" :key="index" :value="index+1">{{ weekDays[index] }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio class="long" value="5">具体天数(可多选)</a-radio>
|
||||||
|
<a-select style="width:354px;" size="small" mode="multiple" v-model="result.day.specificSpecific">
|
||||||
|
<a-select-option v-for="(val,index) in Array(31)" :key="index" :value="index+1">{{ index+1 }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="6">在这个月的最后一天</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="7">在这个月的最后一个工作日</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="8">在这个月的最后一个
|
||||||
|
<a-select size="small" v-model="result.day.cronLastSpecificDomDay">
|
||||||
|
<a-select-option v-for="(val,index) in Array(7)" :key="index" :value="index+1">{{ weekDays[index] }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="9">
|
||||||
|
在本月底前
|
||||||
|
<a-input-number size="small" v-model="result.day.cronDaysBeforeEomMinus" :min="1" :max="31"></a-input-number>
|
||||||
|
天
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="10">最近的工作日(周一至周五)至本月
|
||||||
|
<a-input-number size="small" v-model="result.day.cronDaysNearestWeekday" :min="1" :max="31"></a-input-number>
|
||||||
|
日
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="11">在这个月的第
|
||||||
|
<a-input-number size="small" v-model="result.week.cronNthDayNth" :min="1" :max="5"></a-input-number>
|
||||||
|
个
|
||||||
|
<a-select size="small" v-model="result.week.cronNthDayDay">
|
||||||
|
<a-select-option v-for="(val,index) in Array(7)" :key="index" :value="index+1">{{ weekDays[index] }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="5">
|
||||||
|
<span slot="tab"><a-icon type="schedule" /> 月</span>
|
||||||
|
<div class="tabBody">
|
||||||
|
<a-radio-group v-model="result.month.cronEvery">
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="1">每一月</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="2">每隔
|
||||||
|
<a-input-number size="small" v-model="result.month.incrementIncrement" :min="0" :max="12"></a-input-number>
|
||||||
|
月执行 从
|
||||||
|
<a-input-number size="small" v-model="result.month.incrementStart" :min="0" :max="12"></a-input-number>
|
||||||
|
月开始
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio class="long" value="3">具体月数(可多选)</a-radio>
|
||||||
|
<a-select style="width:354px;" size="small" filterable mode="multiple" v-model="result.month.specificSpecific">
|
||||||
|
<a-select-option v-for="(val,index) in Array(12)" :key="index" :value="index+1">{{ index+1 }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="4">从
|
||||||
|
<a-input-number size="small" v-model="result.month.rangeStart" :min="1" :max="12"></a-input-number>
|
||||||
|
到
|
||||||
|
<a-input-number size="small" v-model="result.month.rangeEnd" :min="1" :max="12"></a-input-number>
|
||||||
|
月之间的每个月
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="6">
|
||||||
|
<span slot="tab"><a-icon type="schedule" /> 年</span>
|
||||||
|
<div class="tabBody">
|
||||||
|
<a-radio-group v-model="result.year.cronEvery">
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="1">每一年</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="2">每隔
|
||||||
|
<a-input-number size="small" v-model="result.year.incrementIncrement" :min="1" :max="99"></a-input-number>
|
||||||
|
年执行 从
|
||||||
|
<a-input-number size="small" v-model="result.year.incrementStart" :min="2019" :max="2119"></a-input-number>
|
||||||
|
年开始
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio class="long" value="3">具体年份(可多选)</a-radio>
|
||||||
|
<a-select style="width:354px;" size="small" filterable mode="multiple" v-model="result.year.specificSpecific">
|
||||||
|
<a-select-option v-for="(val,index) in Array(100)" :key="index" :value="2019+index">{{ 2019+index }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-radio value="4">从
|
||||||
|
<a-input-number size="small" v-model="result.year.rangeStart" :min="2019" :max="2119"></a-input-number>
|
||||||
|
到
|
||||||
|
<a-input-number size="small" v-model="result.year.rangeEnd" :min="2019" :max="2119"></a-input-number>
|
||||||
|
年之间的每一年
|
||||||
|
</a-radio>
|
||||||
|
</a-row>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
<div class="bottom">
|
||||||
|
<span class="value">{{this.cron }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name:'VueCron',
|
||||||
|
props:['data'],
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
confirmLoading:false,
|
||||||
|
size:'large',
|
||||||
|
weekDays:['天','一','二','三','四','五','六'].map(val=>'星期'+val),
|
||||||
|
result: {
|
||||||
|
second:{},
|
||||||
|
minute:{},
|
||||||
|
hour:{},
|
||||||
|
day:{},
|
||||||
|
week:{},
|
||||||
|
month:{},
|
||||||
|
year:{}
|
||||||
|
},
|
||||||
|
defaultValue: {
|
||||||
|
second:{
|
||||||
|
cronEvery:'',
|
||||||
|
incrementStart:3,
|
||||||
|
incrementIncrement:5,
|
||||||
|
rangeStart:1,
|
||||||
|
rangeEnd:0,
|
||||||
|
specificSpecific:[],
|
||||||
|
},
|
||||||
|
minute:{
|
||||||
|
cronEvery:'',
|
||||||
|
incrementStart:3,
|
||||||
|
incrementIncrement:5,
|
||||||
|
rangeStart:1,
|
||||||
|
rangeEnd:'0',
|
||||||
|
specificSpecific:[],
|
||||||
|
},
|
||||||
|
hour:{
|
||||||
|
cronEvery:'',
|
||||||
|
incrementStart:3,
|
||||||
|
incrementIncrement:5,
|
||||||
|
rangeStart:'0',
|
||||||
|
rangeEnd:'0',
|
||||||
|
specificSpecific:[],
|
||||||
|
},
|
||||||
|
day:{
|
||||||
|
cronEvery:'',
|
||||||
|
incrementStart:1,
|
||||||
|
incrementIncrement:'1',
|
||||||
|
rangeStart:'',
|
||||||
|
rangeEnd:'',
|
||||||
|
specificSpecific:[],
|
||||||
|
cronLastSpecificDomDay:1,
|
||||||
|
cronDaysBeforeEomMinus:1,
|
||||||
|
cronDaysNearestWeekday:1,
|
||||||
|
},
|
||||||
|
week:{
|
||||||
|
cronEvery:'',
|
||||||
|
incrementStart:1,
|
||||||
|
incrementIncrement:1,
|
||||||
|
specificSpecific:[],
|
||||||
|
cronNthDayDay:1,
|
||||||
|
cronNthDayNth:1,
|
||||||
|
},
|
||||||
|
month:{
|
||||||
|
cronEvery:'',
|
||||||
|
incrementStart:3,
|
||||||
|
incrementIncrement:5,
|
||||||
|
rangeStart:1,
|
||||||
|
rangeEnd:1,
|
||||||
|
specificSpecific:[],
|
||||||
|
},
|
||||||
|
year:{
|
||||||
|
cronEvery:'',
|
||||||
|
incrementStart:2017,
|
||||||
|
incrementIncrement:1,
|
||||||
|
rangeStart:2019,
|
||||||
|
rangeEnd: 2019,
|
||||||
|
specificSpecific:[],
|
||||||
|
},
|
||||||
|
label:''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
modalWidth(){
|
||||||
|
return 608;
|
||||||
|
},
|
||||||
|
secondsText() {
|
||||||
|
let seconds = '';
|
||||||
|
let cronEvery=this.result.second.cronEvery||'';
|
||||||
|
switch (cronEvery.toString()){
|
||||||
|
case '1':
|
||||||
|
seconds = '*';
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
seconds = this.result.second.incrementStart+'/'+this.result.second.incrementIncrement;
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
this.result.second.specificSpecific.map(val=> {seconds += val+','});
|
||||||
|
seconds = seconds.slice(0, -1);
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
seconds = this.result.second.rangeStart+'-'+this.result.second.rangeEnd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return seconds;
|
||||||
|
},
|
||||||
|
minutesText() {
|
||||||
|
let minutes = '';
|
||||||
|
let cronEvery=this.result.minute.cronEvery||'';
|
||||||
|
switch (cronEvery.toString()){
|
||||||
|
case '1':
|
||||||
|
minutes = '*';
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
minutes = this.result.minute.incrementStart+'/'+this.result.minute.incrementIncrement;
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
this.result.minute.specificSpecific.map(val=> {
|
||||||
|
minutes += val+','
|
||||||
|
});
|
||||||
|
minutes = minutes.slice(0, -1);
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
minutes = this.result.minute.rangeStart+'-'+this.result.minute.rangeEnd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return minutes;
|
||||||
|
},
|
||||||
|
hoursText() {
|
||||||
|
let hours = '';
|
||||||
|
let cronEvery=this.result.hour.cronEvery||'';
|
||||||
|
switch (cronEvery.toString()){
|
||||||
|
case '1':
|
||||||
|
hours = '*';
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
hours = this.result.hour.incrementStart+'/'+this.result.hour.incrementIncrement;
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
this.result.hour.specificSpecific.map(val=> {
|
||||||
|
hours += val+','
|
||||||
|
});
|
||||||
|
hours = hours.slice(0, -1);
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
hours = this.result.hour.rangeStart+'-'+this.result.hour.rangeEnd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return hours;
|
||||||
|
},
|
||||||
|
daysText() {
|
||||||
|
let days='';
|
||||||
|
let cronEvery=this.result.day.cronEvery||'';
|
||||||
|
switch (cronEvery.toString()){
|
||||||
|
case '1':
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
case '4':
|
||||||
|
case '11':
|
||||||
|
days = '?';
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
days = this.result.day.incrementStart+'/'+this.result.day.incrementIncrement;
|
||||||
|
break;
|
||||||
|
case '5':
|
||||||
|
this.result.day.specificSpecific.map(val=> {
|
||||||
|
days += val+','
|
||||||
|
});
|
||||||
|
days = days.slice(0, -1);
|
||||||
|
break;
|
||||||
|
case '6':
|
||||||
|
days = "L";
|
||||||
|
break;
|
||||||
|
case '7':
|
||||||
|
days = "LW";
|
||||||
|
break;
|
||||||
|
case '8':
|
||||||
|
days = this.result.day.cronLastSpecificDomDay + 'L';
|
||||||
|
break;
|
||||||
|
case '9':
|
||||||
|
days = 'L-' + this.result.day.cronDaysBeforeEomMinus;
|
||||||
|
break;
|
||||||
|
case '10':
|
||||||
|
days = this.result.day.cronDaysNearestWeekday+"W";
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return days;
|
||||||
|
},
|
||||||
|
weeksText() {
|
||||||
|
let weeks = '';
|
||||||
|
let cronEvery=this.result.day.cronEvery||'';
|
||||||
|
switch (cronEvery.toString()){
|
||||||
|
case '1':
|
||||||
|
case '3':
|
||||||
|
case '5':
|
||||||
|
weeks = '?';
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
weeks = this.result.week.incrementStart+'/'+this.result.week.incrementIncrement;
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
this.result.week.specificSpecific.map(val=> {
|
||||||
|
weeks += val+','
|
||||||
|
});
|
||||||
|
weeks = weeks.slice(0, -1);
|
||||||
|
break;
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
case '10':
|
||||||
|
weeks = "?";
|
||||||
|
break;
|
||||||
|
case '11':
|
||||||
|
weeks = this.result.week.cronNthDayDay+"#"+this.result.week.cronNthDayNth;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return weeks;
|
||||||
|
},
|
||||||
|
monthsText() {
|
||||||
|
let months = '';
|
||||||
|
let cronEvery=this.result.month.cronEvery||'';
|
||||||
|
switch (cronEvery.toString()){
|
||||||
|
case '1':
|
||||||
|
months = '*';
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
months = this.result.month.incrementStart+'/'+this.result.month.incrementIncrement;
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
this.result.month.specificSpecific.map(val=> {
|
||||||
|
months += val+','
|
||||||
|
});
|
||||||
|
months = months.slice(0, -1);
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
months = this.result.month.rangeStart+'-'+this.result.month.rangeEnd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return months;
|
||||||
|
},
|
||||||
|
yearsText() {
|
||||||
|
let years = '';
|
||||||
|
let cronEvery=this.result.year.cronEvery||'';
|
||||||
|
switch (cronEvery.toString()){
|
||||||
|
case '1':
|
||||||
|
years = '*';
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
years = this.result.year.incrementStart+'/'+this.result.year.incrementIncrement;
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
this.result.year.specificSpecific.map(val=> {
|
||||||
|
years += val+','
|
||||||
|
});
|
||||||
|
years = years.slice(0, -1);
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
years = this.result.year.rangeStart+'-'+this.result.year.rangeEnd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return years;
|
||||||
|
},
|
||||||
|
cron(){
|
||||||
|
return `${this.secondsText||'*'} ${this.minutesText||'*'} ${this.hoursText||'*'} ${this.daysText||'*'} ${this.monthsText||'*'} ${this.weeksText||'?'} ${this.yearsText||'*'}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch:{
|
||||||
|
visible:{
|
||||||
|
handler() {
|
||||||
|
// if(this.data){
|
||||||
|
// //this. result = Object.keys(this.data.value).length>0?this.deepCopy(this.data.value):this.deepCopy(this.defaultValue);
|
||||||
|
// //this.result = Object.keys(this.data.value).length>0?clone(this.data.value):clone(this.defaultValue);
|
||||||
|
// //this.result = Object.keys(this.data.value).length>0?clone(JSON.parse(this.data.value)):clone(this.defaultValue);
|
||||||
|
// this.result = Object.keys(this.data.value).length>0?JSON.parse(this.data.value):JSON.parse(JSON.stringify(this.defaultValue));
|
||||||
|
// }else{
|
||||||
|
// //this.result = this.deepCopy(this.defaultValue);
|
||||||
|
// //this.result = clone(this.defaultValue);
|
||||||
|
// this.result = JSON.parse(JSON.stringify(this.defaultValue));
|
||||||
|
// }
|
||||||
|
let label = this.data;
|
||||||
|
if(label){
|
||||||
|
this.secondsReverseExp(label)
|
||||||
|
this.minutesReverseExp(label);
|
||||||
|
this.hoursReverseExp(label);
|
||||||
|
this.daysReverseExp(label);
|
||||||
|
this.daysReverseExp(label);
|
||||||
|
this.monthsReverseExp(label);
|
||||||
|
this.yearReverseExp(label);
|
||||||
|
JSON.parse(JSON.stringify(label));
|
||||||
|
}else {
|
||||||
|
this.result = JSON.parse(JSON.stringify(this.defaultValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
show(){
|
||||||
|
this.visible = true;
|
||||||
|
// console.log('secondsReverseExp',this.secondsReverseExp(this.data));
|
||||||
|
// console.log('minutesReverseExp',this.minutesReverseExp(this.data));
|
||||||
|
// console.log('hoursReverseExp',this.hoursReverseExp(this.data));
|
||||||
|
// console.log('daysReverseExp',this.daysReverseExp(this.data));
|
||||||
|
// console.log('monthsReverseExp',this.monthsReverseExp(this.data));
|
||||||
|
// console.log('yearReverseExp',this.yearReverseExp(this.data));
|
||||||
|
|
||||||
|
},
|
||||||
|
handleSubmit(){
|
||||||
|
this.$emit('ok',this.cron);
|
||||||
|
this.close();
|
||||||
|
this.visible = false;
|
||||||
|
},
|
||||||
|
close(){
|
||||||
|
this.visible = false;
|
||||||
|
},
|
||||||
|
secondsReverseExp(seconds) {
|
||||||
|
let val = seconds.split(" ")[0];
|
||||||
|
//alert(val);
|
||||||
|
let second = {
|
||||||
|
cronEvery:'',
|
||||||
|
incrementStart:3,
|
||||||
|
incrementIncrement:5,
|
||||||
|
rangeStart:1,
|
||||||
|
rangeEnd:0,
|
||||||
|
specificSpecific:[]
|
||||||
|
};
|
||||||
|
switch (true) {
|
||||||
|
case val.includes('*'):
|
||||||
|
second.cronEvery = '1';
|
||||||
|
break;
|
||||||
|
case val.includes('/'):
|
||||||
|
second.cronEvery = '2';
|
||||||
|
second.incrementStart = val.split('/')[0];
|
||||||
|
second.incrementIncrement = val.split('/')[1];
|
||||||
|
break;
|
||||||
|
case val.includes(','):
|
||||||
|
second.cronEvery = '3';
|
||||||
|
second.specificSpecific = val.split(',').map(Number).sort();
|
||||||
|
break;
|
||||||
|
case val.includes('-'):
|
||||||
|
second.cronEvery = '4';
|
||||||
|
second.rangeStart = val.split('-')[0];
|
||||||
|
second.rangeEnd = val.split('-')[1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
second.cronEvery = '1';
|
||||||
|
}
|
||||||
|
this.result.second = second;
|
||||||
|
},
|
||||||
|
minutesReverseExp(minutes) {
|
||||||
|
let val = minutes.split(" ")[1];
|
||||||
|
let minute = {
|
||||||
|
cronEvery:'',
|
||||||
|
incrementStart:3,
|
||||||
|
incrementIncrement:5,
|
||||||
|
rangeStart:1,
|
||||||
|
rangeEnd:0,
|
||||||
|
specificSpecific:[],
|
||||||
|
}
|
||||||
|
switch (true) {
|
||||||
|
case val.includes('*'):
|
||||||
|
minute.cronEvery = '1';
|
||||||
|
break;
|
||||||
|
case val.includes('/'):
|
||||||
|
minute.cronEvery = '2';
|
||||||
|
minute.incrementStart = val.split('/')[0];
|
||||||
|
minute.incrementIncrement = val.split('/')[1];
|
||||||
|
break;
|
||||||
|
case val.includes(','):
|
||||||
|
minute.cronEvery = '3';
|
||||||
|
minute.specificSpecific = val.split(',').map(Number).sort();
|
||||||
|
break;
|
||||||
|
case val.includes('-'):
|
||||||
|
minute.cronEvery = '4';
|
||||||
|
minute.rangeStart = val.split('-')[0];
|
||||||
|
minute.rangeEnd = val.split('-')[1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
minute.cronEvery = '1';
|
||||||
|
}
|
||||||
|
this.result.minute = minute;
|
||||||
|
},
|
||||||
|
hoursReverseExp(hours) {
|
||||||
|
let val = hours.split(" ")[2];
|
||||||
|
let hour ={
|
||||||
|
cronEvery:'',
|
||||||
|
incrementStart:3,
|
||||||
|
incrementIncrement:5,
|
||||||
|
rangeStart:1,
|
||||||
|
rangeEnd:'0',
|
||||||
|
specificSpecific:[],
|
||||||
|
};
|
||||||
|
switch (true) {
|
||||||
|
case val.includes('*'):
|
||||||
|
hour.cronEvery = '1';
|
||||||
|
break;
|
||||||
|
case val.includes('/'):
|
||||||
|
hour.cronEvery = '2';
|
||||||
|
hour.incrementStart = val.split('/')[0];
|
||||||
|
hour.incrementIncrement = val.split('/')[1];
|
||||||
|
break;
|
||||||
|
case val.includes(','):
|
||||||
|
hour.cronEvery = '3';
|
||||||
|
hour.specificSpecific = val.split(',').map(Number).sort();
|
||||||
|
break;
|
||||||
|
case val.includes('-'):
|
||||||
|
hour.cronEvery = '4';
|
||||||
|
hour.rangeStart = val.split('-')[0];
|
||||||
|
hour.rangeEnd = val.split('-')[1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
hour.cronEvery = '1';
|
||||||
|
}
|
||||||
|
this.result.hour = hour;
|
||||||
|
},
|
||||||
|
daysReverseExp(cron) {
|
||||||
|
let days = cron.split(" ")[3];
|
||||||
|
let weeks = cron.split(" ")[5];
|
||||||
|
let day ={
|
||||||
|
cronEvery:'',
|
||||||
|
incrementStart:1,
|
||||||
|
incrementIncrement:1,
|
||||||
|
rangeStart:1,
|
||||||
|
rangeEnd:1,
|
||||||
|
specificSpecific:[],
|
||||||
|
cronLastSpecificDomDay:1,
|
||||||
|
cronDaysBeforeEomMinus:1,
|
||||||
|
cronDaysNearestWeekday:1,
|
||||||
|
};
|
||||||
|
let week = {
|
||||||
|
cronEvery:'',
|
||||||
|
incrementStart:1,
|
||||||
|
incrementIncrement:1,
|
||||||
|
specificSpecific:[],
|
||||||
|
cronNthDayDay:1,
|
||||||
|
cronNthDayNth:'1',
|
||||||
|
};
|
||||||
|
if (!days.includes('?')) {
|
||||||
|
switch (true) {
|
||||||
|
case days.includes('*'):
|
||||||
|
day.cronEvery = '1';
|
||||||
|
break;
|
||||||
|
case days.includes('?'):
|
||||||
|
// 2、4、11
|
||||||
|
break;
|
||||||
|
case days.includes('/'):
|
||||||
|
day.cronEvery = '3';
|
||||||
|
day.incrementStart = days.split('/')[0];
|
||||||
|
day.incrementIncrement = days.split('/')[1];
|
||||||
|
break;
|
||||||
|
case days.includes(','):
|
||||||
|
day.cronEvery = '5';
|
||||||
|
day.specificSpecific = days.split(',').map(Number).sort();
|
||||||
|
// day.specificSpecific.forEach(function (value, index) {
|
||||||
|
// day.specificSpecific[index] = value -1;
|
||||||
|
// });
|
||||||
|
break;
|
||||||
|
case days.includes('LW'):
|
||||||
|
day.cronEvery = '7';
|
||||||
|
break;
|
||||||
|
case days.includes('L-'):
|
||||||
|
day.cronEvery = '9';
|
||||||
|
day.cronDaysBeforeEomMinus = days.split('L-')[1];
|
||||||
|
break;
|
||||||
|
case days.includes('L'):
|
||||||
|
|
||||||
|
//alert(days);
|
||||||
|
if(days.len == 1){
|
||||||
|
day.cronEvery = '6';
|
||||||
|
day.cronLastSpecificDomDay = '1';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
day.cronEvery = '8';
|
||||||
|
day.cronLastSpecificDomDay = Number(days.split('L')[0]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case days.includes('W'):
|
||||||
|
day.cronEvery = '10';
|
||||||
|
day.cronDaysNearestWeekday = days.split('W')[0];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
day.cronEvery = '1';
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
switch (true){
|
||||||
|
case weeks.includes('/'):
|
||||||
|
day.cronEvery = '2';
|
||||||
|
week.incrementStart = weeks.split("/")[0];
|
||||||
|
week.incrementIncrement = weeks.split("/")[1];
|
||||||
|
break;
|
||||||
|
case weeks.includes(','):
|
||||||
|
day.cronEvery = '4';
|
||||||
|
week.specificSpecific = weeks.split(',').map(Number).sort();
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
day.cronEvery = '11';
|
||||||
|
week.cronNthDayDay = weeks.split("#")[0];
|
||||||
|
week.cronNthDayNth = weeks.split("#")[1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
day.cronEvery = '1';
|
||||||
|
week.cronEvery = '1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.result.day = day;
|
||||||
|
this.result.week = week;
|
||||||
|
},
|
||||||
|
monthsReverseExp(cron) {
|
||||||
|
let months = cron.split(" ")[4];
|
||||||
|
let month = {
|
||||||
|
cronEvery:'',
|
||||||
|
incrementStart:3,
|
||||||
|
incrementIncrement:5,
|
||||||
|
rangeStart:1,
|
||||||
|
rangeEnd:1,
|
||||||
|
specificSpecific:[],
|
||||||
|
};
|
||||||
|
switch (true){
|
||||||
|
case months.includes('*'):
|
||||||
|
month.cronEvery = '1';
|
||||||
|
break;
|
||||||
|
case months.includes('/'):
|
||||||
|
month.cronEvery = '2';
|
||||||
|
month.incrementStart = months.split('/')[0];
|
||||||
|
month.incrementIncrement = months.split('/')[1];
|
||||||
|
break;
|
||||||
|
case months.includes(','):
|
||||||
|
month.cronEvery = '3';
|
||||||
|
month.specificSpecific = months.split(',').map(Number).sort();
|
||||||
|
break;
|
||||||
|
case months.includes('-'):
|
||||||
|
month.cronEvery = '4';
|
||||||
|
month.rangeStart = months.split('-')[0];
|
||||||
|
month.rangeEnd = months.split('-')[1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
month.cronEvery = '1';
|
||||||
|
}
|
||||||
|
this.result.month = month;
|
||||||
|
},
|
||||||
|
yearReverseExp(cron) {
|
||||||
|
let years = cron.split(" ")[6];
|
||||||
|
let year = {
|
||||||
|
cronEvery:'',
|
||||||
|
incrementStart:3,
|
||||||
|
incrementIncrement:5,
|
||||||
|
rangeStart:2019,
|
||||||
|
rangeEnd:2019,
|
||||||
|
specificSpecific:[],
|
||||||
|
};
|
||||||
|
switch (true){
|
||||||
|
case years.includes('*'):
|
||||||
|
year.cronEvery = '1';
|
||||||
|
break;
|
||||||
|
case years.includes('/'):
|
||||||
|
year.cronEvery = '2';
|
||||||
|
year.incrementStart = years.split('/')[0];
|
||||||
|
year.incrementIncrement = years.split('/')[1];
|
||||||
|
break;
|
||||||
|
case years.includes(','):
|
||||||
|
year.cronEvery = '3';
|
||||||
|
year.specificSpecific = years.split(',').map(Number).sort();
|
||||||
|
break;
|
||||||
|
case years.includes('-'):
|
||||||
|
year.cronEvery = '4';
|
||||||
|
year.rangeStart = years.split('-')[0];
|
||||||
|
year.rangeEnd = years.split('-')[1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
year.cronEvery = '1';
|
||||||
|
}
|
||||||
|
this.result.year = year;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.card-container {
|
||||||
|
background: #fff;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 12px;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
.ant-tabs{
|
||||||
|
border:1px solid #e6ebf5;
|
||||||
|
padding: 0;
|
||||||
|
.ant-tabs-bar {
|
||||||
|
margin: 0;
|
||||||
|
outline: none;
|
||||||
|
border-bottom: none;
|
||||||
|
.ant-tabs-nav-container{
|
||||||
|
margin: 0;
|
||||||
|
.ant-tabs-tab {
|
||||||
|
padding: 0 24px!important;
|
||||||
|
background-color: #f5f7fa!important;
|
||||||
|
margin-right: 0px!important;
|
||||||
|
border-radius: 0;
|
||||||
|
line-height: 38px;
|
||||||
|
border: 1px solid transparent!important;
|
||||||
|
border-bottom: 1px solid #e6ebf5!important;
|
||||||
|
}
|
||||||
|
.ant-tabs-tab-active.ant-tabs-tab{
|
||||||
|
color: #409eff;
|
||||||
|
background-color: #fff!important;
|
||||||
|
border-right:1px solid #e6ebf5!important;
|
||||||
|
border-left:1px solid #e6ebf5!important;
|
||||||
|
border-bottom:1px solid #fff!important;
|
||||||
|
font-weight: normal;
|
||||||
|
transition:none!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ant-tabs-tabpane{
|
||||||
|
padding: 15px;
|
||||||
|
.ant-row{
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
.ant-select,.ant-input-number{
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.container-widthEn{
|
||||||
|
width: 755px;
|
||||||
|
}
|
||||||
|
.container-widthCn{
|
||||||
|
width: 608px;
|
||||||
|
}
|
||||||
|
.language{
|
||||||
|
text-align: center;
|
||||||
|
position: absolute;
|
||||||
|
right: 13px;
|
||||||
|
top: 13px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 38px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #409eff;
|
||||||
|
z-index: 1;
|
||||||
|
background: #f5f7fa;
|
||||||
|
outline: none;
|
||||||
|
width: 47px;
|
||||||
|
border-bottom: 1px solid #e6ebf5;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
.card-container{
|
||||||
|
.bottom{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10px 0 0 0;
|
||||||
|
.cronButton{
|
||||||
|
margin: 0 10px;
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tabBody{
|
||||||
|
.a-row{
|
||||||
|
margin: 10px 0;
|
||||||
|
.long{
|
||||||
|
.a-select{
|
||||||
|
width:354px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.a-input-number{
|
||||||
|
width: 110px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<template>
|
||||||
|
<global-layout>
|
||||||
|
<transition name="page-transition">
|
||||||
|
<keep-alive v-if="keepAlive">
|
||||||
|
<router-view />
|
||||||
|
</keep-alive>
|
||||||
|
<router-view v-else />
|
||||||
|
</transition>
|
||||||
|
</global-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import GlobalLayout from '@/components/page/GlobalLayout'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "BasicLayout",
|
||||||
|
components: {
|
||||||
|
GlobalLayout
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
keepAlive () {
|
||||||
|
return this.$route.meta.keepAlive
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following styles are auto-applied to elements with
|
||||||
|
* transition="page-transition" when their visibility is toggled
|
||||||
|
* by Vue.js.
|
||||||
|
*
|
||||||
|
* You can easily play with the page transition by editing
|
||||||
|
* these styles.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.page-transition-enter {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-transition-leave-active {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-transition-enter .page-transition-container,
|
||||||
|
.page-transition-leave-active .page-transition-container {
|
||||||
|
-webkit-transform: scale(1.1);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<iframe :id="id" :src="url" frameborder="0" width="100%" height="800px" scrolling="auto"></iframe>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { ACCESS_TOKEN } from "@/store/mutation-types"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "IframePageContent",
|
||||||
|
inject:['closeCurrent'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
url: "",
|
||||||
|
id:""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.goUrl()
|
||||||
|
},
|
||||||
|
updated () {
|
||||||
|
this.goUrl()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route(to, from) {
|
||||||
|
this.goUrl();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
goUrl () {
|
||||||
|
let url = this.$route.meta.url
|
||||||
|
let id = this.$route.path
|
||||||
|
this.id = id
|
||||||
|
//url = "http://www.baidu.com"
|
||||||
|
console.log("------url------"+url)
|
||||||
|
if (url !== null && url !== undefined) {
|
||||||
|
this.url = url;
|
||||||
|
/*update_begin author:wuxianquan date:20190908 for:判断打开方式,新窗口打开时this.$route.meta.internalOrExternal==true */
|
||||||
|
if(this.$route.meta.internalOrExternal != undefined && this.$route.meta.internalOrExternal==true){
|
||||||
|
this.closeCurrent();
|
||||||
|
//外部url加入token
|
||||||
|
let tokenStr = "${token}";
|
||||||
|
if(url.indexOf(tokenStr)!=-1){
|
||||||
|
let token = Vue.ls.get(ACCESS_TOKEN);
|
||||||
|
this.url = url.replace(tokenStr,token);
|
||||||
|
}
|
||||||
|
window.open(this.url);
|
||||||
|
}
|
||||||
|
/*update_end author:wuxianquan date:20190908 for:判断打开方式,新窗口打开时this.$route.meta.internalOrExternal==true */
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<page-layout :desc="description" :title="getTitle" :link-list="linkList" :search="search" :tabs="tabs">
|
||||||
|
<div slot="extra" class="extra-img">
|
||||||
|
<img :src="extraImage"/>
|
||||||
|
</div>
|
||||||
|
<!-- keep-alive -->
|
||||||
|
<route-view ref="content"></route-view>
|
||||||
|
</page-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageLayout from '../page/PageLayout'
|
||||||
|
import RouteView from './RouteView'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "PageContent",
|
||||||
|
components: {
|
||||||
|
RouteView,
|
||||||
|
PageLayout
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
title: '',
|
||||||
|
description: '',
|
||||||
|
linkList: [],
|
||||||
|
extraImage: '',
|
||||||
|
search: false,
|
||||||
|
tabs: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.getPageHeaderInfo()
|
||||||
|
},
|
||||||
|
updated () {
|
||||||
|
this.getPageHeaderInfo()
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
getTitle () {
|
||||||
|
return this.$route.meta.title
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getPageHeaderInfo () {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
this.title = this.$route.meta.title
|
||||||
|
// 因为套用了一层 route-view 所以要取 ref 对象下的子节点的第一个对象
|
||||||
|
const content = this.$refs.content && this.$refs.content.$children[0]
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
this.description = content.description
|
||||||
|
this.linkList = content.linkList
|
||||||
|
this.extraImage = content.extraImage
|
||||||
|
this.search = content.search == true ? true : false
|
||||||
|
this.tabs = content.tabs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.extra-img {
|
||||||
|
margin-top: -60px;
|
||||||
|
text-align: center;
|
||||||
|
width: 195px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile {
|
||||||
|
.extra-img{
|
||||||
|
margin-top: 0;
|
||||||
|
text-align: center;
|
||||||
|
width: 96px;
|
||||||
|
|
||||||
|
img{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<div class="main">
|
||||||
|
<keep-alive :include="includedComponents">
|
||||||
|
<router-view v-if="keepAlive" />
|
||||||
|
</keep-alive>
|
||||||
|
<router-view v-if="!keepAlive" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { CACHE_INCLUDED_ROUTES } from "@/store/mutation-types"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "RouteView",
|
||||||
|
computed: {
|
||||||
|
//update-begin--Author:scott Date:20201015 for:路由缓存问题,关闭了tab页时再打开就不刷新 #842
|
||||||
|
includedComponents() {
|
||||||
|
const includedRouters = Vue.ls.get(CACHE_INCLUDED_ROUTES)
|
||||||
|
//如果是缓存路由,则加入到 cache_included_routes
|
||||||
|
if (this.$route.meta.keepAlive && this.$route.meta.componentName) {
|
||||||
|
let cacheRouterArray = Vue.ls.get(CACHE_INCLUDED_ROUTES) || []
|
||||||
|
if(!cacheRouterArray.includes(this.$route.meta.componentName)){
|
||||||
|
cacheRouterArray.push(this.$route.meta.componentName)
|
||||||
|
// cacheRouterArray.push("OnlCgformHeadList")
|
||||||
|
Vue.ls.set(CACHE_INCLUDED_ROUTES, cacheRouterArray)
|
||||||
|
return cacheRouterArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return includedRouters;
|
||||||
|
},
|
||||||
|
//update-end--Author:scott Date:20201015 for:路由缓存问题,关闭了tab页时再打开就不刷新 #842
|
||||||
|
keepAlive () {
|
||||||
|
return this.$route.meta.keepAlive
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.main{
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,426 @@
|
|||||||
|
<template>
|
||||||
|
<global-layout @dynamicRouterShow="dynamicRouterShow">
|
||||||
|
<!-- update-begin- author:sunjianlei --- date:20191009 --- for: 提升右键菜单的层级 -->
|
||||||
|
<contextmenu :itemList="menuItemList" :visible.sync="menuVisible" style="z-index: 9999;" @select="onMenuSelect"/>
|
||||||
|
<!-- update-end- author:sunjianlei --- date:20191009 --- for: 提升右键菜单的层级 -->
|
||||||
|
<a-tabs
|
||||||
|
@contextmenu.native="e => onContextmenu(e)"
|
||||||
|
v-if="multipage"
|
||||||
|
:active-key="activePage"
|
||||||
|
class="tab-layout-tabs"
|
||||||
|
style="height:52px"
|
||||||
|
:hide-add="true"
|
||||||
|
type="editable-card"
|
||||||
|
@change="changePage"
|
||||||
|
@tabClick="tabCallBack"
|
||||||
|
@edit="editPage">
|
||||||
|
<a-tab-pane :id="page.fullPath" :key="page.fullPath" v-for="page in pageList" :closable="!(page.meta.title=='首页')">
|
||||||
|
<span slot="tab" :pagekey="page.fullPath">{{ page.meta.title }}</span>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
<div style="margin: 12px 12px 0;">
|
||||||
|
<!-- update-begin-author:taoyan date:20201221 for:此处删掉transition标签 不知道为什么加上后 页面路由切换的时候即1及菜单切到2及菜单的时候 两个菜单页面会同时出现300-500秒左右 -->
|
||||||
|
<keep-alive v-if="multipage">
|
||||||
|
<router-view v-if="reloadFlag"/>
|
||||||
|
</keep-alive>
|
||||||
|
<template v-else>
|
||||||
|
<router-view v-if="reloadFlag"/>
|
||||||
|
</template>
|
||||||
|
<!-- update-end-author:taoyan date:20201221 for:此处删掉transition标签 不知道为什么加上后 页面路由切换的时候即1及菜单切到2及菜单的时候 两个菜单页面会同时出现300-500秒左右 -->
|
||||||
|
</div>
|
||||||
|
</global-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import GlobalLayout from '@/components/page/GlobalLayout'
|
||||||
|
import Contextmenu from '@/components/menu/Contextmenu'
|
||||||
|
import { mixin, mixinDevice } from '@/utils/mixin.js'
|
||||||
|
import { triggerWindowResizeEvent } from '@/utils/util'
|
||||||
|
const indexKey = '/dashboard/analysis'
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { CACHE_INCLUDED_ROUTES } from "@/store/mutation-types"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TabLayout',
|
||||||
|
components: {
|
||||||
|
GlobalLayout,
|
||||||
|
Contextmenu
|
||||||
|
},
|
||||||
|
mixins: [mixin, mixinDevice],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
pageList: [],
|
||||||
|
linkList: [],
|
||||||
|
activePage: '',
|
||||||
|
menuVisible: false,
|
||||||
|
menuItemList: [
|
||||||
|
{ key: '4', icon: 'reload', text: '刷 新' },
|
||||||
|
{ key: '1', icon: 'arrow-left', text: '关闭左侧' },
|
||||||
|
{ key: '2', icon: 'arrow-right', text: '关闭右侧' },
|
||||||
|
{ key: '3', icon: 'close', text: '关闭其它' }
|
||||||
|
],
|
||||||
|
reloadFlag:true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* update_begin author:wuxianquan date:20190828 for: 关闭当前tab页,供子页面调用 ->望菜单能配置外链,直接弹出新页面而不是嵌入iframe #428 */
|
||||||
|
provide(){
|
||||||
|
return{
|
||||||
|
closeCurrent:this.closeCurrent
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* update_end author:wuxianquan date:20190828 for: 关闭当前tab页,供子页面调用->望菜单能配置外链,直接弹出新页面而不是嵌入iframe #428 */
|
||||||
|
computed: {
|
||||||
|
multipage() {
|
||||||
|
//判断如果是手机模式,自动切换为单页面模式
|
||||||
|
if (this.isMobile()) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return this.$store.state.app.multipage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
if (this.$route.path != indexKey) {
|
||||||
|
this.addIndexToFirst()
|
||||||
|
}
|
||||||
|
// 复制一个route对象出来,不能影响原route
|
||||||
|
let currentRoute = Object.assign({}, this.$route)
|
||||||
|
currentRoute.meta = Object.assign({}, currentRoute.meta)
|
||||||
|
// update-begin-author:sunjianlei date:20191223 for: 修复刷新后菜单Tab名字显示异常
|
||||||
|
let storeKey = 'route:title:' + currentRoute.fullPath
|
||||||
|
let routeTitle = this.$ls.get(storeKey)
|
||||||
|
if (routeTitle) {
|
||||||
|
currentRoute.meta.title = routeTitle
|
||||||
|
}
|
||||||
|
// update-end-author:sunjianlei date:20191223 for: 修复刷新后菜单Tab名字显示异常
|
||||||
|
this.pageList.push(currentRoute)
|
||||||
|
this.linkList.push(currentRoute.fullPath)
|
||||||
|
this.activePage = currentRoute.fullPath
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route': function(newRoute) {
|
||||||
|
//console.log("新的路由",newRoute)
|
||||||
|
this.activePage = newRoute.fullPath
|
||||||
|
if (!this.multipage) {
|
||||||
|
this.linkList = [newRoute.fullPath]
|
||||||
|
this.pageList = [Object.assign({},newRoute)]
|
||||||
|
// update-begin-author:taoyan date:20200211 for: TASK #3368 【路由缓存】首页的缓存设置有问题,需要根据后台的路由配置来实现是否缓存
|
||||||
|
} else if(indexKey==newRoute.fullPath) {
|
||||||
|
//首页时 判断是否缓存 没有缓存 刷新之
|
||||||
|
if (newRoute.meta.keepAlive === false) {
|
||||||
|
this.routeReload()
|
||||||
|
}
|
||||||
|
// update-end-author:taoyan date:20200211 for: TASK #3368 【路由缓存】首页的缓存设置有问题,需要根据后台的路由配置来实现是否缓存
|
||||||
|
}else if (this.linkList.indexOf(newRoute.fullPath) < 0) {
|
||||||
|
this.linkList.push(newRoute.fullPath)
|
||||||
|
this.pageList.push(Object.assign({},newRoute))
|
||||||
|
//// update-begin-author:sunjianlei date:20200103 for: 如果新增的页面配置了缓存路由,那么就强制刷新一遍 #842
|
||||||
|
// if (newRoute.meta.keepAlive) {
|
||||||
|
// this.routeReload()
|
||||||
|
// }
|
||||||
|
//// update-end-author:sunjianlei date:20200103 for: 如果新增的页面配置了缓存路由,那么就强制刷新一遍 #842
|
||||||
|
} else if (this.linkList.indexOf(newRoute.fullPath) >= 0) {
|
||||||
|
let oldIndex = this.linkList.indexOf(newRoute.fullPath)
|
||||||
|
let oldPositionRoute = this.pageList[oldIndex]
|
||||||
|
this.pageList.splice(oldIndex, 1, Object.assign({},newRoute,{meta:oldPositionRoute.meta}))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'activePage': function(key) {
|
||||||
|
let index = this.linkList.lastIndexOf(key)
|
||||||
|
let waitRouter = this.pageList[index]
|
||||||
|
// 【TESTA-523】修复:不允许重复跳转路由异常
|
||||||
|
if (waitRouter.fullPath !== this.$route.fullPath) {
|
||||||
|
this.$router.push(Object.assign({}, waitRouter))
|
||||||
|
}
|
||||||
|
this.changeTitle(waitRouter.meta.title)
|
||||||
|
},
|
||||||
|
'multipage': function(newVal) {
|
||||||
|
if(this.reloadFlag){
|
||||||
|
if (!newVal) {
|
||||||
|
this.linkList = [this.$route.fullPath]
|
||||||
|
this.pageList = [this.$route]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// update-begin-author:sunjianlei date:20191223 for: 修复从单页模式切换回多页模式后首页不居第一位的 BUG
|
||||||
|
device() {
|
||||||
|
if (this.multipage && this.linkList.indexOf(indexKey) === -1) {
|
||||||
|
this.addIndexToFirst()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// update-end-author:sunjianlei date:20191223 for: 修复从单页模式切换回多页模式后首页不居第一位的 BUG
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// update-begin-author:sunjianlei date:20191223 for: 修复从单页模式切换回多页模式后首页不居第一位的 BUG
|
||||||
|
// 将首页添加到第一位
|
||||||
|
addIndexToFirst() {
|
||||||
|
this.pageList.splice(0, 0, {
|
||||||
|
name: 'dashboard-analysis',
|
||||||
|
path: indexKey,
|
||||||
|
fullPath: indexKey,
|
||||||
|
meta: {
|
||||||
|
icon: 'dashboard',
|
||||||
|
title: '首页'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.linkList.splice(0, 0, indexKey)
|
||||||
|
},
|
||||||
|
// update-end-author:sunjianlei date:20191223 for: 修复从单页模式切换回多页模式后首页不居第一位的 BUG
|
||||||
|
|
||||||
|
// update-begin-author:sunjianlei date:20200120 for: 动态更改页面标题
|
||||||
|
changeTitle(title) {
|
||||||
|
let projectTitle = "Jero-Boot 企业级快速开发平台"
|
||||||
|
// 首页特殊处理
|
||||||
|
if (this.$route.path === indexKey) {
|
||||||
|
document.title = projectTitle
|
||||||
|
} else {
|
||||||
|
document.title = title + ' · ' + projectTitle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// update-end-author:sunjianlei date:20200120 for: 动态更改页面标题
|
||||||
|
|
||||||
|
changePage(key) {
|
||||||
|
this.activePage = key
|
||||||
|
},
|
||||||
|
tabCallBack() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
//update-begin-author:taoyan date: 20201211 for:【新版】online报错 JT-100
|
||||||
|
setTimeout(()=>{
|
||||||
|
//省市区组件里面给window绑定了个resize事件 导致切换页面的时候触发了他的resize,但是切换页面,省市区组件还没被销毁前就触发了该事件,导致控制台报错,加个延迟
|
||||||
|
triggerWindowResizeEvent()
|
||||||
|
},20)
|
||||||
|
//update-end-author:taoyan date: 20201211 for:【新版】online报错 JT-100
|
||||||
|
})
|
||||||
|
},
|
||||||
|
editPage(key, action) {
|
||||||
|
this[action](key)
|
||||||
|
},
|
||||||
|
remove(key) {
|
||||||
|
if (key == indexKey) {
|
||||||
|
this.$message.warning('首页不能关闭!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.pageList.length === 1) {
|
||||||
|
this.$message.warning('这是最后一页,不能再关闭了啦')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log("this.pageList ",this.pageList );
|
||||||
|
let removeRoute = this.pageList.filter(item => item.fullPath == key)
|
||||||
|
this.pageList = this.pageList.filter(item => item.fullPath !== key)
|
||||||
|
let index = this.linkList.indexOf(key)
|
||||||
|
this.linkList = this.linkList.filter(item => item !== key)
|
||||||
|
index = index >= this.linkList.length ? this.linkList.length - 1 : index
|
||||||
|
this.activePage = this.linkList[index]
|
||||||
|
|
||||||
|
//update-begin--Author:scott Date:20201015 for:路由缓存问题,关闭了tab页时再打开就不刷新 #842
|
||||||
|
//关闭页面则从缓存cache_included_routes中删除路由,下次点击菜单会重新加载页面
|
||||||
|
let cacheRouterArray = Vue.ls.get(CACHE_INCLUDED_ROUTES) || []
|
||||||
|
if (removeRoute && removeRoute[0]) {
|
||||||
|
let componentName = removeRoute[0].meta.componentName
|
||||||
|
console.log("key: ", key);
|
||||||
|
console.log("componentName: ", componentName);
|
||||||
|
if(cacheRouterArray.includes(componentName)){
|
||||||
|
cacheRouterArray.splice(cacheRouterArray.findIndex(item => item === componentName), 1)
|
||||||
|
Vue.ls.set(CACHE_INCLUDED_ROUTES, cacheRouterArray)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//update-end--Author:scott Date:20201015 for:路由缓存问题,关闭了tab页时再打开就不刷新 #842
|
||||||
|
|
||||||
|
},
|
||||||
|
onContextmenu(e) {
|
||||||
|
const pagekey = this.getPageKey(e.target)
|
||||||
|
if (pagekey !== null) {
|
||||||
|
e.preventDefault()
|
||||||
|
this.menuVisible = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getPageKey(target, depth) {
|
||||||
|
depth = depth || 0
|
||||||
|
if (depth > 2) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
let pageKey = target.getAttribute('pagekey')
|
||||||
|
pageKey = pageKey || (target.previousElementSibling ? target.previousElementSibling.getAttribute('pagekey') : null)
|
||||||
|
return pageKey || (target.firstElementChild ? this.getPageKey(target.firstElementChild, ++depth) : null)
|
||||||
|
},
|
||||||
|
onMenuSelect(key, target) {
|
||||||
|
let pageKey = this.getPageKey(target)
|
||||||
|
switch (key) {
|
||||||
|
case '1':
|
||||||
|
this.closeLeft(pageKey)
|
||||||
|
break
|
||||||
|
case '2':
|
||||||
|
this.closeRight(pageKey)
|
||||||
|
break
|
||||||
|
case '3':
|
||||||
|
this.closeOthers(pageKey)
|
||||||
|
break
|
||||||
|
case '4':
|
||||||
|
this.routeReload()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* update_begin author:wuxianquan date:20190828 for: 关闭当前tab页,供子页面调用->望菜单能配置外链,直接弹出新页面而不是嵌入iframe #428 */
|
||||||
|
closeCurrent(){
|
||||||
|
this.remove(this.activePage);
|
||||||
|
},
|
||||||
|
/* update_end author:wuxianquan date:20190828 for: 关闭当前tab页,供子页面调用->望菜单能配置外链,直接弹出新页面而不是嵌入iframe #428 */
|
||||||
|
closeOthers(pageKey) {
|
||||||
|
let index = this.linkList.indexOf(pageKey)
|
||||||
|
if (pageKey == indexKey || pageKey.indexOf('?ticke=')>=0) {
|
||||||
|
this.linkList = this.linkList.slice(index, index + 1)
|
||||||
|
this.pageList = this.pageList.slice(index, index + 1)
|
||||||
|
this.activePage = this.linkList[0]
|
||||||
|
} else {
|
||||||
|
let indexContent = this.pageList.slice(0, 1)[0]
|
||||||
|
this.linkList = this.linkList.slice(index, index + 1)
|
||||||
|
this.pageList = this.pageList.slice(index, index + 1)
|
||||||
|
this.linkList.unshift(indexContent.fullPath)
|
||||||
|
this.pageList.unshift(indexContent)
|
||||||
|
this.activePage = this.linkList[1]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
closeLeft(pageKey) {
|
||||||
|
if (pageKey == indexKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let tempList = [...this.pageList]
|
||||||
|
let indexContent = tempList.slice(0, 1)[0]
|
||||||
|
let index = this.linkList.indexOf(pageKey)
|
||||||
|
this.linkList = this.linkList.slice(index)
|
||||||
|
this.pageList = this.pageList.slice(index)
|
||||||
|
this.linkList.unshift(indexContent.fullPath)
|
||||||
|
this.pageList.unshift(indexContent)
|
||||||
|
if (this.linkList.indexOf(this.activePage) < 0) {
|
||||||
|
this.activePage = this.linkList[0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
closeRight(pageKey) {
|
||||||
|
let index = this.linkList.indexOf(pageKey)
|
||||||
|
this.linkList = this.linkList.slice(0, index + 1)
|
||||||
|
this.pageList = this.pageList.slice(0, index + 1)
|
||||||
|
if (this.linkList.indexOf(this.activePage < 0)) {
|
||||||
|
this.activePage = this.linkList[this.linkList.length - 1]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//update-begin-author:taoyan date:20190430 for:动态路由title显示配置的菜单title而不是其对应路由的title
|
||||||
|
dynamicRouterShow(key,title){
|
||||||
|
let keyIndex = this.linkList.indexOf(key)
|
||||||
|
if(keyIndex>=0){
|
||||||
|
let currRouter = this.pageList[keyIndex]
|
||||||
|
let meta = Object.assign({},currRouter.meta,{title:title})
|
||||||
|
this.pageList.splice(keyIndex, 1, Object.assign({},currRouter,{meta:meta}))
|
||||||
|
if (key === this.activePage) {
|
||||||
|
this.changeTitle(title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//update-end-author:taoyan date:20190430 for:动态路由title显示配置的菜单title而不是其对应路由的title
|
||||||
|
|
||||||
|
//update-begin-author:taoyan date:20191008 for:路由刷新
|
||||||
|
routeReload(){
|
||||||
|
this.reloadFlag = false
|
||||||
|
let ToggleMultipage = "ToggleMultipage"
|
||||||
|
this.$store.dispatch(ToggleMultipage,false)
|
||||||
|
this.$nextTick(()=>{
|
||||||
|
this.$store.dispatch(ToggleMultipage,true)
|
||||||
|
this.reloadFlag = true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
//update-end-author:taoyan date:20191008 for:路由刷新
|
||||||
|
//新增一个返回方法
|
||||||
|
excuteCallback(callback){
|
||||||
|
callback()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following styles are auto-applied to elements with
|
||||||
|
* transition="page-transition" when their visibility is toggled
|
||||||
|
* by Vue.js.
|
||||||
|
*
|
||||||
|
* You can easily play with the page transition by editing
|
||||||
|
* these styles.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.page-transition-enter {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-transition-leave-active {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-transition-enter .page-transition-container,
|
||||||
|
.page-transition-leave-active .page-transition-container {
|
||||||
|
-webkit-transform: scale(1.1);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*美化弹出Tab样式*/
|
||||||
|
.ant-tabs-nav-container {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 修改 ant-tabs 样式 */
|
||||||
|
.tab-layout-tabs.ant-tabs {
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
border-left: 1px solid #ccc;
|
||||||
|
background-color: white;
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
.ant-tabs-bar {
|
||||||
|
margin: 4px 0 0;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-layout-tabs.ant-tabs {
|
||||||
|
|
||||||
|
&.ant-tabs-card .ant-tabs-tab {
|
||||||
|
|
||||||
|
padding: 0 24px !important;
|
||||||
|
background-color: white !important;
|
||||||
|
margin-right: 10px !important;
|
||||||
|
|
||||||
|
.ant-tabs-close-x {
|
||||||
|
width: 12px !important;
|
||||||
|
height: 12px !important;
|
||||||
|
opacity: 0 !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
position: absolute;
|
||||||
|
top: 36%;
|
||||||
|
right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .ant-tabs-close-x {
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-layout-tabs.ant-tabs.ant-tabs-card > .ant-tabs-bar {
|
||||||
|
.ant-tabs-tab {
|
||||||
|
border: none !important;
|
||||||
|
border-bottom: 1px solid transparent !important;
|
||||||
|
}
|
||||||
|
.ant-tabs-tab-active {
|
||||||
|
border-color: @primary-color!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
<template>
|
||||||
|
<div id="userLayout" :class="['user-layout-wrapper',device]">
|
||||||
|
<div :class="!mobile ? 'container' : 'container-mobile'">
|
||||||
|
<div class="top" id="top" v-if="!mobile">
|
||||||
|
<div class="logo">
|
||||||
|
<img id="logo" src="~@/assets/logo.png" alt="">
|
||||||
|
</div>
|
||||||
|
<div id="company">
|
||||||
|
<span id="c-company">合众未来</span>
|
||||||
|
<span id="p-company">HEZHONGWEILAI</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="mobile">
|
||||||
|
<span class="top-bg"><img src="~/@assets/mobileBg.png" alt=""></span>
|
||||||
|
<span class="btm-bg"><img src="~/@assets/mobileHome.png" alt=""></span>
|
||||||
|
</div>
|
||||||
|
<div v-if="mobile" class="mobile">
|
||||||
|
<div class="logo">
|
||||||
|
<img src="~@/assets/logo.png" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="company">
|
||||||
|
<span>合众未来</span>
|
||||||
|
<span>HEZHONGWEILAI</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="illustration" v-if="!mobile">
|
||||||
|
<img src="@/assets/home.png" alt="">
|
||||||
|
</div>
|
||||||
|
<route-view></route-view>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import RouteView from "@/components/layouts/RouteView"
|
||||||
|
import { mixinDevice } from '@/utils/mixin.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "UserLayout",
|
||||||
|
components: { RouteView },
|
||||||
|
mixins: [mixinDevice],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
mobile:isMobile() //是否是mobile
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
document.body.classList.add('userLayout')
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
document.body.classList.remove('userLayout')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// 判断当前设备
|
||||||
|
function isMobile() {
|
||||||
|
var userAgentInfo = navigator.userAgent;
|
||||||
|
|
||||||
|
var mobileAgents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
|
||||||
|
|
||||||
|
var mobile_flag = false;
|
||||||
|
|
||||||
|
//根据userAgent判断是否是手机
|
||||||
|
for (var v = 0; v < mobileAgents.length; v++) {
|
||||||
|
if (userAgentInfo.indexOf(mobileAgents[v]) > 0) {
|
||||||
|
mobile_flag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var screen_width = window.screen.width;
|
||||||
|
var screen_height = window.screen.height;
|
||||||
|
|
||||||
|
//根据屏幕分辨率判断是否是手机
|
||||||
|
if (screen_width < 500 && screen_height < 800) {
|
||||||
|
mobile_flag = true;
|
||||||
|
}
|
||||||
|
return mobile_flag;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" >
|
||||||
|
#userLayout.user-layout-wrapper{
|
||||||
|
height: 100%;
|
||||||
|
background: #4daaff;
|
||||||
|
overflow: auto;
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 770px;
|
||||||
|
min-height: 500px;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
.top {
|
||||||
|
position: absolute;
|
||||||
|
left: 2%;
|
||||||
|
top: 3%;
|
||||||
|
min-width: 100px;
|
||||||
|
max-height: 50px;
|
||||||
|
z-index: 9;
|
||||||
|
.logo {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: absolute;
|
||||||
|
overflow: hidden;
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
min-height: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#company {
|
||||||
|
position: absolute;
|
||||||
|
max-height: 62px;
|
||||||
|
left: 5.5rem;
|
||||||
|
min-height: 61px;
|
||||||
|
color: #000;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
&:first-child {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.3rem;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 0.9rem;
|
||||||
|
letter-spacing: 0.06rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.illustration{
|
||||||
|
position: absolute;
|
||||||
|
max-width: 50%;
|
||||||
|
max-height: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-width: 400px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
img{
|
||||||
|
max-width: 70%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.container-mobile{
|
||||||
|
/*移动端的logo样式*/
|
||||||
|
.top-bg{
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: -3.5rem;
|
||||||
|
img{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btm-bg{
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
max-width: 75%;
|
||||||
|
overflow: hidden;
|
||||||
|
img{
|
||||||
|
margin-left: -5rem;
|
||||||
|
max-width: 100%;
|
||||||
|
margin-bottom: -3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
background: #f0f2f5;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
.mobile{
|
||||||
|
position: absolute;
|
||||||
|
top: 6rem;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
.logo{
|
||||||
|
max-width: 4rem;
|
||||||
|
img{
|
||||||
|
max-width: 100%;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.company{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
span:first-child{
|
||||||
|
color: #000;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: 0.3rem;
|
||||||
|
}
|
||||||
|
span:last-child{
|
||||||
|
color: #000;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
line-height: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import UserLayout from '@/components/layouts/UserLayout'
|
||||||
|
import BasicLayout from '@/components/layouts/BasicLayout'
|
||||||
|
import RouteView from '@/components/layouts/RouteView'
|
||||||
|
import PageView from '@/components/layouts/PageView'
|
||||||
|
import TabLayout from '@/components/layouts/TabLayout'
|
||||||
|
|
||||||
|
export { UserLayout, BasicLayout, RouteView, PageView, TabLayout }
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<template>
|
||||||
|
<a-menu :style="style" class="contextmenu" v-show="visible" @click="handleClick" :selectedKeys="selectedKeys">
|
||||||
|
<a-menu-item :key="item.key" v-for="item in itemList">
|
||||||
|
<a-icon role="menuitemicon" v-if="item.icon" :type="item.icon" />{{ item.text }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Contextmenu',
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
itemList: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
target: null,
|
||||||
|
selectedKeys: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
style () {
|
||||||
|
return {
|
||||||
|
left: this.left + 'px',
|
||||||
|
top: this.top + 'px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('mousedown', e => this.closeMenu(e))
|
||||||
|
window.addEventListener('contextmenu', e => this.setPosition(e))
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closeMenu (e) {
|
||||||
|
if (this.visible === true && ['menuitemicon', 'menuitem'].indexOf(e.target.getAttribute('role')) < 0) {
|
||||||
|
this.$emit('update:visible', false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setPosition (e) {
|
||||||
|
this.left = e.clientX
|
||||||
|
this.top = e.clientY
|
||||||
|
this.target = e.target
|
||||||
|
},
|
||||||
|
handleClick ({key}) {
|
||||||
|
this.$emit('select', key, this.target)
|
||||||
|
this.$emit('update:visible', false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.contextmenu{
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1;
|
||||||
|
border: 1px solid #9e9e9e;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 2px 2px 10px #aaaaaa !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
<template>
|
||||||
|
<a-layout-sider
|
||||||
|
:class="['sider', isDesktop() ? null : 'shadow', theme, fixSiderbar ? 'ant-fixed-sidemenu' : null ]"
|
||||||
|
width="208px"
|
||||||
|
:collapsible="collapsible"
|
||||||
|
v-model="collapsed"
|
||||||
|
:trigger="null">
|
||||||
|
<logo />
|
||||||
|
<s-menu
|
||||||
|
:collapsed="collapsed"
|
||||||
|
:menu="menus"
|
||||||
|
:theme="theme"
|
||||||
|
@select="onSelect"
|
||||||
|
:mode="mode"
|
||||||
|
:style="smenuStyle">
|
||||||
|
</s-menu>
|
||||||
|
</a-layout-sider>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ALayoutSider from "ant-design-vue/es/layout/Sider"
|
||||||
|
import Logo from '../tools/Logo'
|
||||||
|
import SMenu from './index'
|
||||||
|
import { mixin, mixinDevice } from '@/utils/mixin.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "SideMenu",
|
||||||
|
components: { ALayoutSider, Logo, SMenu },
|
||||||
|
mixins: [mixin, mixinDevice],
|
||||||
|
props: {
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'inline'
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'dark'
|
||||||
|
},
|
||||||
|
collapsible: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
collapsed: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
menus: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed:{
|
||||||
|
smenuStyle() {
|
||||||
|
let style = { 'padding': '0' }
|
||||||
|
if (this.fixSiderbar) {
|
||||||
|
style['height'] = 'calc(100% - 59px)'
|
||||||
|
style['overflow'] = 'auto'
|
||||||
|
style['overflow-x'] = 'hidden'
|
||||||
|
}
|
||||||
|
return style
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSelect (obj) {
|
||||||
|
this.$emit('menuSelect', obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
/* update_begin author:sunjianlei date:20190509 for: 修改侧边导航栏滚动条的样式 */
|
||||||
|
.sider {
|
||||||
|
@scrollBarSize: 10px;
|
||||||
|
|
||||||
|
ul.ant-menu {
|
||||||
|
|
||||||
|
/* 定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: @scrollBarSize;
|
||||||
|
height: @scrollBarSize;
|
||||||
|
background-color: transparent;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .-o-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 兼容IE */
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
-ms-scroll-chaining: chained;
|
||||||
|
-ms-content-zooming: zoom;
|
||||||
|
-ms-scroll-rails: none;
|
||||||
|
-ms-content-zoom-limit-min: 100%;
|
||||||
|
-ms-content-zoom-limit-max: 500%;
|
||||||
|
-ms-scroll-snap-type: proximity;
|
||||||
|
-ms-scroll-snap-points-x: snapList(100%, 200%, 300%, 400%, 500%);
|
||||||
|
|
||||||
|
/* 定义滚动条轨道 */
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 定义滑块 */
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: @scrollBarSize;
|
||||||
|
background-color: #eee;
|
||||||
|
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #dddddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #bbbbbb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 暗色系滚动条样式 */
|
||||||
|
&.dark ul.ant-menu {
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #666666;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #808080;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update_end author:sunjianlei date:20190509 for: 修改侧边导航栏滚动条的样式 */
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- update_begin author:sunjianlei date:20190530 for: 选中首页的时候不显示背景颜色 -->
|
||||||
|
<style lang="less">
|
||||||
|
.ant-menu.ant-menu-root {
|
||||||
|
& > .ant-menu-item:first-child {
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
& > a, & > a:hover {
|
||||||
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ant-menu-item-selected {
|
||||||
|
& > a, & > a:hover {
|
||||||
|
color: @primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ant-menu-dark > .ant-menu-item:first-child {
|
||||||
|
& > a, & > a:hover {
|
||||||
|
color: rgba(255, 255, 255, 0.65);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ant-menu-item-selected {
|
||||||
|
& > a, & > a:hover {
|
||||||
|
color: rgba(255, 255, 255, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!-- update_end author:sunjianlei date:20190530 for: 选中首页的时候不显示背景颜色 -->
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
import Menu from 'ant-design-vue/es/menu'
|
||||||
|
import Icon from 'ant-design-vue/es/icon'
|
||||||
|
|
||||||
|
const { Item, SubMenu } = Menu
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SMenu',
|
||||||
|
props: {
|
||||||
|
menu: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'dark'
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'inline'
|
||||||
|
},
|
||||||
|
collapsed: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
openKeys: [],
|
||||||
|
selectedKeys: [],
|
||||||
|
cachedOpenKeys: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
rootSubmenuKeys: vm => {
|
||||||
|
const keys = []
|
||||||
|
vm.menu.forEach(item => keys.push(item.path))
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.updateMenu()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
collapsed (val) {
|
||||||
|
if (val) {
|
||||||
|
this.cachedOpenKeys = this.openKeys.concat()
|
||||||
|
this.openKeys = []
|
||||||
|
} else {
|
||||||
|
this.openKeys = this.cachedOpenKeys
|
||||||
|
}
|
||||||
|
},
|
||||||
|
$route: function () {
|
||||||
|
this.updateMenu()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// select menu item
|
||||||
|
onOpenChange (openKeys) {
|
||||||
|
|
||||||
|
// 在水平模式下时执行,并且不再执行后续
|
||||||
|
if (this.mode === 'horizontal') {
|
||||||
|
this.openKeys = openKeys
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 非水平模式时
|
||||||
|
const latestOpenKey = openKeys.find(key => !this.openKeys.includes(key))
|
||||||
|
if (!this.rootSubmenuKeys.includes(latestOpenKey)) {
|
||||||
|
this.openKeys = openKeys
|
||||||
|
} else {
|
||||||
|
this.openKeys = latestOpenKey ? [latestOpenKey] : []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateMenu () {
|
||||||
|
const routes = this.$route.matched.concat()
|
||||||
|
const { hidden } = this.$route.meta
|
||||||
|
if (routes.length >= 3 && hidden) {
|
||||||
|
routes.pop()
|
||||||
|
this.selectedKeys = [routes[routes.length - 1].path]
|
||||||
|
} else {
|
||||||
|
this.selectedKeys = [routes.pop().path]
|
||||||
|
}
|
||||||
|
const openKeys = []
|
||||||
|
if (this.mode === 'inline') {
|
||||||
|
routes.forEach(item => {
|
||||||
|
openKeys.push(item.path)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//update-begin-author:taoyan date:20190510 for:online表单菜单点击展开的一级目录不对
|
||||||
|
if(!this.selectedKeys || this.selectedKeys[0].indexOf(":")<0){
|
||||||
|
this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys)
|
||||||
|
}
|
||||||
|
//update-end-author:taoyan date:20190510 for:online表单菜单点击展开的一级目录不对
|
||||||
|
},
|
||||||
|
|
||||||
|
// render
|
||||||
|
renderItem (menu) {
|
||||||
|
if (!menu.hidden) {
|
||||||
|
return menu.children && !menu.alwaysShow ? this.renderSubMenu(menu) : this.renderMenuItem(menu)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
renderMenuItem (menu) {
|
||||||
|
const target = menu.meta.target || null
|
||||||
|
const tag = target && 'a' || 'router-link'
|
||||||
|
let props = { to: { name: menu.name } }
|
||||||
|
if(menu.route && menu.route === '0'){
|
||||||
|
props = { to: { path: menu.path } }
|
||||||
|
}
|
||||||
|
|
||||||
|
const attrs = { href: menu.path, target: menu.meta.target }
|
||||||
|
|
||||||
|
if (menu.children && menu.alwaysShow) {
|
||||||
|
// 把有子菜单的 并且 父菜单是要隐藏子菜单的
|
||||||
|
// 都给子菜单增加一个 hidden 属性
|
||||||
|
// 用来给刷新页面时, selectedKeys 做控制用
|
||||||
|
menu.children.forEach(item => {
|
||||||
|
item.meta = Object.assign(item.meta, { hidden: true })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Item {...{ key: menu.path }}>
|
||||||
|
<tag {...{ props, attrs }}>
|
||||||
|
{this.renderIcon(menu.meta.icon)}
|
||||||
|
<span>{menu.meta.title}</span>
|
||||||
|
</tag>
|
||||||
|
</Item>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
renderSubMenu (menu) {
|
||||||
|
const itemArr = []
|
||||||
|
if (!menu.alwaysShow) {
|
||||||
|
menu.children.forEach(item => itemArr.push(this.renderItem(item)))
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<SubMenu {...{ key: menu.path }}>
|
||||||
|
<span slot="title">
|
||||||
|
{this.renderIcon(menu.meta.icon)}
|
||||||
|
<span>{menu.meta.title}</span>
|
||||||
|
</span>
|
||||||
|
{itemArr}
|
||||||
|
</SubMenu>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
renderIcon (icon) {
|
||||||
|
if (icon === 'none' || icon === undefined) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const props = {}
|
||||||
|
typeof (icon) === 'object' ? props.component = icon : props.type = icon
|
||||||
|
return (
|
||||||
|
<Icon {... { props } }/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { mode, theme, menu } = this
|
||||||
|
const props = {
|
||||||
|
mode: mode,
|
||||||
|
theme: theme,
|
||||||
|
openKeys: this.openKeys
|
||||||
|
}
|
||||||
|
const on = {
|
||||||
|
select: obj => {
|
||||||
|
this.selectedKeys = obj.selectedKeys
|
||||||
|
this.$emit('select', obj)
|
||||||
|
},
|
||||||
|
openChange: this.onOpenChange
|
||||||
|
}
|
||||||
|
|
||||||
|
const menuTree = menu.map(item => {
|
||||||
|
if (item.hidden) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return this.renderItem(item)
|
||||||
|
})
|
||||||
|
// {...{ props, on: on }}
|
||||||
|
return (
|
||||||
|
<Menu vModel={this.selectedKeys} {...{ props, on: on }}>
|
||||||
|
{menuTree}
|
||||||
|
</Menu>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<div class="footer">
|
||||||
|
<div class="links">
|
||||||
|
<a href="http://www.hzwlsoft.com" target="_blank">JERO 首页</a>
|
||||||
|
<a href="https://ant.design/">Ant Design</a>
|
||||||
|
<a href="https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn/">Vue Antd</a>
|
||||||
|
</div>
|
||||||
|
<div class="copyright">
|
||||||
|
Copyright
|
||||||
|
<a-icon type="copyright"/>
|
||||||
|
2019 <span>hzwl&syxy</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "LayoutFooter"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.footer {
|
||||||
|
padding: 0 16px;
|
||||||
|
margin: 48px 0 24px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.links {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(0, 0, 0, .45);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgba(0, 0, 0, .65);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.copyright {
|
||||||
|
color: rgba(0, 0, 0, .45);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
<template>
|
||||||
|
<!-- , width: fixedHeader ? `calc(100% - ${sidebarOpened ? 256 : 80}px)` : '100%' -->
|
||||||
|
<a-layout-header
|
||||||
|
v-if="!headerBarFixed"
|
||||||
|
:class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]"
|
||||||
|
:style="{ padding: '0' }">
|
||||||
|
|
||||||
|
<div v-if="mode === 'sidemenu'" class="header" :class="theme">
|
||||||
|
<a-icon
|
||||||
|
v-if="device==='mobile'"
|
||||||
|
class="trigger"
|
||||||
|
:type="collapsed ? 'menu-fold' : 'menu-unfold'"
|
||||||
|
@click="toggle"></a-icon>
|
||||||
|
<a-icon
|
||||||
|
v-else
|
||||||
|
class="trigger"
|
||||||
|
:type="collapsed ? 'menu-unfold' : 'menu-fold'"
|
||||||
|
@click="toggle"/>
|
||||||
|
|
||||||
|
<span v-if="device === 'desktop'">欢迎进入 Jero-Boot 平台</span>
|
||||||
|
<span v-else>Jero-Boot</span>
|
||||||
|
|
||||||
|
<user-menu :theme="theme"/>
|
||||||
|
</div>
|
||||||
|
<!-- 顶部导航栏模式 -->
|
||||||
|
<div v-else :class="['top-nav-header-index', theme]">
|
||||||
|
<div class="header-index-wide">
|
||||||
|
<div class="header-index-left" :style="topMenuStyle.headerIndexLeft">
|
||||||
|
<logo class="top-nav-header" :show-title="device !== 'mobile'" :style="topMenuStyle.topNavHeader"/>
|
||||||
|
<div v-if="device !== 'mobile'" :style="topMenuStyle.topSmenuStyle">
|
||||||
|
<s-menu
|
||||||
|
mode="horizontal"
|
||||||
|
:menu="menus"
|
||||||
|
:theme="theme"></s-menu>
|
||||||
|
</div>
|
||||||
|
<a-icon
|
||||||
|
v-else
|
||||||
|
class="trigger"
|
||||||
|
:type="collapsed ? 'menu-fold' : 'menu-unfold'"
|
||||||
|
@click="toggle"></a-icon>
|
||||||
|
</div>
|
||||||
|
<user-menu class="header-index-right" :theme="theme" :style="topMenuStyle.headerIndexRight"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</a-layout-header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import UserMenu from '../tools/UserMenu'
|
||||||
|
import SMenu from '../menu/'
|
||||||
|
import Logo from '../tools/Logo'
|
||||||
|
|
||||||
|
import { mixin } from '@/utils/mixin.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'GlobalHeader',
|
||||||
|
components: {
|
||||||
|
UserMenu,
|
||||||
|
SMenu,
|
||||||
|
Logo,
|
||||||
|
},
|
||||||
|
mixins: [mixin],
|
||||||
|
props: {
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
// sidemenu, topmenu
|
||||||
|
default: 'sidemenu'
|
||||||
|
},
|
||||||
|
menus: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'dark'
|
||||||
|
},
|
||||||
|
collapsed: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
device: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'desktop'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
headerBarFixed: false,
|
||||||
|
//update-begin--author:sunjianlei---date:20190508------for: 顶部导航栏过长时显示更多按钮-----
|
||||||
|
topMenuStyle: {
|
||||||
|
headerIndexLeft: {},
|
||||||
|
topNavHeader: {},
|
||||||
|
headerIndexRight: {},
|
||||||
|
topSmenuStyle: {}
|
||||||
|
},
|
||||||
|
chatStatus: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
/** 监听设备变化 */
|
||||||
|
device() {
|
||||||
|
if (this.mode === 'topmenu') {
|
||||||
|
this.buildTopMenuStyle()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 监听导航栏模式变化 */
|
||||||
|
mode(newVal) {
|
||||||
|
if (newVal === 'topmenu') {
|
||||||
|
this.buildTopMenuStyle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//update-end--author:sunjianlei---date:20190508------for: 顶部导航栏过长时显示更多按钮-----
|
||||||
|
mounted() {
|
||||||
|
window.addEventListener('scroll', this.handleScroll)
|
||||||
|
//update-begin--author:sunjianlei---date:20190508------for: 顶部导航栏过长时显示更多按钮-----
|
||||||
|
if (this.mode === 'topmenu') {
|
||||||
|
this.buildTopMenuStyle()
|
||||||
|
}
|
||||||
|
//update-end--author:sunjianlei---date:20190508------for: 顶部导航栏过长时显示更多按钮-----
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleScroll() {
|
||||||
|
if (this.autoHideHeader) {
|
||||||
|
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
|
||||||
|
if (scrollTop > 100) {
|
||||||
|
this.headerBarFixed = true
|
||||||
|
} else {
|
||||||
|
this.headerBarFixed = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.headerBarFixed = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggle() {
|
||||||
|
this.$emit('toggle')
|
||||||
|
},
|
||||||
|
//update-begin--author:sunjianlei---date:20190508------for: 顶部导航栏过长时显示更多按钮-----
|
||||||
|
buildTopMenuStyle() {
|
||||||
|
if (this.mode === 'topmenu') {
|
||||||
|
if (this.device === 'mobile') {
|
||||||
|
// 手机端需要清空样式,否则显示会错乱
|
||||||
|
this.topMenuStyle.topNavHeader = {}
|
||||||
|
this.topMenuStyle.topSmenuStyle = {}
|
||||||
|
this.topMenuStyle.headerIndexRight = {}
|
||||||
|
this.topMenuStyle.headerIndexLeft = {}
|
||||||
|
} else {
|
||||||
|
let rightWidth = '360px'
|
||||||
|
this.topMenuStyle.topNavHeader = { 'min-width': '165px' }
|
||||||
|
this.topMenuStyle.topSmenuStyle = { 'width': 'calc(100% - 165px)' }
|
||||||
|
this.topMenuStyle.headerIndexRight = { 'min-width': rightWidth }
|
||||||
|
this.topMenuStyle.headerIndexLeft = { 'width': `calc(100% - ${rightWidth})` }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//update-begin--author:sunjianlei---date:20190508------for: 顶部导航栏过长时显示更多按钮-----
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
/* update_begin author:scott date:20190220 for: 缩小首页布局顶部的高度*/
|
||||||
|
|
||||||
|
@height: 59px;
|
||||||
|
|
||||||
|
.layout {
|
||||||
|
|
||||||
|
.top-nav-header-index {
|
||||||
|
|
||||||
|
.header-index-wide {
|
||||||
|
margin-left: 10px;
|
||||||
|
|
||||||
|
.ant-menu.ant-menu-horizontal {
|
||||||
|
height: @height;
|
||||||
|
line-height: @height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.trigger {
|
||||||
|
line-height: 64px;
|
||||||
|
&:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
z-index: 2;
|
||||||
|
color: white;
|
||||||
|
height: @height;
|
||||||
|
background-color: @primary-color;
|
||||||
|
transition: background 300ms;
|
||||||
|
|
||||||
|
/* dark 样式 */
|
||||||
|
&.dark {
|
||||||
|
color: #000000;
|
||||||
|
box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header, .top-nav-header-index {
|
||||||
|
&.dark .trigger:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-layout-header {
|
||||||
|
height: @height;
|
||||||
|
line-height: @height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update_end author:scott date:20190220 for: 缩小首页布局顶部的高度*/
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,701 @@
|
|||||||
|
<template>
|
||||||
|
<a-layout class="layout" :class="[device]">
|
||||||
|
|
||||||
|
<template v-if="layoutMode === 'sidemenu'">
|
||||||
|
<a-drawer
|
||||||
|
v-if="device === 'mobile'"
|
||||||
|
:wrapClassName="'drawer-sider ' + navTheme"
|
||||||
|
placement="left"
|
||||||
|
@close="() => this.collapsed = false"
|
||||||
|
:closable="false"
|
||||||
|
:visible="collapsed"
|
||||||
|
width="200px"
|
||||||
|
>
|
||||||
|
<side-menu
|
||||||
|
mode="inline"
|
||||||
|
v-if="device === 'mobile'"
|
||||||
|
:menus="menus"
|
||||||
|
@menuSelect="menuSelect"
|
||||||
|
:theme="navTheme"
|
||||||
|
:collapsed="false"
|
||||||
|
:collapsible="true"></side-menu>
|
||||||
|
</a-drawer>
|
||||||
|
|
||||||
|
<side-menu
|
||||||
|
v-show="device === 'desktop'"
|
||||||
|
mode="inline"
|
||||||
|
:menus="menus"
|
||||||
|
@menuSelect="myMenuSelect"
|
||||||
|
:theme="navTheme"
|
||||||
|
:collapsed="collapsed"
|
||||||
|
:collapsible="true"></side-menu>
|
||||||
|
</template>
|
||||||
|
<!-- 下次优化这些代码 -->
|
||||||
|
<template v-else>
|
||||||
|
<a-drawer
|
||||||
|
v-if="device === 'mobile'"
|
||||||
|
:wrapClassName="'drawer-sider ' + navTheme"
|
||||||
|
placement="left"
|
||||||
|
@close="() => this.collapsed = false"
|
||||||
|
:closable="false"
|
||||||
|
:visible="collapsed"
|
||||||
|
width="200px"
|
||||||
|
>
|
||||||
|
<side-menu
|
||||||
|
mode="inline"
|
||||||
|
:menus="menus"
|
||||||
|
@menuSelect="menuSelect"
|
||||||
|
:theme="navTheme"
|
||||||
|
:collapsed="false"
|
||||||
|
:collapsible="true"></side-menu>
|
||||||
|
</a-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<a-layout
|
||||||
|
:class="[layoutMode, `content-width-${contentWidth}`]"
|
||||||
|
:style="{ paddingLeft: fixSiderbar && isDesktop() ? `${sidebarOpened ? 200 : 80}px` : '0' }">
|
||||||
|
<!-- layout header -->
|
||||||
|
<global-header
|
||||||
|
:mode="layoutMode"
|
||||||
|
:menus="menus"
|
||||||
|
:theme="navTheme"
|
||||||
|
:collapsed="collapsed"
|
||||||
|
:device="device"
|
||||||
|
@toggle="toggle"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- layout content -->
|
||||||
|
<a-layout-content :style="{ height: '100%', paddingTop: fixedHeader ? '59px' : '0' }">
|
||||||
|
<slot></slot>
|
||||||
|
</a-layout-content>
|
||||||
|
|
||||||
|
<!-- layout footer -->
|
||||||
|
<!-- <a-layout-footer style="padding: 0px">-->
|
||||||
|
<!-- <global-footer />-->
|
||||||
|
<!-- </a-layout-footer>-->
|
||||||
|
</a-layout>
|
||||||
|
|
||||||
|
<!-- update-start---- author:os_chengtgen -- date:20190830 -- for:issues/463 -编译主题颜色已生效,但还一直转圈,显示主题 正在编译 ---- -->
|
||||||
|
<!--<setting-drawer></setting-drawer>-->
|
||||||
|
<!-- update-end---- author:os_chengtgen -- date:20190830 -- for:issues/463 -编译主题颜色已生效,但还一直转圈,显示主题 正在编译 ---- -->
|
||||||
|
</a-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SideMenu from "@/components/menu/SideMenu";
|
||||||
|
import GlobalHeader from "@/components/page/GlobalHeader";
|
||||||
|
import GlobalFooter from "@/components/page/GlobalFooter";
|
||||||
|
// update-start---- author:os_chengtgen -- date:20190830 -- for:issues/463 -编译主题颜色已生效,但还一直转圈,显示主题 正在编译 ------
|
||||||
|
// import SettingDrawer from '@/components/setting/SettingDrawer'
|
||||||
|
// 注释这个因为在个人设置模块已经加载了SettingDrawer页面
|
||||||
|
// update-end ---- author:os_chengtgen -- date:20190830 -- for:issues/463 -编译主题颜色已生效,但还一直转圈,显示主题 正在编译 ------
|
||||||
|
|
||||||
|
import { triggerWindowResizeEvent } from "@/utils/util";
|
||||||
|
import { mapState, mapActions } from "vuex";
|
||||||
|
import { mixin, mixinDevice } from "@/utils/mixin.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "GlobalLayout",
|
||||||
|
components: {
|
||||||
|
SideMenu,
|
||||||
|
GlobalHeader,
|
||||||
|
GlobalFooter
|
||||||
|
// update-start---- author:os_chengtgen -- date:20190830 -- for:issues/463 -编译主题颜色已生效,但还一直转圈,显示主题 正在编译 ------
|
||||||
|
// // SettingDrawer
|
||||||
|
// 注释这个因为在个人设置模块已经加载了SettingDrawer页面
|
||||||
|
// update-end ---- author:os_chengtgen -- date:20190830 -- for:issues/463 -编译主题颜色已生效,但还一直转圈,显示主题 正在编译 ------
|
||||||
|
|
||||||
|
},
|
||||||
|
mixins: [mixin, mixinDevice],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
collapsed: false,
|
||||||
|
activeMenu: {},
|
||||||
|
menus: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
// 主路由
|
||||||
|
mainRouters: state => state.permission.addRouters,
|
||||||
|
// 后台菜单
|
||||||
|
permissionMenuList: state => state.user.permissionList
|
||||||
|
})
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
sidebarOpened(val) {
|
||||||
|
this.collapsed = !val;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
//--update-begin----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
|
||||||
|
//this.menus = this.mainRouters.find((item) => item.path === '/').children;
|
||||||
|
this.menus = this.permissionMenuList;
|
||||||
|
|
||||||
|
//--update-begin----author:liusq---date:20210223------for:关于测边菜单遮挡内容问题详细说明 #2255
|
||||||
|
this.collapsed = !this.sidebarOpened;
|
||||||
|
//--update-begin----author:liusq---date:20210223------for:关于测边菜单遮挡内容问题详细说明 #2255
|
||||||
|
|
||||||
|
// 根据后台配置菜单,重新排序加载路由信息
|
||||||
|
//console.log('----加载菜单逻辑----')
|
||||||
|
//console.log(this.mainRouters)
|
||||||
|
//console.log(this.permissionMenuList)
|
||||||
|
//console.log('----navTheme------'+this.navTheme)
|
||||||
|
//--update-end----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(["setSidebar"]),
|
||||||
|
toggle() {
|
||||||
|
this.collapsed = !this.collapsed;
|
||||||
|
this.setSidebar(!this.collapsed);
|
||||||
|
triggerWindowResizeEvent();
|
||||||
|
},
|
||||||
|
menuSelect() {
|
||||||
|
if (!this.isDesktop()) {
|
||||||
|
this.collapsed = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//update-begin-author:taoyan date:20190430 for:动态路由title显示配置的菜单title而不是其对应路由的title
|
||||||
|
myMenuSelect(value) {
|
||||||
|
//此处触发动态路由被点击事件
|
||||||
|
this.findMenuBykey(this.menus, value.key);
|
||||||
|
this.$emit("dynamicRouterShow", value.key, this.activeMenu.meta.title);
|
||||||
|
// update-begin-author:sunjianlei date:20191223 for: 修复刷新后菜单Tab名字显示异常
|
||||||
|
let storeKey = "route:title:" + this.activeMenu.path;
|
||||||
|
this.$ls.set(storeKey, this.activeMenu.meta.title);
|
||||||
|
// update-end-author:sunjianlei date:20191223 for: 修复刷新后菜单Tab名字显示异常
|
||||||
|
},
|
||||||
|
findMenuBykey(menus, key) {
|
||||||
|
for (let i of menus) {
|
||||||
|
if (i.path == key) {
|
||||||
|
this.activeMenu = { ...i };
|
||||||
|
} else if (i.children && i.children.length > 0) {
|
||||||
|
this.findMenuBykey(i.children, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//update-end-author:taoyan date:20190430 for:动态路由title显示配置的菜单title而不是其对应路由的title
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
body {
|
||||||
|
// 打开滚动条:有即显示,无则不显示
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
&.colorWeak {
|
||||||
|
filter: invert(80%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout {
|
||||||
|
min-height: 100vh !important;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
&.mobile {
|
||||||
|
|
||||||
|
.ant-layout-content {
|
||||||
|
|
||||||
|
.content {
|
||||||
|
margin: 24px 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ant-table-wrapper
|
||||||
|
* 覆盖的表格手机模式样式,如果想修改在手机上表格最低宽度,可以在这里改动
|
||||||
|
*/
|
||||||
|
|
||||||
|
.ant-table-wrapper {
|
||||||
|
.ant-table-content {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-body {
|
||||||
|
min-width: 800px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidemenu {
|
||||||
|
.ant-header-fixedHeader {
|
||||||
|
|
||||||
|
&.ant-header-side-opened, &.ant-header-side-closed {
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.topmenu {
|
||||||
|
/* 必须为 topmenu 才能启用流式布局 */
|
||||||
|
|
||||||
|
&.content-width-Fluid {
|
||||||
|
.header-index-wide {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header, .top-nav-header-index {
|
||||||
|
.user-wrapper .action {
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ant-layout-has-sider {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trigger {
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 42px;
|
||||||
|
padding: 0 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 300ms, background 300ms;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.topmenu {
|
||||||
|
.ant-header-fixedHeader {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 9;
|
||||||
|
width: 100%;
|
||||||
|
transition: width .2s;
|
||||||
|
|
||||||
|
&.ant-header-side-opened {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ant-header-side-closed {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 必须为 topmenu 才能启用流式布局 */
|
||||||
|
|
||||||
|
&.content-width-Fluid {
|
||||||
|
.header-index-wide {
|
||||||
|
max-width: unset;
|
||||||
|
margin-left: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header-index-wide {
|
||||||
|
max-width: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidemenu {
|
||||||
|
.ant-header-fixedHeader {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 9;
|
||||||
|
width: 100%;
|
||||||
|
transition: width .2s;
|
||||||
|
|
||||||
|
&.ant-header-side-opened {
|
||||||
|
width: calc(100% - 200px)
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ant-header-side-closed {
|
||||||
|
width: calc(100% - 80px)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
height: 64px;
|
||||||
|
padding: 0 12px 0 0;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header, .top-nav-header-index {
|
||||||
|
|
||||||
|
.user-wrapper {
|
||||||
|
float: right;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.action {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 14px;
|
||||||
|
display: inline-block;
|
||||||
|
transition: all .3s;
|
||||||
|
|
||||||
|
height: 70%;
|
||||||
|
line-height: 46px;
|
||||||
|
|
||||||
|
&.action-full {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
margin: 20px 10px 20px 0;
|
||||||
|
color: #1890ff;
|
||||||
|
background: hsla(0, 0%, 100%, .85);
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anticon {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dark {
|
||||||
|
.user-wrapper {
|
||||||
|
|
||||||
|
.action {
|
||||||
|
color: black;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.anticon {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mobile {
|
||||||
|
.top-nav-header-index {
|
||||||
|
|
||||||
|
.header-index-wide {
|
||||||
|
|
||||||
|
.header-index-left {
|
||||||
|
|
||||||
|
.trigger {
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo.top-nav-header {
|
||||||
|
text-align: center;
|
||||||
|
width: 56px;
|
||||||
|
line-height: 58px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-wrapper .action .avatar {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.light {
|
||||||
|
|
||||||
|
.header-index-wide {
|
||||||
|
|
||||||
|
.header-index-left {
|
||||||
|
.trigger {
|
||||||
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tablet {
|
||||||
|
// overflow: hidden; text-overflow:ellipsis; white-space: nowrap;
|
||||||
|
.top-nav-header-index {
|
||||||
|
|
||||||
|
.header-index-wide {
|
||||||
|
|
||||||
|
.header-index-left {
|
||||||
|
.logo > a {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav-header-index {
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
|
||||||
|
position: relative;
|
||||||
|
transition: background .3s, width .2s;
|
||||||
|
|
||||||
|
.header-index-wide {
|
||||||
|
width: 100%;
|
||||||
|
margin: auto;
|
||||||
|
padding: 0 20px 0 0;
|
||||||
|
display: flex;
|
||||||
|
height: 59px;
|
||||||
|
|
||||||
|
.ant-menu.ant-menu-horizontal {
|
||||||
|
border: none;
|
||||||
|
height: 64px;
|
||||||
|
line-height: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-index-left {
|
||||||
|
flex: 1 1;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.logo.top-nav-header {
|
||||||
|
width: 165px;
|
||||||
|
height: 64px;
|
||||||
|
position: relative;
|
||||||
|
line-height: 64px;
|
||||||
|
transition: all .3s;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
img {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #fff;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0 0 0 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-index-right {
|
||||||
|
float: right;
|
||||||
|
height: 59px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.action:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.light {
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
.header-index-wide {
|
||||||
|
.header-index-left {
|
||||||
|
.logo {
|
||||||
|
h1 {
|
||||||
|
color: #002140;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dark {
|
||||||
|
|
||||||
|
.user-wrapper {
|
||||||
|
|
||||||
|
.action {
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-index-wide .header-index-left .trigger:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内容区
|
||||||
|
.layout-content {
|
||||||
|
margin: 24px 24px 0px;
|
||||||
|
height: 64px;
|
||||||
|
padding: 0 12px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.topmenu {
|
||||||
|
.page-header-index-wide {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// drawer-sider 自定义
|
||||||
|
.ant-drawer.drawer-sider {
|
||||||
|
.sider {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dark {
|
||||||
|
.ant-drawer-content {
|
||||||
|
background-color: rgb(0, 21, 41);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.light {
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
.ant-drawer-content {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-drawer-body {
|
||||||
|
padding: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 菜单样式
|
||||||
|
.sider {
|
||||||
|
box-shadow: 2px 116px 6px 0 rgba(0, 21, 41, .35);
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
&.ant-fixed-sidemenu {
|
||||||
|
position: fixed;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
height: 64px;
|
||||||
|
position: relative;
|
||||||
|
line-height: 64px;
|
||||||
|
padding-left: 24px;
|
||||||
|
-webkit-transition: all .3s;
|
||||||
|
transition: all .3s;
|
||||||
|
background: #002140;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
img, h1 {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 18px;
|
||||||
|
margin: 0 0 0 8px;
|
||||||
|
font-family: "Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.light {
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 2px 116px 8px 0 rgba(29, 35, 41, 0.05);
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 1px 1px 0 0 #e8e8e8;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu-light {
|
||||||
|
border-right-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 外置的样式控制
|
||||||
|
.user-dropdown-menu-wrapper.ant-dropdown-menu {
|
||||||
|
padding: 4px 0;
|
||||||
|
|
||||||
|
.ant-dropdown-menu-item {
|
||||||
|
width: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-dropdown-menu-item > .anticon:first-child,
|
||||||
|
.ant-dropdown-menu-item > a > .anticon:first-child,
|
||||||
|
.ant-dropdown-menu-submenu-title > .anticon:first-child
|
||||||
|
.ant-dropdown-menu-submenu-title > a > .anticon:first-child {
|
||||||
|
min-width: 12px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据列表 样式
|
||||||
|
.table-alert {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-page-search-wrapper {
|
||||||
|
|
||||||
|
.ant-form-inline {
|
||||||
|
|
||||||
|
.ant-form-item {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
margin-right: 0;
|
||||||
|
|
||||||
|
.ant-form-item-control-wrapper {
|
||||||
|
flex: 1 1;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .ant-form-item-label {
|
||||||
|
line-height: 32px;
|
||||||
|
padding-right: 8px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-form-item-control {
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-page-search-submitButtons {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
|
||||||
|
.table-operator {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="page-header-index-wide">
|
||||||
|
<a-breadcrumb class="breadcrumb">
|
||||||
|
<a-breadcrumb-item v-for="(item, index) in breadList" :key="index">
|
||||||
|
<router-link v-if="item.name != name" :to="{ path: item.path }">
|
||||||
|
{{ item.meta.title }}
|
||||||
|
</router-link>
|
||||||
|
<span v-else>{{ item.meta.title }}</span>
|
||||||
|
</a-breadcrumb-item>
|
||||||
|
</a-breadcrumb>
|
||||||
|
|
||||||
|
<div class="detail">
|
||||||
|
<div class="main" v-if="!$route.meta.hiddenHeaderContent">
|
||||||
|
<div class="row">
|
||||||
|
<img v-if="logo" :src="logo" class="logo"/>
|
||||||
|
<h1 v-if="title" class="title">{{ title }}</h1>
|
||||||
|
<div class="action">
|
||||||
|
<slot name="action"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div v-if="avatar" class="avatar">
|
||||||
|
<a-avatar :src="avatar"/>
|
||||||
|
</div>
|
||||||
|
<div v-if="this.$slots.content" class="headerContent">
|
||||||
|
<slot name="content"></slot>
|
||||||
|
</div>
|
||||||
|
<div v-if="this.$slots.extra" class="extra">
|
||||||
|
<slot name="extra"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<slot name="pageMenu"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Breadcrumb from '@/components/tools/Breadcrumb'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "PageHeader",
|
||||||
|
components: {
|
||||||
|
"s-breadcrumb": Breadcrumb
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
breadcrumb: {
|
||||||
|
type: Array,
|
||||||
|
default: null,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
logo: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
name: '',
|
||||||
|
breadList: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getBreadcrumb()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getBreadcrumb() {
|
||||||
|
|
||||||
|
this.breadList = []
|
||||||
|
// this.breadList.push({name: 'index', path: '/dashboard/', meta: {title: '首页'}})
|
||||||
|
|
||||||
|
this.name = this.$route.name
|
||||||
|
this.$route.matched.forEach((item) => {
|
||||||
|
// item.name !== 'index' && this.breadList.push(item)
|
||||||
|
this.breadList.push(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route() {
|
||||||
|
this.getBreadcrumb()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
background: #fff;
|
||||||
|
padding: 16px 32px 0;
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail {
|
||||||
|
display: flex;
|
||||||
|
/*margin-bottom: 16px;*/
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
flex: 0 1 72px;
|
||||||
|
margin: 0 24px 8px 0;
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
border-radius: 72px;
|
||||||
|
display: block;
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
width: 100%;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 28px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: rgba(0,0,0,.85);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
flex: auto;
|
||||||
|
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
.content, .headerContent {
|
||||||
|
flex: auto;
|
||||||
|
color: rgba(0,0,0,.45);
|
||||||
|
line-height: 22px;
|
||||||
|
|
||||||
|
.link {
|
||||||
|
margin-top: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.extra {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
margin-left: 88px;
|
||||||
|
min-width: 242px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.action {
|
||||||
|
margin-left: 56px;
|
||||||
|
min-width: 266px;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
text-align: right;
|
||||||
|
&:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile .page-header {
|
||||||
|
|
||||||
|
.main {
|
||||||
|
|
||||||
|
.row {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
flex: 0 1 25%;
|
||||||
|
margin: 0 2% 8px 0;
|
||||||
|
}
|
||||||
|
.content, .headerContent {
|
||||||
|
flex: 0 1 70%;
|
||||||
|
|
||||||
|
.link {
|
||||||
|
margin-top: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.extra {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
margin-left: 0;
|
||||||
|
min-width: 0;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.action {
|
||||||
|
margin-left: unset;
|
||||||
|
min-width: 266px;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
&:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
<template>
|
||||||
|
<div :style="!$route.meta.pageHeader ? 'margin: -10px -24px 0;' : null">
|
||||||
|
<!-- pageHeader , route meta hideHeader:true on hide -->
|
||||||
|
<page-header v-if="!$route.meta.pageHeader" :title="title" :logo="logo" :avatar="avatar">
|
||||||
|
<slot slot="action" name="action"></slot>
|
||||||
|
<slot slot="content" name="headerContent"></slot>
|
||||||
|
<div slot="content" v-if="!this.$slots.headerContent && desc">
|
||||||
|
<p style="font-size: 14px;color: rgba(0,0,0,.65)">{{ desc }}</p>
|
||||||
|
<div class="link">
|
||||||
|
<template v-for="(link, index) in linkList">
|
||||||
|
<a :key="index" :href="link.href">
|
||||||
|
<a-icon :type="link.icon"/>
|
||||||
|
<span>{{ link.title }}</span>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<slot slot="extra" name="extra"></slot>
|
||||||
|
<div slot="pageMenu">
|
||||||
|
<div class="page-menu-search" v-if="search">
|
||||||
|
<a-input-search style="width: 80%; max-width: 522px;" placeholder="请输入..." size="large" enterButton="搜索" />
|
||||||
|
</div>
|
||||||
|
<div class="page-menu-tabs" v-if="tabs && tabs.items">
|
||||||
|
<!-- @change="callback" :activeKey="activeKey" -->
|
||||||
|
<a-tabs :tabBarStyle="{margin: 0}" @change="tabs.callback" :activeKey="tabs.active()">
|
||||||
|
<a-tab-pane v-for="item in tabs.items" :tab="item.title" :key="item.key"></a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</page-header>
|
||||||
|
<div class="content">
|
||||||
|
<div :class="['page-header-index-wide']">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageHeader from './PageHeader'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "LayoutContent",
|
||||||
|
components: {
|
||||||
|
PageHeader
|
||||||
|
},
|
||||||
|
// ['desc', 'logo', 'title', 'avatar', 'linkList', 'extraImage']
|
||||||
|
props: {
|
||||||
|
desc: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
logo: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
linkList: {
|
||||||
|
type: Array,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
extraImage: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
tabs: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.content {
|
||||||
|
margin: 24px 24px 0;
|
||||||
|
|
||||||
|
.link {
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
&:not(:empty) {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
margin-right: 32px;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-right: 8px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.page-menu-search {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.page-menu-tabs {
|
||||||
|
margin-top: 48px;
|
||||||
|
}
|
||||||
|
.page-header[data-v-6740ec88] {
|
||||||
|
margin: 0px 24px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<a-popover trigger="click" placement="bottomRight" :overlayStyle="{ width: '300px' }">
|
||||||
|
<template slot="content">
|
||||||
|
<a-spin :spinning="loadding">
|
||||||
|
<a-tabs>
|
||||||
|
<a-tab-pane v-for="(tab, k) in tabs" :tab="tab.title" :key="k">
|
||||||
|
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
<span @click="fetchNotice" class="header-notice">
|
||||||
|
<a-badge count="12">
|
||||||
|
<a-icon style="font-size: 16px; padding: 4px" type="bell" />
|
||||||
|
</a-badge>
|
||||||
|
</span>
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "HeaderNotice",
|
||||||
|
props: {
|
||||||
|
tabs: {
|
||||||
|
type: Array,
|
||||||
|
default: null,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loadding: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchNotice () {
|
||||||
|
if (this.loadding) {
|
||||||
|
this.loadding = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loadding = true
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loadding = false
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.header-notice{
|
||||||
|
display: inline-block;
|
||||||
|
transition: all 0.3s;
|
||||||
|
span {
|
||||||
|
vertical-align: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,330 @@
|
|||||||
|
<template>
|
||||||
|
<div class="setting-drawer">
|
||||||
|
<a-drawer
|
||||||
|
width="300"
|
||||||
|
placement="right"
|
||||||
|
:closable="false"
|
||||||
|
@close="onClose"
|
||||||
|
:visible="visible"
|
||||||
|
style="height: 100%;overflow: auto;"
|
||||||
|
>
|
||||||
|
<div class="setting-drawer-index-content">
|
||||||
|
|
||||||
|
<div :style="{ marginBottom: '24px' }">
|
||||||
|
<h3 class="setting-drawer-index-title">整体风格设置</h3>
|
||||||
|
|
||||||
|
<div class="setting-drawer-index-blockChecbox">
|
||||||
|
<a-tooltip>
|
||||||
|
<template slot="title">
|
||||||
|
暗色菜单风格
|
||||||
|
</template>
|
||||||
|
<div class="setting-drawer-index-item" @click="handleMenuTheme('dark')">
|
||||||
|
<img src="https://gw.alipayobjects.com/zos/rmsportal/LCkqqYNmvBEbokSDscrm.svg" alt="dark">
|
||||||
|
<div class="setting-drawer-index-selectIcon" v-if="navTheme === 'dark'">
|
||||||
|
<a-icon type="check" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
|
||||||
|
<a-tooltip>
|
||||||
|
<template slot="title">
|
||||||
|
亮色菜单风格
|
||||||
|
</template>
|
||||||
|
<div class="setting-drawer-index-item" @click="handleMenuTheme('light')">
|
||||||
|
<img src="https://gw.alipayobjects.com/zos/rmsportal/jpRkZQMyYRryryPNtyIC.svg" alt="light">
|
||||||
|
<div class="setting-drawer-index-selectIcon" v-if="navTheme !== 'dark'">
|
||||||
|
<a-icon type="check" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div :style="{ marginBottom: '24px' }">
|
||||||
|
<h3 class="setting-drawer-index-title">主题色</h3>
|
||||||
|
|
||||||
|
<div style="height: 20px">
|
||||||
|
<a-tooltip class="setting-drawer-theme-color-colorBlock" v-for="(item, index) in colorList" :key="index">
|
||||||
|
<template slot="title">
|
||||||
|
{{ item.key }}
|
||||||
|
</template>
|
||||||
|
<a-tag :color="item.color" @click="changeColor(item.color)">
|
||||||
|
<a-icon type="check" v-if="item.color === primaryColor"></a-icon>
|
||||||
|
</a-tag>
|
||||||
|
</a-tooltip>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-divider />
|
||||||
|
|
||||||
|
<div :style="{ marginBottom: '24px' }">
|
||||||
|
<h3 class="setting-drawer-index-title">导航模式</h3>
|
||||||
|
|
||||||
|
<div class="setting-drawer-index-blockChecbox">
|
||||||
|
<a-tooltip>
|
||||||
|
<template slot="title">
|
||||||
|
侧边栏导航
|
||||||
|
</template>
|
||||||
|
<div class="setting-drawer-index-item" @click="handleLayout('sidemenu')">
|
||||||
|
<img src="https://gw.alipayobjects.com/zos/rmsportal/JopDzEhOqwOjeNTXkoje.svg" alt="sidemenu">
|
||||||
|
<div class="setting-drawer-index-selectIcon" v-if="layoutMode === 'sidemenu'">
|
||||||
|
<a-icon type="check" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
|
||||||
|
<a-tooltip>
|
||||||
|
<template slot="title">
|
||||||
|
顶部栏导航
|
||||||
|
</template>
|
||||||
|
<div class="setting-drawer-index-item" @click="handleLayout('topmenu')">
|
||||||
|
<img src="https://gw.alipayobjects.com/zos/rmsportal/KDNDBbriJhLwuqMoxcAr.svg" alt="topmenu">
|
||||||
|
<div class="setting-drawer-index-selectIcon" v-if="layoutMode !== 'sidemenu'">
|
||||||
|
<a-icon type="check" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<div :style="{ marginTop: '24px' }">
|
||||||
|
<a-list :split="false">
|
||||||
|
<a-list-item>
|
||||||
|
<a-tooltip slot="actions">
|
||||||
|
<template slot="title">
|
||||||
|
该设定仅 [顶部栏导航] 时有效
|
||||||
|
</template>
|
||||||
|
<a-select size="small" style="width: 80px;" :defaultValue="contentWidth"
|
||||||
|
@change="handleContentWidthChange">
|
||||||
|
<a-select-option value="Fixed">固定</a-select-option>
|
||||||
|
<a-select-option value="Fluid" v-if="layoutMode !== 'sidemenu'">流式</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-list-item-meta>
|
||||||
|
<div slot="title">内容区域宽度</div>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
<a-list-item>
|
||||||
|
<a-switch slot="actions" size="small" :defaultChecked="fixedHeader" @change="handleFixedHeader" />
|
||||||
|
<a-list-item-meta>
|
||||||
|
<div slot="title">固定 Header</div>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
<a-list-item>
|
||||||
|
<a-switch slot="actions" size="small" :disabled="!fixedHeader" :defaultChecked="autoHideHeader"
|
||||||
|
@change="handleFixedHeaderHidden" />
|
||||||
|
<a-list-item-meta>
|
||||||
|
<div slot="title" :style="{ textDecoration: !fixedHeader ? 'line-through' : 'unset' }">下滑时隐藏 Header
|
||||||
|
</div>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
<a-list-item>
|
||||||
|
<a-switch slot="actions" size="small" :disabled="(layoutMode === 'topmenu')" :checked="dataFixSiderbar"
|
||||||
|
@change="handleFixSiderbar" />
|
||||||
|
<a-list-item-meta>
|
||||||
|
<div slot="title" :style="{ textDecoration: layoutMode === 'topmenu' ? 'line-through' : 'unset' }">
|
||||||
|
固定侧边菜单
|
||||||
|
</div>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
</a-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-divider />
|
||||||
|
|
||||||
|
<div :style="{ marginBottom: '24px' }">
|
||||||
|
<h3 class="setting-drawer-index-title">其他设置</h3>
|
||||||
|
<div>
|
||||||
|
<a-list :split="false">
|
||||||
|
<a-list-item>
|
||||||
|
<a-switch slot="actions" size="small" :defaultChecked="colorWeak" @change="onColorWeak" />
|
||||||
|
<a-list-item-meta>
|
||||||
|
<div slot="title">色弱模式</div>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
<a-list-item>
|
||||||
|
<a-switch slot="actions" size="small" :defaultChecked="multipage" @change="onMultipageWeak" />
|
||||||
|
<a-list-item-meta>
|
||||||
|
<div slot="title">多页签模式</div>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
</a-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-divider />
|
||||||
|
<div :style="{ marginBottom: '24px' }">
|
||||||
|
<a-alert type="warning">
|
||||||
|
<span slot="message">
|
||||||
|
配置栏只在开发环境用于预览,生产环境不会展现,请手动修改配置文件
|
||||||
|
<a href="https://github.com/sendya/ant-design-pro-vue/blob/master/src/defaultSettings.js" target="_blank">src/defaultSettings.js</a>
|
||||||
|
</span>
|
||||||
|
</a-alert>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-drawer-index-handle" @click="toggle" v-if="visible">
|
||||||
|
<!-- <a-icon type="setting" v-if="!visible"/>-->
|
||||||
|
<!-- <a-icon type="close" v-else/>-->
|
||||||
|
<a-icon type="close" />
|
||||||
|
</div>
|
||||||
|
</a-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import DetailList from "@/components/tools/DetailList";
|
||||||
|
import SettingItem from "@/components/setting/SettingItem";
|
||||||
|
import config from "@/defaultSettings";
|
||||||
|
import { updateTheme, updateColorWeak, colorList } from "@/components/tools/setting";
|
||||||
|
import { mixin, mixinDevice } from "@/utils/mixin.js";
|
||||||
|
import { triggerWindowResizeEvent } from "@/utils/util";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
DetailList,
|
||||||
|
SettingItem
|
||||||
|
},
|
||||||
|
mixins: [mixin, mixinDevice],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
colorList,
|
||||||
|
dataFixSiderbar: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// 当主题色不是默认色时,才进行主题编译
|
||||||
|
if (this.primaryColor !== config.primaryColor) {
|
||||||
|
updateTheme(this.primaryColor);
|
||||||
|
}
|
||||||
|
if (this.colorWeak !== config.colorWeak) {
|
||||||
|
updateColorWeak(this.colorWeak);
|
||||||
|
}
|
||||||
|
if (this.multipage !== config.multipage) {
|
||||||
|
this.$store.dispatch("ToggleMultipage", this.multipage);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showDrawer() {
|
||||||
|
this.visible = true;
|
||||||
|
},
|
||||||
|
onClose() {
|
||||||
|
this.visible = false;
|
||||||
|
},
|
||||||
|
toggle() {
|
||||||
|
this.visible = !this.visible;
|
||||||
|
},
|
||||||
|
onColorWeak(checked) {
|
||||||
|
this.$store.dispatch("ToggleWeak", checked);
|
||||||
|
updateColorWeak(checked);
|
||||||
|
},
|
||||||
|
onMultipageWeak(checked) {
|
||||||
|
this.$store.dispatch("ToggleMultipage", checked);
|
||||||
|
},
|
||||||
|
handleMenuTheme(theme) {
|
||||||
|
this.$store.dispatch("ToggleTheme", theme);
|
||||||
|
},
|
||||||
|
handleLayout(mode) {
|
||||||
|
this.$store.dispatch("ToggleLayoutMode", mode);
|
||||||
|
// 因为顶部菜单不能固定左侧菜单栏,所以强制关闭
|
||||||
|
this.handleFixSiderbar(false);
|
||||||
|
// 触发窗口resize事件
|
||||||
|
triggerWindowResizeEvent();
|
||||||
|
},
|
||||||
|
handleContentWidthChange(type) {
|
||||||
|
this.$store.dispatch("ToggleContentWidth", type);
|
||||||
|
},
|
||||||
|
changeColor(color) {
|
||||||
|
if (this.primaryColor !== color) {
|
||||||
|
this.$store.dispatch("ToggleColor", color);
|
||||||
|
updateTheme(color);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleFixedHeader(fixed) {
|
||||||
|
this.$store.dispatch("ToggleFixedHeader", fixed);
|
||||||
|
},
|
||||||
|
handleFixedHeaderHidden(autoHidden) {
|
||||||
|
this.$store.dispatch("ToggleFixedHeaderHidden", autoHidden);
|
||||||
|
},
|
||||||
|
handleFixSiderbar(fixed) {
|
||||||
|
if (this.layoutMode === "topmenu") {
|
||||||
|
fixed = false;
|
||||||
|
}
|
||||||
|
this.dataFixSiderbar = fixed;
|
||||||
|
this.$store.dispatch("ToggleFixSiderbar", fixed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
.setting-drawer-index-content {
|
||||||
|
|
||||||
|
.setting-drawer-index-blockChecbox {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.setting-drawer-index-item {
|
||||||
|
margin-right: 16px;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-drawer-index-selectIcon {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 15px;
|
||||||
|
padding-left: 24px;
|
||||||
|
height: 100%;
|
||||||
|
color: #1890ff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-drawer-theme-color-colorBlock {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 2px;
|
||||||
|
float: left;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 8px;
|
||||||
|
padding-left: 0px;
|
||||||
|
padding-right: 0px;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 700;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-drawer-index-handle {
|
||||||
|
position: absolute;
|
||||||
|
top: 240px;
|
||||||
|
background: #1890ff;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
right: 300px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
pointer-events: auto;
|
||||||
|
z-index: 1001;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<div class="setting-drawer-index-item">
|
||||||
|
<h3 class="setting-drawer-index-title">{{ title }}</h3>
|
||||||
|
<slot></slot>
|
||||||
|
<a-divider v-if="divider"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "SettingItem",
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
divider: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
.setting-drawer-index-item {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.setting-drawer-index-title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: rgba(0, 0, 0, .85);
|
||||||
|
line-height: 22px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<a-breadcrumb class="breadcrumb">
|
||||||
|
<a-breadcrumb-item v-for="(item, index) in breadList" :key="index">
|
||||||
|
<router-link v-if="item.name != name" :to="{ path: item.path }">
|
||||||
|
{{ item.meta.title }}
|
||||||
|
</router-link>
|
||||||
|
<span v-else>{{ item.meta.title }}</span>
|
||||||
|
</a-breadcrumb-item>
|
||||||
|
</a-breadcrumb>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
name: '',
|
||||||
|
breadList: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.getBreadcrumb()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getBreadcrumb() {
|
||||||
|
|
||||||
|
console.log('this.$route.matched', this.$route.matched)
|
||||||
|
|
||||||
|
this.breadList = []
|
||||||
|
this.breadList.push({ name: 'dashboard-analysis', path: '/dashboard/analysis', meta: { title: '首页' } })
|
||||||
|
|
||||||
|
this.name = this.$route.name
|
||||||
|
this.$route.matched.forEach((item) => {
|
||||||
|
// item.meta.name === 'dashboard' ? item.path = '/dashboard' : this.$route.path === item.path
|
||||||
|
this.breadList.push(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route() {
|
||||||
|
this.getBreadcrumb()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:title="currTitle"
|
||||||
|
:width="450"
|
||||||
|
:visible="visible"
|
||||||
|
:closable="false"
|
||||||
|
:maskClosable="closable">
|
||||||
|
<template slot="footer">
|
||||||
|
<a-button v-if="closable" @click="close">关闭</a-button>
|
||||||
|
<a-button type="primary" @click="departOk">确认</a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<a-form>
|
||||||
|
<a-form-item
|
||||||
|
:labelCol="{span:4}"
|
||||||
|
:wrapperCol="{span:20}"
|
||||||
|
style="margin-bottom:10px"
|
||||||
|
:validate-status="validate_status">
|
||||||
|
<a-tooltip placement="topLeft" >
|
||||||
|
<template slot="title">
|
||||||
|
<span>您隶属于多部门,请选择当前所在部门</span>
|
||||||
|
</template>
|
||||||
|
<a-avatar style="backgroundColor:#87d068" icon="gold" />
|
||||||
|
</a-tooltip>
|
||||||
|
<a-select v-model="departSelected" :class="{'valid-error':validate_status=='error'}" placeholder="请选择登录部门" style="margin-left:10px;width: 80%">
|
||||||
|
<a-icon slot="suffixIcon" type="gold" />
|
||||||
|
<a-select-option
|
||||||
|
v-for="d in departList"
|
||||||
|
:key="d.id"
|
||||||
|
:value="d.orgCode">
|
||||||
|
{{ d.departName }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getAction,putAction } from '@/api/manage'
|
||||||
|
import Vue from 'vue'
|
||||||
|
import store from '@/store/'
|
||||||
|
import { USER_INFO } from "@/store/mutation-types"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DepartSelect',
|
||||||
|
props:{
|
||||||
|
title:{
|
||||||
|
type:String,
|
||||||
|
default:"部门选择",
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
closable:{
|
||||||
|
type:Boolean,
|
||||||
|
default:false,
|
||||||
|
required:false
|
||||||
|
},
|
||||||
|
username:{
|
||||||
|
type:String,
|
||||||
|
default:"",
|
||||||
|
required:false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch:{
|
||||||
|
username(val){
|
||||||
|
if(val){
|
||||||
|
this.loadDepartList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
currTitle:this.title,
|
||||||
|
visible:false,
|
||||||
|
departList:[],
|
||||||
|
departSelected:"",
|
||||||
|
validate_status:"",
|
||||||
|
currDepartName:"",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created(){
|
||||||
|
//this.loadDepartList()
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
loadDepartList(){
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let url = "/sys/user/getCurrentUserDeparts"
|
||||||
|
this.currDepartName=''
|
||||||
|
getAction(url).then(res=>{
|
||||||
|
if(res.success){
|
||||||
|
let departs = res.result.list
|
||||||
|
let orgCode = res.result.orgCode
|
||||||
|
if(departs && departs.length>0){
|
||||||
|
for(let i of departs){
|
||||||
|
if(i.orgCode == orgCode){
|
||||||
|
this.currDepartName = i.departName
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.departSelected = orgCode
|
||||||
|
this.departList = departs
|
||||||
|
if(this.currDepartName){
|
||||||
|
this.currTitle ="部门切换(当前部门 : "+this.currDepartName+")"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
close(){
|
||||||
|
this.departClear()
|
||||||
|
},
|
||||||
|
departOk(){
|
||||||
|
if(!this.departSelected){
|
||||||
|
this.validate_status='error'
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let obj = {
|
||||||
|
orgCode:this.departSelected,
|
||||||
|
username:this.username
|
||||||
|
}
|
||||||
|
putAction("/sys/selectDepart",obj).then(res=>{
|
||||||
|
if(res.success){
|
||||||
|
const userInfo = res.result.userInfo;
|
||||||
|
Vue.ls.set(USER_INFO, userInfo, 7 * 24 * 60 * 60 * 1000);
|
||||||
|
store.commit('SET_INFO', userInfo);
|
||||||
|
//console.log("---切换组织机构---userInfo-------",store.getters.userInfo.orgCode);
|
||||||
|
this.departClear()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
show(){
|
||||||
|
//如果组件传值username此处就不用loadDepartList了
|
||||||
|
this.loadDepartList().then(()=>{
|
||||||
|
this.visible=true
|
||||||
|
if(!this.departList || this.departList.length<=0){
|
||||||
|
this.$message.warning("您尚未设置部门信息!")
|
||||||
|
this.departClear()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
departClear(){
|
||||||
|
this.departList=[]
|
||||||
|
this.departSelected=""
|
||||||
|
this.visible=false
|
||||||
|
this.validate_status=''
|
||||||
|
this.currDepartName=""
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.valid-error .ant-select-selection__placeholder{
|
||||||
|
color: #f5222d;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="['detail-list', size, layout === 'vertical' ? 'vertical': 'horizontal']">
|
||||||
|
<div v-if="title" class="title">{{ title }}</div>
|
||||||
|
<a-row>
|
||||||
|
<slot></slot>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Col } from 'ant-design-vue/es/grid/'
|
||||||
|
|
||||||
|
const Item = {
|
||||||
|
name: 'DetailListItem',
|
||||||
|
props: {
|
||||||
|
term: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
inject: {
|
||||||
|
col: {
|
||||||
|
type: Number
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<Col {...{props: responsive[this.col]}}>
|
||||||
|
<div class="term">{this.$props.term}</div>
|
||||||
|
<div class="content">{this.$slots.default}</div>
|
||||||
|
</Col>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const responsive = {
|
||||||
|
1: { xs: 24 },
|
||||||
|
2: { xs: 24, sm: 12 },
|
||||||
|
3: { xs: 24, sm: 12, md: 8 },
|
||||||
|
4: { xs: 24, sm: 12, md: 6 }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "DetailList",
|
||||||
|
Item: Item,
|
||||||
|
components: {
|
||||||
|
Col
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
col: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 3
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'large'
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'horizontal'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
provide () {
|
||||||
|
return {
|
||||||
|
col: this.col > 4 ? 4 : this.col
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
|
||||||
|
.detail-list {
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: rgba(0,0,0,.85);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.term {
|
||||||
|
color: rgba(0,0,0,.85);
|
||||||
|
display: table-cell;
|
||||||
|
line-height: 20px;
|
||||||
|
margin-right: 8px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: ":";
|
||||||
|
margin: 0 8px 0 2px;
|
||||||
|
position: relative;
|
||||||
|
top: -.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
color: rgba(0,0,0,.65);
|
||||||
|
display: table-cell;
|
||||||
|
line-height: 22px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: rgba(0, 0, 0, .65);
|
||||||
|
font-weight: normal;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.term, .content {
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.large {
|
||||||
|
.term, .content {
|
||||||
|
padding-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vertical {
|
||||||
|
.term {
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
.term, .content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="comp"
|
||||||
|
:formData="formData"
|
||||||
|
ref="compModel"
|
||||||
|
v-if="comp">
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'DynamicNotice',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
compName: this.path
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
comp: function () {
|
||||||
|
if(!this.path){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return () => import(`@/views/${this.path}.vue`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: ['path','formData'],
|
||||||
|
methods: {
|
||||||
|
detail () {
|
||||||
|
setTimeout(() => {
|
||||||
|
if(this.path){
|
||||||
|
this.$refs.compModel.view(this.formData);
|
||||||
|
}
|
||||||
|
}, 200)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,337 @@
|
|||||||
|
<template>
|
||||||
|
<a-popover
|
||||||
|
trigger="click"
|
||||||
|
placement="bottomRight"
|
||||||
|
:autoAdjustOverflow="true"
|
||||||
|
:arrowPointAtCenter="true"
|
||||||
|
overlayClassName="header-notice-wrapper"
|
||||||
|
@visibleChange="handleHoverChange"
|
||||||
|
:overlayStyle="{ width: '300px', top: '50px' }">
|
||||||
|
<template slot="content">
|
||||||
|
<a-spin :spinning="loadding">
|
||||||
|
<a-tabs>
|
||||||
|
<a-tab-pane :tab="msg1Title" key="1">
|
||||||
|
<!--<a-list>
|
||||||
|
<a-list-item>
|
||||||
|
<a-list-item-meta title="你收到了 14 份新周报" description="一年前">
|
||||||
|
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"/>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
<a-list-item>
|
||||||
|
<a-list-item-meta title="你推荐的 IT大牛 已通过第三轮面试" description="一年前">
|
||||||
|
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png"/>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
<a-list-item>
|
||||||
|
<a-list-item-meta title="这种模板可以区分多种通知类型" description="一年前">
|
||||||
|
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png"/>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
</a-list>-->
|
||||||
|
<a-list>
|
||||||
|
<a-list-item :key="index" v-for="(record, index) in announcement1">
|
||||||
|
<div style="margin-left: 5%;width: 80%">
|
||||||
|
<p><a @click="showAnnouncement(record)">{{ record.titile }}</a></p>
|
||||||
|
<p style="color: rgba(0,0,0,.45);margin-bottom: 0px">{{ record.createTime }} 发布</p>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: right">
|
||||||
|
<a-tag @click="showAnnouncement(record)" v-if="record.priority === 'L'" color="blue">一般消息</a-tag>
|
||||||
|
<a-tag @click="showAnnouncement(record)" v-if="record.priority === 'M'" color="orange">重要消息</a-tag>
|
||||||
|
<a-tag @click="showAnnouncement(record)" v-if="record.priority === 'H'" color="red">紧急消息</a-tag>
|
||||||
|
</div>
|
||||||
|
</a-list-item>
|
||||||
|
<div style="margin-top: 5px;text-align: center">
|
||||||
|
<a-button @click="toMyAnnouncement()" type="dashed" block>查看更多</a-button>
|
||||||
|
</div>
|
||||||
|
</a-list>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :tab="msg2Title" key="2">
|
||||||
|
<a-list>
|
||||||
|
<a-list-item :key="index" v-for="(record, index) in announcement2">
|
||||||
|
<div style="margin-left: 5%;width: 80%">
|
||||||
|
<p><a @click="showAnnouncement(record)">{{ record.titile }}</a></p>
|
||||||
|
<p style="color: rgba(0,0,0,.45);margin-bottom: 0px">{{ record.createTime }} 发布</p>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: right">
|
||||||
|
<a-tag @click="showAnnouncement(record)" v-if="record.priority === 'L'" color="blue">一般消息</a-tag>
|
||||||
|
<a-tag @click="showAnnouncement(record)" v-if="record.priority === 'M'" color="orange">重要消息</a-tag>
|
||||||
|
<a-tag @click="showAnnouncement(record)" v-if="record.priority === 'H'" color="red">紧急消息</a-tag>
|
||||||
|
</div>
|
||||||
|
</a-list-item>
|
||||||
|
<div style="margin-top: 5px;text-align: center">
|
||||||
|
<a-button @click="toMyAnnouncement()" type="dashed" block>查看更多</a-button>
|
||||||
|
</div>
|
||||||
|
</a-list>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
<span @click="fetchNotice" class="header-notice">
|
||||||
|
<a-badge :count="msgTotal">
|
||||||
|
<a-icon style="font-size: 16px; padding: 4px" type="bell" />
|
||||||
|
</a-badge>
|
||||||
|
</span>
|
||||||
|
<!-- <show-announcement ref="ShowAnnouncement" @ok="modalFormOk"></show-announcement>-->
|
||||||
|
<dynamic-notice ref="showDynamNotice" :path="openPath" :formData="formData"/>
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getAction,putAction } from '@/api/manage'
|
||||||
|
// import ShowAnnouncement from './ShowAnnouncement'
|
||||||
|
import store from '@/store/'
|
||||||
|
import DynamicNotice from './DynamicNotice'
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "HeaderNotice",
|
||||||
|
components: {
|
||||||
|
DynamicNotice,
|
||||||
|
// ShowAnnouncement,
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loadding: false,
|
||||||
|
url:{
|
||||||
|
listCementByUser:"/sys/annountCement/listByUser",
|
||||||
|
editCementSend:"/sys/sysAnnouncementSend/editByAnntIdAndUserId",
|
||||||
|
queryById:"/sys/annountCement/queryById",
|
||||||
|
},
|
||||||
|
hovered: false,
|
||||||
|
announcement1:[],
|
||||||
|
announcement2:[],
|
||||||
|
msg1Count:"0",
|
||||||
|
msg2Count:"0",
|
||||||
|
msg1Title:"通知(0)",
|
||||||
|
msg2Title:"",
|
||||||
|
stopTimer:false,
|
||||||
|
websock: null,
|
||||||
|
lockReconnect:false,
|
||||||
|
heartCheck:null,
|
||||||
|
formData:{},
|
||||||
|
openPath:''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed:{
|
||||||
|
msgTotal () {
|
||||||
|
return parseInt(this.msg1Count)+parseInt(this.msg2Count);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadData();
|
||||||
|
//this.timerFun();
|
||||||
|
this.initWebSocket();
|
||||||
|
// this.heartCheckFun();
|
||||||
|
},
|
||||||
|
destroyed: function () { // 离开页面生命周期函数
|
||||||
|
this.websocketOnclose();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
timerFun() {
|
||||||
|
this.stopTimer = false;
|
||||||
|
let myTimer = setInterval(()=>{
|
||||||
|
// 停止定时器
|
||||||
|
if (this.stopTimer == true) {
|
||||||
|
clearInterval(myTimer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loadData()
|
||||||
|
},6000)
|
||||||
|
},
|
||||||
|
loadData (){
|
||||||
|
try {
|
||||||
|
// 获取系统消息
|
||||||
|
getAction(this.url.listCementByUser).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
this.announcement1 = res.result.anntMsgList;
|
||||||
|
this.msg1Count = res.result.anntMsgTotal;
|
||||||
|
this.msg1Title = "通知(" + res.result.anntMsgTotal + ")";
|
||||||
|
this.announcement2 = res.result.sysMsgList;
|
||||||
|
this.msg2Count = res.result.sysMsgTotal;
|
||||||
|
this.msg2Title = "系统消息(" + res.result.sysMsgTotal + ")";
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.log("系统消息通知异常",error);//这行打印permissionName is undefined
|
||||||
|
this.stopTimer = true;
|
||||||
|
console.log("清理timer");
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
this.stopTimer = true;
|
||||||
|
console.log("通知异常",err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetchNotice () {
|
||||||
|
if (this.loadding) {
|
||||||
|
this.loadding = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loadding = true
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loadding = false
|
||||||
|
}, 200)
|
||||||
|
},
|
||||||
|
showAnnouncement(record){
|
||||||
|
putAction(this.url.editCementSend,{anntId:record.id}).then((res)=>{
|
||||||
|
if(res.success){
|
||||||
|
this.loadData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.hovered = false;
|
||||||
|
if(record.openType==='component'){
|
||||||
|
this.openPath = record.openPage;
|
||||||
|
this.formData = {id:record.busId};
|
||||||
|
this.$refs.showDynamNotice.detail(record.openPage);
|
||||||
|
}else{
|
||||||
|
// this.$refs.ShowAnnouncement.detail(record);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toMyAnnouncement(){
|
||||||
|
this.$router.push({
|
||||||
|
path: '/isps/userAnnouncement'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
modalFormOk(){
|
||||||
|
},
|
||||||
|
handleHoverChange (visible) {
|
||||||
|
this.hovered = visible;
|
||||||
|
},
|
||||||
|
|
||||||
|
initWebSocket: function () {
|
||||||
|
// WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
|
||||||
|
var userId = store.getters.userInfo.id;
|
||||||
|
var url = window._CONFIG['domianWebSocketURL'].replace("https://","wss://").replace("http://","ws://")+"/websocket/"+userId;
|
||||||
|
//console.log(url);
|
||||||
|
this.websock = new WebSocket(url);
|
||||||
|
this.websock.onopen = this.websocketOnopen;
|
||||||
|
this.websock.onerror = this.websocketOnerror;
|
||||||
|
this.websock.onmessage = this.websocketOnmessage;
|
||||||
|
this.websock.onclose = this.websocketOnclose;
|
||||||
|
},
|
||||||
|
websocketOnopen: function () {
|
||||||
|
console.log("WebSocket连接成功");
|
||||||
|
//心跳检测重置
|
||||||
|
//this.heartCheck.reset().start();
|
||||||
|
},
|
||||||
|
websocketOnerror: function (e) {
|
||||||
|
console.log("WebSocket连接发生错误");
|
||||||
|
this.reconnect();
|
||||||
|
},
|
||||||
|
websocketOnmessage: function (e) {
|
||||||
|
console.log("-----接收消息-------",e.data);
|
||||||
|
var data = eval("(" + e.data + ")"); //解析对象
|
||||||
|
if(data.cmd == "topic"){
|
||||||
|
//系统通知
|
||||||
|
this.loadData();
|
||||||
|
}else if(data.cmd == "user"){
|
||||||
|
//用户消息
|
||||||
|
this.loadData();
|
||||||
|
}
|
||||||
|
//心跳检测重置
|
||||||
|
//this.heartCheck.reset().start();
|
||||||
|
},
|
||||||
|
websocketOnclose: function (e) {
|
||||||
|
console.log("connection closed (" + e + ")");
|
||||||
|
if(e){
|
||||||
|
console.log("connection closed (" + e.code + ")");
|
||||||
|
}
|
||||||
|
this.reconnect();
|
||||||
|
},
|
||||||
|
websocketSend(text) { // 数据发送
|
||||||
|
try {
|
||||||
|
this.websock.send(text);
|
||||||
|
} catch (err) {
|
||||||
|
console.log("send failed (" + err.code + ")");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
openNotification (data) {
|
||||||
|
var text = data.msgTxt;
|
||||||
|
const key = `open${Date.now()}`;
|
||||||
|
this.$notification.open({
|
||||||
|
message: '消息提醒',
|
||||||
|
placement:'bottomRight',
|
||||||
|
description: text,
|
||||||
|
key,
|
||||||
|
btn: (h)=>{
|
||||||
|
return h('a-button', {
|
||||||
|
props: {
|
||||||
|
type: 'primary',
|
||||||
|
size: 'small',
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
click: () => this.showDetail(key,data)
|
||||||
|
}
|
||||||
|
}, '查看详情')
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
reconnect() {
|
||||||
|
var that = this;
|
||||||
|
if(that.lockReconnect) return;
|
||||||
|
that.lockReconnect = true;
|
||||||
|
//没连接上会一直重连,设置延迟避免请求过多
|
||||||
|
setTimeout(function () {
|
||||||
|
console.info("尝试重连...");
|
||||||
|
that.initWebSocket();
|
||||||
|
that.lockReconnect = false;
|
||||||
|
}, 5000);
|
||||||
|
},
|
||||||
|
heartCheckFun(){
|
||||||
|
var that = this;
|
||||||
|
//心跳检测,每20s心跳一次
|
||||||
|
that.heartCheck = {
|
||||||
|
timeout: 20000,
|
||||||
|
timeoutObj: null,
|
||||||
|
serverTimeoutObj: null,
|
||||||
|
reset: function(){
|
||||||
|
clearTimeout(this.timeoutObj);
|
||||||
|
//clearTimeout(this.serverTimeoutObj);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
start: function(){
|
||||||
|
var self = this;
|
||||||
|
this.timeoutObj = setTimeout(function(){
|
||||||
|
//这里发送一个心跳,后端收到后,返回一个心跳消息,
|
||||||
|
//onmessage拿到返回的心跳就说明连接正常
|
||||||
|
that.websocketSend("HeartBeat");
|
||||||
|
console.info("客户端发送心跳");
|
||||||
|
//self.serverTimeoutObj = setTimeout(function(){//如果超过一定时间还没重置,说明后端主动断开了
|
||||||
|
// that.websock.close();//如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
|
||||||
|
//}, self.timeout)
|
||||||
|
}, this.timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
showDetail(key,data){
|
||||||
|
this.$notification.close(key);
|
||||||
|
var id = data.msgId;
|
||||||
|
getAction(this.url.queryById,{id:id}).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
var record = res.result;
|
||||||
|
this.showAnnouncement(record);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="css">
|
||||||
|
.header-notice-wrapper {
|
||||||
|
top: 50px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.header-notice{
|
||||||
|
display: inline-block;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
span {
|
||||||
|
vertical-align: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<div class="logo">
|
||||||
|
<router-link :to="{name:'dashboard'}">
|
||||||
|
|
||||||
|
<!-- update-begin- author:sunjianlei --- date:20190814 --- for: logo颜色根据主题颜色变化 -->
|
||||||
|
<img v-if="navTheme === 'dark'" src="~@/assets/logo.png" alt="logo">
|
||||||
|
<img v-else src="~@/assets/logo.png" alt="logo">
|
||||||
|
<!-- update-begin- author:sunjianlei --- date:20190814 --- for: logo颜色根据主题颜色变化 -->
|
||||||
|
|
||||||
|
<h1 v-if="showTitle">{{ title }}</h1>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mixin } from '@/utils/mixin.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Logo',
|
||||||
|
mixins: [mixin],
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: 'Jero-Boot Pro',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
showTitle: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
/*缩小首页布 局顶部的高度*/
|
||||||
|
@height: 59px;
|
||||||
|
|
||||||
|
.sider {
|
||||||
|
box-shadow: none !important;
|
||||||
|
.logo {
|
||||||
|
height: @height !important;
|
||||||
|
line-height: @height !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
transition: background 300ms;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: white;
|
||||||
|
&:hover {
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.light .logo {
|
||||||
|
background-color: @primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
<template>
|
||||||
|
<j-modal
|
||||||
|
:title="title"
|
||||||
|
:width="modelStyle.width"
|
||||||
|
:visible="visible"
|
||||||
|
:bodyStyle ="bodyStyle"
|
||||||
|
:switchFullscreen="switchFullscreen"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<template slot="footer">
|
||||||
|
<a-button key="back" @click="handleCancel">关闭</a-button>
|
||||||
|
<a-button v-if="record.openType==='url'" type="primary" @click="toHandle">去处理</a-button>
|
||||||
|
</template>
|
||||||
|
<a-card class="daily-article" :loading="loading">
|
||||||
|
<a-card-meta
|
||||||
|
:title="record.titile"
|
||||||
|
:description="'发布人:'+record.sender + ' 发布时间: ' + record.sendTime">
|
||||||
|
</a-card-meta>
|
||||||
|
<a-divider />
|
||||||
|
<span v-html="record.msgContent" class="article-content"></span>
|
||||||
|
</a-card>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "SysAnnouncementModal",
|
||||||
|
components: {
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
title:"通知消息",
|
||||||
|
record: {},
|
||||||
|
labelCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 5 },
|
||||||
|
},
|
||||||
|
wrapperCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 16 },
|
||||||
|
},
|
||||||
|
visible: false,
|
||||||
|
switchFullscreen: true,
|
||||||
|
loading: false,
|
||||||
|
bodyStyle:{
|
||||||
|
padding: "0",
|
||||||
|
height:(window.innerHeight*0.8)+"px",
|
||||||
|
"overflow-y":"auto",
|
||||||
|
|
||||||
|
},
|
||||||
|
modelStyle:{
|
||||||
|
width: '60%',
|
||||||
|
style: { top: '20px' },
|
||||||
|
fullScreen: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
detail (record) {
|
||||||
|
this.visible = true;
|
||||||
|
this.record = record;
|
||||||
|
},
|
||||||
|
handleCancel () {
|
||||||
|
this.visible = false;
|
||||||
|
},
|
||||||
|
/** 切换全屏显示 */
|
||||||
|
handleClickToggleFullScreen() {
|
||||||
|
let mode = !this.modelStyle.fullScreen
|
||||||
|
if (mode) {
|
||||||
|
this.modelStyle.width = '100%'
|
||||||
|
this.modelStyle.style.top = '20px'
|
||||||
|
} else {
|
||||||
|
this.modelStyle.width = '60%'
|
||||||
|
this.modelStyle.style.top = '50px'
|
||||||
|
}
|
||||||
|
this.modelStyle.fullScreen = mode
|
||||||
|
},
|
||||||
|
toHandle(){
|
||||||
|
if(this.record.openType==='url'){
|
||||||
|
this.visible = false;
|
||||||
|
//链接跳转
|
||||||
|
this.$router.push({path: this.record.openPage})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.announcementCustomModal{
|
||||||
|
.ant-modal-header {
|
||||||
|
border: none;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
right: 56px;
|
||||||
|
padding: 0;
|
||||||
|
.ant-modal-title{
|
||||||
|
.custom-btn{
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.daily-article{
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style scoped lang="less">
|
||||||
|
.daily-article {
|
||||||
|
.article-button {
|
||||||
|
font-size: 1.2rem !important;
|
||||||
|
}
|
||||||
|
.ant-card-body {
|
||||||
|
padding: 18px !important;
|
||||||
|
}
|
||||||
|
.ant-card-head {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
.ant-card-meta {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.article-content {
|
||||||
|
p {
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-all;
|
||||||
|
text-overflow: initial;
|
||||||
|
white-space: normal;
|
||||||
|
font-size: .9rem !important;
|
||||||
|
margin-bottom: .8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
<template>
|
||||||
|
<div class="user-wrapper" :class="theme">
|
||||||
|
<header-notice class="action"/>
|
||||||
|
<a-dropdown>
|
||||||
|
<span class="action action-full ant-dropdown-link user-dropdown-menu">
|
||||||
|
<a-avatar class="avatar" size="small" :src="getAvatar()"/>
|
||||||
|
<span v-if="isDesktop()">欢迎您,{{ nickname() }}</span>
|
||||||
|
</span>
|
||||||
|
<a-menu slot="overlay" class="user-dropdown-menu-wrapper">
|
||||||
|
<a-menu-item key="0">
|
||||||
|
<router-link :to="{ name: 'account-center' }">
|
||||||
|
<a-icon type="user"/>
|
||||||
|
<span>个人中心</span>
|
||||||
|
</router-link>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="1">
|
||||||
|
<router-link :to="{ name: 'account-settings-base' }">
|
||||||
|
<a-icon type="setting"/>
|
||||||
|
<span>账户设置</span>
|
||||||
|
</router-link>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="3" @click="systemSetting">
|
||||||
|
<a-icon type="tool"/>
|
||||||
|
<span>系统设置</span>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="4" @click="updatePassword">
|
||||||
|
<a-icon type="setting"/>
|
||||||
|
<span>密码修改</span>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="5" @click="updateCurrentDepart">
|
||||||
|
<a-icon type="cluster"/>
|
||||||
|
<span>切换部门</span>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="6" @click="clearCache">
|
||||||
|
<a-icon type="sync"/>
|
||||||
|
<span>清理缓存</span>
|
||||||
|
</a-menu-item>
|
||||||
|
<!-- <a-menu-item key="2" disabled>
|
||||||
|
<a-icon type="setting"/>
|
||||||
|
<span>测试</span>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-divider/>
|
||||||
|
<a-menu-item key="3">
|
||||||
|
<a href="javascript:;" @click="handleLogout">
|
||||||
|
<a-icon type="logout"/>
|
||||||
|
<span>退出登录</span>
|
||||||
|
</a>
|
||||||
|
</a-menu-item>-->
|
||||||
|
</a-menu>
|
||||||
|
</a-dropdown>
|
||||||
|
<span class="action">
|
||||||
|
<a class="logout_title" href="javascript:;" @click="handleLogout">
|
||||||
|
<a-icon type="logout"/>
|
||||||
|
<span v-if="isDesktop()"> 退出登录</span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<user-password ref="userPassword"></user-password>
|
||||||
|
<depart-select ref="departSelect" :closable="true" title="部门切换"></depart-select>
|
||||||
|
<setting-drawer ref="settingDrawer" :closable="true" title="系统设置"></setting-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import HeaderNotice from './HeaderNotice'
|
||||||
|
import UserPassword from './UserPassword'
|
||||||
|
import SettingDrawer from "@/components/setting/SettingDrawer";
|
||||||
|
import DepartSelect from './DepartSelect'
|
||||||
|
import { mapActions, mapGetters,mapState } from 'vuex'
|
||||||
|
import { mixinDevice } from '@/utils/mixin.js'
|
||||||
|
import { getFileAccessHttpUrl,getAction } from "@/api/manage"
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { UI_CACHE_DB_DICT_DATA } from "@/store/mutation-types"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "UserMenu",
|
||||||
|
mixins: [mixinDevice],
|
||||||
|
data(){
|
||||||
|
return{
|
||||||
|
// update-begin author:sunjianlei date:20200219 for: 头部菜单搜索规范命名 --------------
|
||||||
|
searchMenuOptions:[],
|
||||||
|
searchMenuComp: 'span',
|
||||||
|
searchMenuVisible: false,
|
||||||
|
// update-begin author:sunjianlei date:20200219 for: 头部菜单搜索规范命名 --------------
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
HeaderNotice,
|
||||||
|
UserPassword,
|
||||||
|
DepartSelect,
|
||||||
|
SettingDrawer
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
theme: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'dark'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* update_begin author:zhaoxin date:20191129 for: 做头部菜单栏导航*/
|
||||||
|
created() {
|
||||||
|
let lists = []
|
||||||
|
this.searchMenus(lists,this.permissionMenuList)
|
||||||
|
this.searchMenuOptions=[...lists]
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
//如果是单点登录模式
|
||||||
|
if (process.env.VUE_APP_SSO == 'true') {
|
||||||
|
let depart = this.userInfo().orgCode
|
||||||
|
if (!depart) {
|
||||||
|
this.updateCurrentDepart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
// 后台菜单
|
||||||
|
permissionMenuList: state => state.user.permissionList
|
||||||
|
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/* update_end author:zhaoxin date:20191129 for: 做头部菜单栏导航*/
|
||||||
|
watch: {
|
||||||
|
// update-begin author:sunjianlei date:20200219 for: 菜单搜索改为动态组件,在手机端呈现出弹出框
|
||||||
|
device: {
|
||||||
|
immediate: true,
|
||||||
|
handler() {
|
||||||
|
this.searchMenuVisible = false
|
||||||
|
this.searchMenuComp = this.isMobile() ? 'a-modal' : 'span'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// update-end author:sunjianlei date:20200219 for: 菜单搜索改为动态组件,在手机端呈现出弹出框
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/* update_begin author:zhaoxin date:20191129 for: 做头部菜单栏导航*/
|
||||||
|
showClick() {
|
||||||
|
this.searchMenuVisible = true
|
||||||
|
},
|
||||||
|
hiddenClick(){
|
||||||
|
this.shows = false
|
||||||
|
},
|
||||||
|
/* update_end author:zhaoxin date:20191129 for: 做头部菜单栏导航*/
|
||||||
|
...mapActions(["Logout"]),
|
||||||
|
...mapGetters(["nickname", "avatar","userInfo"]),
|
||||||
|
getAvatar(){
|
||||||
|
return getFileAccessHttpUrl(this.avatar())
|
||||||
|
},
|
||||||
|
handleLogout() {
|
||||||
|
const that = this
|
||||||
|
|
||||||
|
this.$confirm({
|
||||||
|
title: '提示',
|
||||||
|
content: '真的要注销登录吗 ?',
|
||||||
|
onOk() {
|
||||||
|
return that.Logout({}).then(() => {
|
||||||
|
// update-begin author:wangshuai date:20200601 for: 退出登录跳转登录页面
|
||||||
|
that.$router.push({ path: '/user/login' });
|
||||||
|
// update-end author:wangshuai date:20200601 for: 退出登录跳转登录页面
|
||||||
|
//window.location.reload()
|
||||||
|
}).catch(err => {
|
||||||
|
that.$message.error({
|
||||||
|
title: '错误',
|
||||||
|
description: err.message
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updatePassword(){
|
||||||
|
let username = this.userInfo().username
|
||||||
|
this.$refs.userPassword.show(username)
|
||||||
|
},
|
||||||
|
updateCurrentDepart(){
|
||||||
|
this.$refs.departSelect.show()
|
||||||
|
},
|
||||||
|
systemSetting(){
|
||||||
|
this.$refs.settingDrawer.showDrawer()
|
||||||
|
},
|
||||||
|
/* update_begin author:zhaoxin date:20191129 for: 做头部菜单栏导航*/
|
||||||
|
searchMenus(arr,menus){
|
||||||
|
for(let i of menus){
|
||||||
|
if(!i.hidden && "layouts/RouteView"!==i.component){
|
||||||
|
arr.push(i)
|
||||||
|
}
|
||||||
|
if(i.children&& i.children.length>0){
|
||||||
|
this.searchMenus(arr,i.children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filterOption(input, option) {
|
||||||
|
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
},
|
||||||
|
// update_begin author:sunjianlei date:20191230 for: 解决外部链接打开失败的问题
|
||||||
|
searchMethods(value) {
|
||||||
|
let route = this.searchMenuOptions.filter(item => item.id === value)[0]
|
||||||
|
if (route.meta.internalOrExternal === true || route.component.includes('layouts/IframePageView')) {
|
||||||
|
window.open(route.meta.url, '_blank')
|
||||||
|
} else {
|
||||||
|
this.$router.push({ path: route.path })
|
||||||
|
}
|
||||||
|
this.searchMenuVisible = false
|
||||||
|
},
|
||||||
|
// update_end author:sunjianlei date:20191230 for: 解决外部链接打开失败的问题
|
||||||
|
/*update_end author:zhaoxin date:20191129 for: 做头部菜单栏导航*/
|
||||||
|
/*update_begin author:liushaoqian date:20200507 for: 刷新缓存*/
|
||||||
|
clearCache(){
|
||||||
|
getAction("sys/dict/refleshCache").then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
//重新加载缓存
|
||||||
|
getAction("sys/dict/queryAllDictItems").then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
Vue.ls.remove(UI_CACHE_DB_DICT_DATA)
|
||||||
|
Vue.ls.set(UI_CACHE_DB_DICT_DATA, res.result, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.$message.success("刷新缓存完成!");
|
||||||
|
}
|
||||||
|
}).catch(e=>{
|
||||||
|
this.$message.warn("刷新缓存失败!");
|
||||||
|
console.log("刷新失败",e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/*update_end author:liushaoqian date:20200507 for: 刷新缓存*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
/* update_begin author:zhaoxin date:20191129 for: 让搜索框颜色能随主题颜色变换*/
|
||||||
|
/* update-begin author:sunjianlei date:20191220 for: 解决全局样式冲突问题 */
|
||||||
|
.user-wrapper .search-input {
|
||||||
|
width: 180px;
|
||||||
|
color: inherit;
|
||||||
|
|
||||||
|
/deep/ .ant-select-selection {
|
||||||
|
background-color: inherit;
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 1px solid white;
|
||||||
|
&__placeholder, &__field__placeholder {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* update-end author:sunjianlei date:20191220 for: 解决全局样式冲突问题 */
|
||||||
|
/* update_end author:zhaoxin date:20191129 for: 让搜索框颜色能随主题颜色变换*/
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.logout_title {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:title="title"
|
||||||
|
:width="modalWidth"
|
||||||
|
:visible="visible"
|
||||||
|
:confirmLoading="confirmLoading"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
cancelText="关闭"
|
||||||
|
>
|
||||||
|
<a-spin :spinning="confirmLoading">
|
||||||
|
<a-form :form="form">
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:labelCol="labelCol"
|
||||||
|
:wrapperCol="wrapperCol"
|
||||||
|
label="旧密码">
|
||||||
|
<a-input type="password" placeholder="请输入旧密码" v-decorator="[ 'oldpassword', validatorRules.oldpassword]" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:labelCol="labelCol"
|
||||||
|
:wrapperCol="wrapperCol"
|
||||||
|
label="新密码">
|
||||||
|
<a-input type="password" placeholder="请输入新密码" v-decorator="[ 'password', validatorRules.password]" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:labelCol="labelCol"
|
||||||
|
:wrapperCol="wrapperCol"
|
||||||
|
label="确认新密码">
|
||||||
|
<a-input type="password" @blur="handleConfirmBlur" placeholder="请确认新密码" v-decorator="[ 'confirmpassword', validatorRules.confirmpassword]"/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
</a-form>
|
||||||
|
</a-spin>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import { putAction } from '@/api/manage'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "UserPassword",
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
title:"修改密码",
|
||||||
|
modalWidth:800,
|
||||||
|
visible: false,
|
||||||
|
confirmLoading: false,
|
||||||
|
validatorRules:{
|
||||||
|
oldpassword:{
|
||||||
|
rules: [{
|
||||||
|
required: true, message: '请输入旧密码!',
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
password:{
|
||||||
|
rules: [
|
||||||
|
{ required: true, message: '请输入新密码!' },
|
||||||
|
{ pattern:/^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,./]).{8,}$/, message: '密码由8位数字、大小写字母和特殊符号组成!'},
|
||||||
|
{ validator: this.validateToNextPassword }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
confirmpassword:{
|
||||||
|
rules: [
|
||||||
|
{ required: true, message: '请确认新密码!' },
|
||||||
|
{ pattern:/^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,./]).{8,}$/, message: '密码由8位数字、大小写字母和特殊符号组成!'},
|
||||||
|
{ validator: this.validateToNextPassword }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmDirty:false,
|
||||||
|
labelCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 5 },
|
||||||
|
},
|
||||||
|
wrapperCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 16 },
|
||||||
|
},
|
||||||
|
|
||||||
|
form:this.$form.createForm(this),
|
||||||
|
url: "sys/user/updatePassword",
|
||||||
|
username:"",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
show(uname){
|
||||||
|
if(!uname){
|
||||||
|
this.$message.warning("当前系统无登录用户!");
|
||||||
|
return
|
||||||
|
}else{
|
||||||
|
this.username = uname
|
||||||
|
this.form.resetFields();
|
||||||
|
this.visible = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleCancel () {
|
||||||
|
this.close()
|
||||||
|
},
|
||||||
|
close () {
|
||||||
|
this.$emit('close');
|
||||||
|
this.visible = false;
|
||||||
|
this.disableSubmit = false;
|
||||||
|
this.selectedRole = [];
|
||||||
|
},
|
||||||
|
handleOk () {
|
||||||
|
const that = this;
|
||||||
|
// 触发表单验证
|
||||||
|
this.form.validateFields((err, values) => {
|
||||||
|
if (!err) {
|
||||||
|
that.confirmLoading = true;
|
||||||
|
let params = Object.assign({username:this.username},values)
|
||||||
|
console.log("修改密码提交数据",params)
|
||||||
|
putAction(this.url,params).then((res)=>{
|
||||||
|
if(res.success){
|
||||||
|
console.log(res)
|
||||||
|
that.$message.success(res.message);
|
||||||
|
that.close();
|
||||||
|
}else{
|
||||||
|
that.$message.warning(res.message);
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
that.confirmLoading = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
validateToNextPassword (rule, value, callback) {
|
||||||
|
const form = this.form;
|
||||||
|
if (value && this.confirmDirty) {
|
||||||
|
form.validateFields(['confirm'], { force: true })
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
compareToFirstPassword (rule, value, callback) {
|
||||||
|
const form = this.form;
|
||||||
|
if (value && value !== form.getFieldValue('password')) {
|
||||||
|
callback('两次输入的密码不一样!');
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleConfirmBlur (e) {
|
||||||
|
const value = e.target.value
|
||||||
|
this.confirmDirty = this.confirmDirty || !!value
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import { message } from 'ant-design-vue/es';
|
||||||
|
// import defaultSettings from '../defaultSettings';
|
||||||
|
|
||||||
|
let lessNodesAppended;
|
||||||
|
|
||||||
|
const colorList = [
|
||||||
|
{
|
||||||
|
key: '薄暮', color: '#F5222D',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '火山', color: '#FA541C',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '日暮', color: '#FAAD14',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '明青', color: '#13C2C2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '极光绿', color: '#52C41A',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '拂晓蓝(默认)', color: '#1890FF',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '极客蓝', color: '#2F54EB',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '酱紫', color: '#722ED1',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const updateTheme = primaryColor => {
|
||||||
|
// Don't compile less in production!
|
||||||
|
/* if (process.env.NODE_ENV === 'production') {
|
||||||
|
return;
|
||||||
|
} */
|
||||||
|
// Determine if the component is remounted
|
||||||
|
if (!primaryColor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const hideMessage = message.loading('正在编译主题!', 0);
|
||||||
|
console.info(`正在编译主题!`)
|
||||||
|
function buildIt() {
|
||||||
|
// 正确的判定less是否已经加载less.modifyVars可用
|
||||||
|
if (!window.less || !window.less.modifyVars) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// less.modifyVars可用
|
||||||
|
window.less.modifyVars({
|
||||||
|
'@primary-color': primaryColor,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
hideMessage();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
message.error('Failed to update theme');
|
||||||
|
hideMessage();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!lessNodesAppended) {
|
||||||
|
// insert less.js and color.less
|
||||||
|
const lessStyleNode = document.createElement('link');
|
||||||
|
const lessConfigNode = document.createElement('script');
|
||||||
|
const lessScriptNode = document.createElement('script');
|
||||||
|
lessStyleNode.setAttribute('rel', 'stylesheet/less');
|
||||||
|
lessStyleNode.setAttribute('href', __webpack_public_path__ + 'color.less')
|
||||||
|
lessConfigNode.innerHTML = `
|
||||||
|
window.less = {
|
||||||
|
async: true,
|
||||||
|
env: 'production',
|
||||||
|
javascriptEnabled: true
|
||||||
|
};
|
||||||
|
`;
|
||||||
|
lessScriptNode.src = 'https://gw.alipayobjects.com/os/lib/less.js/3.8.1/less.min.js';
|
||||||
|
lessScriptNode.async = true;
|
||||||
|
lessScriptNode.onload = () => {
|
||||||
|
buildIt();
|
||||||
|
lessScriptNode.onload = null;
|
||||||
|
};
|
||||||
|
document.body.appendChild(lessStyleNode);
|
||||||
|
document.body.appendChild(lessConfigNode);
|
||||||
|
document.body.appendChild(lessScriptNode);
|
||||||
|
lessNodesAppended = true;
|
||||||
|
} else {
|
||||||
|
buildIt();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateColorWeak = colorWeak => {
|
||||||
|
// document.body.className = colorWeak ? 'colorWeak' : '';
|
||||||
|
colorWeak ? document.body.classList.add('colorWeak') : document.body.classList.remove('colorWeak')
|
||||||
|
};
|
||||||
|
|
||||||
|
export { updateTheme, colorList, updateColorWeak }
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { UserLayout, TabLayout } from "@/components/layouts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 走菜单,走权限控制
|
||||||
|
* @type {[null,null]}
|
||||||
|
*/
|
||||||
|
export const asyncRouterMap = [
|
||||||
|
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
name: "dashboard",
|
||||||
|
component: TabLayout,
|
||||||
|
meta: { title: "首页" },
|
||||||
|
redirect: "/dashboard/analysis",
|
||||||
|
children: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "*", redirect: "/404", hidden: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础路由
|
||||||
|
* @type { *[] }
|
||||||
|
*/
|
||||||
|
export const constantRouterMap = [
|
||||||
|
{
|
||||||
|
path: "/user",
|
||||||
|
component: UserLayout,
|
||||||
|
redirect: "/user/login",
|
||||||
|
hidden: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "login",
|
||||||
|
name: "login",
|
||||||
|
component: () => import(/* webpackChunkName: "user" */ "@/views/user/Login")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "register",
|
||||||
|
name: "register",
|
||||||
|
component: () => import(/* webpackChunkName: "user" */ "@/views/user/register/Register")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "register-result",
|
||||||
|
name: "registerResult",
|
||||||
|
component: () => import(/* webpackChunkName: "user" */ "@/views/user/register/RegisterResult")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "alteration",
|
||||||
|
name: "alteration",
|
||||||
|
component: () => import(/* webpackChunkName: "user" */ "@/views/user/alteration/Alteration")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* 项目默认配置项
|
||||||
|
* primaryColor - 默认主题色
|
||||||
|
* navTheme - sidebar theme ['dark', 'light'] 两种主题
|
||||||
|
* colorWeak - 色盲模式
|
||||||
|
* layout - 整体布局方式 ['sidemenu', 'topmenu'] 两种布局
|
||||||
|
* fixedHeader - 固定 Header : boolean
|
||||||
|
* fixSiderbar - 固定左侧菜单栏 : boolean
|
||||||
|
* autoHideHeader - 向下滚动时,隐藏 Header : boolean
|
||||||
|
* contentWidth - 内容区布局: 流式 | 固定
|
||||||
|
*
|
||||||
|
* storageOptions: {} - Vue-ls 插件配置项 (localStorage/sessionStorage)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
primaryColor: '#1890FF', // primary color of ant design
|
||||||
|
navTheme: 'light', // theme for nav menu
|
||||||
|
layout: 'sidemenu', // nav menu position: sidemenu or topmenu
|
||||||
|
contentWidth: 'Fixed', // layout of content: Fluid or Fixed, only works when layout is topmenu
|
||||||
|
fixedHeader: false, // sticky header
|
||||||
|
fixSiderbar: false, // sticky siderbar
|
||||||
|
autoHideHeader: false, // auto hide header
|
||||||
|
colorWeak: false,
|
||||||
|
multipage: true, //默认多页签模式
|
||||||
|
// vue-ls options
|
||||||
|
storageOptions: {
|
||||||
|
namespace: 'pro__', // key prefix
|
||||||
|
name: 'ls', // name variable Vue.[ls] or this.[$ls],
|
||||||
|
storage: 'local', // storage name session, local, memory
|
||||||
|
}
|
||||||
|
}
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
import Vue from "vue";
|
||||||
|
import App from "./App.vue";
|
||||||
|
import router from "./router";
|
||||||
|
import store from "./store";
|
||||||
|
import "./config";
|
||||||
|
import Storage from "vue-ls";
|
||||||
|
import Antd from "ant-design-vue";
|
||||||
|
import "ant-design-vue/dist/antd.less"; // or 'ant-design-vue/dist/antd.less'
|
||||||
|
import "@/permission"; // permission control
|
||||||
|
import hasPermission from "@/utils/hasPermission";
|
||||||
|
import {
|
||||||
|
ACCESS_TOKEN,
|
||||||
|
DEFAULT_COLOR,
|
||||||
|
DEFAULT_THEME,
|
||||||
|
DEFAULT_LAYOUT_MODE,
|
||||||
|
DEFAULT_COLOR_WEAK,
|
||||||
|
SIDEBAR_TYPE,
|
||||||
|
DEFAULT_FIXED_HEADER,
|
||||||
|
DEFAULT_FIXED_HEADER_HIDDEN,
|
||||||
|
DEFAULT_FIXED_SIDEMENU,
|
||||||
|
DEFAULT_CONTENT_WIDTH_TYPE,
|
||||||
|
DEFAULT_MULTI_PAGE
|
||||||
|
} from "@/store/mutation-types";
|
||||||
|
import config from "@/defaultSettings";
|
||||||
|
import JeroComponents from "@/components/jero/index";
|
||||||
|
import JDictSelectTag from "./components/dict/index.js";
|
||||||
|
import '@/utils/filter' // base filtermybatis-plus
|
||||||
|
import vTrim from "@/utils/vTrim";
|
||||||
|
|
||||||
|
Vue.config.productionTip = false;
|
||||||
|
Vue.use(Storage, config.storageOptions);
|
||||||
|
Vue.use(Antd);
|
||||||
|
Vue.use(hasPermission)
|
||||||
|
Vue.use(router);
|
||||||
|
Vue.use(JeroComponents);
|
||||||
|
Vue.use(JDictSelectTag);
|
||||||
|
Vue.use(vTrim)
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
router,
|
||||||
|
store,
|
||||||
|
mounted() {
|
||||||
|
store.commit("SET_SIDEBAR_TYPE", Vue.ls.get(SIDEBAR_TYPE, true));
|
||||||
|
store.commit("TOGGLE_THEME", Vue.ls.get(DEFAULT_THEME, config.navTheme));
|
||||||
|
store.commit("TOGGLE_LAYOUT_MODE", Vue.ls.get(DEFAULT_LAYOUT_MODE, config.layout));
|
||||||
|
store.commit("TOGGLE_FIXED_HEADER", Vue.ls.get(DEFAULT_FIXED_HEADER, config.fixedHeader));
|
||||||
|
store.commit("TOGGLE_FIXED_SIDERBAR", Vue.ls.get(DEFAULT_FIXED_SIDEMENU, config.fixSiderbar));
|
||||||
|
store.commit("TOGGLE_CONTENT_WIDTH", Vue.ls.get(DEFAULT_CONTENT_WIDTH_TYPE, config.contentWidth));
|
||||||
|
store.commit("TOGGLE_FIXED_HEADER_HIDDEN", Vue.ls.get(DEFAULT_FIXED_HEADER_HIDDEN, config.autoHideHeader));
|
||||||
|
store.commit("TOGGLE_WEAK", Vue.ls.get(DEFAULT_COLOR_WEAK, config.colorWeak));
|
||||||
|
store.commit("TOGGLE_COLOR", Vue.ls.get(DEFAULT_COLOR, config.primaryColor));
|
||||||
|
store.commit("SET_TOKEN", Vue.ls.get(ACCESS_TOKEN));
|
||||||
|
store.commit("SET_MULTI_PAGE", Vue.ls.get(DEFAULT_MULTI_PAGE, config.multipage));
|
||||||
|
},
|
||||||
|
render: (h) => h(App)
|
||||||
|
}).$mount("#app");
|
||||||
@@ -0,0 +1,375 @@
|
|||||||
|
/**
|
||||||
|
* 新增修改完成调用 modalFormOk方法 编辑弹框组件ref定义为modalForm
|
||||||
|
* 高级查询按钮调用 superQuery方法 高级查询组件ref定义为superQueryModal
|
||||||
|
* data中url定义 list为查询列表 delete为删除单条记录 deleteBatch为批量删除
|
||||||
|
*/
|
||||||
|
import { filterObj } from '@/utils/util';
|
||||||
|
import { deleteAction, getAction,downFile,getFileAccessHttpUrl } from '@/api/manage'
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { ACCESS_TOKEN, TENANT_ID } from "@/store/mutation-types"
|
||||||
|
import store from '@/store'
|
||||||
|
import {Modal} from 'ant-design-vue'
|
||||||
|
import { deleteByDepartId } from "@api/api";
|
||||||
|
|
||||||
|
export const JeroListMixin = {
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
/* 查询条件-请不要在queryParam中声明非字符串值的属性 */
|
||||||
|
queryParam: {},
|
||||||
|
// 记录上一次查询的条件,防止查询处输入框的数据发生变化,未点击查询按钮,再点击分页的时候数据发生变化
|
||||||
|
lastQueryParam: {},
|
||||||
|
/* 数据源 */
|
||||||
|
dataSource:[],
|
||||||
|
/* 分页参数 */
|
||||||
|
ipagination:{
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
pageSizeOptions: ['2','10', '20', '30'],
|
||||||
|
showTotal: (total, range) => {
|
||||||
|
return range[0] + "-" + range[1] + " 共" + total + "条"
|
||||||
|
},
|
||||||
|
showQuickJumper: true,
|
||||||
|
showSizeChanger: true,
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
/* 排序参数 */
|
||||||
|
isorter:{
|
||||||
|
column: 'createTime',
|
||||||
|
order: 'desc',
|
||||||
|
},
|
||||||
|
/* 筛选参数 */
|
||||||
|
filters: {},
|
||||||
|
/* table加载状态 */
|
||||||
|
loading:false,
|
||||||
|
/* table选中keys*/
|
||||||
|
selectedRowKeys: [],
|
||||||
|
/* table选中records*/
|
||||||
|
selectionRows: [],
|
||||||
|
/* 查询折叠 */
|
||||||
|
toggleSearchStatus:false,
|
||||||
|
/* 高级查询条件生效状态 */
|
||||||
|
superQueryFlag:false,
|
||||||
|
/* 高级查询条件 */
|
||||||
|
superQueryParams: '',
|
||||||
|
/** 高级查询拼接方式 */
|
||||||
|
superQueryMatchType: 'and',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
if(!this.disableMixinCreated){
|
||||||
|
this.loadData();
|
||||||
|
//初始化字典配置 在自己页面定义
|
||||||
|
this.initDictConfig();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
//token header
|
||||||
|
tokenHeader(){
|
||||||
|
let head = {'X-Access-Token': Vue.ls.get(ACCESS_TOKEN)}
|
||||||
|
let tenantid = Vue.ls.get(TENANT_ID)
|
||||||
|
if(tenantid){
|
||||||
|
head['tenant_id'] = tenantid
|
||||||
|
}
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
loadData(arg) {
|
||||||
|
if(!this.url.list){
|
||||||
|
this.$message.error("请设置url.list属性!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//加载数据 若传入参数1则加载第一页的内容
|
||||||
|
if (arg === 1) {
|
||||||
|
this.ipagination.current = 1;
|
||||||
|
}
|
||||||
|
var params = this.getQueryParams();//查询条件
|
||||||
|
this.loading = true;
|
||||||
|
getAction(this.url.list, params).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
//update-begin---author:zhangyafei Date:20201118 for:适配不分页的数据列表------------
|
||||||
|
this.dataSource = res.result.records||res.result;
|
||||||
|
if(res.result.total)
|
||||||
|
{
|
||||||
|
this.ipagination.total = res.result.total;
|
||||||
|
//清空列表选中
|
||||||
|
this.onClearSelected()
|
||||||
|
}else{
|
||||||
|
this.ipagination.total = 0;
|
||||||
|
}
|
||||||
|
//update-end---author:zhangyafei Date:20201118 for:适配不分页的数据列表------------
|
||||||
|
}
|
||||||
|
if(res.code===510){
|
||||||
|
this.$message.warning(res.message)
|
||||||
|
}
|
||||||
|
this.loading = false;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
initDictConfig(){
|
||||||
|
|
||||||
|
},
|
||||||
|
handleSuperQuery(params, matchType) {
|
||||||
|
//高级查询方法
|
||||||
|
if(!params){
|
||||||
|
this.superQueryParams=''
|
||||||
|
this.superQueryFlag = false
|
||||||
|
}else{
|
||||||
|
this.superQueryFlag = true
|
||||||
|
this.superQueryParams=JSON.stringify(params)
|
||||||
|
this.superQueryMatchType = matchType
|
||||||
|
}
|
||||||
|
this.loadData(1)
|
||||||
|
},
|
||||||
|
getQueryParams() {
|
||||||
|
//获取查询条件
|
||||||
|
let sqp = {}
|
||||||
|
if(this.superQueryParams){
|
||||||
|
sqp['superQueryParams']=encodeURI(this.superQueryParams)
|
||||||
|
sqp['superQueryMatchType'] = this.superQueryMatchType
|
||||||
|
}
|
||||||
|
// var param = Object.assign(sqp, this.queryParam, this.isorter ,this.filters);
|
||||||
|
var param = Object.assign(sqp, this.lastQueryParam, this.isorter ,this.filters);
|
||||||
|
param.field = this.getQueryField();
|
||||||
|
param.pageNo = this.ipagination.current;
|
||||||
|
param.pageSize = this.ipagination.pageSize;
|
||||||
|
return filterObj(param);
|
||||||
|
},
|
||||||
|
getQueryField() {
|
||||||
|
//TODO 字段权限控制
|
||||||
|
var str = "id,";
|
||||||
|
this.columns.forEach(function (value) {
|
||||||
|
str += "," + value.dataIndex;
|
||||||
|
});
|
||||||
|
return str;
|
||||||
|
},
|
||||||
|
|
||||||
|
onSelectChange(selectedRowKeys, selectionRows) {
|
||||||
|
this.selectedRowKeys = selectedRowKeys;
|
||||||
|
this.selectionRows = selectionRows;
|
||||||
|
},
|
||||||
|
onClearSelected() {
|
||||||
|
this.selectedRowKeys = [];
|
||||||
|
this.selectionRows = [];
|
||||||
|
},
|
||||||
|
searchQuery() {
|
||||||
|
this.lastQueryParam = { ...this.queryParam }
|
||||||
|
this.loadData(1);
|
||||||
|
},
|
||||||
|
superQuery() {
|
||||||
|
this.$refs.superQueryModal.show();
|
||||||
|
},
|
||||||
|
searchReset() {
|
||||||
|
this.lastQueryParam = {}
|
||||||
|
this.queryParam = {}
|
||||||
|
this.loadData(1);
|
||||||
|
},
|
||||||
|
batchDel: function () {
|
||||||
|
if(!this.url.deleteBatch){
|
||||||
|
this.$message.error("请设置url.deleteBatch属性!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.selectedRowKeys.length <= 0) {
|
||||||
|
this.$message.warning('请选择一条记录!');
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
var ids = "";
|
||||||
|
for (var a = 0; a < this.selectedRowKeys.length; a++) {
|
||||||
|
ids += this.selectedRowKeys[a] + ",";
|
||||||
|
}
|
||||||
|
var that = this;
|
||||||
|
this.$confirm({
|
||||||
|
title: "确认删除",
|
||||||
|
content: "是否删除选中数据?",
|
||||||
|
onOk: function () {
|
||||||
|
that.loading = true;
|
||||||
|
deleteAction(that.url.deleteBatch, {ids: ids}).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
that.$message.success(res.message);
|
||||||
|
// 判断当前删除的数据是否是最后一页的全部数据,如果是的话页码减一
|
||||||
|
if (that.ipagination.current > 1 && (that.ipagination.current === Math.ceil(that.ipagination.total / that.ipagination.pageSize)) &&
|
||||||
|
that.selectedRowKeys.length === that.dataSource.length
|
||||||
|
) {
|
||||||
|
that.ipagination.current -= 1
|
||||||
|
}
|
||||||
|
that.loadData();
|
||||||
|
that.onClearSelected();
|
||||||
|
} else {
|
||||||
|
that.$message.warning(res.message);
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
that.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleDelete: function (id) {
|
||||||
|
if(!this.url.delete){
|
||||||
|
this.$message.error("请设置url.delete属性!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var that = this;
|
||||||
|
this.$confirm({
|
||||||
|
title: "确认删除",
|
||||||
|
content: "确定要删除此条数据吗?",
|
||||||
|
onOk: function() {
|
||||||
|
deleteAction(that.url.delete, {id: id}).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
that.$message.success(res.message);
|
||||||
|
// 判断当前删除的数据是否是最后一页的最后一条数据,如果是的话页码减一
|
||||||
|
if (that.ipagination.current > 1 && ((that.ipagination.current - 1) * that.ipagination.pageSize) + 1 === that.ipagination.total) {
|
||||||
|
that.ipagination.current -= 1
|
||||||
|
}
|
||||||
|
that.loadData();
|
||||||
|
} else {
|
||||||
|
that.$message.warning(res.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleEdit: function (record) {
|
||||||
|
this.$refs.modalForm.edit(record);
|
||||||
|
this.$refs.modalForm.title = "编辑";
|
||||||
|
this.$refs.modalForm.disableSubmit = false;
|
||||||
|
},
|
||||||
|
handleAdd: function () {
|
||||||
|
this.$refs.modalForm.add();
|
||||||
|
this.$refs.modalForm.title = "新增";
|
||||||
|
this.$refs.modalForm.disableSubmit = false;
|
||||||
|
},
|
||||||
|
handleTableChange(pagination, filters, sorter) {
|
||||||
|
//分页、排序、筛选变化时触发
|
||||||
|
//TODO 筛选
|
||||||
|
if (Object.keys(sorter).length > 0) {
|
||||||
|
this.isorter.column = sorter.field;
|
||||||
|
this.isorter.order = "ascend" == sorter.order ? "asc" : "desc"
|
||||||
|
}
|
||||||
|
this.ipagination = pagination;
|
||||||
|
this.loadData();
|
||||||
|
},
|
||||||
|
handleToggleSearch(){
|
||||||
|
this.toggleSearchStatus = !this.toggleSearchStatus;
|
||||||
|
},
|
||||||
|
// 给popup查询使用(查询区域不支持回填多个字段,限制只返回一个字段)
|
||||||
|
getPopupField(fields){
|
||||||
|
return fields.split(',')[0]
|
||||||
|
},
|
||||||
|
modalFormOk() {
|
||||||
|
// 新增/修改 成功时,重载列表
|
||||||
|
this.loadData();
|
||||||
|
//清空列表选中
|
||||||
|
this.onClearSelected()
|
||||||
|
},
|
||||||
|
handleDetail:function(record){
|
||||||
|
this.$refs.modalForm.edit(record);
|
||||||
|
this.$refs.modalForm.title="详情";
|
||||||
|
this.$refs.modalForm.disableSubmit = true;
|
||||||
|
},
|
||||||
|
/* 导出 */
|
||||||
|
handleExportXls2(){
|
||||||
|
let paramsStr = encodeURI(JSON.stringify(this.getQueryParams()));
|
||||||
|
let url = `${window._CONFIG['domianURL']}/${this.url.exportXlsUrl}?paramsStr=${paramsStr}`;
|
||||||
|
window.location.href = url;
|
||||||
|
},
|
||||||
|
handleExportXls(fileName){
|
||||||
|
if(!fileName || typeof fileName != "string"){
|
||||||
|
fileName = "导出文件"
|
||||||
|
}
|
||||||
|
let param = this.getQueryParams();
|
||||||
|
if(this.selectedRowKeys && this.selectedRowKeys.length>0){
|
||||||
|
param['selections'] = this.selectedRowKeys.join(",")
|
||||||
|
}
|
||||||
|
console.log("导出参数",param)
|
||||||
|
downFile(this.url.exportXlsUrl,param).then((data)=>{
|
||||||
|
if (!data) {
|
||||||
|
this.$message.warning("文件下载失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (typeof window.navigator.msSaveBlob !== 'undefined') {
|
||||||
|
window.navigator.msSaveBlob(new Blob([data],{type: 'application/vnd.ms-excel'}), fileName+'.xls')
|
||||||
|
}else{
|
||||||
|
let url = window.URL.createObjectURL(new Blob([data],{type: 'application/vnd.ms-excel'}))
|
||||||
|
let link = document.createElement('a')
|
||||||
|
link.style.display = 'none'
|
||||||
|
link.href = url
|
||||||
|
link.setAttribute('download', fileName+'.xls')
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link); //下载完成移除元素
|
||||||
|
window.URL.revokeObjectURL(url); //释放掉blob对象
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/* 导入 */
|
||||||
|
handleImportExcel(info){
|
||||||
|
if (info.file.status !== 'uploading') {
|
||||||
|
console.log(info.file, info.fileList);
|
||||||
|
}
|
||||||
|
if (info.file.status === 'done') {
|
||||||
|
if (info.file.response.success) {
|
||||||
|
// this.$message.success(`${info.file.name} 文件上传成功`);
|
||||||
|
if (info.file.response.code === 201) {
|
||||||
|
let { message, result: { msg, fileUrl, fileName } } = info.file.response
|
||||||
|
let href = window._CONFIG['domianURL'] + fileUrl
|
||||||
|
this.$warning({
|
||||||
|
title: message,
|
||||||
|
content: (<div>
|
||||||
|
<span>{msg}</span><br/>
|
||||||
|
<span>具体详情请 <a href={href} target="_blank" download={fileName}>点击下载</a> </span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.$message.success(info.file.response.message || `${info.file.name} 文件上传成功`)
|
||||||
|
}
|
||||||
|
this.loadData()
|
||||||
|
} else {
|
||||||
|
this.$message.error(`${info.file.name} ${info.file.response.message}.`);
|
||||||
|
}
|
||||||
|
} else if (info.file.status === 'error') {
|
||||||
|
if (info.file.response.status === 500) {
|
||||||
|
let data = info.file.response
|
||||||
|
const token = Vue.ls.get(ACCESS_TOKEN)
|
||||||
|
if (token && data.message.includes("Token失效")) {
|
||||||
|
Modal.error({
|
||||||
|
title: '登录已过期',
|
||||||
|
content: '很抱歉,登录已过期,请重新登录',
|
||||||
|
okText: '重新登录',
|
||||||
|
mask: false,
|
||||||
|
onOk: () => {
|
||||||
|
store.dispatch('Logout').then(() => {
|
||||||
|
Vue.ls.remove(ACCESS_TOKEN)
|
||||||
|
window.location.reload();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$message.error(`文件上传失败: ${info.file.msg} `);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* 图片预览 */
|
||||||
|
getImgView(text){
|
||||||
|
if(text && text.indexOf(",")>0){
|
||||||
|
text = text.substring(0,text.indexOf(","))
|
||||||
|
}
|
||||||
|
return getFileAccessHttpUrl(text)
|
||||||
|
},
|
||||||
|
/* 文件下载 */
|
||||||
|
// update--autor:lvdandan-----date:20200630------for:修改下载文件方法名uploadFile改为downloadFile------
|
||||||
|
downloadFile(text){
|
||||||
|
if(!text){
|
||||||
|
this.$message.warning("未知的文件")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(text.indexOf(",")>0){
|
||||||
|
text = text.substring(0,text.indexOf(","))
|
||||||
|
}
|
||||||
|
let url = getFileAccessHttpUrl(text)
|
||||||
|
window.open(url);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import Vue from "vue";
|
||||||
|
import router from "./router";
|
||||||
|
import store from "./store";
|
||||||
|
import NProgress from "nprogress"; // progress bar
|
||||||
|
import "nprogress/nprogress.css"; // progress bar style
|
||||||
|
// import notification from 'ant-design-vue/es/notification'
|
||||||
|
import { ACCESS_TOKEN, INDEX_MAIN_PAGE_PATH } from "@/store/mutation-types";
|
||||||
|
import { generateIndexRouter } from "@/utils/util";
|
||||||
|
|
||||||
|
NProgress.configure({ showSpinner: false }); // NProgress Configuration
|
||||||
|
|
||||||
|
const whiteList = ["/user/login", "/user/register", "/user/register-result", "/user/alteration"]; // no redirect whitelist
|
||||||
|
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
NProgress.start(); // start progress bar
|
||||||
|
|
||||||
|
if (Vue.ls.get(ACCESS_TOKEN)) {
|
||||||
|
/* has token */
|
||||||
|
if (to.path === "/user/login") {
|
||||||
|
next({ path: INDEX_MAIN_PAGE_PATH });
|
||||||
|
NProgress.done();
|
||||||
|
} else {
|
||||||
|
if (store.getters.permissionList.length === 0) {
|
||||||
|
store.dispatch("GetPermissionList").then(res => {
|
||||||
|
const menuData = res.result.menu;
|
||||||
|
if (menuData === null || menuData === "" || menuData === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let constRoutes = [];
|
||||||
|
constRoutes = generateIndexRouter(menuData);
|
||||||
|
// 添加主界面路由
|
||||||
|
store.dispatch("UpdateAppRouter", { constRoutes }).then(() => {
|
||||||
|
// 根据roles权限生成可访问的路由表
|
||||||
|
// 动态添加可访问路由表
|
||||||
|
router.addRoutes(store.getters.addRouters);
|
||||||
|
const redirect = decodeURIComponent(from.query.redirect || to.path);
|
||||||
|
if (to.path === redirect) {
|
||||||
|
// hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
|
||||||
|
next({ ...to, replace: true });
|
||||||
|
} else {
|
||||||
|
// 跳转到目的路由
|
||||||
|
next({ path: redirect });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
/* notification.error({
|
||||||
|
message: '系统提示',
|
||||||
|
description: '请求用户信息失败,请重试!'
|
||||||
|
})*/
|
||||||
|
store.dispatch("Logout").then(() => {
|
||||||
|
next({ path: "/user/login", query: { redirect: to.fullPath } });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (whiteList.indexOf(to.path) !== -1) {
|
||||||
|
// 在免登录白名单,直接进入
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
next({ path: "/user/login", query: { redirect: to.fullPath } });
|
||||||
|
NProgress.done(); // if current page is login will not trigger afterEach hook, so manually handle it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.afterEach(() => {
|
||||||
|
NProgress.done(); // finish progress bar
|
||||||
|
});
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import Vue from "vue";
|
||||||
|
import Router from "vue-router";
|
||||||
|
import { constantRouterMap } from "@/config/router.config";
|
||||||
|
|
||||||
|
Vue.use(Router);
|
||||||
|
|
||||||
|
const originalPush = Router.prototype.push;
|
||||||
|
Router.prototype.push = function push(location, onResolve, onReject) {
|
||||||
|
if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject);
|
||||||
|
return originalPush.call(this, location).catch(err => err);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default new Router({
|
||||||
|
mode: "history",
|
||||||
|
base: process.env.BASE_URL,
|
||||||
|
scrollBehavior: () => ({ y: 0 }),
|
||||||
|
routes: constantRouterMap
|
||||||
|
});
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import { USER_INFO, ENHANCE_PRE } from "@/store/mutation-types"
|
||||||
|
const getters = {
|
||||||
|
device: state => state.app.device,
|
||||||
|
theme: state => state.app.theme,
|
||||||
|
color: state => state.app.color,
|
||||||
|
token: state => state.user.token,
|
||||||
|
avatar: state => {state.user.avatar = Vue.ls.get(USER_INFO).avatar; return state.user.avatar},
|
||||||
|
username: state => state.user.username,
|
||||||
|
nickname: state => {state.user.realname = Vue.ls.get(USER_INFO).realname; return state.user.realname},
|
||||||
|
welcome: state => state.user.welcome,
|
||||||
|
permissionList: state => state.user.permissionList,
|
||||||
|
userInfo: state => {state.user.info = Vue.ls.get(USER_INFO); return state.user.info},
|
||||||
|
addRouters: state => state.permission.addRouters,
|
||||||
|
onlAuthFields: state => {return state.online.authFields },
|
||||||
|
enhanceJs:(state) => (code) => {
|
||||||
|
state.enhance.enhanceJs[code] = Vue.ls.get(ENHANCE_PRE+code);
|
||||||
|
return state.enhance.enhanceJs[code]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getters
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import Vue from "vue";
|
||||||
|
import Vuex from "vuex";
|
||||||
|
|
||||||
|
import app from "./modules/app";
|
||||||
|
import user from "./modules/user";
|
||||||
|
import permission from "./modules/permission";
|
||||||
|
import enhance from "./modules/enhance";
|
||||||
|
import online from "./modules/online";
|
||||||
|
import getters from "./getters";
|
||||||
|
|
||||||
|
Vue.use(Vuex);
|
||||||
|
|
||||||
|
export default new Vuex.Store({
|
||||||
|
modules: {
|
||||||
|
app,
|
||||||
|
user,
|
||||||
|
permission,
|
||||||
|
enhance,
|
||||||
|
online
|
||||||
|
},
|
||||||
|
state: {},
|
||||||
|
mutations: {},
|
||||||
|
actions: {},
|
||||||
|
getters
|
||||||
|
});
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
import Vue from "vue";
|
||||||
|
import {
|
||||||
|
SIDEBAR_TYPE,
|
||||||
|
DEFAULT_THEME,
|
||||||
|
DEFAULT_LAYOUT_MODE,
|
||||||
|
DEFAULT_COLOR,
|
||||||
|
DEFAULT_COLOR_WEAK,
|
||||||
|
DEFAULT_FIXED_HEADER,
|
||||||
|
DEFAULT_FIXED_SIDEMENU,
|
||||||
|
DEFAULT_FIXED_HEADER_HIDDEN,
|
||||||
|
DEFAULT_CONTENT_WIDTH_TYPE,
|
||||||
|
DEFAULT_MULTI_PAGE
|
||||||
|
} from "@/store/mutation-types";
|
||||||
|
|
||||||
|
const app = {
|
||||||
|
state: {
|
||||||
|
sidebar: {
|
||||||
|
opened: true,
|
||||||
|
withoutAnimation: false
|
||||||
|
},
|
||||||
|
device: "desktop",
|
||||||
|
theme: "",
|
||||||
|
layout: "",
|
||||||
|
contentWidth: "",
|
||||||
|
fixedHeader: false,
|
||||||
|
fixSiderbar: false,
|
||||||
|
autoHideHeader: false,
|
||||||
|
color: null,
|
||||||
|
weak: false,
|
||||||
|
multipage: true //默认多页签模式
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
SET_SIDEBAR_TYPE: (state, type) => {
|
||||||
|
state.sidebar.opened = type;
|
||||||
|
Vue.ls.set(SIDEBAR_TYPE, type);
|
||||||
|
},
|
||||||
|
CLOSE_SIDEBAR: (state, withoutAnimation) => {
|
||||||
|
Vue.ls.set(SIDEBAR_TYPE, true);
|
||||||
|
state.sidebar.opened = false;
|
||||||
|
state.sidebar.withoutAnimation = withoutAnimation;
|
||||||
|
},
|
||||||
|
TOGGLE_DEVICE: (state, device) => {
|
||||||
|
state.device = device;
|
||||||
|
},
|
||||||
|
TOGGLE_THEME: (state, theme) => {
|
||||||
|
// setStore('_DEFAULT_THEME', theme)
|
||||||
|
Vue.ls.set(DEFAULT_THEME, theme);
|
||||||
|
state.theme = theme;
|
||||||
|
},
|
||||||
|
TOGGLE_LAYOUT_MODE: (state, layout) => {
|
||||||
|
Vue.ls.set(DEFAULT_LAYOUT_MODE, layout);
|
||||||
|
state.layout = layout;
|
||||||
|
},
|
||||||
|
TOGGLE_FIXED_HEADER: (state, fixed) => {
|
||||||
|
Vue.ls.set(DEFAULT_FIXED_HEADER, fixed);
|
||||||
|
state.fixedHeader = fixed;
|
||||||
|
},
|
||||||
|
TOGGLE_FIXED_SIDERBAR: (state, fixed) => {
|
||||||
|
Vue.ls.set(DEFAULT_FIXED_SIDEMENU, fixed);
|
||||||
|
state.fixSiderbar = fixed;
|
||||||
|
},
|
||||||
|
TOGGLE_FIXED_HEADER_HIDDEN: (state, show) => {
|
||||||
|
Vue.ls.set(DEFAULT_FIXED_HEADER_HIDDEN, show);
|
||||||
|
state.autoHideHeader = show;
|
||||||
|
},
|
||||||
|
TOGGLE_CONTENT_WIDTH: (state, type) => {
|
||||||
|
Vue.ls.set(DEFAULT_CONTENT_WIDTH_TYPE, type);
|
||||||
|
state.contentWidth = type;
|
||||||
|
},
|
||||||
|
TOGGLE_COLOR: (state, color) => {
|
||||||
|
Vue.ls.set(DEFAULT_COLOR, color);
|
||||||
|
state.color = color;
|
||||||
|
},
|
||||||
|
TOGGLE_WEAK: (state, flag) => {
|
||||||
|
Vue.ls.set(DEFAULT_COLOR_WEAK, flag);
|
||||||
|
state.weak = flag;
|
||||||
|
},
|
||||||
|
SET_MULTI_PAGE(state, multipageFlag) {
|
||||||
|
Vue.ls.set(DEFAULT_MULTI_PAGE, multipageFlag);
|
||||||
|
state.multipage = multipageFlag;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
setSidebar: ({ commit }, type) => {
|
||||||
|
commit("SET_SIDEBAR_TYPE", type);
|
||||||
|
},
|
||||||
|
CloseSidebar({ commit }, { withoutAnimation }) {
|
||||||
|
commit("CLOSE_SIDEBAR", withoutAnimation);
|
||||||
|
},
|
||||||
|
ToggleDevice({ commit }, device) {
|
||||||
|
commit("TOGGLE_DEVICE", device);
|
||||||
|
},
|
||||||
|
ToggleTheme({ commit }, theme) {
|
||||||
|
commit("TOGGLE_THEME", theme);
|
||||||
|
},
|
||||||
|
ToggleLayoutMode({ commit }, mode) {
|
||||||
|
commit("TOGGLE_LAYOUT_MODE", mode);
|
||||||
|
},
|
||||||
|
ToggleFixedHeader({ commit }, fixedHeader) {
|
||||||
|
if (!fixedHeader) {
|
||||||
|
commit("TOGGLE_FIXED_HEADER_HIDDEN", false);
|
||||||
|
}
|
||||||
|
commit("TOGGLE_FIXED_HEADER", fixedHeader);
|
||||||
|
},
|
||||||
|
ToggleFixSiderbar({ commit }, fixSiderbar) {
|
||||||
|
commit("TOGGLE_FIXED_SIDERBAR", fixSiderbar);
|
||||||
|
},
|
||||||
|
ToggleFixedHeaderHidden({ commit }, show) {
|
||||||
|
commit("TOGGLE_FIXED_HEADER_HIDDEN", show);
|
||||||
|
},
|
||||||
|
ToggleContentWidth({ commit }, type) {
|
||||||
|
commit("TOGGLE_CONTENT_WIDTH", type);
|
||||||
|
},
|
||||||
|
ToggleColor({ commit }, color) {
|
||||||
|
commit("TOGGLE_COLOR", color);
|
||||||
|
},
|
||||||
|
ToggleWeak({ commit }, weakFlag) {
|
||||||
|
commit("TOGGLE_WEAK", weakFlag);
|
||||||
|
},
|
||||||
|
ToggleMultipage({ commit }, multipageFlag) {
|
||||||
|
commit("SET_MULTI_PAGE", multipageFlag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default app;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
const enhance = {
|
||||||
|
state: {
|
||||||
|
enhanceJs:{
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
ADD_TABLE_ENHANCE: (state, record) => {
|
||||||
|
if(!state.enhanceJs){
|
||||||
|
let obj = {}
|
||||||
|
let arr = []
|
||||||
|
arr.push({...record})
|
||||||
|
obj[record.code] = arr
|
||||||
|
state.enhanceJs = obj
|
||||||
|
}else{
|
||||||
|
if(!state.enhanceJs[record.code]){
|
||||||
|
let arr = []
|
||||||
|
arr.push({...record})
|
||||||
|
state.enhanceJs[record.code] = arr
|
||||||
|
}
|
||||||
|
state.enhanceJs[record.code].push({...record})
|
||||||
|
}
|
||||||
|
let arr = state.enhanceJs[record.code]
|
||||||
|
while(arr.length>16){
|
||||||
|
arr.shift()
|
||||||
|
}
|
||||||
|
Vue.ls.set('enhance_'+record['code'], arr)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
addEhanceRecord({ commit }, record) {
|
||||||
|
commit('ADD_TABLE_ENHANCE', record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default enhance
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import Vue from "vue";
|
||||||
|
|
||||||
|
const online = {
|
||||||
|
state: {
|
||||||
|
//存储对象属性 value,text
|
||||||
|
authFields: []
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
SET_AUTHFIELDS: (state, fields) => {
|
||||||
|
console.log("fields", fields);
|
||||||
|
Vue.set(state, "authFields", fields);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
// TODO 如果没找到可以尝试请求一下
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
xxxxxx({ commit }, userInfo) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default online;
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { asyncRouterMap, constantRouterMap } from "@/config/router.config"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤账户是否拥有某一个权限,并将菜单从加载列表移除
|
||||||
|
*
|
||||||
|
* @param permission
|
||||||
|
* @param route
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function hasPermission(permission, route) {
|
||||||
|
if (route.meta && route.meta.permission) {
|
||||||
|
let flag = -1
|
||||||
|
for (let i = 0, len = permission.length; i < len; i++) {
|
||||||
|
flag = route.meta.permission.indexOf(permission[i])
|
||||||
|
if (flag >= 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单账户多角色时,使用该方法可过滤角色不存在的菜单
|
||||||
|
*
|
||||||
|
* @param roles
|
||||||
|
* @param route
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line
|
||||||
|
function hasRole(roles, route) {
|
||||||
|
if (route.meta && route.meta.roles) {
|
||||||
|
return route.meta.roles.indexOf(roles.id)
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterAsyncRouter(routerMap, roles) {
|
||||||
|
const accessedRouters = routerMap.filter(route => {
|
||||||
|
if (hasPermission(roles.permissionList, route)) {
|
||||||
|
if (route.children && route.children.length) {
|
||||||
|
route.children = filterAsyncRouter(route.children, roles)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
return accessedRouters
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const permission = {
|
||||||
|
state: {
|
||||||
|
routers: constantRouterMap,
|
||||||
|
addRouters: []
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
SET_ROUTERS: (state, data) => {
|
||||||
|
state.addRouters = data
|
||||||
|
state.routers = constantRouterMap.concat(data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
GenerateRoutes({ commit }, data) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const { roles } = data
|
||||||
|
let accessedRouters
|
||||||
|
accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
|
||||||
|
commit('SET_ROUTERS', accessedRouters)
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 动态添加主界面路由,需要缓存
|
||||||
|
UpdateAppRouter({ commit }, routes) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
//const [ roles ] = routes.constRoutes
|
||||||
|
let routelist = routes.constRoutes;
|
||||||
|
commit('SET_ROUTERS', routelist)
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default permission
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import { login, logout, phoneLogin, thirdLogin } from "@/api/login"
|
||||||
|
import { ACCESS_TOKEN, USER_NAME,USER_INFO,USER_AUTH,SYS_BUTTON_AUTH,UI_CACHE_DB_DICT_DATA,TENANT_ID,CACHE_INCLUDED_ROUTES } from "@/store/mutation-types"
|
||||||
|
import { welcome } from "@/utils/util"
|
||||||
|
import { queryPermissionsByUser } from '@/api/api'
|
||||||
|
import { getAction } from '@/api/manage'
|
||||||
|
|
||||||
|
const user = {
|
||||||
|
state: {
|
||||||
|
token: '',
|
||||||
|
username: '',
|
||||||
|
realname: '',
|
||||||
|
tenantid:'',
|
||||||
|
welcome: '',
|
||||||
|
avatar: '',
|
||||||
|
permissionList: [],
|
||||||
|
info: {}
|
||||||
|
},
|
||||||
|
|
||||||
|
mutations: {
|
||||||
|
SET_TOKEN: (state, token) => {
|
||||||
|
state.token = token
|
||||||
|
},
|
||||||
|
SET_NAME: (state, { username, realname, welcome }) => {
|
||||||
|
state.username = username
|
||||||
|
state.realname = realname
|
||||||
|
state.welcome = welcome
|
||||||
|
},
|
||||||
|
SET_AVATAR: (state, avatar) => {
|
||||||
|
state.avatar = avatar
|
||||||
|
},
|
||||||
|
SET_PERMISSIONLIST: (state, permissionList) => {
|
||||||
|
state.permissionList = permissionList
|
||||||
|
},
|
||||||
|
SET_INFO: (state, info) => {
|
||||||
|
state.info = info
|
||||||
|
},
|
||||||
|
SET_TENANT: (state, id) => {
|
||||||
|
state.tenantid = id
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
// CAS验证登录
|
||||||
|
ValidateLogin({ commit }, userInfo) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
getAction("/sys/cas/client/validateLogin",userInfo).then(response => {
|
||||||
|
console.log("----cas 登录--------",response);
|
||||||
|
if(response.success){
|
||||||
|
const result = response.result
|
||||||
|
const userInfo = result.userInfo
|
||||||
|
Vue.ls.set(ACCESS_TOKEN, result.token, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
Vue.ls.set(USER_NAME, userInfo.username, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
Vue.ls.set(USER_INFO, userInfo, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
commit('SET_TOKEN', result.token)
|
||||||
|
commit('SET_INFO', userInfo)
|
||||||
|
commit('SET_NAME', { username: userInfo.username,realname: userInfo.realname, welcome: welcome() })
|
||||||
|
commit('SET_AVATAR', userInfo.avatar)
|
||||||
|
resolve(response)
|
||||||
|
}else{
|
||||||
|
resolve(response)
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 登录
|
||||||
|
Login({ commit }, userInfo) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
login(userInfo).then(response => {
|
||||||
|
if(response.code =='200'){
|
||||||
|
const result = response.result
|
||||||
|
const userInfo = result.userInfo
|
||||||
|
Vue.ls.set(ACCESS_TOKEN, result.token, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
Vue.ls.set(USER_NAME, userInfo.username, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
Vue.ls.set(USER_INFO, userInfo, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
Vue.ls.set(UI_CACHE_DB_DICT_DATA, result.sysAllDictItems, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
commit('SET_TOKEN', result.token)
|
||||||
|
commit('SET_INFO', userInfo)
|
||||||
|
commit('SET_NAME', { username: userInfo.username,realname: userInfo.realname, welcome: welcome() })
|
||||||
|
commit('SET_AVATAR', userInfo.avatar)
|
||||||
|
resolve(response)
|
||||||
|
}else{
|
||||||
|
reject(response)
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
//手机号登录
|
||||||
|
PhoneLogin({ commit }, userInfo) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
phoneLogin(userInfo).then(response => {
|
||||||
|
if(response.code =='200'){
|
||||||
|
const result = response.result
|
||||||
|
const userInfo = result.userInfo
|
||||||
|
Vue.ls.set(ACCESS_TOKEN, result.token, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
Vue.ls.set(USER_NAME, userInfo.username, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
Vue.ls.set(USER_INFO, userInfo, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
Vue.ls.set(UI_CACHE_DB_DICT_DATA, result.sysAllDictItems, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
commit('SET_TOKEN', result.token)
|
||||||
|
commit('SET_INFO', userInfo)
|
||||||
|
commit('SET_NAME', { username: userInfo.username,realname: userInfo.realname, welcome: welcome() })
|
||||||
|
commit('SET_AVATAR', userInfo.avatar)
|
||||||
|
resolve(response)
|
||||||
|
}else{
|
||||||
|
reject(response)
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 获取用户信息
|
||||||
|
GetPermissionList({ commit }) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
queryPermissionsByUser().then(response => {
|
||||||
|
const menuData = response.result.menu;
|
||||||
|
const authData = response.result.auth;
|
||||||
|
const allAuthData = response.result.allAuth;
|
||||||
|
//Vue.ls.set(USER_AUTH,authData);
|
||||||
|
sessionStorage.setItem(USER_AUTH,JSON.stringify(authData));
|
||||||
|
sessionStorage.setItem(SYS_BUTTON_AUTH,JSON.stringify(allAuthData));
|
||||||
|
if (menuData && menuData.length > 0) {
|
||||||
|
//update--begin--autor:qinfeng-----date:20200109------for:JEECG-63 一级菜单的子菜单全部是隐藏路由,则一级菜单不显示------
|
||||||
|
menuData.forEach((item, index) => {
|
||||||
|
if (item["children"]) {
|
||||||
|
let hasChildrenMenu = item["children"].filter((i) => {
|
||||||
|
return !i.hidden || i.hidden == false
|
||||||
|
})
|
||||||
|
if (hasChildrenMenu == null || hasChildrenMenu.length == 0) {
|
||||||
|
item["hidden"] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//console.log(" menu show json ", menuData)
|
||||||
|
//update--end--autor:qinfeng-----date:20200109------for:JEECG-63 一级菜单的子菜单全部是隐藏路由,则一级菜单不显示------
|
||||||
|
commit('SET_PERMISSIONLIST', menuData)
|
||||||
|
} else {
|
||||||
|
reject('getPermissionList: permissions must be a non-null array !')
|
||||||
|
}
|
||||||
|
resolve(response)
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 登出
|
||||||
|
Logout({ commit, state }) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let logoutToken = state.token;
|
||||||
|
commit('SET_TOKEN', '')
|
||||||
|
commit('SET_PERMISSIONLIST', [])
|
||||||
|
Vue.ls.remove(ACCESS_TOKEN)
|
||||||
|
Vue.ls.remove(USER_INFO)
|
||||||
|
Vue.ls.remove(USER_NAME)
|
||||||
|
Vue.ls.remove(UI_CACHE_DB_DICT_DATA)
|
||||||
|
Vue.ls.remove(CACHE_INCLUDED_ROUTES)
|
||||||
|
//console.log('logoutToken: '+ logoutToken)
|
||||||
|
logout(logoutToken).then(() => {
|
||||||
|
if (process.env.VUE_APP_SSO == 'true') {
|
||||||
|
let sevice = 'http://' + window.location.host + '/'
|
||||||
|
let serviceUrl = encodeURIComponent(sevice)
|
||||||
|
window.location.href = process.env.VUE_APP_CAS_BASE_URL + '/logout?service=' + serviceUrl
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
}).catch(() => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 第三方登录
|
||||||
|
ThirdLogin({ commit }, param) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
thirdLogin(param.token,param.thirdType).then(response => {
|
||||||
|
if(response.code =='200'){
|
||||||
|
const result = response.result
|
||||||
|
const userInfo = result.userInfo
|
||||||
|
Vue.ls.set(ACCESS_TOKEN, result.token, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
Vue.ls.set(USER_NAME, userInfo.username, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
Vue.ls.set(USER_INFO, userInfo, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
commit('SET_TOKEN', result.token)
|
||||||
|
commit('SET_INFO', userInfo)
|
||||||
|
commit('SET_NAME', { username: userInfo.username,realname: userInfo.realname, welcome: welcome() })
|
||||||
|
commit('SET_AVATAR', userInfo.avatar)
|
||||||
|
resolve(response)
|
||||||
|
}else{
|
||||||
|
reject(response)
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
saveTenant({ commit }, id){
|
||||||
|
Vue.ls.set(TENANT_ID, id, 7 * 24 * 60 * 60 * 1000)
|
||||||
|
commit('SET_TENANT', id)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default user
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
export const ACCESS_TOKEN = 'Access-Token'
|
||||||
|
export const SIDEBAR_TYPE = 'SIDEBAR_TYPE'
|
||||||
|
export const DEFAULT_THEME = 'DEFAULT_THEME'
|
||||||
|
export const DEFAULT_LAYOUT_MODE = 'DEFAULT_LAYOUT_MODE'
|
||||||
|
export const DEFAULT_COLOR = 'DEFAULT_COLOR'
|
||||||
|
export const DEFAULT_COLOR_WEAK = 'DEFAULT_COLOR_WEAK'
|
||||||
|
export const DEFAULT_FIXED_HEADER = 'DEFAULT_FIXED_HEADER'
|
||||||
|
export const DEFAULT_FIXED_SIDEMENU= 'DEFAULT_FIXED_SIDEMENU'
|
||||||
|
export const DEFAULT_FIXED_HEADER_HIDDEN = 'DEFAULT_FIXED_HEADER_HIDDEN'
|
||||||
|
export const DEFAULT_CONTENT_WIDTH_TYPE = 'DEFAULT_CONTENT_WIDTH_TYPE'
|
||||||
|
export const DEFAULT_MULTI_PAGE = 'DEFAULT_MULTI_PAGE'
|
||||||
|
export const USER_NAME = 'Login_Username'
|
||||||
|
export const USER_INFO = 'Login_Userinfo'
|
||||||
|
export const USER_AUTH = 'LOGIN_USER_BUTTON_AUTH'
|
||||||
|
export const SYS_BUTTON_AUTH = 'SYS_BUTTON_AUTH'
|
||||||
|
export const ENCRYPTED_STRING = 'ENCRYPTED_STRING'
|
||||||
|
export const ENHANCE_PRE = 'enhance_'
|
||||||
|
export const UI_CACHE_DB_DICT_DATA = 'UI_CACHE_DB_DICT_DATA'
|
||||||
|
export const INDEX_MAIN_PAGE_PATH = '/dashboard/analysis'
|
||||||
|
export const TENANT_ID = 'TENANT_ID'
|
||||||
|
export const ONL_AUTH_FIELDS = 'ONL_AUTH_FIELDS'
|
||||||
|
//路由缓存问题,关闭了tab页时再打开就不刷新 #842
|
||||||
|
export const CACHE_INCLUDED_ROUTES = 'CACHE_INCLUDED_ROUTES'
|
||||||
|
export const CONTENT_WIDTH_TYPE = {
|
||||||
|
Fluid: 'Fluid',
|
||||||
|
Fixed: 'Fixed'
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
import { getVmParentByName } from '@/utils/util'
|
||||||
|
|
||||||
|
const FormTypes = {
|
||||||
|
normal: 'normal',
|
||||||
|
input: 'input',
|
||||||
|
inputNumber: 'inputNumber',
|
||||||
|
checkbox: 'checkbox',
|
||||||
|
select: 'select',
|
||||||
|
date: 'date',
|
||||||
|
datetime: 'datetime',
|
||||||
|
upload: 'upload',
|
||||||
|
file: 'file',
|
||||||
|
image: 'image',
|
||||||
|
popup:'popup',
|
||||||
|
list_multi:"list_multi",
|
||||||
|
sel_search:"sel_search",
|
||||||
|
radio:'radio',
|
||||||
|
checkbox_meta:"checkbox_meta",
|
||||||
|
input_pop:'input_pop',
|
||||||
|
sel_depart: 'sel_depart',
|
||||||
|
sel_user: 'sel_user',
|
||||||
|
slot: 'slot',
|
||||||
|
hidden: 'hidden'
|
||||||
|
}
|
||||||
|
const VALIDATE_NO_PASSED = Symbol()
|
||||||
|
export { FormTypes, VALIDATE_NO_PASSED }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定的 $refs 对象
|
||||||
|
* 有时候可能会遇到组件未挂载到页面中的情况,导致无法获取 $refs 中的某个对象
|
||||||
|
* 这个方法可以等待挂载完成之后再返回 $refs 的对象,避免报错
|
||||||
|
* @author sunjianlei
|
||||||
|
**/
|
||||||
|
export function getRefPromise(vm, name) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
(function next() {
|
||||||
|
let ref = vm.$refs[name]
|
||||||
|
if (ref) {
|
||||||
|
resolve(ref)
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
next()
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一次性验证主表单和所有的次表单
|
||||||
|
* @param form 主表单 form 对象
|
||||||
|
* @param cases 接收一个数组,每项都是一个JEditableTable实例
|
||||||
|
* @returns {Promise<any>}
|
||||||
|
* @author sunjianlei
|
||||||
|
*/
|
||||||
|
export function validateFormAndTables(form, cases) {
|
||||||
|
|
||||||
|
if (!(form && typeof form.validateFields === 'function')) {
|
||||||
|
throw `form 参数需要的是一个form对象,而传入的却是${typeof form}`
|
||||||
|
}
|
||||||
|
|
||||||
|
let options = {}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// 验证主表表单
|
||||||
|
form.validateFields((err, values) => {
|
||||||
|
err ? reject({ error: VALIDATE_NO_PASSED }) : resolve(values)
|
||||||
|
})
|
||||||
|
}).then(values => {
|
||||||
|
Object.assign(options, { formValue: values })
|
||||||
|
// 验证所有子表的表单
|
||||||
|
return validateTables(cases)
|
||||||
|
}).then(all => {
|
||||||
|
Object.assign(options, { tablesValue: all })
|
||||||
|
return Promise.resolve(options)
|
||||||
|
}).catch(error => {
|
||||||
|
return Promise.reject(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证并获取一个或多个表格的所有值
|
||||||
|
* @param cases 接收一个数组,每项都是一个JEditableTable实例
|
||||||
|
* @param deleteTempId 是否删除临时ID,如果设为true,行编辑就不返回新增行的ID,ID需要后台生成
|
||||||
|
* @author sunjianlei
|
||||||
|
*/
|
||||||
|
export function validateTables(cases, deleteTempId) {
|
||||||
|
if (!(cases instanceof Array)) {
|
||||||
|
throw `'validateTables'函数的'cases'参数需要的是一个数组,而传入的却是${typeof cases}`
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let tables = []
|
||||||
|
let index = 0;
|
||||||
|
if(!cases || cases.length === 0){
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
(function next() {
|
||||||
|
let vm = cases[index]
|
||||||
|
vm.getAll(true, deleteTempId).then(all => {
|
||||||
|
tables[index] = all
|
||||||
|
// 判断校验是否全部完成,完成返回成功,否则继续进行下一步校验
|
||||||
|
if (++index === cases.length) {
|
||||||
|
resolve(tables)
|
||||||
|
} else (
|
||||||
|
next()
|
||||||
|
)
|
||||||
|
}, error => {
|
||||||
|
// 出现未验证通过的表单,不再进行下一步校验,直接返回失败并跳转到该表格
|
||||||
|
if (error === VALIDATE_NO_PASSED) {
|
||||||
|
// 尝试获取tabKey,如果在ATab组件内即可获取
|
||||||
|
let paneKey;
|
||||||
|
let tabPane = getVmParentByName(vm, 'ATabPane')
|
||||||
|
if (tabPane) {
|
||||||
|
paneKey = tabPane.$vnode.key
|
||||||
|
}
|
||||||
|
reject({error: VALIDATE_NO_PASSED, index, paneKey})
|
||||||
|
}
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})()
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
import { USER_AUTH,SYS_BUTTON_AUTH } from "@/store/mutation-types"
|
||||||
|
|
||||||
|
export function disabledAuthFilter(code,formData) {
|
||||||
|
if(nodeDisabledAuth(code,formData)){
|
||||||
|
return true;
|
||||||
|
}else{
|
||||||
|
return globalDisabledAuth(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function nodeDisabledAuth(code,formData){
|
||||||
|
// console.log("页面权限禁用--NODE--开始");
|
||||||
|
let permissionList = [];
|
||||||
|
try {
|
||||||
|
// console.log("页面权限禁用--NODE--开始",formData);
|
||||||
|
if (formData) {
|
||||||
|
let bpmList = formData.permissionList;
|
||||||
|
permissionList = bpmList.filter(item=>item.type=='2')
|
||||||
|
// for (let bpm of bpmList) {
|
||||||
|
// if(bpm.type == '2') {
|
||||||
|
// permissionList.push(bpm);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}else{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
//console.log("页面权限异常----", e);
|
||||||
|
}
|
||||||
|
if (permissionList.length == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// console.log("流程节点页面权限禁用--NODE--开始");
|
||||||
|
let permissions = [];
|
||||||
|
for (let item of permissionList) {
|
||||||
|
if(item.type == '2') {
|
||||||
|
permissions.push(item.action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log("页面权限----"+code);
|
||||||
|
if (!permissions.includes(code)) {
|
||||||
|
return false;
|
||||||
|
}else{
|
||||||
|
for (let item2 of permissionList) {
|
||||||
|
if(code === item2.action){
|
||||||
|
// console.log("流程节点页面权限禁用--NODE--生效");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function globalDisabledAuth(code){
|
||||||
|
// console.log("全局页面禁用权限--Global--开始");
|
||||||
|
|
||||||
|
let permissionList = [];
|
||||||
|
let allPermissionList = [];
|
||||||
|
|
||||||
|
//let authList = Vue.ls.get(USER_AUTH);
|
||||||
|
let authList = JSON.parse(sessionStorage.getItem(USER_AUTH) || "[]");
|
||||||
|
for (let auth of authList) {
|
||||||
|
if(auth.type == '2') {
|
||||||
|
permissionList.push(auth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log("页面禁用权限--Global--",sessionStorage.getItem(SYS_BUTTON_AUTH));
|
||||||
|
let allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || "[]");
|
||||||
|
for (let gauth of allAuthList) {
|
||||||
|
if(gauth.type == '2') {
|
||||||
|
allPermissionList.push(gauth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//设置全局配置是否有命中
|
||||||
|
let gFlag = false;//禁用命中
|
||||||
|
let invalidFlag = false;//无效命中
|
||||||
|
if(allPermissionList != null && allPermissionList != "" && allPermissionList != undefined && allPermissionList.length > 0){
|
||||||
|
for (let itemG of allPermissionList) {
|
||||||
|
if(code === itemG.action){
|
||||||
|
if(itemG.status == '0'){
|
||||||
|
invalidFlag = true;
|
||||||
|
break;
|
||||||
|
}else{
|
||||||
|
gFlag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(invalidFlag){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (permissionList === null || permissionList === "" || permissionList === undefined||permissionList.length<=0) {
|
||||||
|
return gFlag;
|
||||||
|
}
|
||||||
|
let permissions = [];
|
||||||
|
for (let item of permissionList) {
|
||||||
|
if(item.type == '2') {
|
||||||
|
permissions.push(item.action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log("页面禁用权限----"+code);
|
||||||
|
if (!permissions.includes(code)) {
|
||||||
|
return gFlag;
|
||||||
|
}else{
|
||||||
|
for (let item2 of permissionList) {
|
||||||
|
if(code === item2.action){
|
||||||
|
// console.log("全局页面权限解除禁用--Global--生效");
|
||||||
|
gFlag = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return gFlag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function colAuthFilter(columns,pre) {
|
||||||
|
let authList = getNoAuthCols(pre);
|
||||||
|
const cols = columns.filter(item => {
|
||||||
|
if (hasColoum(item,authList)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
return cols
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【子表行编辑】实现两个功能:
|
||||||
|
* 1、隐藏JEditableTable无权限的字段
|
||||||
|
* 2、禁用JEditableTable无权限的字段
|
||||||
|
* @param columns
|
||||||
|
* @param pre
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function colAuthFilterJEditableTable(columns,pre) {
|
||||||
|
let authList = getAllShowAndDisabledAuthCols(pre);
|
||||||
|
const cols = columns.filter(item => {
|
||||||
|
let oneAuth = authList.find(auth => {
|
||||||
|
return auth.action === pre + item.key;
|
||||||
|
});
|
||||||
|
if(!oneAuth){
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//代码严谨处理,防止一个授权标识,配置多次
|
||||||
|
if(oneAuth instanceof Array){
|
||||||
|
oneAuth = oneAuth[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
//禁用逻辑
|
||||||
|
if (oneAuth.type == '2' && !oneAuth.isAuth) {
|
||||||
|
item["disabled"] = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
//隐藏逻辑逻辑
|
||||||
|
if (oneAuth.type == '1' && !oneAuth.isAuth) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return cols
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function hasColoum(item,authList){
|
||||||
|
if (authList.includes(item.dataIndex)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//权限无效时不做控制,有效时控制,只能控制 显示不显示
|
||||||
|
//根据授权码前缀获取未授权的列信息
|
||||||
|
export function getNoAuthCols(pre){
|
||||||
|
if(!pre || pre.length==0){
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
let permissionList = [];
|
||||||
|
let allPermissionList = [];
|
||||||
|
|
||||||
|
//let authList = Vue.ls.get(USER_AUTH);
|
||||||
|
let authList = JSON.parse(sessionStorage.getItem(USER_AUTH) || "[]");
|
||||||
|
for (let auth of authList) {
|
||||||
|
//显示策略,有效状态
|
||||||
|
if(auth.type == '1'&&startWith(auth.action,pre)) {
|
||||||
|
permissionList.push(substrPre(auth.action,pre));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log("页面禁用权限--Global--",sessionStorage.getItem(SYS_BUTTON_AUTH));
|
||||||
|
let allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || "[]");
|
||||||
|
for (let gauth of allAuthList) {
|
||||||
|
//显示策略,有效状态
|
||||||
|
if(gauth.type == '1'&&gauth.status == '1'&&startWith(gauth.action,pre)) {
|
||||||
|
allPermissionList.push(substrPre(gauth.action,pre));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const cols = allPermissionList.filter(item => {
|
||||||
|
if (permissionList.includes(item)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
return cols;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Online的行编辑按钮权限,添加至本地存储
|
||||||
|
*/
|
||||||
|
export function addOnlineBtAuth2Storage(pre, authList){
|
||||||
|
let allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || "[]");
|
||||||
|
let newAuthList = allAuthList.filter(item=>{
|
||||||
|
if(!item.action){
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return item.action.indexOf(pre)<0
|
||||||
|
})
|
||||||
|
if(authList && authList.length>0){
|
||||||
|
for(let item of authList){
|
||||||
|
newAuthList.push({
|
||||||
|
action: pre+item,
|
||||||
|
type:1,
|
||||||
|
status:1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
let temp = JSON.parse(sessionStorage.getItem(USER_AUTH) || "[]");
|
||||||
|
let newArr = temp.filter(item=>{
|
||||||
|
if(!item.action){
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return item.action.indexOf(pre)<0 || authList.indexOf(item.action.replace(pre, ''))<0
|
||||||
|
})
|
||||||
|
sessionStorage.setItem(USER_AUTH, JSON.stringify(newArr))
|
||||||
|
}
|
||||||
|
sessionStorage.setItem(SYS_BUTTON_AUTH, JSON.stringify(newAuthList))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 额外增加方法【用于行编辑组件】
|
||||||
|
* date: 2020-04-05
|
||||||
|
* author: scott
|
||||||
|
* @param pre
|
||||||
|
* @returns {*[]}
|
||||||
|
*/
|
||||||
|
function getAllShowAndDisabledAuthCols(pre){
|
||||||
|
//用户拥有的权限
|
||||||
|
let userAuthList = JSON.parse(sessionStorage.getItem(USER_AUTH) || "[]");
|
||||||
|
//全部权限配置
|
||||||
|
let allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || "[]");
|
||||||
|
|
||||||
|
let newAllAuthList = allAuthList.map(function (item, index) {
|
||||||
|
let hasAuthArray = userAuthList.filter(u => u.action===item.action );
|
||||||
|
if (hasAuthArray && hasAuthArray.length>0) {
|
||||||
|
item["isAuth"] = true
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
|
||||||
|
return newAllAuthList;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startWith(str,pre) {
|
||||||
|
if (pre == null || pre == "" || str==null|| str==""|| str.length == 0 || pre.length > str.length)
|
||||||
|
return false;
|
||||||
|
if (str.substr(0, pre.length) == pre)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function substrPre(str,pre) {
|
||||||
|
return str.substr(pre.length);
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
//判断是否IE<11浏览器
|
||||||
|
export function isIE() {
|
||||||
|
return navigator.userAgent.indexOf('compatible') > -1 && navigator.userAgent.indexOf('MSIE') > -1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isIE11() {
|
||||||
|
return navigator.userAgent.indexOf('Trident') > -1 && navigator.userAgent.indexOf('rv:11.0') > -1
|
||||||
|
}
|
||||||
|
|
||||||
|
//判断是否IE的Edge浏览器
|
||||||
|
export function isEdge() {
|
||||||
|
return navigator.userAgent.indexOf('Edge') > -1 && !isIE()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getIEVersion() {
|
||||||
|
let userAgent = navigator.userAgent //取得浏览器的userAgent字符串
|
||||||
|
let isIE = isIE()
|
||||||
|
let isIE11 = isIE11()
|
||||||
|
let isEdge = isEdge()
|
||||||
|
|
||||||
|
if (isIE) {
|
||||||
|
let reIE = new RegExp('MSIE (\\d+\\.\\d+);')
|
||||||
|
reIE.test(userAgent)
|
||||||
|
let fIEVersion = parseFloat(RegExp['$1'])
|
||||||
|
if (fIEVersion === 7 || fIEVersion === 8 || fIEVersion === 9 || fIEVersion === 10) {
|
||||||
|
return fIEVersion
|
||||||
|
} else {
|
||||||
|
return 6//IE版本<7
|
||||||
|
}
|
||||||
|
} else if (isEdge) {
|
||||||
|
return 'edge'
|
||||||
|
} else if (isIE11) {
|
||||||
|
return 11
|
||||||
|
} else {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import enquireJs from 'enquire.js'
|
||||||
|
|
||||||
|
const enquireScreen = function (call) {
|
||||||
|
// tablet
|
||||||
|
const handler = {
|
||||||
|
match: function () {
|
||||||
|
call && call(0)
|
||||||
|
},
|
||||||
|
unmatch: function () {
|
||||||
|
call && call(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// mobile
|
||||||
|
const handler2 = {
|
||||||
|
match: () => {
|
||||||
|
call && call(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enquireJs.register('screen and (max-width: 1087.99px)', handler)
|
||||||
|
enquireJs.register('screen and (max-width: 767.99px)', handler2)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default enquireScreen
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
|||||||
|
import Vue from "vue";
|
||||||
|
import * as dayjs from "dayjs";
|
||||||
|
|
||||||
|
Vue.filter('NumberFormat', function (value) {
|
||||||
|
if (!value) {
|
||||||
|
return '0'
|
||||||
|
}
|
||||||
|
let intPartFormat = value.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,') //将整数部分逢三一断
|
||||||
|
return intPartFormat
|
||||||
|
})
|
||||||
|
|
||||||
|
Vue.filter('dayjs', function(dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') {
|
||||||
|
return dayjs(dataStr).format(pattern)
|
||||||
|
})
|
||||||
|
|
||||||
|
Vue.filter('moment', function(dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') {
|
||||||
|
return dayjs(dataStr).format(pattern)
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 字符串超长截取省略号显示 */
|
||||||
|
Vue.filter('ellipsis', function (value, vlength = 25) {
|
||||||
|
if(!value){
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (value.length > vlength) {
|
||||||
|
return value.slice(0, vlength) + '...'
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
})
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import { USER_AUTH,SYS_BUTTON_AUTH } from "@/store/mutation-types"
|
||||||
|
|
||||||
|
const hasPermission = {
|
||||||
|
install (Vue, options) {
|
||||||
|
Vue.directive('has', {
|
||||||
|
inserted: (el, binding, vnode)=>{
|
||||||
|
// console.log("页面权限控制----");
|
||||||
|
//console.time()
|
||||||
|
//节点权限处理,如果命中则不进行全局权限处理
|
||||||
|
if(!filterNodePermission(el, binding, vnode)){
|
||||||
|
filterGlobalPermission(el, binding, vnode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程节点权限控制
|
||||||
|
*/
|
||||||
|
export function filterNodePermission(el, binding, vnode) {
|
||||||
|
let permissionList = [];
|
||||||
|
try {
|
||||||
|
let obj = vnode.context.$props.formData;
|
||||||
|
if (obj) {
|
||||||
|
let bpmList = obj.permissionList;
|
||||||
|
for (let bpm of bpmList) {
|
||||||
|
if(bpm.type != '2') {
|
||||||
|
permissionList.push(bpm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
//console.log("页面权限异常----", e);
|
||||||
|
}
|
||||||
|
if (permissionList === null || permissionList === "" || permissionList === undefined||permissionList.length<=0) {
|
||||||
|
//el.parentNode.removeChild(el)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("流程节点页面权限--NODE--");
|
||||||
|
let permissions = [];
|
||||||
|
for (let item of permissionList) {
|
||||||
|
if(item.type != '2') {
|
||||||
|
permissions.push(item.action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log("页面权限----"+permissions);
|
||||||
|
//console.log("页面权限----"+binding.value);
|
||||||
|
if (!permissions.includes(binding.value)) {
|
||||||
|
//el.parentNode.removeChild(el)
|
||||||
|
return false;
|
||||||
|
}else{
|
||||||
|
for (let item2 of permissionList) {
|
||||||
|
if(binding.value === item2.action){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局权限控制
|
||||||
|
*/
|
||||||
|
export function filterGlobalPermission(el, binding, vnode) {
|
||||||
|
console.log("全局页面权限--Global--");
|
||||||
|
|
||||||
|
let permissionList = [];
|
||||||
|
let allPermissionList = [];
|
||||||
|
|
||||||
|
//let authList = Vue.ls.get(USER_AUTH);
|
||||||
|
let authList = JSON.parse(sessionStorage.getItem(USER_AUTH) || "[]");
|
||||||
|
for (let auth of authList) {
|
||||||
|
if(auth.type != '2') {
|
||||||
|
permissionList.push(auth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log("页面权限--Global--",sessionStorage.getItem(SYS_BUTTON_AUTH));
|
||||||
|
let allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || "[]");
|
||||||
|
for (let gauth of allAuthList) {
|
||||||
|
if(gauth.type != '2') {
|
||||||
|
allPermissionList.push(gauth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//设置全局配置是否有命中
|
||||||
|
let invalidFlag = false;//无效命中
|
||||||
|
if(allPermissionList != null && allPermissionList != "" && allPermissionList != undefined && allPermissionList.length > 0){
|
||||||
|
for (let itemG of allPermissionList) {
|
||||||
|
if(binding.value === itemG.action){
|
||||||
|
if(itemG.status == '0'){
|
||||||
|
invalidFlag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(invalidFlag){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (permissionList === null || permissionList === "" || permissionList === undefined||permissionList.length<=0) {
|
||||||
|
el.parentNode.removeChild(el);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let permissions = [];
|
||||||
|
for (let item of permissionList) {
|
||||||
|
//权限策略1显示2禁用
|
||||||
|
if(item.type != '2'){
|
||||||
|
//update--begin--autor:wangshuai-----date:20200729------for:按钮权限,授权标识的提示信息是多个用逗号分隔逻辑处理 gitee#I1OUGU-------
|
||||||
|
if(item.action){
|
||||||
|
if(item.action.includes(",")){
|
||||||
|
let split = item.action.split(",")
|
||||||
|
for (let i = 0; i <split.length ; i++) {
|
||||||
|
if(!split[i] ||split[i].length==0){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
permissions.push(split[i]);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
permissions.push(item.action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//update--end--autor:wangshuai-----date:20200729------for:按钮权限,授权标识的提示信息是多个用逗号分隔逻辑处理 gitee#I1OUGU------
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!permissions.includes(binding.value)) {
|
||||||
|
el.parentNode.removeChild(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default hasPermission;
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// import Vue from 'vue'
|
||||||
|
import { mapState } from "vuex";
|
||||||
|
|
||||||
|
// const mixinsComputed = Vue.config.optionMergeStrategies.computed
|
||||||
|
// const mixinsMethods = Vue.config.optionMergeStrategies.methods
|
||||||
|
|
||||||
|
const mixin = {
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
layoutMode: state => state.app.layout,
|
||||||
|
navTheme: state => state.app.theme,
|
||||||
|
primaryColor: state => state.app.color,
|
||||||
|
colorWeak: state => state.app.weak,
|
||||||
|
multipage: state => state.app.multipage,//多页签设置
|
||||||
|
fixedHeader: state => state.app.fixedHeader,
|
||||||
|
fixSiderbar: state => state.app.fixSiderbar,
|
||||||
|
contentWidth: state => state.app.contentWidth,
|
||||||
|
autoHideHeader: state => state.app.autoHideHeader,
|
||||||
|
sidebarOpened: state => state.app.sidebar.opened
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mixinDevice = {
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
device: state => state.app.device,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isMobile () {
|
||||||
|
return this.device === 'mobile'
|
||||||
|
},
|
||||||
|
isDesktop () {
|
||||||
|
return this.device === 'desktop'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { mixin, mixinDevice }
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export function actionToObject(json) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(json)
|
||||||
|
} catch (e) {
|
||||||
|
console.log('err', e.message)
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* 功能模块
|
||||||
|
* @type {{LEAVE_EARLY: {text: string, value: string}, LATE: {text: string, value: string}, ABSENT: {text: string, value: string}, NO_SIGN: {text: string, value: string}, NORMAL: {text: string, value: string}}}
|
||||||
|
*/
|
||||||
|
export const FunctionEnum = {
|
||||||
|
EoaCmsBanner: { path: 'modules/eoa/cmsoa/modules/EoaCmsBanner', formData: 'carouselImg' },
|
||||||
|
EoaCmsNewsInfo: { path: 'modules/eoa/cmsoa/modules/EoaCmsNewsInfo', formData: 'newsInfo' },
|
||||||
|
EoaCmsRuleInfo: { path: 'modules/eoa/cmsoa/modules/EoaCmsRuleInfo', formData: 'ruleDownInfo' },
|
||||||
|
EoaCmsSignNews: { path: 'modules/eoa/cmsoa/modules/EoaCmsSignNews', formData: 'signNews' },
|
||||||
|
EoaCmsUserNotice: { path: 'modules/eoa/cmsoa/modules/EoaCmsUserNotice', formData: 'userNotice' },
|
||||||
|
EoaCmsPlan: { path: 'modules/eoa/cmsoa/modules/EoaCmsPlan', formData: '' },
|
||||||
|
EoaCmsLink: { path: 'modules/eoa/cmsoa/modules/EoaCmsLink', formData: '' },
|
||||||
|
EoaCmsCommUse:{ path: 'modules/eoa/cmsbpm/modules/EoaCmsCommUse', formData: '' },
|
||||||
|
EoaCmsMyProcess:{ path: 'modules/eoa/cmsbpm/modules/EoaCmsMyProcess', formData: '' },
|
||||||
|
EoaCmsApplyProcess:{ path: 'modules/eoa/cmsbpm/modules/EoaCmsApplyProcess', formData: '' },
|
||||||
|
EoaCmsProcessNotice:{ path: 'modules/eoa/cmsbpm/modules/EoaCmsProcessNotice', formData: '' },
|
||||||
|
EoaCmsProcessChatData:{ path: 'modules/eoa/cmsbpm/modules/EoaCmsProcessChatData', formData: '' },
|
||||||
|
EoaCmsProcessTypeChat:{ path: 'modules/eoa/cmsbpm/modules/EoaCmsProcessTypeChat', formData: '' },
|
||||||
|
EoaCmsEmail:{ path: 'modules/eoa/cmsbpm/modules/EoaCmsEmail', formData: '' },
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
* 该文件截取自 "ant-design-vue/es/_util/props-util.js" 文件,并对其做出特殊修改
|
||||||
|
*/
|
||||||
|
function classNames() {
|
||||||
|
let classes = []
|
||||||
|
|
||||||
|
for (let i = 0; i < arguments.length; i++) {
|
||||||
|
let arg = arguments[i]
|
||||||
|
if (!arg) continue
|
||||||
|
|
||||||
|
let argType = typeof arg
|
||||||
|
|
||||||
|
if (argType === 'string' || argType === 'number') {
|
||||||
|
classes.push(arg)
|
||||||
|
} else if (Array.isArray(arg) && arg.length) {
|
||||||
|
let inner = classNames.apply(null, arg)
|
||||||
|
if (inner) {
|
||||||
|
classes.push(inner)
|
||||||
|
}
|
||||||
|
} else if (argType === 'object') {
|
||||||
|
for (let key in arg) {
|
||||||
|
if (arg.hasOwnProperty(key) && arg[key]) {
|
||||||
|
classes.push(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return classes.join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
const camelizeRE = /-(\w)/g
|
||||||
|
|
||||||
|
function camelize(str) {
|
||||||
|
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function objectCamelize(obj) {
|
||||||
|
let res = {}
|
||||||
|
Object.keys(obj).forEach(k => (res[camelize(k)] = obj[k]))
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseStyleText(cssText = '', camel) {
|
||||||
|
const res = {}
|
||||||
|
const listDelimiter = /;(?![^(]*\))/g
|
||||||
|
const propertyDelimiter = /:(.+)/
|
||||||
|
cssText.split(listDelimiter).forEach(function (item) {
|
||||||
|
if (item) {
|
||||||
|
const tmp = item.split(propertyDelimiter)
|
||||||
|
if (tmp.length > 1) {
|
||||||
|
const k = camel ? camelize(tmp[0].trim()) : tmp[0].trim()
|
||||||
|
res[k] = tmp[1].trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getClass(ele) {
|
||||||
|
let data = {}
|
||||||
|
if (ele.data) {
|
||||||
|
data = ele.data
|
||||||
|
} else if (ele.$vnode && ele.$vnode.data) {
|
||||||
|
data = ele.$vnode.data
|
||||||
|
}
|
||||||
|
const tempCls = data.class || {}
|
||||||
|
const staticClass = data.staticClass
|
||||||
|
let cls = {}
|
||||||
|
staticClass &&
|
||||||
|
staticClass.split(' ').forEach(c => {
|
||||||
|
cls[c.trim()] = true
|
||||||
|
})
|
||||||
|
if (typeof tempCls === 'string') {
|
||||||
|
tempCls.split(' ').forEach(c => {
|
||||||
|
cls[c.trim()] = true
|
||||||
|
})
|
||||||
|
} else if (Array.isArray(tempCls)) {
|
||||||
|
classNames(tempCls)
|
||||||
|
.split(' ')
|
||||||
|
.forEach(c => {
|
||||||
|
cls[c.trim()] = true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
cls = { ...cls, ...tempCls }
|
||||||
|
}
|
||||||
|
return cls
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStyle(ele, camel) {
|
||||||
|
|
||||||
|
getClass(ele)
|
||||||
|
|
||||||
|
let data = {}
|
||||||
|
if (ele.data) {
|
||||||
|
data = ele.data
|
||||||
|
} else if (ele.$vnode && ele.$vnode.data) {
|
||||||
|
data = ele.$vnode.data
|
||||||
|
}
|
||||||
|
|
||||||
|
// update-begin-author:sunjianlei date:20200303 for: style 和 staticStyle 可以共存
|
||||||
|
let style = data.style || {}
|
||||||
|
let staticStyle = data.staticStyle
|
||||||
|
staticStyle = staticStyle ? objectCamelize(data.staticStyle) : {}
|
||||||
|
// update-end-author:sunjianlei date:20200303 for: style 和 staticStyle 可以共存
|
||||||
|
|
||||||
|
if (typeof style === 'string') {
|
||||||
|
style = parseStyleText(style, camel)
|
||||||
|
} else if (camel && style) {
|
||||||
|
// 驼峰化
|
||||||
|
style = objectCamelize(style)
|
||||||
|
}
|
||||||
|
return { ...staticStyle, ...style }
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import axios from 'axios'
|
||||||
|
import store from '@/store'
|
||||||
|
import {Modal, notification} from 'ant-design-vue'
|
||||||
|
import { ACCESS_TOKEN, TENANT_ID } from "@/store/mutation-types"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【指定 axios的 baseURL】
|
||||||
|
* 如果手工指定 baseURL: '/jero-boot'
|
||||||
|
* 则映射后端域名,通过 vue.config.js
|
||||||
|
* @type {*|string}
|
||||||
|
*/
|
||||||
|
let apiBaseUrl = window._CONFIG['domianURL'] || "/jero-boot";
|
||||||
|
//console.log("apiBaseUrl= ",apiBaseUrl)
|
||||||
|
// 创建 axios 实例
|
||||||
|
const service = axios.create({
|
||||||
|
//baseURL: '/jero-boot',
|
||||||
|
baseURL: apiBaseUrl, // api base_url
|
||||||
|
timeout: 9000 // 请求超时时间
|
||||||
|
})
|
||||||
|
|
||||||
|
const err = (error) => {
|
||||||
|
if (error.response) {
|
||||||
|
let data = error.response.data
|
||||||
|
const token = Vue.ls.get(ACCESS_TOKEN)
|
||||||
|
console.log("------异常响应------",token)
|
||||||
|
console.log("------异常响应------",error.response.status)
|
||||||
|
switch (error.response.status) {
|
||||||
|
case 403:
|
||||||
|
notification.error({ message: '系统提示', description: '拒绝访问',duration: 4})
|
||||||
|
break
|
||||||
|
case 500:
|
||||||
|
console.log("------error.response------",error.response)
|
||||||
|
// update-begin- --- author:liusq ------ date:20200910 ---- for:处理Blob情况----
|
||||||
|
let type=error.response.request.responseType;
|
||||||
|
if(type === 'blob'){
|
||||||
|
blobToJson(data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// update-end- --- author:liusq ------ date:20200910 ---- for:处理Blob情况----
|
||||||
|
//notification.error({ message: '系统提示', description:'Token失效,请重新登录!',duration: 4})
|
||||||
|
if(token && data.message.includes("Token失效")){
|
||||||
|
// update-begin- --- author:scott ------ date:20190225 ---- for:Token失效采用弹框模式,不直接跳转----
|
||||||
|
Modal.error({
|
||||||
|
title: '登录已过期',
|
||||||
|
content: '很抱歉,登录已过期,请重新登录',
|
||||||
|
okText: '重新登录',
|
||||||
|
mask: false,
|
||||||
|
onOk: () => {
|
||||||
|
store.dispatch('Logout').then(() => {
|
||||||
|
Vue.ls.remove(ACCESS_TOKEN)
|
||||||
|
try {
|
||||||
|
let path = window.document.location.pathname
|
||||||
|
console.log("location pathname -> "+path)
|
||||||
|
if(path!="/" && path.indexOf('/user/login')==-1){
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
}catch (e) {
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// update-end- --- author:scott ------ date:20190225 ---- for:Token失效采用弹框模式,不直接跳转----
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 404:
|
||||||
|
notification.error({ message: '系统提示', description:'很抱歉,资源未找到!',duration: 4})
|
||||||
|
break
|
||||||
|
case 504:
|
||||||
|
notification.error({ message: '系统提示', description: '网络超时'})
|
||||||
|
break
|
||||||
|
case 401:
|
||||||
|
notification.error({ message: '系统提示', description:'未授权,请重新登录',duration: 4})
|
||||||
|
if (token) {
|
||||||
|
store.dispatch('Logout').then(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload()
|
||||||
|
}, 1500)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
notification.error({
|
||||||
|
message: '系统提示',
|
||||||
|
description: data.message,
|
||||||
|
duration: 4
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.reject(error)
|
||||||
|
};
|
||||||
|
|
||||||
|
// request interceptor
|
||||||
|
service.interceptors.request.use(config => {
|
||||||
|
const token = Vue.ls.get(ACCESS_TOKEN)
|
||||||
|
if (token) {
|
||||||
|
config.headers[ 'X-Access-Token' ] = token // 让每个请求携带自定义 token 请根据实际情况自行修改
|
||||||
|
}
|
||||||
|
//update-begin-author:taoyan date:2020707 for:多租户
|
||||||
|
let tenantid = Vue.ls.get(TENANT_ID)
|
||||||
|
if (!tenantid) {
|
||||||
|
tenantid = 0;
|
||||||
|
}
|
||||||
|
config.headers[ 'tenant_id' ] = tenantid
|
||||||
|
//update-end-author:taoyan date:2020707 for:多租户
|
||||||
|
if(config.method=='get'){
|
||||||
|
if(config.url.indexOf("sys/dict/getDictItems")<0){
|
||||||
|
config.params = {
|
||||||
|
_t: Date.parse(new Date())/1000,
|
||||||
|
...config.params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
},(error) => {
|
||||||
|
return Promise.reject(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
// response interceptor
|
||||||
|
service.interceptors.response.use((response) => {
|
||||||
|
return response.data
|
||||||
|
}, err)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blob解析
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
function blobToJson(data) {
|
||||||
|
let fileReader = new FileReader();
|
||||||
|
let token = Vue.ls.get(ACCESS_TOKEN);
|
||||||
|
fileReader.onload = function() {
|
||||||
|
try {
|
||||||
|
let jsonData = JSON.parse(this.result); // 说明是普通对象数据,后台转换失败
|
||||||
|
console.log("jsonData",jsonData)
|
||||||
|
if (jsonData.status === 500) {
|
||||||
|
console.log("token----------》",token)
|
||||||
|
if(token && jsonData.message.includes("Token失效")){
|
||||||
|
Modal.error({
|
||||||
|
title: '登录已过期',
|
||||||
|
content: '很抱歉,登录已过期,请重新登录',
|
||||||
|
okText: '重新登录',
|
||||||
|
mask: false,
|
||||||
|
onOk: () => {
|
||||||
|
store.dispatch('Logout').then(() => {
|
||||||
|
Vue.ls.remove(ACCESS_TOKEN)
|
||||||
|
window.location.reload()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// 解析成对象失败,说明是正常的文件流
|
||||||
|
console.log("blob解析fileReader返回err",err)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fileReader.readAsText(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
service as axios
|
||||||
|
}
|
||||||
@@ -0,0 +1,575 @@
|
|||||||
|
import * as api from '@/api/api'
|
||||||
|
import { isURL } from '@/utils/validate'
|
||||||
|
import { JSEncrypt } from "jsencrypt";
|
||||||
|
// import onlineCommons from '@/components/onlineForm/onlineForm'
|
||||||
|
//
|
||||||
|
export function timeFix() {
|
||||||
|
const time = new Date()
|
||||||
|
const hour = time.getHours()
|
||||||
|
return hour < 9 ? '早上好' : (hour <= 11 ? '上午好' : (hour <= 13 ? '中午好' : (hour < 20 ? '下午好' : '晚上好')))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function welcome() {
|
||||||
|
const arr = ['休息一会儿吧', '准备吃什么呢?', '要不要打一把 DOTA', '我猜你可能累了']
|
||||||
|
let index = Math.floor((Math.random()*arr.length))
|
||||||
|
return arr[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发 window.resize
|
||||||
|
*/
|
||||||
|
export function triggerWindowResizeEvent() {
|
||||||
|
let event = document.createEvent('HTMLEvents')
|
||||||
|
event.initEvent('resize', true, true)
|
||||||
|
event.eventType = 'message'
|
||||||
|
window.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤对象中为空的属性
|
||||||
|
* @param obj
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function filterObj(obj) {
|
||||||
|
if (!(typeof obj == 'object')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( let key in obj) {
|
||||||
|
if (obj.hasOwnProperty(key)
|
||||||
|
&& (obj[key] == null || obj[key] == undefined || obj[key] === '')) {
|
||||||
|
delete obj[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间格式化
|
||||||
|
* @param value
|
||||||
|
* @param fmt
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function formatDate(value, fmt) {
|
||||||
|
let regPos = /^\d+(\.\d+)?$/;
|
||||||
|
if(regPos.test(value)){
|
||||||
|
//如果是数字
|
||||||
|
let getDate = new Date(value);
|
||||||
|
let o = {
|
||||||
|
'M+': getDate.getMonth() + 1,
|
||||||
|
'd+': getDate.getDate(),
|
||||||
|
'h+': getDate.getHours(),
|
||||||
|
'm+': getDate.getMinutes(),
|
||||||
|
's+': getDate.getSeconds(),
|
||||||
|
'q+': Math.floor((getDate.getMonth() + 3) / 3),
|
||||||
|
'S': getDate.getMilliseconds()
|
||||||
|
};
|
||||||
|
if (/(y+)/.test(fmt)) {
|
||||||
|
fmt = fmt.replace(RegExp.$1, (getDate.getFullYear() + '').substr(4 - RegExp.$1.length))
|
||||||
|
}
|
||||||
|
for (let k in o) {
|
||||||
|
if (new RegExp('(' + k + ')').test(fmt)) {
|
||||||
|
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt;
|
||||||
|
}else{
|
||||||
|
//TODO
|
||||||
|
value = value.trim();
|
||||||
|
return value.substr(0,fmt.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成首页路由
|
||||||
|
export function generateIndexRouter(data) {
|
||||||
|
let indexRouter = [{
|
||||||
|
path: '/',
|
||||||
|
name: 'dashboard',
|
||||||
|
//component: () => import('@/components/layouts/BasicLayout'),
|
||||||
|
component: resolve => require(['@/components/layouts/TabLayout'], resolve),
|
||||||
|
meta: { title: '首页' },
|
||||||
|
redirect: '/dashboard/analysis',
|
||||||
|
children: [
|
||||||
|
...generateChildRouters(data)
|
||||||
|
]
|
||||||
|
},{
|
||||||
|
"path": "*", "redirect": "/404", "hidden": true
|
||||||
|
}]
|
||||||
|
return indexRouter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成嵌套路由(子路由)
|
||||||
|
|
||||||
|
function generateChildRouters (data) {
|
||||||
|
const routers = [];
|
||||||
|
for (let item of data) {
|
||||||
|
let component = "";
|
||||||
|
if(item.component.indexOf("layouts")>=0){
|
||||||
|
component = "components/"+item.component;
|
||||||
|
}else{
|
||||||
|
component = "views/"+item.component;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
let URL = (item.meta.url|| '').replace(/{{([^}}]+)?}}/g, (s1, s2) => eval(s2)) // URL支持{{ window.xxx }}占位符变量
|
||||||
|
if (isURL(URL)) {
|
||||||
|
item.meta.url = URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
let componentPath
|
||||||
|
// if(item.component=="modules/online/cgform/OnlCgformHeadList"){
|
||||||
|
// componentPath = onlineCommons.OnlCgformHeadList
|
||||||
|
// }else if(item.component=="modules/online/cgform/OnlCgformCopyList"){
|
||||||
|
// componentPath = onlineCommons.OnlCgformCopyList
|
||||||
|
// }else if(item.component=="modules/online/cgform/auto/OnlCgformAutoList"){
|
||||||
|
// componentPath = onlineCommons.OnlCgformAutoList
|
||||||
|
// }else if(item.component=="modules/online/cgform/auto/OnlCgformTreeList"){
|
||||||
|
// componentPath = onlineCommons.OnlCgformTreeList
|
||||||
|
// }else if(item.component=="modules/online/cgform/auto/erp/OnlCgformErpList"){
|
||||||
|
// componentPath = onlineCommons.OnlCgformErpList
|
||||||
|
// }else if(item.component=="modules/online/cgform/auto/tab/OnlCgformTabList"){
|
||||||
|
// componentPath = onlineCommons.OnlCgformTabList
|
||||||
|
// }else if(item.component=="modules/online/cgform/auto/innerTable/OnlCgformInnerTableList"){
|
||||||
|
// componentPath = onlineCommons.OnlCgformInnerTableList
|
||||||
|
// }else if(item.component=="modules/online/cgreport/OnlCgreportHeadList"){
|
||||||
|
// componentPath = onlineCommons.OnlCgreportHeadList
|
||||||
|
// }else if(item.component=="modules/online/cgreport/auto/OnlCgreportAutoList"){
|
||||||
|
// componentPath = onlineCommons.OnlCgreportAutoList
|
||||||
|
// }else{
|
||||||
|
componentPath = resolve => require(['@/' + component+'.vue'], resolve)
|
||||||
|
// }
|
||||||
|
|
||||||
|
let menu = {
|
||||||
|
path: item.path,
|
||||||
|
name: item.name,
|
||||||
|
redirect:item.redirect,
|
||||||
|
component: componentPath,
|
||||||
|
//component: resolve => require(['@/' + component+'.vue'], resolve),
|
||||||
|
hidden:item.hidden,
|
||||||
|
//component:()=> import(`@/views/${item.component}.vue`),
|
||||||
|
meta: {
|
||||||
|
title:item.meta.title ,
|
||||||
|
icon: item.meta.icon,
|
||||||
|
url:item.meta.url ,
|
||||||
|
permissionList:item.meta.permissionList,
|
||||||
|
keepAlive:item.meta.keepAlive,
|
||||||
|
/*update_begin author:wuxianquan date:20190908 for:赋值 */
|
||||||
|
internalOrExternal:item.meta.internalOrExternal,
|
||||||
|
/*update_end author:wuxianquan date:20190908 for:赋值 */
|
||||||
|
componentName:item.meta.componentName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(item.alwaysShow){
|
||||||
|
menu.alwaysShow = true;
|
||||||
|
menu.redirect = menu.path;
|
||||||
|
}
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
menu.children = [...generateChildRouters( item.children)];
|
||||||
|
}
|
||||||
|
//--update-begin----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
|
||||||
|
//判断是否生成路由
|
||||||
|
if(item.route && item.route === '0'){
|
||||||
|
//console.log(' 不生成路由 item.route: '+item.route);
|
||||||
|
//console.log(' 不生成路由 item.path: '+item.path);
|
||||||
|
}else{
|
||||||
|
routers.push(menu);
|
||||||
|
}
|
||||||
|
//--update-end----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
|
||||||
|
}
|
||||||
|
return routers
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 深度克隆对象、数组
|
||||||
|
* @param obj 被克隆的对象
|
||||||
|
* @return 克隆后的对象
|
||||||
|
*/
|
||||||
|
export function cloneObject(obj) {
|
||||||
|
return JSON.parse(JSON.stringify(obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 随机生成数字
|
||||||
|
*
|
||||||
|
* 示例:生成长度为 12 的随机数:randomNumber(12)
|
||||||
|
* 示例:生成 3~23 之间的随机数:randomNumber(3, 23)
|
||||||
|
*
|
||||||
|
* @param1 最小值 | 长度
|
||||||
|
* @param2 最大值
|
||||||
|
* @return int 生成后的数字
|
||||||
|
*/
|
||||||
|
export function randomNumber() {
|
||||||
|
// 生成 最小值 到 最大值 区间的随机数
|
||||||
|
const random = (min, max) => {
|
||||||
|
return Math.floor(Math.random() * (max - min + 1) + min)
|
||||||
|
}
|
||||||
|
if (arguments.length === 1) {
|
||||||
|
let [length] = arguments
|
||||||
|
// 生成指定长度的随机数字,首位一定不是 0
|
||||||
|
let nums = [...Array(length).keys()].map((i) => (i > 0 ? random(0, 9) : random(1, 9)))
|
||||||
|
return parseInt(nums.join(''))
|
||||||
|
} else if (arguments.length >= 2) {
|
||||||
|
let [min, max] = arguments
|
||||||
|
return random(min, max)
|
||||||
|
} else {
|
||||||
|
return Number.NaN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 随机生成字符串
|
||||||
|
* @param length 字符串的长度
|
||||||
|
* @param chats 可选字符串区间(只会生成传入的字符串中的字符)
|
||||||
|
* @return string 生成的字符串
|
||||||
|
*/
|
||||||
|
export function randomString(length, chats) {
|
||||||
|
if (!length) length = 1
|
||||||
|
if (!chats) chats = '0123456789qwertyuioplkjhgfdsazxcvbnm'
|
||||||
|
let str = ''
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
let num = randomNumber(0, chats.length - 1)
|
||||||
|
str += chats[num]
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 随机生成uuid
|
||||||
|
* @return string 生成的uuid
|
||||||
|
*/
|
||||||
|
export function randomUUID() {
|
||||||
|
let chats = '0123456789abcdef'
|
||||||
|
return randomString(32, chats)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下划线转驼峰
|
||||||
|
* @param string
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function underLine2CamelCase(string){
|
||||||
|
return string.replace( /_([a-z])/g, function( all, letter ) {
|
||||||
|
return letter.toUpperCase();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否显示办理按钮
|
||||||
|
* @param bpmStatus
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function showDealBtn(bpmStatus){
|
||||||
|
if(bpmStatus!="1"&&bpmStatus!="3"&&bpmStatus!="4"){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增强CSS,可以在页面上输出全局css
|
||||||
|
* @param css 要增强的css
|
||||||
|
* @param id style标签的id,可以用来清除旧样式
|
||||||
|
*/
|
||||||
|
export function cssExpand(css, id) {
|
||||||
|
let style = document.createElement('style')
|
||||||
|
style.type = "text/css"
|
||||||
|
style.innerHTML = `@charset "UTF-8"; ${css}`
|
||||||
|
// 清除旧样式
|
||||||
|
if (id) {
|
||||||
|
let $style = document.getElementById(id)
|
||||||
|
if ($style != null) $style.outerHTML = ''
|
||||||
|
style.id = id
|
||||||
|
}
|
||||||
|
// 应用新样式
|
||||||
|
document.head.appendChild(style)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** 用于js增强事件,运行JS代码,可以传参 */
|
||||||
|
// options 所需参数:
|
||||||
|
// 参数名 类型 说明
|
||||||
|
// vm VueComponent vue实例
|
||||||
|
// event Object event对象
|
||||||
|
// jsCode String 待执行的js代码
|
||||||
|
// errorMessage String 执行出错后的提示(控制台)
|
||||||
|
export function jsExpand(options = {}) {
|
||||||
|
|
||||||
|
// 绑定到window上的keyName
|
||||||
|
let windowKeyName = 'J_CLICK_EVENT_OPTIONS'
|
||||||
|
if (typeof window[windowKeyName] != 'object') {
|
||||||
|
window[windowKeyName] = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 随机生成JS增强的执行id,防止冲突
|
||||||
|
let id = randomString(16, 'qwertyuioplkjhgfdsazxcvbnm'.toUpperCase())
|
||||||
|
// 封装按钮点击事件
|
||||||
|
let code = `
|
||||||
|
(function (o_${id}) {
|
||||||
|
try {
|
||||||
|
(function (globalEvent, vm) {
|
||||||
|
${options.jsCode}
|
||||||
|
})(o_${id}.event, o_${id}.vm)
|
||||||
|
} catch (e) {
|
||||||
|
o_${id}.error(e)
|
||||||
|
}
|
||||||
|
o_${id}.done()
|
||||||
|
})(window['${windowKeyName}']['EVENT_${id}'])
|
||||||
|
`
|
||||||
|
// 创建script标签
|
||||||
|
const script = document.createElement('script')
|
||||||
|
// 将需要传递的参数挂载到window对象上
|
||||||
|
window[windowKeyName]['EVENT_' + id] = {
|
||||||
|
vm: options.vm,
|
||||||
|
event: options.event,
|
||||||
|
// 当执行完成时,无论如何都会调用的回调事件
|
||||||
|
done() {
|
||||||
|
// 执行完后删除新增的 script 标签不会撤销执行结果(已产生的结果不会被撤销)
|
||||||
|
script.outerHTML = ''
|
||||||
|
delete window[windowKeyName]['EVENT_' + id]
|
||||||
|
},
|
||||||
|
// 当js运行出错的时候调用的事件
|
||||||
|
error(e) {
|
||||||
|
console.group(`${options.errorMessage || '用户自定义JS增强代码运行出错'}(${new Date()})`)
|
||||||
|
console.error(e)
|
||||||
|
console.groupEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 将事件挂载到document中
|
||||||
|
script.innerHTML = code
|
||||||
|
document.body.appendChild(script)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重复值验证工具方法
|
||||||
|
*
|
||||||
|
* 使用示例:
|
||||||
|
* { validator: (rule, value, callback) => validateDuplicateValue('sys_fill_rule', 'rule_code', value, this.model.id, callback) }
|
||||||
|
*
|
||||||
|
* @param tableName 被验证的表名
|
||||||
|
* @param fieldName 被验证的字段名
|
||||||
|
* @param fieldVal 被验证的值
|
||||||
|
* @param dataId 数据ID,可空
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
export function validateDuplicateValue(tableName, fieldName, fieldVal, dataId, callback) {
|
||||||
|
if (fieldVal) {
|
||||||
|
let params = { tableName, fieldName, fieldVal, dataId }
|
||||||
|
api.duplicateCheck(params).then(res => {
|
||||||
|
res['success'] ? callback() : callback(res['message'])
|
||||||
|
}).catch(err => {
|
||||||
|
callback(err.message || err)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据编码校验规则code,校验传入的值是否合法
|
||||||
|
*
|
||||||
|
* 使用示例:
|
||||||
|
* { validator: (rule, value, callback) => validateCheckRule('common', value, callback) }
|
||||||
|
*
|
||||||
|
* @param ruleCode 编码校验规则 code
|
||||||
|
* @param value 被验证的值
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
export function validateCheckRule(ruleCode, value, callback) {
|
||||||
|
if (ruleCode && value) {
|
||||||
|
value = encodeURIComponent(value)
|
||||||
|
api.checkRuleByCode({ ruleCode, value }).then(res => {
|
||||||
|
res['success'] ? callback() : callback(res['message'])
|
||||||
|
}).catch(err => {
|
||||||
|
callback(err.message || err)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果值不存在就 push 进数组,反之不处理
|
||||||
|
* @param array 要操作的数据
|
||||||
|
* @param value 要添加的值
|
||||||
|
* @param key 可空,如果比较的是对象,可能存在地址不一样但值实际上是一样的情况,可以传此字段判断对象中唯一的字段,例如 id。不传则直接比较实际值
|
||||||
|
* @returns {boolean} 成功 push 返回 true,不处理返回 false
|
||||||
|
*/
|
||||||
|
export function pushIfNotExist(array, value, key) {
|
||||||
|
for (let item of array) {
|
||||||
|
if (key && (item[key] === value[key])) {
|
||||||
|
return false
|
||||||
|
} else if (item === value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
array.push(value)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可用于判断是否成功
|
||||||
|
* @type {symbol}
|
||||||
|
*/
|
||||||
|
export const succeedSymbol = Symbol()
|
||||||
|
/**
|
||||||
|
* 可用于判断是否失败
|
||||||
|
* @type {symbol}
|
||||||
|
*/
|
||||||
|
export const failedSymbol = Symbol()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使 promise 无论如何都会 resolve,除非传入的参数不是一个Promise对象或返回Promise对象的方法
|
||||||
|
* 一般用在 Promise.all 中
|
||||||
|
*
|
||||||
|
* @param promise 可传Promise对象或返回Promise对象的方法
|
||||||
|
* @returns {Promise<any>}
|
||||||
|
*/
|
||||||
|
export function alwaysResolve(promise) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let p = promise
|
||||||
|
if (typeof promise === 'function') {
|
||||||
|
p = promise()
|
||||||
|
}
|
||||||
|
if (p instanceof Promise) {
|
||||||
|
p.then(data => {
|
||||||
|
resolve({ type: succeedSymbol, data })
|
||||||
|
}).catch(error => {
|
||||||
|
resolve({ type: failedSymbol, error })
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
reject('alwaysResolve: 传入的参数不是一个Promise对象或返回Promise对象的方法')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简单实现防抖方法
|
||||||
|
*
|
||||||
|
* 防抖(debounce)函数在第一次触发给定的函数时,不立即执行函数,而是给出一个期限值(delay),比如100ms。
|
||||||
|
* 如果100ms内再次执行函数,就重新开始计时,直到计时结束后再真正执行函数。
|
||||||
|
* 这样做的好处是如果短时间内大量触发同一事件,只会执行一次函数。
|
||||||
|
*
|
||||||
|
* @param fn 要防抖的函数
|
||||||
|
* @param delay 防抖的毫秒数
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
export function simpleDebounce(fn, delay = 100) {
|
||||||
|
let timer = null
|
||||||
|
return function () {
|
||||||
|
let args = arguments
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer)
|
||||||
|
}
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
fn.apply(this, args)
|
||||||
|
}, delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不用正则的方式替换所有值
|
||||||
|
* @param text 被替换的字符串
|
||||||
|
* @param checker 替换前的内容
|
||||||
|
* @param replacer 替换后的内容
|
||||||
|
* @returns {String} 替换后的字符串
|
||||||
|
*/
|
||||||
|
export function replaceAll(text, checker, replacer) {
|
||||||
|
let lastText = text
|
||||||
|
text = text.replace(checker, replacer)
|
||||||
|
if (lastText !== text) {
|
||||||
|
return replaceAll(text, checker, replacer)
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取事件冒泡路径,兼容 IE11,Edge,Chrome,Firefox,Safari
|
||||||
|
* 目前使用的地方:JEditableTable Span模式
|
||||||
|
*/
|
||||||
|
export function getEventPath(event) {
|
||||||
|
let target = event.target
|
||||||
|
let path = (event.composedPath && event.composedPath()) || event.path
|
||||||
|
|
||||||
|
if (path != null) {
|
||||||
|
return (path.indexOf(window) < 0) ? path.concat(window) : path
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target === window) {
|
||||||
|
return [window]
|
||||||
|
}
|
||||||
|
|
||||||
|
let getParents = (node, memo) => {
|
||||||
|
memo = memo || []
|
||||||
|
const parentNode = node.parentNode
|
||||||
|
|
||||||
|
if (!parentNode) {
|
||||||
|
return memo
|
||||||
|
} else {
|
||||||
|
return getParents(parentNode, memo.concat(parentNode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [target].concat(getParents(target), window)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据组件名获取父级
|
||||||
|
* @param vm
|
||||||
|
* @param name
|
||||||
|
* @returns {Vue | null|null|Vue}
|
||||||
|
*/
|
||||||
|
export function getVmParentByName(vm, name) {
|
||||||
|
let parent = vm.$parent
|
||||||
|
if (parent && parent.$options) {
|
||||||
|
if (parent.$options.name === name) {
|
||||||
|
return parent
|
||||||
|
} else {
|
||||||
|
let res = getVmParentByName(parent, name)
|
||||||
|
if (res) {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使一个值永远不会为(null | undefined)
|
||||||
|
*
|
||||||
|
* @param value 要处理的值
|
||||||
|
* @param def 默认值,如果value为(null | undefined)则返回的默认值,可不传,默认为''
|
||||||
|
*/
|
||||||
|
export function neverNull(value, def) {
|
||||||
|
return value == null ? (neverNull(def, '')) : value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据元素值移除数组中的一个元素
|
||||||
|
* @param array 数组
|
||||||
|
* @param prod 属性名
|
||||||
|
* @param value 属性值
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function removeArrayElement(array, prod, value) {
|
||||||
|
let index = -1
|
||||||
|
for(let i = 0;i<array.length;i++){
|
||||||
|
if(array[i][prod] == value){
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(index>=0){
|
||||||
|
array.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 前端加密方法
|
||||||
|
* @param publicKey:公钥
|
||||||
|
* @param message:要加密的字符串
|
||||||
|
* */
|
||||||
|
export function encrypt(publicKey, message) {
|
||||||
|
const encrypt = new JSEncrypt()
|
||||||
|
encrypt.setPublicKey(publicKey) // publicKey为公钥
|
||||||
|
const txt = encrypt.encrypt(message)
|
||||||
|
return txt
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user