Commit 3670dbbf1d1ae0093da3731079e8219c280869c4

Authored by zhangdaiscott
1 parent 592bca7b

Jeecg-Boot 2.1.4 版本发布 | 重构较大,较多新功能

Showing 270 changed files with 15817 additions and 24938 deletions

Too many changes to show.

To preserve performance only 65 of 270 files are displayed.

README.md
... ... @@ -7,7 +7,7 @@
7 7 Jeecg-Boot 快速开发平台(前后端分离版本)
8 8 ===============
9 9  
10   -当前最新版本: 2.1.3(发布日期:20191226
  10 +当前最新版本: 2.1.4(发布日期:2020-02-24
11 11  
12 12  
13 13 [![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
... ... @@ -43,7 +43,7 @@ Jeecg-Boot快速开发平台,可以应用在任何J2EE项目的开发中,尤
43 43  
44 44 - 技术官网: [http://www.jeecg.com](http://www.jeecg.com)
45 45  
46   -- 开发文档: [http://doc.jeecg.com/1273753](http://doc.jeecg.com/1273753)
  46 +- 开发文档: [http://doc.jeecg.com](http://doc.jeecg.com/1273753)
47 47  
48 48 - 视频教程 :[JeecgBoot入门系列视频](https://space.bilibili.com/454617261/channel/detail?cid=84186)
49 49  
... ... @@ -67,7 +67,7 @@ Jeecg-Boot快速开发平台,可以应用在任何J2EE项目的开发中,尤
67 67  
68 68 为什么选择JEECG-BOOT?
69 69 -----------------------------------
70   -* 1.采用最新主流前后分离框架(Springboot2+Mybatis+antd),容易上手; 代码生成器依赖性低,灵活的扩展能力,可灵活实现二次开发;
  70 +* 1.采用最新主流前后分离框架(Springboot+Mybatis+antd),容易上手; 代码生成器依赖性低,灵活的扩展能力,可灵活实现二次开发;
71 71 * 2.开发效率很高,采用代码生成器,单表数据模型和一对多(父子表)数据模型,增删改查功能自动生成,菜单配置直接使用;
72 72 * 3.代码生成器提供强大模板机制,支持自定义模板风格。目前提供四套风格模板(单表两套、一对多两套)
73 73 * 4.封装完善的用户、角色、菜单、组织机构、数据字典、在线定时任务等基础功能,支持访问授权、按钮权限、数据权限等功能
... ...
ant-design-vue-jeecg/README.md
1 1 Ant Design Jeecg Vue
2 2 ====
3 3  
4   -当前最新版本: 2.1.3(发布日期:20191226
  4 +当前最新版本: 2.1.4(发布日期:2020-02-24
5 5  
6 6 Overview
7 7 ----
... ...
ant-design-vue-jeecg/package.json
1 1 {
2 2 "name": "vue-antd-jeecg",
3   - "version": "2.1.3",
  3 + "version": "2.1.4",
4 4 "private": true,
5 5 "scripts": {
6 6 "pre": "cnpm install || yarn --registry https://registry.npm.taobao.org || npm install --registry https://registry.npm.taobao.org ",
... ... @@ -10,7 +10,7 @@
10 10 },
11 11 "dependencies": {
12 12 "@antv/data-set": "^0.10.2",
13   - "@jeecg/antd-online-re": "2.1.3",
  13 + "@jeecg/antd-online-214": "^2.1.4",
14 14 "@tinymce/tinymce-vue": "^2.0.0",
15 15 "ant-design-vue": "^1.4.0",
16 16 "apexcharts": "^3.6.5",
... ... @@ -24,7 +24,7 @@
24 24 "lodash.pick": "^4.4.0",
25 25 "md5": "^2.2.1",
26 26 "nprogress": "^0.2.0",
27   - "tinymce": "^5.0.2",
  27 + "tinymce": "^5.1.4",
28 28 "viser-vue": "^2.4.4",
29 29 "vue": "^2.6.10",
30 30 "vue-apexcharts": "^1.3.2",
... ... @@ -51,12 +51,12 @@
51 51 "babel-eslint": "^10.0.1",
52 52 "eslint": "^5.16.0",
53 53 "eslint-plugin-vue": "^5.1.0",
  54 + "html-webpack-plugin": "^4.0.0-beta.11",
54 55 "less": "^3.9.0",
55 56 "less-loader": "^4.1.0",
56 57 "node-sass": "^4.11.0",
57 58 "sass-loader": "^7.0.1",
58   - "vue-template-compiler": "^2.6.10",
59   - "html-webpack-plugin": "^4.0.0-beta.11"
  59 + "vue-template-compiler": "^2.6.10"
60 60 },
61 61 "eslintConfig": {
62 62 "root": true,
... ...
ant-design-vue-jeecg/public/index.html
... ... @@ -5,9 +5,9 @@
5 5 <meta charset="utf-8">
6 6 <meta http-equiv="X-UA-Compatible" content="IE=edge">
7 7 <meta name="viewport" content="width=device-width,initial-scale=1.0">
8   - <title>Jeecg-Boot 快速开发平台</title>
  8 + <title>Jeecg-Boot 企业级快速开发平台</title>
9 9 <link rel="icon" href="<%= BASE_URL %>logo.png">
10   - <script src="https://cdn.bootcss.com/babel-polyfill/7.6.0/polyfill.js"></script>
  10 + <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.2.5/polyfill.js"></script>
11 11 <style>
12 12 html,
13 13 body,
... ... @@ -244,8 +244,8 @@
244 244 window._CONFIG = {};
245 245 window._CONFIG['domianURL'] = 'http://127.0.0.1:8080/jeecg-boot';
246 246 window._CONFIG['casPrefixUrl'] = 'http://cas.example.org:8443/cas';
247   - window._CONFIG['imgDomainURL'] = window._CONFIG['domianURL'] + '/sys/common/view';
248   - window._CONFIG['downloadUrl'] = window._CONFIG['domianURL'] + '/sys/common/download';
  247 + window._CONFIG['onlinePreviewDomainURL'] = 'http://fileview.jeecg.com/onlinePreview'
  248 + window._CONFIG['staticDomainURL'] = window._CONFIG['domianURL'] + '/sys/common/static';
249 249 window._CONFIG['pdfDomainURL'] = window._CONFIG['domianURL'] + '/sys/common/pdf/pdfPreviewIframe';
250 250 </script>
251 251 </head>
... ...
ant-design-vue-jeecg/src/api/api.js
... ... @@ -25,7 +25,7 @@ const frozenBatch = (params)=&gt;putAction(&quot;/sys/user/frozenBatch&quot;,params);
25 25 //验证用户是否存在
26 26 const checkOnlyUser = (params)=>getAction("/sys/user/checkOnlyUser",params);
27 27 //改变密码
28   -const changPassword = (params)=>putAction("/sys/user/changPassword",params);
  28 +const changePassword = (params)=>putAction("/sys/user/changePassword",params);
29 29  
30 30 //权限管理
31 31 const addPermission= (params)=>postAction("/sys/permission/add",params);
... ... @@ -34,6 +34,7 @@ const getPermissionList = (params)=&gt;getAction(&quot;/sys/permission/list&quot;,params);
34 34 /*update_begin author:wuxianquan date:20190908 for:添加查询一级菜单和子菜单查询api */
35 35 const getSystemMenuList = (params)=>getAction("/sys/permission/getSystemMenuList",params);
36 36 const getSystemSubmenu = (params)=>getAction("/sys/permission/getSystemSubmenu",params);
  37 +const getSystemSubmenuBatch = (params) => getAction('/sys/permission/getSystemSubmenuBatch', params)
37 38 /*update_end author:wuxianquan date:20190908 for:添加查询一级菜单和子菜单查询api */
38 39  
39 40 // const deletePermission = (params)=>deleteAction("/sys/permission/delete",params);
... ... @@ -56,6 +57,14 @@ const queryParentName = (params)=&gt;getAction(&quot;/sys/sysDepart/queryParentName&quot;,p
56 57 const searchByKeywords = (params)=>getAction("/sys/sysDepart/searchBy",params);
57 58 const deleteByDepartId = (params)=>deleteAction("/sys/sysDepart/delete",params);
58 59  
  60 +//二级部门管理
  61 +const queryDepartPermission = (params)=>getAction("/sys/permission/queryDepartPermission",params);
  62 +const saveDepartPermission = (params)=>postAction("/sys/permission/saveDepartPermission",params);
  63 +const queryTreeListForDeptRole = (params)=>getAction("/sys/sysDepartPermission/queryTreeListForDeptRole",params);
  64 +const queryDeptRolePermission = (params)=>getAction("/sys/sysDepartPermission/queryDeptRolePermission",params);
  65 +const saveDeptRolePermission = (params)=>postAction("/sys/sysDepartPermission/saveDeptRolePermission",params);
  66 +const queryMyDepartTreeList = (params)=>getAction("/sys/sysDepart/queryMyDeptTreeList",params);
  67 +
59 68 //日志管理
60 69 //const getLogList = (params)=>getAction("/sys/log/list",params);
61 70 const deleteLog = (params)=>deleteAction("/sys/log/delete",params);
... ... @@ -94,6 +103,7 @@ const queryUserRoleMap = (params)=&gt;getAction(&quot;/sys/user/queryUserRoleMap&quot;,params
94 103 const duplicateCheck = (params)=>getAction("/sys/duplicate/check",params);
95 104 // 加载分类字典
96 105 const loadCategoryData = (params)=>getAction("/sys/category/loadAllData",params);
  106 +const checkRuleByCode = (params) => getAction('/sys/checkRule/checkByCode', params)
97 107  
98 108 export {
99 109 // imgView,
... ... @@ -108,7 +118,7 @@ export {
108 118 queryall,
109 119 frozenBatch,
110 120 checkOnlyUser,
111   - changPassword,
  121 + changePassword,
112 122 getPermissionList,
113 123 addPermission,
114 124 editPermission,
... ... @@ -142,7 +152,15 @@ export {
142 152 queryTreeListForRole,
143 153 getSystemMenuList,
144 154 getSystemSubmenu,
145   - loadCategoryData
  155 + getSystemSubmenuBatch,
  156 + loadCategoryData,
  157 + checkRuleByCode,
  158 + queryDepartPermission,
  159 + saveDepartPermission,
  160 + queryTreeListForDeptRole,
  161 + queryDeptRolePermission,
  162 + saveDeptRolePermission,
  163 + queryMyDepartTreeList
146 164 }
147 165  
148 166  
... ...
ant-design-vue-jeecg/src/api/manage.js
... ... @@ -112,3 +112,17 @@ export function downFile(url,parameter){
112 112 })
113 113 }
114 114  
  115 +/**
  116 + * 获取文件访问路径
  117 + * @param avatar
  118 + * @param imgerver
  119 + * @param str
  120 + * @returns {*}
  121 + */
  122 +export function getFileAccessHttpUrl(avatar,imgerver,subStr) {
  123 + if(avatar && avatar.indexOf(subStr) != -1 ){
  124 + return avatar;
  125 + }else{
  126 + return imgerver + "/" + avatar;
  127 + }
  128 +}
... ...
ant-design-vue-jeecg/src/assets/less/common.less
... ... @@ -5,7 +5,7 @@
5 5 }
6 6 /** Button按钮间距 */
7 7 .table-operator .ant-btn {
8   - margin-right: 6px
  8 + margin: 8px 8px 0 0;
9 9 }
10 10 /*列表td的padding设置 可以控制列表大小*/
11 11 .ant-table-tbody .ant-table-row td {
... ...
ant-design-vue-jeecg/src/components/chart/BarAndLine.vue
1 1 <template>
2 2 <div :style="{ padding: '0 50px 32px 0' }">
3 3 <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
4   - <v-chart :forceFit="true" :height="height" :data="data" :scale="scale" :padding=" padding">
  4 + <v-chart :forceFit="true" :height="height" :data="data" :scale="scale" :padding=" padding" :onClick="handleClick">
5 5 <v-tooltip/>
6 6 <v-legend/>
7 7 <v-axis/>
... ... @@ -12,9 +12,11 @@
12 12 </template>
13 13  
14 14 <script>
  15 + import { ChartEventMixins } from './mixins/ChartMixins'
15 16  
16 17 export default {
17   - name: 'BarMultid',
  18 + name: 'BarAndLine',
  19 + mixins: [ChartEventMixins],
18 20 props: {
19 21 title: {
20 22 type: String,
... ...
ant-design-vue-jeecg/src/components/chart/BarMultid.vue
1 1 <template>
2 2 <div :style="{ padding: '0 0 32px 32px' }">
3 3 <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
4   - <v-chart :forceFit="true" :height="254" :data="chartData" :padding="['auto', 'auto', '40', '50']">
5   - <v-tooltip />
6   - <v-axis />
7   - <v-legend />
8   - <v-bar position="x*y" color="type" :adjust="adjust" />
  4 + <v-chart :data="data" :height="height" :force-fit="true" :onClick="handleClick">
  5 + <v-tooltip/>
  6 + <v-axis/>
  7 + <v-legend/>
  8 + <v-bar position="x*y" color="type" :adjust="adjust"/>
9 9 </v-chart>
10 10 </div>
11 11 </template>
12 12  
13 13 <script>
14 14 import { DataSet } from '@antv/data-set'
  15 + import { ChartEventMixins } from './mixins/ChartMixins'
15 16  
16   - const sourceDataConst = [
17   - { type: 'Jeecg', 'Jan.': 18.9, 'Feb.': 28.8, 'Mar.': 39.3, 'Apr.': 81.4, 'May': 47, 'Jun.': 20.3, 'Jul.': 24, 'Aug.': 35.6 },
18   - { type: 'Jeebt', 'Jan.': 12.4, 'Feb.': 23.2, 'Mar.': 34.5, 'Apr.': 99.7, 'May': 52.6, 'Jun.': 35.5, 'Jul.': 37.4, 'Aug.': 42.4 },
19   - ];
20   - const fieldsConst = ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.'];
21 17 export default {
22 18 name: 'BarMultid',
  19 + mixins: [ChartEventMixins],
23 20 props: {
24 21 title: {
25 22 type: String,
26 23 default: ''
27 24 },
28   - sourceData:{
29   - type:Array,
30   - default:()=>[]
  25 + dataSource: {
  26 + type: Array,
  27 + default: () => [
  28 + { type: 'Jeecg', 'Jan.': 18.9, 'Feb.': 28.8, 'Mar.': 39.3, 'Apr.': 81.4, 'May': 47, 'Jun.': 20.3, 'Jul.': 24, 'Aug.': 35.6 },
  29 + { type: 'Jeebt', 'Jan.': 12.4, 'Feb.': 23.2, 'Mar.': 34.5, 'Apr.': 99.7, 'May': 52.6, 'Jun.': 35.5, 'Jul.': 37.4, 'Aug.': 42.4 }
  30 + ]
31 31 },
32   - fields:{
33   - type:Array,
34   - default:()=>[]
  32 + fields: {
  33 + type: Array,
  34 + default: () => ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.']
  35 + },
  36 + // 别名,需要的格式:[{field:'name',alias:'姓名'}, {field:'sex',alias:'性别'}]
  37 + aliases: {
  38 + type: Array,
  39 + default: () => []
  40 + },
  41 + height: {
  42 + type: Number,
  43 + default: 254
35 44 }
36 45 },
37 46 data() {
38 47 return {
39   - chartData:"",
40   - height: 400,
41 48 adjust: [{
42 49 type: 'dodge',
43   - marginRatio: 1 / 32,
44   - }],
45   - };
46   - },
47   - watch: {
48   - 'sourceData': function () {
49   - this.drawChart();
  50 + marginRatio: 1 / 32
  51 + }]
50 52 }
51 53 },
52   - mounted(){
53   - this.drawChart()
54   - },
55   - methods:{
56   - drawChart(){
57   - let temp = sourceDataConst;
58   - if(this.sourceData && this.sourceData.length>0){
59   - temp = this.sourceData
60   - }
61   - const dv = new DataSet.View().source(temp);
  54 + computed: {
  55 + data() {
  56 + const dv = new DataSet.View().source(this.dataSource)
62 57 dv.transform({
63 58 type: 'fold',
64   - fields:(!this.fields||this.fields.length==0)?fieldsConst:this.fields,
  59 + fields: this.fields,
65 60 key: 'x',
66   - value: 'y',
67   - });
68   - this.chartData=dv.rows;
  61 + value: 'y'
  62 + })
  63 +
  64 + // bar 使用不了 - 和 / 所以替换下
  65 + let rows = dv.rows.map(row => {
  66 + if (typeof row.x === 'string') {
  67 + row.x = row.x.replace(/[-/]/g, '_')
  68 + }
  69 + return row
  70 + })
  71 + // 替换别名
  72 + rows.forEach(row => {
  73 + for (let item of this.aliases) {
  74 + if (item.field === row.type) {
  75 + row.type = item.alias
  76 + break
  77 + }
  78 + }
  79 + })
  80 + return rows
69 81 }
70 82 }
71 83 }
... ...
ant-design-vue-jeecg/src/components/chart/LineChartMultid.vue
1 1 <template>
2 2 <div :style="{ padding: '0 0 32px 32px' }">
3 3 <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
4   - <v-chart :force-fit="true" :height="height" :data="data" :scale="scale">
  4 + <v-chart :force-fit="true" :height="height" :data="data" :scale="scale" :onClick="handleClick">
5 5 <v-tooltip/>
6 6 <v-axis/>
7 7 <v-legend/>
... ... @@ -13,9 +13,11 @@
13 13  
14 14 <script>
15 15 import { DataSet } from '@antv/data-set'
  16 + import { ChartEventMixins } from './mixins/ChartMixins'
16 17  
17 18 export default {
18 19 name: 'LineChartMultid',
  20 + mixins: [ChartEventMixins],
19 21 props: {
20 22 title: {
21 23 type: String,
... ...
ant-design-vue-jeecg/src/components/chart/Pie.vue
1 1 <template>
2   - <v-chart :forceFit="true" :height="height" :data="data" :scale="scale">
  2 + <v-chart :forceFit="true" :height="height" :data="data" :scale="scale" :onClick="handleClick">
3 3 <v-tooltip :showTitle="false" dataKey="item*percent"/>
4 4 <v-axis/>
5 5 <v-legend dataKey="item"/>
... ... @@ -10,8 +10,11 @@
10 10  
11 11 <script>
12 12 const DataSet = require('@antv/data-set')
  13 + import { ChartEventMixins } from './mixins/ChartMixins'
13 14  
14 15 export default {
  16 + name: 'Pie',
  17 + mixins: [ChartEventMixins],
15 18 props: {
16 19 title: {
17 20 type: String,
... ...
ant-design-vue-jeecg/src/components/chart/mixins/ChartMixins.js 0 → 100644
  1 +export const ChartEventMixins = {
  2 + methods: {
  3 + handleClick(event, chart) {
  4 + this.handleEvent('click', event, chart)
  5 + },
  6 + handleEvent(eventName, event, chart) {
  7 + this.$emit(eventName, event, chart)
  8 + },
  9 + }
  10 +}
0 11 \ No newline at end of file
... ...
ant-design-vue-jeecg/src/components/dict/JDictSelectTag.vue
1 1 <template>
2   - <a-radio-group v-if="tagType=='radio'" @change="handleInput" :value="value" :disabled="disabled">
  2 + <a-radio-group v-if="tagType=='radio'" @change="handleInput" :value="getValueSting" :disabled="disabled">
3 3 <a-radio v-for="(item, key) in dictOptions" :key="key" :value="item.value">{{ item.text }}</a-radio>
4 4 </a-radio-group>
5 5  
6   - <a-select v-else-if="tagType=='select'" :getPopupContainer = "(target) => target.parentNode" :placeholder="placeholder" :disabled="disabled" :value="value" @change="handleInput">
7   - <a-select-option value="">请选择</a-select-option>
  6 + <a-select v-else-if="tagType=='select'" :getPopupContainer = "(target) => target.parentNode" :placeholder="placeholder" :disabled="disabled" :value="getValueSting" @change="handleInput">
  7 + <a-select-option :value="undefined">请选择</a-select-option>
8 8 <a-select-option v-for="(item, key) in dictOptions" :key="key" :value="item.value">
9 9 <span style="display: inline-block;width: 100%" :title=" item.text || item.label ">
10 10 {{ item.text || item.label }}
... ... @@ -23,7 +23,7 @@
23 23 placeholder: String,
24 24 triggerChange: Boolean,
25 25 disabled: Boolean,
26   - value: String,
  26 + value: [String, Number],
27 27 type: String
28 28 },
29 29 data() {
... ... @@ -50,6 +50,11 @@
50 50 //获取字典数据
51 51 // this.initDictData();
52 52 },
  53 + computed: {
  54 + getValueSting(){
  55 + return this.value ? this.value.toString() : null;
  56 + },
  57 + },
53 58 methods: {
54 59 initDictData() {
55 60 //根据字典Code, 初始化字典数组
... ...
ant-design-vue-jeecg/src/components/dict/JDictSelectUtil.js
... ... @@ -47,10 +47,20 @@ export function filterDictText(dictOptions, text) {
47 47 * @return String
48 48 */
49 49 export function filterMultiDictText(dictOptions, text) {
  50 + //js “!text” 认为0为空,所以做提前处理
  51 + if(text === 0 || text === '0'){
  52 + for (let dictItem of dictOptions) {
  53 + if (text == dictItem.value) {
  54 + return dictItem.text
  55 + }
  56 + }
  57 + }
  58 +
50 59 if(!text || !dictOptions || dictOptions.length==0){
51 60 return ""
52 61 }
53 62 let re = "";
  63 + text = text.toString()
54 64 let arr = text.split(",")
55 65 dictOptions.forEach(function (option) {
56 66 for(let i=0;i<arr.length;i++){
... ...
ant-design-vue-jeecg/src/components/jeecg/JEditableTable.vue
... ... @@ -32,6 +32,8 @@
32 32 </a-col>
33 33 </a-row>
34 34  
  35 + <slot name="actionButtonAfter" :target="getVM()"/>
  36 +
35 37 <div :id="`${caseId}inputTable`" class="input-table">
36 38 <!-- 渲染表头 -->
37 39 <div class="thead" ref="thead">
... ... @@ -142,7 +144,9 @@
142 144 placement="top"
143 145 :title="(tooltips[id] || {}).title"
144 146 :visible="(tooltips[id] || {}).visible || false"
145   - :autoAdjustOverflow="true">
  147 + :autoAdjustOverflow="true"
  148 + :getPopupContainer="getParentContainer"
  149 + >
146 150  
147 151 <input
148 152 :id="id"
... ... @@ -175,7 +179,9 @@
175 179 placement="top"
176 180 :title="(tooltips[id] || {}).title"
177 181 :visible="(tooltips[id] || {}).visible || false"
178   - :autoAdjustOverflow="true">
  182 + :autoAdjustOverflow="true"
  183 + :getPopupContainer="getParentContainer"
  184 + >
179 185  
180 186 <span
181 187 @mouseover="()=>{handleMouseoverCommono(row,col)}"
... ... @@ -211,7 +217,9 @@
211 217 placement="top"
212 218 :title="(tooltips[id] || {}).title"
213 219 :visible="(tooltips[id] || {}).visible || false"
214   - :autoAdjustOverflow="true">
  220 + :autoAdjustOverflow="true"
  221 + :getPopupContainer="getParentContainer"
  222 + >
215 223  
216 224 <span
217 225 @mouseover="()=>{handleMouseoverCommono(row,col)}"
... ... @@ -278,19 +286,33 @@
278 286 </template>
279 287  
280 288 <div :hidden="uploadValues[id] != null">
281   -
282   - <a-upload
283   - name="file"
284   - :data="{'isup':1}"
285   - :multiple="false"
286   - :action="col.action"
287   - :headers="uploadGetHeaders(row,col)"
288   - :showUploadList="false"
289   - v-bind="buildProps(row,col)"
290   - @change="(v)=>handleChangeUpload(v,id,row,col)"
  289 + <a-tooltip
  290 + :key="i"
  291 + :id="id"
  292 + placement="top"
  293 + :title="(tooltips[id] || {}).title"
  294 + :visible="(tooltips[id] || {}).visible || false"
  295 + :autoAdjustOverflow="true"
  296 + :getPopupContainer="getParentContainer"
291 297 >
292   - <a-button icon="upload">{{ col.placeholder }}</a-button>
293   - </a-upload>
  298 +
  299 + <span
  300 + @mouseover="()=>{handleMouseoverCommono(row,col)}"
  301 + @mouseout="()=>{handleMouseoutCommono(row,col)}">
  302 + <a-upload
  303 + name="file"
  304 + :data="{'isup':1}"
  305 + :multiple="false"
  306 + :action="col.action"
  307 + :headers="uploadGetHeaders(row,col)"
  308 + :showUploadList="false"
  309 + v-bind="buildProps(row,col)"
  310 + @change="(v)=>handleChangeUpload(v,id,row,col)"
  311 + >
  312 + <a-button icon="upload">{{ col.placeholder }}</a-button>
  313 + </a-upload>
  314 + </span>
  315 + </a-tooltip>
294 316 </div>
295 317  
296 318 </div>
... ... @@ -303,7 +325,10 @@
303 325 placement="top"
304 326 :title="(tooltips[id] || {}).title"
305 327 :visible="(tooltips[id] || {}).visible || false"
306   - :autoAdjustOverflow="true">
  328 + :autoAdjustOverflow="true"
  329 + :getPopupContainer="getParentContainer"
  330 + >
  331 +
307 332 <span
308 333 @mouseover="()=>{handleMouseoverCommono(row,col)}"
309 334 @mouseout="()=>{handleMouseoutCommono(row,col)}">
... ... @@ -359,19 +384,33 @@
359 384 </template>
360 385  
361 386 <div :hidden="uploadValues[id] != null">
362   -
363   - <a-upload
364   - name="file"
365   - :data="{'isup':1}"
366   - :multiple="false"
367   - :action="getUploadAction(col.action)"
368   - :headers="uploadGetHeaders(row,col)"
369   - :showUploadList="false"
370   - v-bind="buildProps(row,col)"
371   - @change="(v)=>handleChangeUpload(v,id,row,col)"
  387 + <a-tooltip
  388 + :key="i"
  389 + :id="id"
  390 + placement="top"
  391 + :title="(tooltips[id] || {}).title"
  392 + :visible="(tooltips[id] || {}).visible || false"
  393 + :autoAdjustOverflow="true"
  394 + :getPopupContainer="getParentContainer"
372 395 >
373   - <a-button icon="upload">{{ col.placeholder }}</a-button>
374   - </a-upload>
  396 +
  397 + <span
  398 + @mouseover="()=>{handleMouseoverCommono(row,col)}"
  399 + @mouseout="()=>{handleMouseoutCommono(row,col)}">
  400 + <a-upload
  401 + name="file"
  402 + :data="{'isup':1}"
  403 + :multiple="false"
  404 + :action="getUploadAction(col.action)"
  405 + :headers="uploadGetHeaders(row,col)"
  406 + :showUploadList="false"
  407 + v-bind="buildProps(row,col)"
  408 + @change="(v)=>handleChangeUpload(v,id,row,col)"
  409 + >
  410 + <a-button icon="upload">{{ col.placeholder }}</a-button>
  411 + </a-upload>
  412 + </span>
  413 + </a-tooltip>
375 414 </div>
376 415  
377 416 </div>
... ... @@ -406,19 +445,33 @@
406 445 </template>
407 446  
408 447 <div :hidden="uploadValues[id] != null">
409   -
410   - <a-upload
411   - name="file"
412   - :data="{'isup':1}"
413   - :multiple="false"
414   - :action="getUploadAction(col.action)"
415   - :headers="uploadGetHeaders(row,col)"
416   - :showUploadList="false"
417   - v-bind="buildProps(row,col)"
418   - @change="(v)=>handleChangeUpload(v,id,row,col)"
  448 + <a-tooltip
  449 + :key="i"
  450 + :id="id"
  451 + placement="top"
  452 + :title="(tooltips[id] || {}).title"
  453 + :visible="(tooltips[id] || {}).visible || false"
  454 + :autoAdjustOverflow="true"
  455 + :getPopupContainer="getParentContainer"
419 456 >
420   - <a-button icon="upload">请上传图片</a-button>
421   - </a-upload>
  457 +
  458 + <span
  459 + @mouseover="()=>{handleMouseoverCommono(row,col)}"
  460 + @mouseout="()=>{handleMouseoutCommono(row,col)}">
  461 + <a-upload
  462 + name="file"
  463 + :data="{'isup':1}"
  464 + :multiple="false"
  465 + :action="getUploadAction(col.action)"
  466 + :headers="uploadGetHeaders(row,col)"
  467 + :showUploadList="false"
  468 + v-bind="buildProps(row,col)"
  469 + @change="(v)=>handleChangeUpload(v,id,row,col)"
  470 + >
  471 + <a-button icon="upload">请上传图片</a-button>
  472 + </a-upload>
  473 + </span>
  474 + </a-tooltip>
422 475 </div>
423 476  
424 477 </div>
... ... @@ -433,7 +486,10 @@
433 486 placement="top"
434 487 :title="(tooltips[id] || {}).title"
435 488 :visible="(tooltips[id] || {}).visible || false"
436   - :autoAdjustOverflow="true">
  489 + :autoAdjustOverflow="true"
  490 + :getPopupContainer="getParentContainer"
  491 + >
  492 +
437 493 <span
438 494 @mouseover="()=>{handleMouseoverCommono(row,col)}"
439 495 @mouseout="()=>{handleMouseoutCommono(row,col)}">
... ... @@ -459,7 +515,9 @@
459 515 placement="top"
460 516 :title="(tooltips[id] || {}).title"
461 517 :visible="(tooltips[id] || {}).visible || false"
462   - :autoAdjustOverflow="true">
  518 + :autoAdjustOverflow="true"
  519 + :getPopupContainer="getParentContainer"
  520 + >
463 521  
464 522 <span
465 523 @mouseover="()=>{handleMouseoverCommono(row,col)}"
... ... @@ -492,7 +550,9 @@
492 550 placement="top"
493 551 :title="(tooltips[id] || {}).title"
494 552 :visible="(tooltips[id] || {}).visible || false"
495   - :autoAdjustOverflow="true">
  553 + :autoAdjustOverflow="true"
  554 + :getPopupContainer="getParentContainer"
  555 + >
496 556  
497 557 <span
498 558 @mouseover="()=>{handleMouseoverCommono(row,col)}"
... ... @@ -526,7 +586,9 @@
526 586 placement="top"
527 587 :title="(tooltips[id] || {}).title"
528 588 :visible="(tooltips[id] || {}).visible || false"
529   - :autoAdjustOverflow="true">
  589 + :autoAdjustOverflow="true"
  590 + :getPopupContainer="getParentContainer"
  591 + >
530 592  
531 593 <span
532 594 @mouseover="()=>{handleMouseoverCommono(row,col)}"
... ... @@ -693,6 +755,7 @@
693 755 }
694 756 },
695 757 created() {
  758 + this.inputValues = []
696 759 // 当前显示的tr
697 760 this.visibleTrEls = []
698 761 this.disabledRowIds = (this.disabledRowIds || [])
... ... @@ -771,149 +834,155 @@
771 834 dataSource: {
772 835 immediate: true,
773 836 handler: function (newValue) {
774   - this.initialize()
775   -
776   - let rows = []
777   - let checkboxValues = {}
778   - let selectValues = {}
779   - let jdateValues = {}
780   - let slotValues = {}
781   - let uploadValues = {}
782   - let popupValues = {}
783   - let radioValues = {}
784   - let multiSelectValues = {}
785   - let searchSelectValues = {}
786   -
787   - // 禁用行的id
788   - let disabledRowIds = (this.disabledRowIds || [])
789   - newValue.forEach((data, newValueIndex) => {
790   - // 判断源数据是否带有id
791   - if (data.id == null || data.id === '') {
792   - data.id = this.removeCaseId(this.generateId() + newValueIndex)
793   - }
  837 + // 兼容IE
  838 + this.getElementPromise('tbody').then(() => {
  839 +
  840 + this.initialize()
  841 +
  842 + let rows = []
  843 + let checkboxValues = {}
  844 + let selectValues = {}
  845 + let jdateValues = {}
  846 + let slotValues = {}
  847 + let uploadValues = {}
  848 + let popupValues = {}
  849 + let radioValues = {}
  850 + let multiSelectValues = {}
  851 + let searchSelectValues = {}
  852 +
  853 + // 禁用行的id
  854 + let disabledRowIds = (this.disabledRowIds || [])
  855 + newValue.forEach((data, newValueIndex) => {
  856 + // 判断源数据是否带有id
  857 + if (data.id == null || data.id === '') {
  858 + data.id = this.removeCaseId(this.generateId() + newValueIndex)
  859 + }
794 860  
795   - let value = { id: this.caseId + data.id }
796   - let row = { id: value.id }
797   - let disabled = false
798   - this.columns.forEach(column => {
799   - let inputId = column.key + value.id
800   - let sourceValue = (data[column.key] == null ? '' : data[column.key]).toString()
801   - if (column.type === FormTypes.checkbox) {
802   -
803   - // 判断是否设定了customValue(自定义值)
804   - if (column.customValue instanceof Array) {
805   - let customValue = (column.customValue[0] || '').toString()
806   - checkboxValues[inputId] = (sourceValue === customValue)
807   - } else {
808   - checkboxValues[inputId] = sourceValue
809   - }
  861 + let value = { id: this.caseId + data.id }
  862 + let row = { id: value.id }
  863 + let disabled = false
  864 + this.columns.forEach(column => {
  865 + let inputId = column.key + value.id
  866 + let sourceValue = (data[column.key] == null ? '' : data[column.key]).toString()
  867 + if (column.type === FormTypes.checkbox) {
  868 +
  869 + // 判断是否设定了customValue(自定义值)
  870 + if (column.customValue instanceof Array) {
  871 + let customValue = (column.customValue[0] || '').toString()
  872 + checkboxValues[inputId] = (sourceValue === customValue)
  873 + } else {
  874 + checkboxValues[inputId] = sourceValue
  875 + }
810 876  
811   - } else if (column.type === FormTypes.select) {
812   - if (sourceValue) {
813   - // 判断是否是多选
814   - selectValues[inputId] = (column.props || {})['mode'] === 'multiple' ? sourceValue.split(',') : sourceValue
815   - } else {
816   - selectValues[inputId] = undefined
817   - }
  877 + } else if (column.type === FormTypes.select) {
  878 + if (sourceValue) {
  879 + // 判断是否是多选
  880 + selectValues[inputId] = (column.props || {})['mode'] === 'multiple' ? sourceValue.split(',') : sourceValue
  881 + } else {
  882 + selectValues[inputId] = undefined
  883 + }
818 884  
819   - } else if (column.type === FormTypes.date || column.type === FormTypes.datetime) {
820   - jdateValues[inputId] = sourceValue
  885 + } else if (column.type === FormTypes.date || column.type === FormTypes.datetime) {
  886 + jdateValues[inputId] = sourceValue
821 887  
822   - } else if (column.type === FormTypes.slot) {
823   - if (sourceValue !== 0 && !sourceValue) {
824   - slotValues[inputId] = column.defaultValue
825   - } else {
826   - slotValues[inputId] = sourceValue
827   - }
  888 + } else if (column.type === FormTypes.slot) {
  889 + if (sourceValue !== 0 && !sourceValue) {
  890 + slotValues[inputId] = column.defaultValue
  891 + } else {
  892 + slotValues[inputId] = sourceValue
  893 + }
828 894  
829   - } else if (column.type === FormTypes.popup) {
830   - popupValues[inputId] = sourceValue
831   - } else if (column.type === FormTypes.radio) {
832   - radioValues[inputId] = sourceValue
833   - } else if (column.type === FormTypes.sel_search) {
834   - searchSelectValues[inputId] = sourceValue
835   - } else if (column.type === FormTypes.list_multi) {
836   - if (sourceValue.length > 0) {
837   - multiSelectValues[inputId] = sourceValue.split(',')
838   - } else {
839   - multiSelectValues[inputId] = []
840   - }
841   - } else if (column.type === FormTypes.upload || column.type === FormTypes.file || column.type === FormTypes.image) {
842   - if (sourceValue) {
843   - let fileName = sourceValue.substring(sourceValue.lastIndexOf('/') + 1)
844   - uploadValues[inputId] = {
845   - name: fileName,
846   - status: 'done',
847   - path: sourceValue
  895 + } else if (column.type === FormTypes.popup) {
  896 + popupValues[inputId] = sourceValue
  897 + } else if (column.type === FormTypes.radio) {
  898 + radioValues[inputId] = sourceValue
  899 + } else if (column.type === FormTypes.sel_search) {
  900 + searchSelectValues[inputId] = sourceValue
  901 + } else if (column.type === FormTypes.list_multi) {
  902 + if (sourceValue.length > 0) {
  903 + multiSelectValues[inputId] = sourceValue.split(',')
  904 + } else {
  905 + multiSelectValues[inputId] = []
848 906 }
  907 + } else if (column.type === FormTypes.upload || column.type === FormTypes.file || column.type === FormTypes.image) {
  908 + if (sourceValue) {
  909 + let fileName = sourceValue.substring(sourceValue.lastIndexOf('/') + 1)
  910 + uploadValues[inputId] = {
  911 + name: fileName,
  912 + status: 'done',
  913 + path: sourceValue
  914 + }
  915 + }
  916 + } else {
  917 + value[column.key] = sourceValue
849 918 }
850   - } else {
851   - value[column.key] = sourceValue
852   - }
853 919  
854   - // 解析disabledRows
855   - for (let columnKey in this.disabledRows) {
856   - // 判断是否有该属性
857   - if (this.disabledRows.hasOwnProperty(columnKey) && data.hasOwnProperty(columnKey)) {
858   - if (disabled !== true) {
859   - let temp = this.disabledRows[columnKey]
860   - // 禁用规则可以是一个数组
861   - if (temp instanceof Array) {
862   - disabled = temp.includes(data[columnKey])
863   - } else {
864   - disabled = (temp === data[columnKey])
865   - }
866   - if (disabled) {
867   - disabledRowIds.push(row.id)
  920 + // 解析disabledRows
  921 + for (let columnKey in this.disabledRows) {
  922 + // 判断是否有该属性
  923 + if (this.disabledRows.hasOwnProperty(columnKey) && data.hasOwnProperty(columnKey)) {
  924 + if (disabled !== true) {
  925 + let temp = this.disabledRows[columnKey]
  926 + // 禁用规则可以是一个数组
  927 + if (temp instanceof Array) {
  928 + disabled = temp.includes(data[columnKey])
  929 + } else {
  930 + disabled = (temp === data[columnKey])
  931 + }
  932 + if (disabled) {
  933 + disabledRowIds.push(row.id)
  934 + }
868 935 }
869 936 }
870 937 }
871   - }
  938 + })
  939 + this.inputValues.push(value)
  940 + rows.push(row)
  941 + })
  942 + this.disabledRowIds = disabledRowIds
  943 + this.checkboxValues = checkboxValues
  944 + this.selectValues = selectValues
  945 + this.jdateValues = jdateValues
  946 + this.slotValues = slotValues
  947 + this.rows = rows
  948 + this.uploadValues = uploadValues
  949 + this.popupValues = popupValues
  950 + this.radioValues = radioValues
  951 + this.multiSelectValues = multiSelectValues
  952 + this.searchSelectValues = searchSelectValues
  953 +
  954 + // 更新form表单的值
  955 + this.$nextTick(() => {
  956 + this.updateFormValues()
872 957 })
873   - this.inputValues.push(value)
874   - rows.push(row)
875   - })
876   - this.disabledRowIds = disabledRowIds
877   - this.checkboxValues = checkboxValues
878   - this.selectValues = selectValues
879   - this.jdateValues = jdateValues
880   - this.slotValues = slotValues
881   - this.rows = rows
882   - this.uploadValues = uploadValues
883   - this.popupValues = popupValues
884   - this.radioValues = radioValues
885   - this.multiSelectValues = multiSelectValues
886   - this.searchSelectValues = searchSelectValues
887   -
888   - // 更新form表单的值
889   - this.$nextTick(() => {
890   - this.updateFormValues()
891 958 })
892   -
893 959 }
894 960 },
895 961 columns: {
896 962 immediate: true,
897 963 handler(columns) {
898   - columns.forEach(column => {
899   - if (column.type === FormTypes.select || column.type === FormTypes.list_multi || column.type === FormTypes.sel_search) {
900   - // 兼容 旧版本 options
901   - if (column.options instanceof Array) {
902   - column.options = column.options.map(item => {
903   - if (item) {
904   - return {
905   - ...item,
906   - text: item.text || item.title,
907   - title: item.text || item.title
  964 + // 兼容IE
  965 + this.getElementPromise('tbody').then(() => {
  966 + columns.forEach(column => {
  967 + if (column.type === FormTypes.select || column.type === FormTypes.list_multi || column.type === FormTypes.sel_search) {
  968 + // 兼容 旧版本 options
  969 + if (column.options instanceof Array) {
  970 + column.options = column.options.map(item => {
  971 + if (item) {
  972 + return {
  973 + ...item,
  974 + text: item.text || item.title,
  975 + title: item.text || item.title
  976 + }
908 977 }
909   - }
910   - return {}
911   - })
912   - }
913   - if (column.dictCode) {
914   - this._loadDictConcatToOptions(column)
  978 + return {}
  979 + })
  980 + }
  981 + if (column.dictCode) {
  982 + this._loadDictConcatToOptions(column)
  983 + }
915 984 }
916   - }
  985 + })
917 986 })
918 987 }
919 988 },
... ... @@ -923,19 +992,12 @@
923 992 }
924 993 },
925 994 mounted() {
926   - // 获取document element对象
927   - let elements = {};
928   - ['inputTable', 'tbody'].forEach(id => {
929   - elements[id] = document.getElementById(this.caseId + id)
930   - })
931   - this.el = elements
932   -
933 995 let vm = this
934 996 /** 监听滚动条事件 */
935   - this.el.inputTable.onscroll = function (event) {
  997 + this.getElement('inputTable').onscroll = function (event) {
936 998 vm.syncScrollBar(event.target.scrollLeft)
937 999 }
938   - this.el.tbody.onscroll = function (event) {
  1000 + this.getElement('tbody').onscroll = function (event) {
939 1001 // vm.recalcTrHiddenItem(event.target.scrollTop)
940 1002 }
941 1003  
... ... @@ -955,6 +1017,25 @@
955 1017 },
956 1018 methods: {
957 1019  
  1020 + getElement(id, noCaseId = false) {
  1021 + if (!this.el[id]) {
  1022 + this.el[id] = document.getElementById((noCaseId ? '' : this.caseId) + id)
  1023 + }
  1024 + return this.el[id]
  1025 + },
  1026 +
  1027 + getElementPromise(id, noCaseId = false) {
  1028 + return new Promise((resolve) => {
  1029 + let timer = setInterval(() => {
  1030 + let element = this.getElement(id, noCaseId)
  1031 + if (element) {
  1032 + clearInterval(timer)
  1033 + resolve(element)
  1034 + }
  1035 + }, 10)
  1036 + })
  1037 + },
  1038 +
958 1039 /** 初始化列表 */
959 1040 initialize() {
960 1041 // inputValues:用来存储input表单的值
... ... @@ -985,14 +1066,14 @@
985 1066 this.searchSelectValues = []
986 1067 this.scrollTop = 0
987 1068 this.$nextTick(() => {
988   - this.el.tbody.scrollTop = 0
  1069 + this.getElement('tbody').scrollTop = 0
989 1070 })
990 1071 },
991 1072  
992 1073 /** 同步滚动条状态 */
993 1074 syncScrollBar(scrollLeft) {
994 1075 // this.style.tbody.left = `${scrollLeft}px`
995   - // this.el.tbody.scrollLeft = scrollLeft
  1076 + // this.getElement('tbody').scrollLeft = scrollLeft
996 1077 },
997 1078 /** 重置滚动条位置,参数留空则滚动到上次记录的位置 */
998 1079 resetScrollTop(top) {
... ... @@ -1157,7 +1238,7 @@
1157 1238 target: this
1158 1239 })
1159 1240 // 设置滚动条位置
1160   - let tbody = this.el.tbody
  1241 + let tbody = this.getElement('tbody')
1161 1242 let offsetHeight = tbody.offsetHeight
1162 1243 let realScrollTop = tbody.scrollTop + offsetHeight
1163 1244 if (forceScrollToBottom === false) {
... ... @@ -1245,13 +1326,14 @@
1245 1326 return true
1246 1327 },
1247 1328  
1248   - /** 获取表格表单里的值(同步版) */
1249   - getValuesSync(options = {}) {
  1329 + /** 获取表格表单里的值(异步版) */
  1330 + getValuesAsync(options = {}, callback) {
1250 1331 let { validate, rowIds } = options
1251 1332 if (typeof validate !== 'boolean') validate = true
1252 1333 if (!(rowIds instanceof Array)) rowIds = null
1253 1334 // console.log('options:', { validate, rowIds })
1254 1335  
  1336 + let asyncCount = 0
1255 1337 let error = 0
1256 1338 let inputValues = cloneObject(this.inputValues)
1257 1339 let tooltips = Object.assign({}, this.tooltips)
... ... @@ -1314,7 +1396,7 @@
1314 1396 } else if (column.type === FormTypes.sel_search) {
1315 1397 value[column.key] = this.searchSelectValues[inputId]
1316 1398 } else if (column.type === FormTypes.list_multi) {
1317   - if (!this.multiSelectValues[inputId] || this.multiSelectValues[inputId].length == 0) {
  1399 + if (!this.multiSelectValues[inputId] || this.multiSelectValues[inputId].length === 0) {
1318 1400 value[column.key] = ''
1319 1401 } else {
1320 1402 value[column.key] = this.multiSelectValues[inputId].join(',')
... ... @@ -1326,20 +1408,27 @@
1326 1408  
1327 1409 // 检查表单验证
1328 1410 if (validate === true) {
1329   - let results = this.validateOneInput(value[column.key], value, column, notPassedIds, false, 'getValues')
1330   - tooltips[inputId] = results[0]
1331   - if (tooltips[inputId].passed === false) {
1332   - error++
1333   - // if (error++ === 0) {
1334   - // let element = document.getElementById(inputId)
1335   - // while (element.className !== 'tr') {
1336   - // element = element.parentElement
1337   - // }
1338   - // this.jumpToId(inputId, element)
1339   - // }
  1411 + const handleValidateOneInput = (results) => {
  1412 + tooltips[inputId] = results[0]
  1413 + if (tooltips[inputId].passed === false) {
  1414 + error++
  1415 + // if (error++ === 0) {
  1416 + // let element = document.getElementById(inputId)
  1417 + // while (element.className !== 'tr') {
  1418 + // element = element.parentElement
  1419 + // }
  1420 + // this.jumpToId(inputId, element)
  1421 + // }
  1422 + }
  1423 + tooltips[inputId].visible = false
  1424 + notPassedIds = results[1]
1340 1425 }
1341   - tooltips[inputId].visible = false
1342   - notPassedIds = results[1]
  1426 + asyncCount++
  1427 + let results = this.validateOneInputAsync(value[column.key], value, column, notPassedIds, false, 'getValues', (results) => {
  1428 + handleValidateOneInput(results)
  1429 + asyncCount--
  1430 + })
  1431 + handleValidateOneInput(results)
1343 1432 }
1344 1433 })
1345 1434 // 将caseId去除
... ... @@ -1352,25 +1441,42 @@
1352 1441 this.tooltips = tooltips
1353 1442 this.notPassedIds = notPassedIds
1354 1443 }
  1444 +
  1445 + const timer = setInterval(() => {
  1446 + if (asyncCount === 0) {
  1447 + clearInterval(timer)
  1448 + if (typeof callback === 'function') {
  1449 + callback({ error, values })
  1450 + }
  1451 + }
  1452 + }, 50)
  1453 +
1355 1454 return { error, values }
1356 1455 },
1357 1456  
  1457 + /** 获取表格表单里的值(同步版) */
  1458 + getValuesSync(options = {}) {
  1459 + return this.getValuesAsync(options)
  1460 + },
  1461 +
1358 1462 /** 获取表格表单里的值 */
1359 1463 getValues(callback, validate = true, rowIds) {
1360   - let result = this.getValuesSync({ validate, rowIds })
1361   - if (typeof callback === 'function') {
1362   - callback(result.error, result.values)
1363   - }
  1464 + this.getValuesAsync({ validate, rowIds }, ({ error, values }) => {
  1465 + if (typeof callback === 'function') {
  1466 + callback(error, values)
  1467 + }
  1468 + })
1364 1469 },
1365 1470 /** getValues的Promise版 */
1366 1471 getValuesPromise(validate = true, rowIds) {
1367 1472 return new Promise((resolve, reject) => {
1368   - let { error, values } = this.getValuesSync({ validate, rowIds })
1369   - if (error === 0) {
1370   - resolve(values)
1371   - } else {
1372   - reject(VALIDATE_NO_PASSED)
1373   - }
  1473 + this.getValuesAsync({ validate, rowIds }, ({ error, values }) => {
  1474 + if (error === 0) {
  1475 + resolve(values)
  1476 + } else {
  1477 + reject(VALIDATE_NO_PASSED)
  1478 + }
  1479 + })
1374 1480 })
1375 1481 },
1376 1482 /** 获取被删除项的id */
... ... @@ -1468,14 +1574,24 @@
1468 1574 // element = document.getElementById(id)
1469 1575 // }
1470 1576 // if (element != null) {
1471   - // console.log(this.el.tbody.scrollTop, element.offsetTop)
1472   - // this.el.tbody.scrollTop = element.offsetTop
1473   - // console.log(this.el.tbody.scrollTop, element.offsetTop)
  1577 + // console.log(this.getElement('tbody').scrollTop, element.offsetTop)
  1578 + // this.getElement('tbody').scrollTop = element.offsetTop
  1579 + // console.log(this.getElement('tbody').scrollTop, element.offsetTop)
1474 1580 // }
1475 1581 // },
1476 1582  
1477   - /** 验证单个表单 */
1478   - validateOneInput(value, row, column, notPassedIds, update = false, validType = 'input') {
  1583 + /**
  1584 + * 验证单个表单,异步版
  1585 + *
  1586 + * @param value 校验的值
  1587 + * @param row 校验的行
  1588 + * @param column 校验的列
  1589 + * @param notPassedIds 没有通过校验的 id
  1590 + * @param update 是否更新到vue中
  1591 + * @param validType 校验触发的方式(input、blur等)
  1592 + * @param callback
  1593 + */
  1594 + validateOneInputAsync(value, row, column, notPassedIds, update = false, validType = 'input', callback) {
1479 1595 let tooltips = Object.assign({}, this.tooltips)
1480 1596 // let notPassedIds = cloneObject(this.notPassedIds)
1481 1597 let inputId = column.key + row.id
... ... @@ -1515,6 +1631,10 @@
1515 1631 if (column.type === FormTypes.date || column.type === FormTypes.datetime) {
1516 1632 element = element.getElementsByTagName('input')[0]
1517 1633 }
  1634 + // upload 在 .ant-upload .ant-btn 上设置 border-color
  1635 + if (column.type === FormTypes.upload || column.type === FormTypes.file || column.type === FormTypes.image) {
  1636 + element = element.getElementsByClassName('ant-upload')[0].getElementsByClassName('ant-btn')[0]
  1637 + }
1518 1638 element.style.borderColor = borderColor
1519 1639 element.style.boxShadow = boxShadow
1520 1640 if (element.tagName === 'SPAN') {
... ... @@ -1527,6 +1647,10 @@
1527 1647 this.notPassedIds = notPassedIds
1528 1648 }
1529 1649  
  1650 + if (typeof callback === 'function') {
  1651 + callback([tooltips[inputId], notPassedIds])
  1652 + }
  1653 +
1530 1654 }
1531 1655  
1532 1656 if (typeof passed === 'function') {
... ... @@ -1547,9 +1671,13 @@
1547 1671 nextThen([passed, message])
1548 1672 }
1549 1673  
1550   -
1551 1674 return [tooltips[inputId], notPassedIds]
1552 1675 },
  1676 +
  1677 + /** 验证单个表单 */
  1678 + validateOneInput(value, row, column, notPassedIds, update = false, validType = 'input') {
  1679 + return this.validateOneInputAsync(value, row, column, notPassedIds, update, validType)
  1680 + },
1553 1681 /** 通过规则验证值是否正确 */
1554 1682 validateValue(column, value) {
1555 1683 let rules = column.validateRules
... ... @@ -1620,7 +1748,7 @@
1620 1748  
1621 1749 /** 动态更新表单的值 */
1622 1750 updateFormValues() {
1623   - let trs = this.el.tbody.getElementsByClassName('tr')
  1751 + let trs = this.getElement('tbody').getElementsByClassName('tr')
1624 1752 let trEls = []
1625 1753 for (let tr of trs) {
1626 1754 trEls.push(tr)
... ... @@ -1959,7 +2087,7 @@
1959 2087 handleClickDownloadFile(id) {
1960 2088 let { path } = this.uploadValues[id] || {}
1961 2089 if (path) {
1962   - let url = window._CONFIG['downloadUrl'] + '/' + path
  2090 + let url = window._CONFIG['staticDomainURL'] + '/' + path
1963 2091 window.open(url)
1964 2092 }
1965 2093 },
... ... @@ -2130,7 +2258,7 @@
2130 2258 getCellImageView(id) {
2131 2259 let currUploadObj = this.uploadValues[id] || null
2132 2260 if (currUploadObj && currUploadObj['path']) {
2133   - return window._CONFIG['domianURL'] + '/sys/common/view/' + currUploadObj['path']
  2261 + return window._CONFIG['staticDomainURL'] + '/' + currUploadObj['path']
2134 2262 } else {
2135 2263 return ''
2136 2264 }
... ... @@ -2340,7 +2468,7 @@
2340 2468  
2341 2469 .td {
2342 2470 /*flex: 1;*/
2343   - padding: 14px 0 14px @spacing;
  2471 + padding: 14px @spacing 14px 0;
2344 2472 justify-content: center;
2345 2473  
2346 2474 &:last-child {
... ...
ant-design-vue-jeecg/src/components/jeecg/JGraphicCode.vue
... ... @@ -150,7 +150,7 @@
150 150 console.log("aaaaa",res)
151 151 if(res.success){
152 152 this.checkKey = res.result.key
153   - this.code = res.result.code
  153 + this.code = window.atob(res.result.code)
154 154 resolve();
155 155 }else{
156 156 this.$message.error("生成验证码错误,请联系系统管理员")
... ...
ant-design-vue-jeecg/src/components/jeecg/JImageUpload.vue 0 → 100644
  1 +<template>
  2 + <a-upload
  3 + name="file"
  4 + listType="picture-card"
  5 + :multiple="isMultiple"
  6 + :action="uploadAction"
  7 + :headers="headers"
  8 + :data="{biz:bizPath}"
  9 + :fileList="fileList"
  10 + :beforeUpload="beforeUpload"
  11 + :disabled="disabled"
  12 + :isMultiple="isMultiple"
  13 + :showUploadList="isMultiple"
  14 + @change="handleChange"
  15 + @preview="handlePreview">
  16 + <img v-if="!isMultiple && picUrl" :src="getAvatarView()" style="height:104px;max-width:300px"/>
  17 + <div v-else >
  18 + <a-icon :type="uploadLoading ? 'loading' : 'plus'" />
  19 + <div class="ant-upload-text">{{ text }}</div>
  20 + </div>
  21 + <a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel()">
  22 + <img alt="example" style="width: 100%" :src="previewImage"/>
  23 + </a-modal>
  24 + </a-upload>
  25 +</template>
  26 +
  27 +<script>
  28 + import Vue from 'vue'
  29 + import { ACCESS_TOKEN } from "@/store/mutation-types"
  30 + import { getFileAccessHttpUrl } from '@/api/manage'
  31 +
  32 + const uidGenerator=()=>{
  33 + return '-'+parseInt(Math.random()*10000+1,10);
  34 + }
  35 + const getFileName=(path)=>{
  36 + if(path.lastIndexOf("\\")>=0){
  37 + let reg=new RegExp("\\\\","g");
  38 + path = path.replace(reg,"/");
  39 + }
  40 + return path.substring(path.lastIndexOf("/")+1);
  41 + }
  42 + export default {
  43 + name: 'JImageUpload',
  44 + data(){
  45 + return {
  46 + uploadAction:window._CONFIG['domianURL']+"/sys/common/upload",
  47 + urlView:window._CONFIG['staticDomainURL'],
  48 + uploadLoading:false,
  49 + picUrl:false,
  50 + headers:{},
  51 + fileList: [],
  52 + previewImage:"",
  53 + previewVisible: false,
  54 + }
  55 + },
  56 + props:{
  57 + text:{
  58 + type:String,
  59 + required:false,
  60 + default:"上传"
  61 + },
  62 + /*这个属性用于控制文件上传的业务路径*/
  63 + bizPath:{
  64 + type:String,
  65 + required:false,
  66 + default:"temp"
  67 + },
  68 + value:{
  69 + type:[String,Array],
  70 + required:false
  71 + },
  72 + disabled:{
  73 + type:Boolean,
  74 + required:false,
  75 + default: false
  76 + },
  77 + isMultiple:{
  78 + type:Boolean,
  79 + required:false,
  80 + default: false
  81 + }
  82 + },
  83 + watch:{
  84 + value(val){
  85 + if (val instanceof Array) {
  86 + this.initFileList(val.join(','))
  87 + } else {
  88 + this.initFileList(val)
  89 + }
  90 + }
  91 + },
  92 + created(){
  93 + const token = Vue.ls.get(ACCESS_TOKEN);
  94 + this.headers = {"X-Access-Token":token}
  95 + },
  96 + methods:{
  97 + initFileList(paths){
  98 + if(!paths || paths.length==0){
  99 + this.fileList = [];
  100 + return;
  101 + }
  102 + this.picUrl = true;
  103 + let fileList = [];
  104 + let arr = paths.split(",")
  105 + for(var a=0;a<arr.length;a++){
  106 + let url = getFileAccessHttpUrl(arr[a],this.urlView,"http");
  107 + fileList.push({
  108 + uid: uidGenerator(),
  109 + name: getFileName(arr[a]),
  110 + status: 'done',
  111 + url: url,
  112 + response:{
  113 + status:"history",
  114 + message:arr[a]
  115 + }
  116 + })
  117 + }
  118 + this.fileList = fileList
  119 + },
  120 + beforeUpload: function(file){
  121 + var fileType = file.type;
  122 + if(fileType.indexOf('image')<0){
  123 + this.$message.warning('请上传图片');
  124 + return false;
  125 + }
  126 + },
  127 + handleChange(info) {
  128 + this.picUrl = false;
  129 + let fileList = info.fileList
  130 + if(info.file.status==='done'){
  131 + if(info.file.response.success){
  132 + this.picUrl = true;
  133 + fileList = fileList.map((file) => {
  134 + if (file.response) {
  135 + file.url = file.response.message;
  136 + }
  137 + return file;
  138 + });
  139 + }
  140 + //this.$message.success(`${info.file.name} 上传成功!`);
  141 + }else if (info.file.status === 'error') {
  142 + this.$message.error(`${info.file.name} 上传失败.`);
  143 + }else if(info.file.status === 'removed'){
  144 + this.handleDelete(info.file)
  145 + }
  146 + this.fileList = fileList
  147 + if(info.file.status==='done' || info.file.status === 'removed'){
  148 + this.handlePathChange()
  149 + }
  150 + },
  151 + // 预览
  152 + handlePreview (file) {
  153 + this.previewImage = file.url || file.thumbUrl
  154 + this.previewVisible = true
  155 + },
  156 + getAvatarView(){
  157 + if(this.fileList.length>0){
  158 + let url = this.fileList[0].url
  159 + return getFileAccessHttpUrl(url,this.urlView,"http")
  160 + }
  161 + },
  162 + handlePathChange(){
  163 + let uploadFiles = this.fileList
  164 + let path = ''
  165 + if(!uploadFiles || uploadFiles.length==0){
  166 + path = ''
  167 + }
  168 + let arr = [];
  169 + if(!this.isMultiple){
  170 + arr.push(uploadFiles[uploadFiles.length-1].response.message)
  171 + }else{
  172 + for(var a=0;a<uploadFiles.length;a++){
  173 + arr.push(uploadFiles[a].response.message)
  174 + }
  175 + }
  176 + if(arr.length>0){
  177 + path = arr.join(",")
  178 + }
  179 + this.$emit('change', path);
  180 + },
  181 + handleDelete(file){
  182 + //如有需要新增 删除逻辑
  183 + console.log(file)
  184 + },
  185 + handleCancel() {
  186 + this.close();
  187 + this.previewVisible = false;
  188 + },
  189 + close () {
  190 +
  191 + },
  192 + },
  193 + model: {
  194 + prop: 'value',
  195 + event: 'change'
  196 + }
  197 + }
  198 +</script>
  199 +
  200 +<style scoped>
  201 +
  202 +</style>
0 203 \ No newline at end of file
... ...
ant-design-vue-jeecg/src/components/jeecg/JImportModal.vue
... ... @@ -42,6 +42,11 @@
42 42 type: String,
43 43 default: '',
44 44 required: false
  45 + },
  46 + biz:{
  47 + type: String,
  48 + default: '',
  49 + required: false
45 50 }
46 51 },
47 52 data(){
... ... @@ -49,7 +54,8 @@
49 54 visible:false,
50 55 uploading:false,
51 56 fileList:[],
52   - uploadAction:''
  57 + uploadAction:'',
  58 + foreignKeys:''
53 59 }
54 60 },
55 61 watch: {
... ... @@ -67,10 +73,11 @@
67 73 handleClose(){
68 74 this.visible=false
69 75 },
70   - show(){
  76 + show(arg){
71 77 this.fileList = []
72 78 this.uploading = false
73 79 this.visible = true
  80 + this.foreignKeys = arg;
74 81 },
75 82 handleRemove(file) {
76 83 const index = this.fileList.indexOf(file);
... ... @@ -85,6 +92,12 @@
85 92 handleImport() {
86 93 const { fileList } = this;
87 94 const formData = new FormData();
  95 + if(this.biz){
  96 + formData.append('isSingleTableImport',this.biz);
  97 + }
  98 + if(this.foreignKeys && this.foreignKeys.length>0){
  99 + formData.append('foreignKeys',this.foreignKeys);
  100 + }
88 101 fileList.forEach((file) => {
89 102 formData.append('files[]', file);
90 103 });
... ...
ant-design-vue-jeecg/src/components/jeecg/JModal/index.vue 0 → 100644
  1 +<template>
  2 + <a-modal
  3 + ref="modal"
  4 + class="j-modal-box"
  5 + :class="{'fullscreen':innerFullscreen,'no-title':isNoTitle,'no-footer':isNoFooter,}"
  6 + :visible="visible"
  7 + v-bind="_attrs"
  8 + v-on="$listeners"
  9 + @ok="handleOk"
  10 + @cancel="handleCancel"
  11 + >
  12 +
  13 + <slot></slot>
  14 +
  15 + <template v-if="!isNoTitle" slot="title">
  16 + <a-row class="j-modal-title-row" type="flex">
  17 + <a-col class="left">
  18 + <slot name="title">{{ title }}</slot>
  19 + </a-col>
  20 + <a-col v-if="switchFullscreen" class="right" @click="toggleFullscreen">
  21 + <a-button class="ant-modal-close ant-modal-close-x" ghost type="link" :icon="fullscreenButtonIcon"/>
  22 + </a-col>
  23 + </a-row>
  24 + </template>
  25 +
  26 + <!-- 处理 scopedSlots -->
  27 + <template v-for="slotName of scopedSlotsKeys" :slot="slotName">
  28 + <slot :name="slotName"></slot>
  29 + </template>
  30 +
  31 + <!-- 处理 slots -->
  32 + <template v-for="slotName of slotsKeys" v-slot:[slotName]>
  33 + <slot :name="slotName"></slot>
  34 + </template>
  35 +
  36 + </a-modal>
  37 +</template>
  38 +
  39 +<script>
  40 + import ACol from 'ant-design-vue/es/grid/Col'
  41 +
  42 + export default {
  43 + name: 'JModal',
  44 + components: { ACol },
  45 + props: {
  46 + title: String,
  47 + // 可使用 .sync 修饰符
  48 + visible: Boolean,
  49 + // 是否在弹出时禁止 body 滚动
  50 + lockScroll: {
  51 + type: Boolean,
  52 + default: true
  53 + },
  54 + // 是否全屏弹窗,当全屏时无论如何都会禁止 body 滚动。可使用 .sync 修饰符
  55 + fullscreen: {
  56 + type: Boolean,
  57 + default: true
  58 + },
  59 + // 是否允许切换全屏(允许后右上角会出现一个按钮)
  60 + switchFullscreen: {
  61 + type: Boolean,
  62 + default: false
  63 + },
  64 + },
  65 + data() {
  66 + return {
  67 + // 内部使用的 slots ,不再处理
  68 + usedSlots: ['title'],
  69 +
  70 + // 缓存 body 的 overflow
  71 + bodyOverflowCache: '',
  72 + innerFullscreen: this.fullscreen,
  73 + fullscreenButtonIcon: 'fullscreen-exit',
  74 + }
  75 + },
  76 + computed: {
  77 + // 一些未处理的参数或特殊处理的参数绑定到 a-modal 上
  78 + _attrs() {
  79 + let attrs = { ...this.$attrs }
  80 + // 如果全屏就将宽度设为 100%
  81 + if (this.innerFullscreen) {
  82 + attrs['width'] = '100%'
  83 + }
  84 + return attrs
  85 + },
  86 + isNoTitle() {
  87 + return !this.title && !this.allSlotsKeys.includes('title')
  88 + },
  89 + isNoFooter() {
  90 + return this._attrs['footer'] === null
  91 + },
  92 + slotsKeys() {
  93 + return Object.keys(this.$slots).filter(key => !this.usedSlots.includes(key))
  94 + },
  95 + scopedSlotsKeys() {
  96 + return Object.keys(this.$scopedSlots).filter(key => !this.usedSlots.includes(key))
  97 + },
  98 + allSlotsKeys() {
  99 + return this.slotsKeys.concat(this.scopedSlotsKeys)
  100 + },
  101 + // 是否锁定body滚动
  102 + lockBodyScroll() {
  103 + return this.lockScroll || this.innerFullscreen
  104 + }
  105 + },
  106 + watch: {
  107 + visible() {
  108 + if (this.visible) {
  109 + this.innerFullscreen = this.fullscreen
  110 + }
  111 + if (this.lockBodyScroll) {
  112 + if (this.visible) {
  113 + this.bodyOverflowCache = document.body.style.overflow
  114 + document.body.style.overflow = 'hidden'
  115 + } else {
  116 + document.body.style.overflow = this.bodyOverflowCache
  117 + }
  118 + }
  119 + },
  120 + innerFullscreen(val) {
  121 + this.$emit('update:fullscreen', val)
  122 + },
  123 + },
  124 + methods: {
  125 +
  126 + close() {
  127 + this.$emit('update:visible', false)
  128 + },
  129 +
  130 + handleOk() {
  131 + this.close()
  132 + },
  133 + handleCancel() {
  134 + this.close()
  135 + },
  136 +
  137 + toggleFullscreen() {
  138 + if (this.innerFullscreen) {
  139 + this.fullscreenButtonIcon = 'fullscreen'
  140 + } else {
  141 + this.fullscreenButtonIcon = 'fullscreen-exit'
  142 + }
  143 + this.innerFullscreen = !this.innerFullscreen
  144 + },
  145 +
  146 + }
  147 + }
  148 +</script>
  149 +
  150 +<style lang="scss">
  151 + .j-modal-box {
  152 +
  153 + &.fullscreen {
  154 + top: 0;
  155 + left: 0;
  156 + padding: 0;
  157 +
  158 + height: 100vh;
  159 +
  160 + & .ant-modal-content {
  161 + height: 100vh;
  162 + border-radius: 0;
  163 +
  164 + & .ant-modal-body {
  165 + /* title 和 footer 各占 55px */
  166 + height: calc(100% - 55px - 55px);
  167 + overflow: auto;
  168 + }
  169 + }
  170 +
  171 + &.no-title, &.no-footer {
  172 + .ant-modal-body {
  173 + height: calc(100% - 55px);
  174 + }
  175 + }
  176 +
  177 + &.no-title.no-footer {
  178 + .ant-modal-body {
  179 + height: 100%;
  180 + }
  181 + }
  182 +
  183 + }
  184 +
  185 + .j-modal-title-row {
  186 + .left {
  187 + width: calc(100% - 56px - 56px);
  188 + }
  189 +
  190 + .right {
  191 + width: 56px;
  192 +
  193 + .ant-modal-close {
  194 + right: 56px;
  195 + color: rgba(0, 0, 0, 0.45);
  196 +
  197 + &:hover {
  198 + color: rgba(0, 0, 0, 0.75);
  199 + }
  200 +
  201 + }
  202 + }
  203 + }
  204 +
  205 + /deep/ {
  206 +
  207 + }
  208 + }
  209 +</style>
0 210 \ No newline at end of file
... ...
ant-design-vue-jeecg/src/components/jeecg/JSuperQuery.vue
1 1 <template>
2 2 <div class="j-super-query-box">
3 3  
4   - <slot>
5   - <a-tooltip v-if="superQueryFlag" title="已有高级查询条件生效">
6   - <a-button type="primary" @click="visible=true">
7   - <a-icon type="appstore" theme="twoTone" :spin="true"></a-icon>
8   - <span>高级查询</span>
9   - </a-button>
10   - </a-tooltip>
11   - <a-button v-else type="primary" icon="filter" @click="visible=true">高级查询</a-button>
12   - </slot>
  4 + <div @click="visible=true">
  5 + <slot>
  6 + <a-tooltip v-if="superQueryFlag" :mouseLeaveDelay="0.2">
  7 + <template slot="title">
  8 + <span>已有高级查询条件生效</span>
  9 + <a-divider type="vertical"/>
  10 + <a @click="handleReset">清空</a>
  11 + </template>
  12 + <a-button type="primary">
  13 + <a-icon type="appstore" theme="twoTone" :spin="true"></a-icon>
  14 + <span>高级查询</span>
  15 + </a-button>
  16 + </a-tooltip>
  17 + <a-button v-else type="primary" icon="filter" @click="visible=true">高级查询</a-button>
  18 + </slot>
  19 + </div>
13 20  
14 21 <a-modal
15 22 title="高级查询构造器"
... ...
ant-design-vue-jeecg/src/components/jeecg/JUpload.vue
... ... @@ -4,11 +4,12 @@
4 4 :multiple="true"
5 5 :action="uploadAction"
6 6 :headers="headers"
7   - :data="{'isup':1,'bizPath':bizPath}"
  7 + :data="{'biz':bizPath}"
8 8 :fileList="fileList"
9 9 :beforeUpload="beforeUpload"
10 10 @change="handleChange"
11   - :disabled="disabled">
  11 + :disabled="disabled"
  12 + :returnUrl="returnUrl">
12 13 <a-button>
13 14 <a-icon type="upload" />{{ text }}
14 15 </a-button>
... ... @@ -19,6 +20,7 @@
19 20  
20 21 import Vue from 'vue'
21 22 import { ACCESS_TOKEN } from "@/store/mutation-types"
  23 + import { getFileAccessHttpUrl } from '@/api/manage';
22 24  
23 25 const FILE_TYPE_ALL = "all"
24 26 const FILE_TYPE_IMG = "image"
... ... @@ -38,9 +40,10 @@
38 40 data(){
39 41 return {
40 42 uploadAction:window._CONFIG['domianURL']+"/sys/common/upload",
41   - urlDownload:window._CONFIG['domianURL'] + "/sys/common/download/",
  43 + urlDownload:window._CONFIG['staticDomainURL'],
42 44 headers:{},
43   - fileList: []
  45 + fileList: [],
  46 + newFileList: [],
44 47 }
45 48 },
46 49 props:{
... ... @@ -77,11 +80,25 @@
77 80 required: false,
78 81 default: false
79 82 },
  83 + /**
  84 + * update -- author:lvdandan -- date:20190219 -- for:Jupload组件增加是否返回url,
  85 + * true:仅返回url
  86 + * false:返回fileName filePath fileSize
  87 + */
  88 + returnUrl:{
  89 + type:Boolean,
  90 + required:false,
  91 + default: true
  92 + },
80 93 },
81 94 watch:{
82 95 value(val){
83 96 if (val instanceof Array) {
84   - this.initFileList(val.join(','))
  97 + if(this.returnUrl){
  98 + this.initFileList(val.join(','))
  99 + }else{
  100 + this.initFileListArr(val);
  101 + }
85 102 } else {
86 103 this.initFileList(val)
87 104 }
... ... @@ -93,6 +110,26 @@
93 110 },
94 111  
95 112 methods:{
  113 + initFileListArr(val){
  114 + if(!val || val.length==0){
  115 + this.fileList = [];
  116 + return;
  117 + }
  118 + let fileList = [];
  119 + for(var a=0;a<val.length;a++){
  120 + fileList.push({
  121 + uid:uidGenerator(),
  122 + name:val[a].fileName,
  123 + status: 'done',
  124 + url: val[a].filePath,
  125 + response:{
  126 + status:"history",
  127 + message:val[a].filePath
  128 + }
  129 + })
  130 + }
  131 + this.fileList = fileList
  132 + },
96 133 initFileList(paths){
97 134 if(!paths || paths.length==0){
98 135 //return [];
... ... @@ -104,11 +141,12 @@
104 141 let fileList = [];
105 142 let arr = paths.split(",")
106 143 for(var a=0;a<arr.length;a++){
  144 + let url = getFileAccessHttpUrl(arr[a],this.urlDownload,"http");
107 145 fileList.push({
108 146 uid:uidGenerator(),
109 147 name:getFileName(arr[a]),
110 148 status: 'done',
111   - url: this.urlDownload+arr[a],
  149 + url: url,
112 150 response:{
113 151 status:"history",
114 152 message:arr[a]
... ... @@ -156,12 +194,13 @@
156 194 if(info.file.response.success){
157 195 fileList = fileList.map((file) => {
158 196 if (file.response) {
159   - file.url = this.urlDownload+file.response.message;
  197 + let reUrl = file.response.message;
  198 + file.url = getFileAccessHttpUrl(reUrl,this.urlDownload,"http");
160 199 }
161 200 return file;
162 201 });
163 202 }
164   - this.$message.success(`${info.file.name} 上传成功!`);
  203 + //this.$message.success(`${info.file.name} 上传成功!`);
165 204 }else if (info.file.status === 'error') {
166 205 this.$message.error(`${info.file.name} 上传失败.`);
167 206 }else if(info.file.status === 'removed'){
... ... @@ -169,7 +208,26 @@
169 208 }
170 209 this.fileList = fileList
171 210 if(info.file.status==='done' || info.file.status === 'removed'){
172   - this.handlePathChange()
  211 + //returnUrl为true时仅返回文件路径
  212 + if(this.returnUrl){
  213 + this.handlePathChange()
  214 + }else{
  215 + //returnUrl为false时返回文件名称、文件路径及文件大小
  216 + fileList = fileList.filter((file) => {
  217 + if (file.response) {
  218 + return file.response.success === true;
  219 + }
  220 + return false;
  221 + }).map((file) => {
  222 + var fileJson = {
  223 + fileName:file.name,
  224 + filePath:file.url,
  225 + fileSize:file.size
  226 + };
  227 + this.newFileList.push(fileJson);
  228 + this.$emit('change', this.newFileList);
  229 + });
  230 + }
173 231 }
174 232 },
175 233 handleDelete(file){
... ...
ant-design-vue-jeecg/src/components/jeecg/index.js
1   -import T from './JFormContainer.vue'
2   -let install = function (Vue) {
3   - Vue.component('JFormContainer',T);
4   -}
5   -export default { install };
6 1 \ No newline at end of file
  2 +import JModal from './JModal'
  3 +import JFormContainer from './JFormContainer.vue'
  4 +
  5 +export default {
  6 + install(Vue) {
  7 + Vue.component('JFormContainer', JFormContainer)
  8 + Vue.component(JModal.name, JModal)
  9 + }
  10 +}
7 11 \ No newline at end of file
... ...
ant-design-vue-jeecg/src/components/jeecgbiz/modal/JSelectUserByDepModal.vue
... ... @@ -17,9 +17,11 @@
17 17 selectable
18 18 :selectedKeys="selectedDepIds"
19 19 :checkStrictly="true"
20   - @select="onDepSelect"
21 20 :dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
22 21 :treeData="departTree"
  22 + :expandAction="false"
  23 + :expandedKeys.sync="expandedKeys"
  24 + @select="onDepSelect"
23 25 />
24 26 </a-card>
25 27 </a-col>
... ... @@ -28,7 +30,7 @@
28 30 用户账号:
29 31 <a-input-search
30 32 :style="{width:'150px',marginBottom:'15px'}"
31   - placeholder="请输入用户账号"
  33 + placeholder="请输入账号"
32 34 v-model="queryParam.username"
33 35 @search="onSearch"
34 36 ></a-input-search>
... ... @@ -43,6 +45,7 @@
43 45 :dataSource="dataSource"
44 46 :pagination="ipagination"
45 47 :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange,type: getType}"
  48 + :loading="loading"
46 49 @change="handleTableChange">
47 50 </a-table>
48 51 </a-card>
... ... @@ -71,7 +74,7 @@
71 74 dataIndex: 'username'
72 75 },
73 76 {
74   - title: '真实姓名',
  77 + title: '用户姓名',
75 78 align: 'center',
76 79 dataIndex: 'realname'
77 80 },
... ... @@ -90,14 +93,14 @@
90 93 }
91 94 },
92 95 {
93   - title: '手机号码',
  96 + title: '手机',
94 97 align: 'center',
95 98 dataIndex: 'phone'
96 99 },
97 100 {
98   - title: '邮箱',
  101 + title: '部门',
99 102 align: 'center',
100   - dataIndex: 'email'
  103 + dataIndex: 'orgCode'
101 104 }
102 105 ],
103 106 scrollTrigger: {},
... ... @@ -124,60 +127,74 @@
124 127 selectedDepIds: [],
125 128 departTree: [],
126 129 visible: false,
127   - form: this.$form.createForm(this)
  130 + form: this.$form.createForm(this),
  131 + loading: false,
  132 + expandedKeys: [],
128 133 }
129 134 },
130 135 computed: {
131 136 // 计算属性的 getter
132 137 getType: function () {
133   - console.log("multi: ", this.multi);
134 138 return this.multi == true ? 'checkbox' : 'radio';
135 139 }
136 140 },
137 141 watch: {
138   - userIds() {
139   - this.initUserNames()
140   - }
  142 + userIds: {
  143 + immediate: true,
  144 + handler() {
  145 + this.initUserNames()
  146 + }
  147 + },
141 148 },
142 149 created() {
143 150 // 该方法触发屏幕自适应
144 151 this.resetScreenSize();
145   - this.loadData().then((res) => {
146   - this.initUserNames();
147   - })
  152 + this.loadData()
148 153 },
149 154 methods: {
150 155 initUserNames() {
151   - let names = ''
152   - console.log("props userIds: ", this.userIds)
153 156 if (this.userIds) {
154   - let currUserIds = this.userIds
155   - let userIdsArr = currUserIds.split(',');
156   - for (let item of this.dataSource) {
157   - if (userIdsArr.includes(item.username)) {
158   - names += "," + item.realname
  157 + // 这里最后加一个 , 的原因是因为无论如何都要使用 in 查询,防止后台进行了模糊匹配,导致查询结果不准确
  158 + let values = this.userIds.split(',') + ','
  159 + getUserList({
  160 + username: values,
  161 + pageNo: 1,
  162 + pageSize: values.length
  163 + }).then((res) => {
  164 + if (res.success) {
  165 + let selectedRowKeys = []
  166 + let realNames = []
  167 + res.result.records.forEach(user => {
  168 + realNames.push(user['realname'])
  169 + selectedRowKeys.push(user['id'])
  170 + })
  171 + this.selectedRowKeys = selectedRowKeys
  172 + this.$emit('initComp', realNames.join(','))
159 173 }
160   - }
161   - if (names) {
162   - names = names.substring(1)
163   - }
164   - this.$emit("initComp", names)
165   - }else{
  174 + })
  175 + } else {
166 176 // JSelectUserByDep组件bug issues/I16634
167   - this.$emit("initComp", "")
  177 + this.$emit('initComp', '')
168 178 }
169 179 },
170 180 async loadData(arg) {
171 181 if (arg === 1) {
172 182 this.ipagination.current = 1;
173 183 }
174   - let params = this.getQueryParams();//查询条件
175   - await getUserList(params).then((res) => {
176   - if (res.success) {
177   - this.dataSource = res.result.records;
178   - this.ipagination.total = res.result.total;
179   - }
180   - })
  184 + if (this.selectedDepIds && this.selectedDepIds.length > 0) {
  185 + await this.initQueryUserByDepId(this.selectedDepIds)
  186 + } else {
  187 + this.loading = true
  188 + let params = this.getQueryParams()//查询条件
  189 + await getUserList(params).then((res) => {
  190 + if (res.success) {
  191 + this.dataSource = res.result.records
  192 + this.ipagination.total = res.result.total
  193 + }
  194 + }).finally(() => {
  195 + this.loading = false
  196 + })
  197 + }
181 198 },
182 199 // 触发屏幕自适应
183 200 resetScreenSize() {
... ... @@ -191,6 +208,7 @@
191 208 showModal() {
192 209 this.visible = true;
193 210 this.queryDepartTree();
  211 + this.initUserNames()
194 212 this.loadData();
195 213 this.form.resetFields();
196 214 },
... ... @@ -234,7 +252,6 @@
234 252 handleSubmit() {
235 253 let that = this;
236 254 this.getSelectUserRows();
237   - console.log(that.selectUserRows)
238 255 that.$emit('ok', that.selectUserRows, that.selectUserIds);
239 256 that.searchReset(0)
240 257 that.close();
... ... @@ -270,17 +287,22 @@
270 287 },
271 288 // 根据选择的id来查询用户信息
272 289 initQueryUserByDepId(selectedDepIds) {
273   - queryUserByDepId({id: selectedDepIds.toString()}).then((res) => {
  290 + this.loading = true
  291 + return queryUserByDepId({id: selectedDepIds.toString()}).then((res) => {
274 292 if (res.success) {
275 293 this.dataSource = res.result;
276 294 this.ipagination.total = res.result.length;
277 295 }
  296 + }).finally(() => {
  297 + this.loading = false
278 298 })
279 299 },
280 300 queryDepartTree() {
281 301 queryDepartTreeList().then((res) => {
282 302 if (res.success) {
283 303 this.departTree = res.result;
  304 + // 默认展开父节点
  305 + this.expandedKeys = this.departTree.map(item => item.id)
284 306 }
285 307 })
286 308 },
... ...
ant-design-vue-jeecg/src/components/jeecgbiz/modal/SelectUserListModal.vue
... ... @@ -46,7 +46,7 @@
46 46 width: 200
47 47 },
48 48 {
49   - title: '用户真实姓名',
  49 + title: '用户姓名',
50 50 align: "center",
51 51 dataIndex: 'realname',
52 52 },
... ...
ant-design-vue-jeecg/src/components/layouts/IframePageView.vue
... ... @@ -5,6 +5,8 @@
5 5 </template>
6 6  
7 7 <script>
  8 + import Vue from 'vue'
  9 + import { ACCESS_TOKEN } from "@/store/mutation-types"
8 10 import PageLayout from '../page/PageLayout'
9 11 import RouteView from './RouteView'
10 12  
... ... @@ -40,6 +42,12 @@
40 42 /*update_begin author:wuxianquan date:20190908 for:判断打开方式,新窗口打开时this.$route.meta.internalOrExternal==true */
41 43 if(this.$route.meta.internalOrExternal != undefined && this.$route.meta.internalOrExternal==true){
42 44 this.closeCurrent();
  45 + //外部url加入token
  46 + let tokenStr = "${token}";
  47 + if(url.indexOf(tokenStr)!=-1){
  48 + let token = Vue.ls.get(ACCESS_TOKEN);
  49 + this.url = url.replace(tokenStr,token);
  50 + }
43 51 window.open(this.url);
44 52 }
45 53 /*update_end author:wuxianquan date:20190908 for:判断打开方式,新窗口打开时this.$route.meta.internalOrExternal==true */
... ...
ant-design-vue-jeecg/src/components/layouts/TabLayout.vue
... ... @@ -80,30 +80,41 @@
80 80 },
81 81 created() {
82 82 if (this.$route.path != indexKey) {
83   - this.pageList.push({
84   - name: 'dashboard-analysis',
85   - path: indexKey,
86   - fullPath: indexKey,
87   - meta: {
88   - icon: 'dashboard',
89   - title: '首页'
90   - }
91   - })
92   - this.linkList.push(indexKey)
  83 + this.addIndexToFirst()
93 84 }
  85 + // update-begin-author:sunjianlei date:20191223 for: 修复刷新后菜单Tab名字显示异常
  86 + let storeKey = 'route:title:' + this.$route.fullPath
  87 + let routeTitle = this.$ls.get(storeKey)
  88 + if (routeTitle) {
  89 + this.$route.meta.title = routeTitle
  90 + }
  91 + // update-end-author:sunjianlei date:20191223 for: 修复刷新后菜单Tab名字显示异常
94 92 this.pageList.push(this.$route)
95 93 this.linkList.push(this.$route.fullPath)
96 94 this.activePage = this.$route.fullPath
97 95 },
98 96 watch: {
99 97 '$route': function(newRoute) {
  98 + //console.log("新的路由",newRoute)
100 99 this.activePage = newRoute.fullPath
101 100 if (!this.multipage) {
102 101 this.linkList = [newRoute.fullPath]
103 102 this.pageList = [Object.assign({},newRoute)]
104   - } else if (this.linkList.indexOf(newRoute.fullPath) < 0) {
  103 + // update-begin-author:taoyan date:20200211 for: TASK #3368 【路由缓存】首页的缓存设置有问题,需要根据后台的路由配置来实现是否缓存
  104 + } else if(indexKey==newRoute.fullPath) {
  105 + //首页时 判断是否缓存 没有缓存 刷新之
  106 + if (newRoute.meta.keepAlive === false) {
  107 + this.routeReload()
  108 + }
  109 + // update-end-author:taoyan date:20200211 for: TASK #3368 【路由缓存】首页的缓存设置有问题,需要根据后台的路由配置来实现是否缓存
  110 + }else if (this.linkList.indexOf(newRoute.fullPath) < 0) {
105 111 this.linkList.push(newRoute.fullPath)
106 112 this.pageList.push(Object.assign({},newRoute))
  113 + // update-begin-author:sunjianlei date:20200103 for: 如果新增的页面配置了缓存路由,那么就强制刷新一遍
  114 + if (newRoute.meta.keepAlive) {
  115 + this.routeReload()
  116 + }
  117 + // update-end-author:sunjianlei date:20200103 for: 如果新增的页面配置了缓存路由,那么就强制刷新一遍
107 118 } else if (this.linkList.indexOf(newRoute.fullPath) >= 0) {
108 119 let oldIndex = this.linkList.indexOf(newRoute.fullPath)
109 120 let oldPositionRoute = this.pageList[oldIndex]
... ... @@ -114,6 +125,7 @@
114 125 let index = this.linkList.lastIndexOf(key)
115 126 let waitRouter = this.pageList[index]
116 127 this.$router.push(Object.assign({},waitRouter));
  128 + this.changeTitle(waitRouter.meta.title)
117 129 },
118 130 'multipage': function(newVal) {
119 131 if(this.reloadFlag){
... ... @@ -122,9 +134,44 @@
122 134 this.pageList = [this.$route]
123 135 }
124 136 }
125   - }
  137 + },
  138 + // update-begin-author:sunjianlei date:20191223 for: 修复从单页模式切换回多页模式后首页不居第一位的 BUG
  139 + device() {
  140 + if (this.multipage && this.linkList.indexOf(indexKey) === -1) {
  141 + this.addIndexToFirst()
  142 + }
  143 + },
  144 + // update-end-author:sunjianlei date:20191223 for: 修复从单页模式切换回多页模式后首页不居第一位的 BUG
126 145 },
127 146 methods: {
  147 + // update-begin-author:sunjianlei date:20191223 for: 修复从单页模式切换回多页模式后首页不居第一位的 BUG
  148 + // 将首页添加到第一位
  149 + addIndexToFirst() {
  150 + this.pageList.splice(0, 0, {
  151 + name: 'dashboard-analysis',
  152 + path: indexKey,
  153 + fullPath: indexKey,
  154 + meta: {
  155 + icon: 'dashboard',
  156 + title: '首页'
  157 + }
  158 + })
  159 + this.linkList.splice(0, 0, indexKey)
  160 + },
  161 + // update-end-author:sunjianlei date:20191223 for: 修复从单页模式切换回多页模式后首页不居第一位的 BUG
  162 +
  163 + // update-begin-author:sunjianlei date:20200120 for: 动态更改页面标题
  164 + changeTitle(title) {
  165 + let projectTitle = "Jeecg-Boot 企业级快速开发平台"
  166 + // 首页特殊处理
  167 + if (this.$route.path === indexKey) {
  168 + document.title = projectTitle
  169 + } else {
  170 + document.title = title + ' · ' + projectTitle
  171 + }
  172 + },
  173 + // update-end-author:sunjianlei date:20200120 for: 动态更改页面标题
  174 +
128 175 changePage(key) {
129 176 this.activePage = key
130 177 },
... ... @@ -236,6 +283,9 @@
236 283 let currRouter = this.pageList[keyIndex]
237 284 let meta = Object.assign({},currRouter.meta,{title:title})
238 285 this.pageList.splice(keyIndex, 1, Object.assign({},currRouter,{meta:meta}))
  286 + if (key === this.activePage) {
  287 + this.changeTitle(title)
  288 + }
239 289 }
240 290 },
241 291 //update-end-author:taoyan date:20190430 for:动态路由title显示配置的菜单title而不是其对应路由的title
... ...
ant-design-vue-jeecg/src/components/layouts/UserLayout.vue
... ... @@ -17,12 +17,12 @@
17 17  
18 18 <div class="footer">
19 19 <div class="links">
20   - <a href="http://jeecg-boot.mydoc.io" target="_blank">帮助</a>
  20 + <a href="http://doc.jeecg.com" target="_blank">帮助</a>
21 21 <a href="https://github.com/zhangdaiscott/jeecg-boot" target="_blank">隐私</a>
22   - <a href="https://github.com/zhangdaiscott/jeecg-boot" target="_blank">条款</a>
  22 + <a href="https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE" target="_blank">条款</a>
23 23 </div>
24 24 <div class="copyright">
25   - Copyright &copy; 2019 <a href="http://www.jeecg.org" target="_blank">JEECG开源社区</a> 出品
  25 + Copyright &copy; 2019 <a href="http://www.jeecg.com" target="_blank">JEECG开源社区</a> 出品
26 26 </div>
27 27 </div>
28 28 </div>
... ...
ant-design-vue-jeecg/src/components/page/GlobalFooter.vue
1 1 <template>
2 2 <div class="footer">
3 3 <div class="links">
4   - <a href="http://www.jeecg.org" target="_blank">JEECG 首页</a>
  4 + <a href="http://www.jeecg.com" target="_blank">JEECG 首页</a>
5 5 <a href="https://github.com/zhangdaiscott/jeecg-boot" target="_blank">
6 6 <a-icon type="github"/>
7 7 </a>
... ...
ant-design-vue-jeecg/src/components/page/GlobalHeader.vue
... ... @@ -197,7 +197,7 @@
197 197 &.dark {
198 198 color: #000000;
199 199 box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
200   - background-color: @primary-color;
  200 + background-color: white !important;
201 201 }
202 202 }
203 203  
... ...
ant-design-vue-jeecg/src/components/page/GlobalLayout.vue
... ... @@ -154,6 +154,10 @@
154 154 //此处触发动态路由被点击事件
155 155 this.findMenuBykey(this.menus,value.key)
156 156 this.$emit("dynamicRouterShow",value.key,this.activeMenu.meta.title)
  157 + // update-begin-author:sunjianlei date:20191223 for: 修复刷新后菜单Tab名字显示异常
  158 + let storeKey = 'route:title:' + this.activeMenu.path
  159 + this.$ls.set(storeKey, this.activeMenu.meta.title)
  160 + // update-end-author:sunjianlei date:20191223 for: 修复刷新后菜单Tab名字显示异常
157 161 },
158 162 findMenuBykey(menus,key){
159 163 for(let i of menus){
... ... @@ -337,6 +341,10 @@
337 341 font-size: 16px;
338 342 padding: 4px;
339 343 }
  344 +
  345 + .anticon {
  346 + color: white;
  347 + }
340 348 }
341 349 }
342 350  
... ... @@ -349,6 +357,10 @@
349 357 &:hover {
350 358 background: rgba(0, 0, 0, 0.05);
351 359 }
  360 +
  361 + .anticon {
  362 + color: black;
  363 + }
352 364 }
353 365 }
354 366 }
... ...
ant-design-vue-jeecg/src/components/tools/DynamicNotice.vue 0 → 100644
  1 +<template>
  2 + <component
  3 + :is="comp"
  4 + :formData="formData"
  5 + ref="compModel"
  6 + v-if="comp">
  7 + </component>
  8 +</template>
  9 +<script>
  10 + export default {
  11 + name: 'DynamicNotice',
  12 + data () {
  13 + return {
  14 + compName: this.path
  15 + }
  16 + },
  17 + computed: {
  18 + comp: function () {
  19 + if(!this.path){
  20 + return null;
  21 + }
  22 + return () => import(`@/views/${this.path}.vue`)
  23 + }
  24 + },
  25 + props: ['path','formData'],
  26 + methods: {
  27 + detail () {
  28 + setTimeout(() => {
  29 + if(this.path){
  30 + this.$refs.compModel.view(this.formData);
  31 + }
  32 + }, 200)
  33 + },
  34 + }
  35 + }
  36 +</script>
0 37 \ No newline at end of file
... ...
ant-design-vue-jeecg/src/components/tools/HeaderNotice.vue
... ... @@ -72,6 +72,7 @@
72 72 </a-badge>
73 73 </span>
74 74 <show-announcement ref="ShowAnnouncement" @ok="modalFormOk"></show-announcement>
  75 + <dynamic-notice ref="showDynamNotice" :path="openPath" :formData="formData"/>
75 76 </a-popover>
76 77 </template>
77 78  
... ... @@ -79,11 +80,13 @@
79 80 import { getAction,putAction } from '@/api/manage'
80 81 import ShowAnnouncement from './ShowAnnouncement'
81 82 import store from '@/store/'
  83 + import DynamicNotice from './DynamicNotice'
82 84  
83 85  
84 86 export default {
85 87 name: "HeaderNotice",
86 88 components: {
  89 + DynamicNotice,
87 90 ShowAnnouncement,
88 91 },
89 92 data () {
... ... @@ -105,6 +108,8 @@
105 108 websock: null,
106 109 lockReconnect:false,
107 110 heartCheck:null,
  111 + formData:{},
  112 + openPath:''
108 113 }
109 114 },
110 115 computed:{
... ... @@ -172,7 +177,13 @@
172 177 }
173 178 });
174 179 this.hovered = false;
175   - this.$refs.ShowAnnouncement.detail(record);
  180 + if(record.openType==='component'){
  181 + this.openPath = record.openPage;
  182 + this.formData = {id:record.busId};
  183 + this.$refs.showDynamNotice.detail(record.openPage);
  184 + }else{
  185 + this.$refs.ShowAnnouncement.detail(record);
  186 + }
176 187 },
177 188 toMyAnnouncement(){
178 189  
... ...
ant-design-vue-jeecg/src/components/tools/ShowAnnouncement.vue
... ... @@ -5,11 +5,14 @@
5 5 :visible="visible"
6 6 :bodyStyle ="bodyStyle"
7 7 @cancel="handleCancel"
8   - destroyOnClose
9   - :footer="null">
  8 + destroyOnClose>
10 9 <template slot="title">
11 10 <a-button icon="fullscreen" class="custom-btn" @click="handleClickToggleFullScreen"/>
12 11 </template>
  12 + <template slot="footer">
  13 + <a-button key="back" @click="handleCancel">关闭</a-button>
  14 + <a-button v-if="record.openType==='url'&&record.readFlag!=='1'" type="primary" @click="toHandle">去处理</a-button>
  15 + </template>
13 16 <a-card class="daily-article" :loading="loading">
14 17 <a-card-meta
15 18 :title="record.titile"
... ... @@ -74,7 +77,14 @@
74 77 this.modelStyle.style.top = '50px'
75 78 }
76 79 this.modelStyle.fullScreen = mode
77   - }
  80 + },
  81 + toHandle(){
  82 + if(this.record.openType==='url'&&this.record.readFlag!== '1'){
  83 + this.visible = false;
  84 + //链接跳转
  85 + this.$router.push({path: this.record.openPage})
  86 + }
  87 + },
78 88 }
79 89 }
80 90 </script>
... ...
ant-design-vue-jeecg/src/components/tools/UserMenu.vue
... ... @@ -5,7 +5,8 @@
5 5 <span class="action" @click="showClick">
6 6 <a-icon type="search"></a-icon>
7 7 </span>
8   - <span v-show="shows" class="borders">
  8 + <!-- update-begin author:sunjianlei date:20200219 for: 菜单搜索改为动态组件,在手机端呈现出弹出框 -->
  9 + <component :is="searchMenuComp" v-show="searchMenuVisible || isMobile()" class="borders" :visible="searchMenuVisible" title="搜索菜单" :footer="null" @cancel="searchMenuVisible=false">
9 10 <a-select
10 11 class="search-input"
11 12 showSearch
... ... @@ -13,16 +14,20 @@
13 14 placeholder="搜索菜单"
14 15 optionFilterProp="children"
15 16 :filterOption="filterOption"
  17 + :open="isMobile()?true:null"
  18 + :getPopupContainer="(node) => node.parentNode"
  19 + :style="isMobile()?{width: '100%',marginBottom:'50px'}:{}"
16 20 @change="searchMethods"
17 21 @blur="hiddenClick"
18 22 >
19   - <a-select-option v-for="site in search " :value="site.id">{{site.meta.title}}</a-select-option>
  23 + <a-select-option v-for="site in searchMenuOptions" :value="site.id">{{site.meta.title}}</a-select-option>
20 24 </a-select>
21   - </span>
22   - <!-- update-end author:sunjianlei date:20191@20 for: 解决全局样式冲突的问题 -->
  25 + </component>
  26 + <!-- update-end author:sunjianlei date:20200219 for: 菜单搜索改为动态组件,在手机端呈现出弹出框 -->
  27 + <!-- update-end author:sunjianlei date:20191220 for: 解决全局样式冲突的问题 -->
23 28 <!-- update_end author:zhaoxin date:20191129 for: 做头部菜单栏导航 -->
24 29 <span class="action">
25   - <a class="logout_title" target="_blank" href="http://jeecg-boot.mydoc.io">
  30 + <a class="logout_title" target="_blank" href="http://doc.jeecg.com">
26 31 <a-icon type="question-circle-o"></a-icon>
27 32 </a>
28 33 </span>
... ... @@ -95,9 +100,11 @@
95 100 mixins: [mixinDevice],
96 101 data(){
97 102 return{
98   - //菜单搜索
99   - search:[],
100   - shows:false
  103 + // update-begin author:sunjianlei date:20200219 for: 头部菜单搜索规范命名 --------------
  104 + searchMenuOptions:[],
  105 + searchMenuComp: 'span',
  106 + searchMenuVisible: false,
  107 + // update-begin author:sunjianlei date:20200219 for: 头部菜单搜索规范命名 --------------
101 108 }
102 109 },
103 110 components: {
... ... @@ -116,10 +123,8 @@
116 123 /* update_begin author:zhaoxin date:20191129 for: 做头部菜单栏导航*/
117 124 created() {
118 125 let lists = []
119   - console.log("permissionMenuList: ",this.permissionMenuList)
120 126 this.searchMenus(lists,this.permissionMenuList)
121   - this.search=[...lists]
122   - console.log(this.search)
  127 + this.searchMenuOptions=[...lists]
123 128 },
124 129 computed: {
125 130 ...mapState({
... ... @@ -129,10 +134,21 @@
129 134 })
130 135 },
131 136 /* update_end author:zhaoxin date:20191129 for: 做头部菜单栏导航*/
  137 + watch: {
  138 + // update-begin author:sunjianlei date:20200219 for: 菜单搜索改为动态组件,在手机端呈现出弹出框
  139 + device: {
  140 + immediate: true,
  141 + handler() {
  142 + this.searchMenuVisible = false
  143 + this.searchMenuComp = this.isMobile() ? 'a-modal' : 'span'
  144 + },
  145 + },
  146 + // update-end author:sunjianlei date:20200219 for: 菜单搜索改为动态组件,在手机端呈现出弹出框
  147 + },
132 148 methods: {
133 149 /* update_begin author:zhaoxin date:20191129 for: 做头部菜单栏导航*/
134   - showClick(){
135   - this.shows = !this.shows
  150 + showClick() {
  151 + this.searchMenuVisible = true
136 152 },
137 153 hiddenClick(){
138 154 this.shows = false
... ... @@ -141,8 +157,8 @@
141 157 ...mapActions(["Logout"]),
142 158 ...mapGetters(["nickname", "avatar","userInfo"]),
143 159 getAvatar(){
144   - console.log('url = '+ window._CONFIG['imgDomainURL']+"/"+this.avatar())
145   - return window._CONFIG['imgDomainURL']+"/"+this.avatar()
  160 + console.log('url = '+ window._CONFIG['staticDomainURL']+"/"+this.avatar())
  161 + return window._CONFIG['staticDomainURL']+"/"+this.avatar()
146 162 },
147 163 handleLogout() {
148 164 const that = this
... ... @@ -189,10 +205,17 @@
189 205 filterOption(input, option) {
190 206 return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
191 207 },
192   - searchMethods(value){
193   - let jump = this.search.filter(item=>item.id==value)
194   - this.$router.push({ path:jump[0].path})
  208 + // update_begin author:sunjianlei date:20191230 for: 解决外部链接打开失败的问题
  209 + searchMethods(value) {
  210 + let route = this.searchMenuOptions.filter(item => item.id === value)[0]
  211 + if (route.meta.internalOrExternal === true || route.component.includes('layouts/IframePageView')) {
  212 + window.open(route.meta.url, '_blank')
  213 + } else {
  214 + this.$router.push({ path: route.path })
  215 + }
  216 + this.searchMenuVisible = false
195 217 }
  218 + // update_end author:sunjianlei date:20191230 for: 解决外部链接打开失败的问题
196 219 /*update_end author:zhaoxin date:20191129 for: 做头部菜单栏导航*/
197 220 }
198 221 }
... ... @@ -203,7 +226,7 @@
203 226 /* update-begin author:sunjianlei date:20191220 for: 解决全局样式冲突问题 */
204 227 .user-wrapper .search-input {
205 228 width: 180px;
206   - color: white;
  229 + color: inherit;
207 230  
208 231 /deep/ {
209 232 .ant-select-selection {
... ...
ant-design-vue-jeecg/src/main.js
... ... @@ -18,8 +18,8 @@ import VueApexCharts from &#39;vue-apexcharts&#39;
18 18  
19 19 import preview from 'vue-photo-preview'
20 20 import 'vue-photo-preview/dist/skin.css'
21   -import "@jeecg/antd-online-re"
22   -import '@jeecg/antd-online-re/dist/OnlineForm.css'
  21 +import "@jeecg/antd-online-214"
  22 +import '@jeecg/antd-online-214/dist/OnlineForm.css'
23 23  
24 24 import {
25 25 ACCESS_TOKEN,
... ...
ant-design-vue-jeecg/src/mixins/JeecgListMixin.js
... ... @@ -236,9 +236,9 @@ export const JeecgListMixin = {
236 236 return
237 237 }
238 238 if (typeof window.navigator.msSaveBlob !== 'undefined') {
239   - window.navigator.msSaveBlob(new Blob([data]), fileName+'.xls')
  239 + window.navigator.msSaveBlob(new Blob([data],{type: 'application/vnd.ms-excel'}), fileName+'.xls')
240 240 }else{
241   - let url = window.URL.createObjectURL(new Blob([data]))
  241 + let url = window.URL.createObjectURL(new Blob([data],{type: 'application/vnd.ms-excel'}))
242 242 let link = document.createElement('a')
243 243 link.style.display = 'none'
244 244 link.href = url
... ... @@ -286,7 +286,7 @@ export const JeecgListMixin = {
286 286 if(text && text.indexOf(",")>0){
287 287 text = text.substring(0,text.indexOf(","))
288 288 }
289   - return window._CONFIG['imgDomainURL']+"/"+text
  289 + return window._CONFIG['staticDomainURL']+"/"+text
290 290 },
291 291 /* 文件下载 */
292 292 uploadFile(text){
... ... @@ -297,7 +297,7 @@ export const JeecgListMixin = {
297 297 if(text.indexOf(",")>0){
298 298 text = text.substring(0,text.indexOf(","))
299 299 }
300   - window.open(window._CONFIG['domianURL'] + "/sys/common/download/"+text);
  300 + window.open(window._CONFIG['staticDomainURL']+ "/"+text);
301 301 },
302 302 }
303 303  
... ...
ant-design-vue-jeecg/src/mixins/OnlAutoListMixin.js 0 → 100644
  1 +export const HrefJump = {
  2 + data() {
  3 + return {
  4 + fieldHrefSlots: [],
  5 + hrefComponent: {
  6 + model: {
  7 + title: '',
  8 + width: '100%',
  9 + visible: false,
  10 + destroyOnClose: true,
  11 + style: {
  12 + top: 0,
  13 + left: 0,
  14 + height: '100%',
  15 + margin: 0,
  16 + padding: 0
  17 + },
  18 + bodyStyle: { padding: '8px', height: 'calc(100vh - 108px)', overflow: 'auto', overflowX: 'hidden' },
  19 + // 隐藏掉取消按钮
  20 + cancelButtonProps: { style: { display: 'none' } },
  21 + afterClose: () => {
  22 + // 恢复body的滚动
  23 + document.body.style.overflow = null
  24 + }
  25 + },
  26 + on: {
  27 + ok: () => this.hrefComponent.model.visible = false,
  28 + cancel: () => this.hrefComponent.model.visible = false
  29 + },
  30 + is: null,
  31 + params: {},
  32 + }
  33 + }
  34 + },
  35 + methods: {
  36 + //支持链接href跳转
  37 + handleClickFieldHref(field, record) {
  38 + let href = field.href
  39 + let urlPattern = /(ht|f)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&amp;%\$#_]*)?/
  40 + let compPattern = /\.vue(\?.*)?$/
  41 + if (typeof href === 'string') {
  42 + href = href.trim().replace(/\${([^}]+)?}/g, (s1, s2) => record[s2])
  43 + if (urlPattern.test(href)) {
  44 + window.open(href, '_blank')
  45 + } else if (compPattern.test(href)) {
  46 + this.openHrefCompModal(href)
  47 + } else {
  48 + this.$router.push(href)
  49 + }
  50 + }
  51 + },
  52 + openHrefCompModal(href) {
  53 + // 解析 href 参数
  54 + let index = href.indexOf('?')
  55 + let path = href
  56 + if (index !== -1) {
  57 + path = href.substring(0, index)
  58 + let paramString = href.substring(index + 1, href.length)
  59 + let paramArray = paramString.split('&')
  60 + let params = {}
  61 + paramArray.forEach(paramObject => {
  62 + let paramItem = paramObject.split('=')
  63 + params[paramItem[0]] = paramItem[1]
  64 + })
  65 + this.hrefComponent.params = params
  66 + } else {
  67 + this.hrefComponent.params = {}
  68 + }
  69 + this.hrefComponent.model.visible = true
  70 + this.hrefComponent.model.title = '@/views/' + path
  71 + this.hrefComponent.is = () => import('@/views/' + (path.startsWith('/')?path.slice(1):path))
  72 + // 禁止body滚动,防止滚动穿透
  73 + setTimeout(() => {
  74 + document.body.style.overflow = 'hidden'
  75 + }, 300)
  76 + },
  77 + }
  78 +}
0 79 \ No newline at end of file
... ...
ant-design-vue-jeecg/src/store/modules/user.js
... ... @@ -120,6 +120,19 @@ const user = {
120 120 sessionStorage.setItem(USER_AUTH,JSON.stringify(authData));
121 121 sessionStorage.setItem(SYS_BUTTON_AUTH,JSON.stringify(allAuthData));
122 122 if (menuData && menuData.length > 0) {
  123 + //update--begin--autor:qinfeng-----date:20200109------for:JEECG-63 一级菜单的子菜单全部是隐藏路由,则一级菜单不显示------
  124 + menuData.forEach((item, index) => {
  125 + if (item["children"]) {
  126 + let hasChildrenMenu = item["children"].filter((i) => {
  127 + return !i.hidden || i.hidden == false
  128 + })
  129 + if (hasChildrenMenu == null || hasChildrenMenu.length == 0) {
  130 + item["hidden"] = true
  131 + }
  132 + }
  133 + })
  134 + console.log(" menu show json ", menuData)
  135 + //update--end--autor:qinfeng-----date:20200109------for:JEECG-63 一级菜单的子菜单全部是隐藏路由,则一级菜单不显示------
123 136 commit('SET_PERMISSIONLIST', menuData)
124 137 } else {
125 138 reject('getPermissionList: permissions must be a non-null array !')
... ...
ant-design-vue-jeecg/src/utils/authFilter.js
1   -
2 1 import { USER_AUTH,SYS_BUTTON_AUTH } from "@/store/mutation-types"
3 2  
4 3 export function disabledAuthFilter(code,formData) {
... ... @@ -10,27 +9,30 @@ export function disabledAuthFilter(code,formData) {
10 9 }
11 10  
12 11 function nodeDisabledAuth(code,formData){
13   - console.log("页面权限禁用--NODE--开始");
14   - var permissionList = [];
  12 + let permissionList = [];
15 13 try {
16   - var obj = formData;
17 14 //console.log("页面权限禁用--NODE--开始",obj);
18   - if (obj) {
19   - let bpmList = obj.permissionList;
20   - for (var bpm of bpmList) {
21   - if(bpm.type == '2') {
22   - permissionList.push(bpm);
23   - }
24   - }
  15 + if (formData) {
  16 + let bpmList = formData.permissionList;
  17 + permissionList = bpmList.filter(item=>item.type=='2')
  18 + // for (let bpm of bpmList) {
  19 + // if(bpm.type == '2') {
  20 + // permissionList.push(bpm);
  21 + // }
  22 + // }
  23 + }else{
  24 + return false;
25 25 }
26 26 } catch (e) {
27 27 //console.log("页面权限异常----", e);
28 28 }
29   - if (permissionList === null || permissionList === "" || permissionList === undefined||permissionList.length<=0) {
  29 + if (permissionList.length == 0) {
30 30 return false;
31 31 }
  32 +
  33 + console.log("流程节点页面权限禁用--NODE--开始");
32 34 let permissions = [];
33   - for (var item of permissionList) {
  35 + for (let item of permissionList) {
34 36 if(item.type == '2') {
35 37 permissions.push(item.action);
36 38 }
... ... @@ -39,9 +41,9 @@ function nodeDisabledAuth(code,formData){
39 41 if (!permissions.includes(code)) {
40 42 return false;
41 43 }else{
42   - for (var item2 of permissionList) {
  44 + for (let item2 of permissionList) {
43 45 if(code === item2.action){
44   - console.log("页面权限禁用--NODE--生效");
  46 + console.log("流程节点页面权限禁用--NODE--生效");
45 47 return true;
46 48 }
47 49 }
... ... @@ -50,21 +52,21 @@ function nodeDisabledAuth(code,formData){
50 52 }
51 53  
52 54 function globalDisabledAuth(code){
53   - console.log("页面禁用权限--Global--开始");
  55 + //console.log("全局页面禁用权限--Global--开始");
54 56  
55 57 var permissionList = [];
56 58 var allPermissionList = [];
57 59  
58 60 //let authList = Vue.ls.get(USER_AUTH);
59 61 let authList = JSON.parse(sessionStorage.getItem(USER_AUTH) || "[]");
60   - for (var auth of authList) {
  62 + for (let auth of authList) {
61 63 if(auth.type == '2') {
62 64 permissionList.push(auth);
63 65 }
64 66 }
65 67 //console.log("页面禁用权限--Global--",sessionStorage.getItem(SYS_BUTTON_AUTH));
66 68 let allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || "[]");
67   - for (var gauth of allAuthList) {
  69 + for (let gauth of allAuthList) {
68 70 if(gauth.type == '2') {
69 71 allPermissionList.push(gauth);
70 72 }
... ... @@ -73,7 +75,7 @@ function globalDisabledAuth(code){
73 75 var gFlag = false;//禁用命中
74 76 var invalidFlag = false;//无效命中
75 77 if(allPermissionList != null && allPermissionList != "" && allPermissionList != undefined && allPermissionList.length > 0){
76   - for (var itemG of allPermissionList) {
  78 + for (let itemG of allPermissionList) {
77 79 if(code === itemG.action){
78 80 if(itemG.status == '0'){
79 81 invalidFlag = true;
... ... @@ -92,7 +94,7 @@ function globalDisabledAuth(code){
92 94 return gFlag;
93 95 }
94 96 let permissions = [];
95   - for (var item of permissionList) {
  97 + for (let item of permissionList) {
96 98 if(item.type == '2') {
97 99 permissions.push(item.action);
98 100 }
... ... @@ -101,9 +103,9 @@ function globalDisabledAuth(code){
101 103 if (!permissions.includes(code)) {
102 104 return gFlag;
103 105 }else{
104   - for (var item2 of permissionList) {
  106 + for (let item2 of permissionList) {
105 107 if(code === item2.action){
106   - console.log("页面权限解除禁用--Global--生效");
  108 + console.log("全局页面权限解除禁用--Global--生效");
107 109 gFlag = false;
108 110 }
109 111 }
... ... @@ -134,12 +136,12 @@ function hasColoum(item,authList){
134 136 //权限无效时不做控制,有效时控制,只能控制 显示不显示
135 137 //根据授权码前缀获取未授权的列信息
136 138 function getNoAuthCols(pre){
137   - var permissionList = [];
138   - var allPermissionList = [];
  139 + let permissionList = [];
  140 + let allPermissionList = [];
139 141  
140 142 //let authList = Vue.ls.get(USER_AUTH);
141 143 let authList = JSON.parse(sessionStorage.getItem(USER_AUTH) || "[]");
142   - for (var auth of authList) {
  144 + for (let auth of authList) {
143 145 //显示策略,有效状态
144 146 if(auth.type == '1'&&startWith(auth.action,pre)) {
145 147 permissionList.push(substrPre(auth.action,pre));
... ... @@ -147,7 +149,7 @@ function getNoAuthCols(pre){
147 149 }
148 150 //console.log("页面禁用权限--Global--",sessionStorage.getItem(SYS_BUTTON_AUTH));
149 151 let allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || "[]");
150   - for (var gauth of allAuthList) {
  152 + for (let gauth of allAuthList) {
151 153 //显示策略,有效状态
152 154 if(gauth.type == '1'&&gauth.status == '1'&&startWith(gauth.action,pre)) {
153 155 allPermissionList.push(substrPre(gauth.action,pre));
... ...
ant-design-vue-jeecg/src/utils/commonUploadFile.js
... ... @@ -38,7 +38,7 @@ const getUploadFileList=(paths)=&gt;{
38 38 uid:uidGenerator(),
39 39 name:getFileName(arr[a]),
40 40 status: 'done',
41   - url: window._CONFIG['domianURL']+"/sys/common/view/"+arr[a],
  41 + url: window._CONFIG['staticDomainURL']+"/"+arr[a],
42 42 response:{
43 43 status:"history",
44 44 message:arr[a]
... ...
ant-design-vue-jeecg/src/utils/hasPermission.js
... ... @@ -2,35 +2,37 @@ import { USER_AUTH,SYS_BUTTON_AUTH } from &quot;@/store/mutation-types&quot;
2 2  
3 3 const hasPermission = {
4 4 install (Vue, options) {
5   - console.log(options);
  5 + //console.log(options);
6 6 Vue.directive('has', {
7 7 inserted: (el, binding, vnode)=>{
8   - console.log("页面权限控制----");
  8 + //console.log("页面权限控制----");
  9 + console.time()
9 10 //节点权限处理,如果命中则不进行全局权限处理
10 11 if(!filterNodePermission(el, binding, vnode)){
11 12 filterGlobalPermission(el, binding, vnode);
12 13 }
  14 + console.timeEnd() //计时结束并输出时长
13 15 }
14 16 });
15 17 }
16 18 };
17 19  
18 20 /**
19   - * 全局权限控制
  21 + * 流程节点权限控制
20 22 */
21 23 export function filterNodePermission(el, binding, vnode) {
22   - console.log("页面权限--NODE--");
23   -
24   - var permissionList = [];
  24 + let permissionList = [];
25 25 try {
26   - var obj = vnode.context.$props.formData;
  26 + let obj = vnode.context.$props.formData;
27 27 if (obj) {
28 28 let bpmList = obj.permissionList;
29   - for (var bpm of bpmList) {
  29 + for (let bpm of bpmList) {
30 30 if(bpm.type != '2') {
31 31 permissionList.push(bpm);
32 32 }
33 33 }
  34 + }else{
  35 + return false;
34 36 }
35 37 } catch (e) {
36 38 //console.log("页面权限异常----", e);
... ... @@ -39,8 +41,10 @@ export function filterNodePermission(el, binding, vnode) {
39 41 //el.parentNode.removeChild(el)
40 42 return false;
41 43 }
  44 +
  45 + console.log("流程节点页面权限--NODE--");
42 46 let permissions = [];
43   - for (var item of permissionList) {
  47 + for (let item of permissionList) {
44 48 if(item.type != '2') {
45 49 permissions.push(item.action);
46 50 }
... ... @@ -51,7 +55,7 @@ export function filterNodePermission(el, binding, vnode) {
51 55 //el.parentNode.removeChild(el)
52 56 return false;
53 57 }else{
54   - for (var item2 of permissionList) {
  58 + for (let item2 of permissionList) {
55 59 if(binding.value === item2.action){
56 60 return true;
57 61 }
... ... @@ -64,29 +68,29 @@ export function filterNodePermission(el, binding, vnode) {
64 68 * 全局权限控制
65 69 */
66 70 export function filterGlobalPermission(el, binding, vnode) {
67   - console.log("页面权限--Global--");
  71 + console.log("全局页面权限--Global--");
68 72  
69   - var permissionList = [];
70   - var allPermissionList = [];
  73 + let permissionList = [];
  74 + let allPermissionList = [];
71 75  
72 76 //let authList = Vue.ls.get(USER_AUTH);
73 77 let authList = JSON.parse(sessionStorage.getItem(USER_AUTH) || "[]");
74   - for (var auth of authList) {
  78 + for (let auth of authList) {
75 79 if(auth.type != '2') {
76 80 permissionList.push(auth);
77 81 }
78 82 }
79 83 //console.log("页面权限--Global--",sessionStorage.getItem(SYS_BUTTON_AUTH));
80 84 let allAuthList = JSON.parse(sessionStorage.getItem(SYS_BUTTON_AUTH) || "[]");
81   - for (var gauth of allAuthList) {
  85 + for (let gauth of allAuthList) {
82 86 if(gauth.type != '2') {
83 87 allPermissionList.push(gauth);
84 88 }
85 89 }
86 90 //设置全局配置是否有命中
87   - var invalidFlag = false;//无效命中
  91 + let invalidFlag = false;//无效命中
88 92 if(allPermissionList != null && allPermissionList != "" && allPermissionList != undefined && allPermissionList.length > 0){
89   - for (var itemG of allPermissionList) {
  93 + for (let itemG of allPermissionList) {
90 94 if(binding.value === itemG.action){
91 95 if(itemG.status == '0'){
92 96 invalidFlag = true;
... ... @@ -103,7 +107,7 @@ export function filterGlobalPermission(el, binding, vnode) {
103 107 return;
104 108 }
105 109 let permissions = [];
106   - for (var item of permissionList) {
  110 + for (let item of permissionList) {
107 111 if(item.type != '2'){
108 112 permissions.push(item.action);
109 113 }
... ...
ant-design-vue-jeecg/src/utils/request.js
... ... @@ -5,9 +5,16 @@ import { VueAxios } from &#39;./axios&#39;
5 5 import {Modal, notification} from 'ant-design-vue'
6 6 import { ACCESS_TOKEN } from "@/store/mutation-types"
7 7  
  8 +//自动设置后台服务 baseURL (也可以手工指定写死项目名字)
  9 +let baseDomain = window._CONFIG['domianURL'];
  10 +let baseProject = baseDomain.substring(baseDomain.lastIndexOf("/"));
  11 +console.log("baseDomain= ",baseDomain)
  12 +console.log("baseProject= ",baseProject)
  13 +
8 14 // 创建 axios 实例
9 15 const service = axios.create({
10   - baseURL: '/jeecg-boot', // api base_url
  16 + //baseURL: '/jeecg-boot',
  17 + baseURL: baseProject, // api base_url
11 18 timeout: 9000 // 请求超时时间
12 19 })
13 20  
... ...
ant-design-vue-jeecg/src/utils/util.js
... ... @@ -257,6 +257,62 @@ export function cssExpand(css, id) {
257 257 document.head.appendChild(style)
258 258 }
259 259  
  260 +
  261 +/** 用于js增强事件,运行JS代码,可以传参 */
  262 +// options 所需参数:
  263 +// 参数名 类型 说明
  264 +// vm VueComponent vue实例
  265 +// event Object event对象
  266 +// jsCode String 待执行的js代码
  267 +// errorMessage String 执行出错后的提示(控制台)
  268 +export function jsExpand(options = {}) {
  269 +
  270 + // 绑定到window上的keyName
  271 + let windowKeyName = 'J_CLICK_EVENT_OPTIONS'
  272 + if (typeof window[windowKeyName] != 'object') {
  273 + window[windowKeyName] = {}
  274 + }
  275 +
  276 + // 随机生成JS增强的执行id,防止冲突
  277 + let id = randomString(16, 'qwertyuioplkjhgfdsazxcvbnm'.toUpperCase())
  278 + // 封装按钮点击事件
  279 + let code = `
  280 + (function (o_${id}) {
  281 + try {
  282 + (function (globalEvent, vm) {
  283 + ${options.jsCode}
  284 + })(o_${id}.event, o_${id}.vm)
  285 + } catch (e) {
  286 + o_${id}.error(e)
  287 + }
  288 + o_${id}.done()
  289 + })(window['${windowKeyName}']['EVENT_${id}'])
  290 + `
  291 + // 创建script标签
  292 + const script = document.createElement('script')
  293 + // 将需要传递的参数挂载到window对象上
  294 + window[windowKeyName]['EVENT_' + id] = {
  295 + vm: options.vm,
  296 + event: options.event,
  297 + // 当执行完成时,无论如何都会调用的回调事件
  298 + done() {
  299 + // 执行完后删除新增的 script 标签不会撤销执行结果(已产生的结果不会被撤销)
  300 + script.outerHTML = ''
  301 + delete window[windowKeyName]['EVENT_' + id]
  302 + },
  303 + // 当js运行出错的时候调用的事件
  304 + error(e) {
  305 + console.group(`${options.errorMessage || '用户自定义JS增强代码运行出错'}(${new Date()})`)
  306 + console.error(e)
  307 + console.groupEnd()
  308 + }
  309 + }
  310 + // 将事件挂载到document中
  311 + script.innerHTML = code
  312 + document.body.appendChild(script)
  313 +}
  314 +
  315 +
260 316 /**
261 317 * 重复值验证工具方法
262 318 *
... ... @@ -270,12 +326,39 @@ export function cssExpand(css, id) {
270 326 * @param callback
271 327 */
272 328 export function validateDuplicateValue(tableName, fieldName, fieldVal, dataId, callback) {
273   - let params = { tableName, fieldName, fieldVal, dataId }
274   - api.duplicateCheck(params).then(res => {
275   - res['success'] ? callback() : callback(res['message'])
276   - }).catch(err => {
277   - callback(err.message || err)
278   - })
  329 + if (fieldVal) {
  330 + let params = { tableName, fieldName, fieldVal, dataId }
  331 + api.duplicateCheck(params).then(res => {
  332 + res['success'] ? callback() : callback(res['message'])
  333 + }).catch(err => {
  334 + callback(err.message || err)
  335 + })
  336 + } else {
  337 + callback()
  338 + }
  339 +}
  340 +
  341 +/**
  342 + * 根据编码校验规则code,校验传入的值是否合法
  343 + *
  344 + * 使用示例:
  345 + * { validator: (rule, value, callback) => validateCheckRule('common', value, callback) }
  346 + *
  347 + * @param ruleCode 编码校验规则 code
  348 + * @param value 被验证的值
  349 + * @param callback
  350 + */
  351 +export function validateCheckRule(ruleCode, value, callback) {
  352 + if (ruleCode && value) {
  353 + value = encodeURIComponent(value)
  354 + api.checkRuleByCode({ ruleCode, value }).then(res => {
  355 + res['success'] ? callback() : callback(res['message'])
  356 + }).catch(err => {
  357 + callback(err.message || err)
  358 + })
  359 + } else {
  360 + callback()
  361 + }
279 362 }
280 363  
281 364 /**
... ... @@ -295,4 +378,40 @@ export function pushIfNotExist(array, value, key) {
295 378 }
296 379 array.push(value)
297 380 return true
  381 +}
  382 +
  383 +/**
  384 + * 可用于判断是否成功
  385 + * @type {symbol}
  386 + */
  387 +export const succeedSymbol = Symbol()
  388 +/**
  389 + * 可用于判断是否失败
  390 + * @type {symbol}
  391 + */
  392 +export const failedSymbol = Symbol()
  393 +
  394 +/**
  395 + * 使 promise 无论如何都会 resolve,除非传入的参数不是一个Promise对象或返回Promise对象的方法
  396 + * 一般用在 Promise.all 中
  397 + *
  398 + * @param promise 可传Promise对象或返回Promise对象的方法
  399 + * @returns {Promise<any>}
  400 + */
  401 +export function alwaysResolve(promise) {
  402 + return new Promise((resolve, reject) => {
  403 + let p = promise
  404 + if (typeof promise === 'function') {
  405 + p = promise()
  406 + }
  407 + if (p instanceof Promise) {
  408 + p.then(data => {
  409 + resolve({ type: succeedSymbol, data })
  410 + }).catch(error => {
  411 + resolve({ type: failedSymbol, error })
  412 + })
  413 + } else {
  414 + reject('alwaysResolve: 传入的参数不是一个Promise对象或返回Promise对象的方法')
  415 + }
  416 + })
298 417 }
299 418 \ No newline at end of file
... ...
ant-design-vue-jeecg/src/views/account/center/Index.vue
... ... @@ -133,7 +133,7 @@
133 133 methods: {
134 134 ...mapGetters(["nickname", "avatar"]),
135 135 getAvatar(){
136   - return window._CONFIG['imgDomainURL']+"/"+this.avatar();
  136 + return window._CONFIG['staticDomainURL']+"/"+this.avatar();
137 137 },
138 138 getTeams() {
139 139 this.$http.get('/api/workplace/teams')
... ...
ant-design-vue-jeecg/src/views/dashboard/IndexChart.vue
... ... @@ -20,14 +20,14 @@
20 20 </chart-card>
21 21 </a-col>
22 22 <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
23   - <chart-card :loading="loading" title="访问量" :total="8846 | NumberFormat">
  23 + <chart-card :loading="loading" title="订单量" :total="8846 | NumberFormat">
24 24 <a-tooltip title="指标说明" slot="action">
25 25 <a-icon type="info-circle-o" />
26 26 </a-tooltip>
27 27 <div>
28 28 <mini-area />
29 29 </div>
30   - <template slot="footer">日访问量<span> {{ '1234' | NumberFormat }}</span></template>
  30 + <template slot="footer">日订单量<span> {{ '1234' | NumberFormat }}</span></template>
31 31 </chart-card>
32 32 </a-col>
33 33 <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }">
... ... @@ -85,7 +85,7 @@
85 85 </a-col>
86 86 </a-row>
87 87 </a-tab-pane>
88   - <a-tab-pane tab="访问量" key="2">
  88 + <a-tab-pane tab="销售趋势" key="2">
89 89 <a-row>
90 90 <a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
91 91 <bar title="销售额趋势" :dataSource="barData"/>
... ... @@ -101,10 +101,10 @@
101 101  
102 102 <a-row>
103 103 <a-col :span="24">
104   - <a-card :loading="loading" :bordered="false" title="最近一周访问次数统计" :style="{ marginTop: '24px' }">
  104 + <a-card :loading="loading" :bordered="false" title="最近一周访问统计" :style="{ marginTop: '24px' }">
105 105 <a-row>
106 106 <a-col :span="6">
107   - <head-info title="今日访问IP数" :content="loginfo.todayIp"></head-info>
  107 + <head-info title="今日IP" :content="loginfo.todayIp"></head-info>
108 108 </a-col>
109 109 <a-col :span="2">
110 110 <a-spin class='circle-cust'>
... ... @@ -112,7 +112,7 @@
112 112 </a-spin>
113 113 </a-col>
114 114 <a-col :span="6">
115   - <head-info title="今日访问次数" :content="loginfo.todayVisitCount"></head-info>
  115 + <head-info title="今日访问" :content="loginfo.todayVisitCount"></head-info>
116 116 </a-col>
117 117 <a-col :span="2">
118 118 <a-spin class='circle-cust'>
... ... @@ -120,7 +120,7 @@
120 120 </a-spin>
121 121 </a-col>
122 122 <a-col :span="6">
123   - <head-info title="访问总次数" :content="loginfo.totalVisitCount"></head-info>
  123 + <head-info title="总访问量" :content="loginfo.totalVisitCount"></head-info>
124 124 </a-col>
125 125 <a-col :span="2">
126 126 <a-spin class='circle-cust'>
... ...
ant-design-vue-jeecg/src/views/dashboard/Workplace.vue
... ... @@ -185,7 +185,7 @@
185 185 },
186 186 created() {
187 187 this.user = this.userInfo
188   - this.avatar = window._CONFIG['imgDomainURL'] +"/"+ this.userInfo.avatar
  188 + this.avatar = window._CONFIG['staticDomainURL'] +"/"+ this.userInfo.avatar
189 189 console.log('this.avatar :'+ this.avatar)
190 190  
191 191 getRoleList().then(res => {
... ...
ant-design-vue-jeecg/src/views/jeecg/SelectDemo.vue
... ... @@ -9,7 +9,7 @@
9 9 <a-row :gutter="24">
10 10 <a-col :span="12">
11 11 <a-form-item label="性别">
12   - <j-dict-select-tag v-model="formData.sex" title="性别" dictCode="sex"/>
  12 + <j-dict-select-tag v-model="formData.sex" title="性别" dictCode="sex" placeholder="请选择性别"/>
13 13 <!-- <j-dict-select-tag title="性别" dictCode="sex" disabled/>-->
14 14 </a-form-item>
15 15 </a-col>
... ... @@ -176,34 +176,28 @@
176 176 <a-row :gutter="24">
177 177 <a-col>
178 178  
179   - <a-form-item label="最大化弹窗">
180   - <a-button @click="()=>modal.visible=true">最大化弹窗</a-button>
  179 + <a-form-item label="JModal弹窗">
  180 + <a-button style="margin-right: 8px;" @click="()=>modal.visible=true">点击弹出JModal</a-button>
  181 + <span style="margin-right: 8px;">全屏化:<a-switch v-model="modal.fullscreen"/></span>
  182 + <span style="margin-right: 8px;">允许切换全屏:<a-switch v-model="modal.switchFullscreen"/></span>
  183 + <span>锁定Body滚动:<a-switch v-model="modal.lockScroll"/></span>
  184 +
181 185 </a-form-item>
182 186  
183   - <a-modal
184   - :visible="modal.visible"
185   - :width="modal.width"
186   - :style="modal.style"
187   - @ok="()=>modal.visible=false"
188   - @cancel="()=>modal.visible=false">
189   -
190   - <template slot="title">
191   - <div style="width: 100%;height:20px;padding-right:32px;">
192   - <div style="float: left;">{{ modal.title }}</div>
193   - <div style="float: right;">
194   - <a-button
195   - icon="fullscreen"
196   - style="width:56px;height:100%;border:0"
197   - @click="handleClickToggleFullScreen"/>
198   - </div>
199   - </div>
200   - </template>
  187 + <j-modal
  188 + :visible.sync="modal.visible"
  189 + :width="1200"
  190 + :title="modal.title"
  191 + :lockScroll="modal.lockScroll"
  192 + :fullscreen.sync="modal.fullscreen"
  193 + :switchFullscreen="modal.switchFullscreen"
  194 + >
201 195  
202 196 <template v-for="(i,k) of 30">
203 197 <p :key="k">这是主体内容,高度是自适应的</p>
204 198 </template>
205 199  
206   - </a-modal>
  200 + </j-modal>
207 201  
208 202 </a-col>
209 203 </a-row>
... ... @@ -217,10 +211,10 @@
217 211 <a-row :gutter="24">
218 212 <a-col :span="12">
219 213 <a-form-item label="树字典">
220   - <j-tree-dict parentCode="B01" />
  214 + <j-tree-dict v-model="formData.treeDict" placeholder="请选择树字典" parentCode="A01" />
221 215 </a-form-item>
222 216 </a-col>
223   - <a-col :span="12"></a-col>
  217 + <a-col :span="12">选中的值(v-model):{{ formData.treeDict }}</a-col>
224 218 </a-row>
225 219  
226 220 <a-row :gutter="24">
... ... @@ -262,6 +256,45 @@
262 256 </a-form-item>
263 257 </a-col>
264 258 </a-row>
  259 +
  260 + <a-row :gutter="24">
  261 + <a-col :span="12">
  262 + <a-form-item label="高级查询">
  263 + <j-super-query :fieldList="superQuery.fieldList" />
  264 + </a-form-item>
  265 + </a-col>
  266 + </a-row>
  267 +
  268 + <a-row :gutter="24">
  269 + <a-col :span="12">
  270 + <a-form-item label="高级查询(自定义按钮)">
  271 + <j-super-query :fieldList="superQuery.fieldList">
  272 + <!-- 直接在内部写一个按钮即可,点击事件自动添加 -->
  273 + <a-button type="primary" ghost icon="clock-circle">高级查询</a-button>
  274 + </j-super-query>
  275 + </a-form-item>
  276 + </a-col>
  277 + </a-row>
  278 + <a-row :gutter="24">
  279 + <a-col :span="12">
  280 + <a-form-item label="图片上传">
  281 + <j-image-upload v-model="imgList"></j-image-upload>
  282 + </a-form-item>
  283 + </a-col>
  284 + <a-col :spapn="12">选中的值(v-model):{{ imgList }}</a-col>
  285 + </a-row>
  286 + <a-row :gutter="24" style="margin-top: 65px;margin-bottom:50px;">
  287 + <a-col :span="12">
  288 + <a-form-item label="文件上传">
  289 + <j-upload v-model="fileList"></j-upload>
  290 + </a-form-item>
  291 + </a-col>
  292 + <a-col :spapn="12">
  293 + 选中的值(v-model):
  294 + <j-ellipsis :value="fileList" :length="30" v-if="fileList.length>0"/>
  295 + </a-col>
  296 + </a-row>
  297 +
265 298 </a-form>
266 299 </div>
267 300  
... ... @@ -286,10 +319,15 @@
286 319 import JTreeDict from "../../components/jeecg/JTreeDict.vue";
287 320 import JCron from "@/components/jeecg/JCron.vue";
288 321 import JTreeSelect from '@/components/jeecg/JTreeSelect'
  322 + import JSuperQuery from '@/components/jeecg/JSuperQuery'
  323 + import JUpload from '@/components/jeecg/JUpload'
  324 + import JImageUpload from '@/components/jeecg/JImageUpload'
289 325  
290 326 export default {
291 327 name: 'SelectDemo',
292 328 components: {
  329 + JImageUpload,
  330 + JUpload,
293 331 JTreeDict,
294 332 JDictSelectTag,
295 333 JSelectDepart,
... ... @@ -299,7 +337,7 @@
299 337 JCheckbox,
300 338 JCodeEditor,
301 339 JDate, JEditor, JEllipsis, JGraphicCode, JSlider, JSelectMultiple,
302   - JCron, JTreeSelect
  340 + JCron, JTreeSelect, JSuperQuery
303 341 },
304 342 data() {
305 343 return {
... ... @@ -352,11 +390,20 @@ sayHi(&#39;hello, world!&#39;)`
352 390 modal: {
353 391 title: '这里是标题',
354 392 visible: false,
355   - width: '100%',
356   - style: { top: '20px' },
357   - fullScreen: true
  393 + lockScroll: true,
  394 + fullscreen: true,
  395 + switchFullscreen: true,
358 396 },
359 397 cron: '',
  398 + superQuery: {
  399 + fieldList: [
  400 + { type: 'input', value: 'name', text: '姓名', },
  401 + { type: 'select', value: 'sex', text: '性别', dictCode: 'sex' },
  402 + { type: 'number', value: 'age', text: '年龄', }
  403 + ]
  404 + },
  405 + fileList:[],
  406 + imgList:[],
360 407 }
361 408 },
362 409 computed: {
... ... @@ -399,18 +446,6 @@ sayHi(&#39;hello, world!&#39;)`
399 446 handleJSliderSuccess(value) {
400 447 this.jslider.value = value
401 448 },
402   - /** 切换全屏显示 */
403   - handleClickToggleFullScreen() {
404   - let mode = !this.modal.fullScreen
405   - if (mode) {
406   - this.modal.width = '100%'
407   - this.modal.style.top = '20px'
408   - } else {
409   - this.modal.width = '1200px'
410   - this.modal.style.top = '50px'
411   - }
412   - this.modal.fullScreen = mode
413   - },
414 449 setCorn(data){
415 450 this.$nextTick(() => {
416 451 this.form.cronExpression = data;
... ...
ant-design-vue-jeecg/src/views/jeecg/helloworld.vue
... ... @@ -21,7 +21,9 @@
21 21 </a-col>
22 22 <a-form-item :wrapperCol="{ span: 12, offset: 5 }">
23 23 <a-col :md="24" :sm="24">
24   - <a-button type="primary" htmlType="submit">Submit</a-button>
  24 + <a-form-item :wrapperCol="{ span: 12, offset: 5 }">
  25 + <a-button type="primary" htmlType="submit">Submit</a-button>
  26 + </a-form-item>
25 27 </a-col>
26 28 </a-form-item>
27 29 </a-form>
... ...
ant-design-vue-jeecg/src/views/jeecg/modules/JeecgDemoModal.vue
... ... @@ -35,12 +35,13 @@
35 35 <a-form-item
36 36 :labelCol="labelCol"
37 37 :wrapperCol="wrapperCol"
38   - label="sex">
39   - <a-select v-decorator="['sex', {}]" placeholder="请选择性别">
40   - <a-select-option value="">请选择性别</a-select-option>
41   - <a-select-option value="1">男性</a-select-option>
42   - <a-select-option value="2">女性</a-select-option>
43   - </a-select>
  38 + label="性别">
  39 + <!-- <a-select v-decorator="['sex', {}]" placeholder="请选择性别">
  40 + <a-select-option value="">请选择</a-select-option>
  41 + <a-select-option value="1">男</a-select-option>
  42 + <a-select-option value="2">女</a-select-option>
  43 + </a-select>-->
  44 + <j-dict-select-tag type="radio" v-decorator="['sex', {}]" :trigger-change="true" dictCode="sex"/>
44 45 </a-form-item>
45 46 <a-form-item
46 47 :labelCol="labelCol"
... ...
ant-design-vue-jeecg/src/views/jeecg/report/ArchivesStatisticst.vue
... ... @@ -2,7 +2,7 @@
2 2 <a-card :bordered="false">
3 3 <a-tabs defaultActiveKey="1" @change="callback">
4 4 <a-tab-pane tab="柱状图" key="1">
5   - <a-row :gutter="24">
  5 + <a-row>
6 6 <a-col :span="10">
7 7 <a-radio-group :value="barType" @change="statisticst">
8 8 <a-radio-button value="year">按年统计</a-radio-button>
... ...
ant-design-vue-jeecg/src/views/jeecg/tablist/form/JeecgOrderCustomerModal.vue
... ... @@ -43,24 +43,7 @@
43 43 :wrapperCol="wrapperCol"
44 44 label="身份证扫描件"
45 45 hasFeedback>
46   - <a-upload
47   - :action="uploadAction"
48   - listType="picture-card"
49   - :headers="headers"
50   - :fileList="fileList"
51   - @change="handleChange"
52   - @preview="handlePreview"
53   - >
54   - <a-button>
55   - <a-icon type="upload"/>
56   - upload
57   - </a-button>
58   - </a-upload>
59   - <a-modal :visible="previewVisible" :footer="null" @cancel="handlePicCancel">
60   - <img alt="example" style="width: 100%" :src="previewImage"/>
61   - </a-modal>
62   -
63   - <br/>
  46 + <j-image-upload text="上传" v-model="fileList" :isMultiple="true"></j-image-upload>
64 47 </a-form-item>
65 48 <a-form-item
66 49 :labelCol="labelCol"
... ... @@ -88,11 +71,11 @@
88 71 import pick from 'lodash.pick'
89 72 import Vue from 'vue'
90 73 import {ACCESS_TOKEN} from "@/store/mutation-types"
91   -
92   - import { getUploadFileList,getFilePaths } from '@/utils/commonUploadFile.js'
  74 + import JImageUpload from '../../../../components/jeecg/JImageUpload'
93 75  
94 76 export default {
95 77 name: "JeecgOrderCustomerModal",
  78 + components: { JImageUpload },
96 79 data() {
97 80 return {
98 81 title: "操作",
... ... @@ -177,7 +160,7 @@
177 160 add: "/test/order/addCustomer",
178 161 edit: "/test/order/editCustomer",
179 162 fileUpload: window._CONFIG['domianURL'] + "/sys/common/upload",
180   - imgerver: window._CONFIG['domianURL'] + "/sys/common/view",
  163 + imgerver: window._CONFIG['staticDomainURL'],
181 164 getOrderCustomerList: "/test/order/listOrderCustomerByMainId",
182 165 },
183 166 validatorRules: {
... ... @@ -222,8 +205,6 @@
222 205  
223 206 this.form.resetFields();
224 207 this.orderId = record.orderId;
225   - let currFileList = getUploadFileList(record.idcardPic)
226   - this.fileList = [...currFileList]
227 208 this.model = Object.assign({}, record);
228 209 if (record.id) {
229 210 this.hiding = false;
... ... @@ -232,6 +213,9 @@
232 213 this.$nextTick(() => {
233 214 this.form.setFieldsValue(pick(this.model, 'id', 'name', 'sex', 'idcard','telphone', 'orderId', 'createBy', 'createTime', 'updateBy', 'updateTime'))
234 215 });
  216 + setTimeout(() => {
  217 + this.fileList = record.idcardPic
  218 + }, 5)
235 219 } else {
236 220 this.addStatus = false;
237 221 this.editStatus = true;
... ... @@ -262,7 +246,7 @@
262 246 let formData = Object.assign(this.model, values);
263 247 console.log(formData);
264 248 formData.orderId = this.orderId;
265   - formData.idcardPic = getFilePaths(this.fileList)
  249 + formData.idcardPic = this.fileList;
266 250 httpAction(httpurl, formData, method).then((res) => {
267 251 if (res.success) {
268 252 that.$message.success(res.message);
... ...
ant-design-vue-jeecg/src/views/modules/online/cgform/OnlCgformCopyList.vue
... ... @@ -6,13 +6,13 @@
6 6 <a-form layout="inline">
7 7 <a-row :gutter="24">
8 8  
9   - <a-col :md="6" :sm="24">
  9 + <a-col :xl="6" :lg="7" :md="8" :sm="24">
10 10 <a-form-item label="表名">
11 11 <a-input placeholder="请输入表名" v-model="queryParam.tableName"></a-input>
12 12 </a-form-item>
13 13 </a-col>
14 14  
15   - <a-col :md="6" :sm="24">
  15 + <a-col :xl="6" :lg="7" :md="8" :sm="24">
16 16 <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
17 17 <a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
18 18 <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
... ... @@ -25,10 +25,10 @@
25 25  
26 26 <!-- 操作按钮区域 -->
27 27 <div class="table-operator">
28   - <a-button @click="doCgformButton" type="primary" icon="highlight" style="margin-left:8px">自定义按钮</a-button>
29   - <a-button @click="doEnhanceJs" type="primary" icon="strikethrough" style="margin-left:8px">JS增强</a-button>
30   - <a-button @click="doEnhanceSql" type="primary" icon="filter" style="margin-left:8px">SQL增强</a-button>
31   - <a-button @click="doEnhanceJava" type="primary" icon="tool" style="margin-left:8px">Java增强</a-button>
  28 + <a-button @click="doCgformButton" type="primary" icon="highlight">自定义按钮</a-button>
  29 + <a-button @click="doEnhanceJs" type="primary" icon="strikethrough">JS增强</a-button>
  30 + <a-button @click="doEnhanceSql" type="primary" icon="filter">SQL增强</a-button>
  31 + <a-button @click="doEnhanceJava" type="primary" icon="tool">Java增强</a-button>
32 32  
33 33 <a-dropdown v-if="selectedRowKeys.length > 0">
34 34 <a-menu slot="overlay">
... ... @@ -37,7 +37,7 @@
37 37 删除
38 38 </a-menu-item>
39 39 </a-menu>
40   - <a-button style="margin-left: 8px"> 批量操作
  40 + <a-button> 批量操作
41 41 <a-icon type="down"/>
42 42 </a-button>
43 43 </a-dropdown>
... ... @@ -348,6 +348,9 @@
348 348 }
349 349 }
350 350 </script>
  351 +<style scoped>
  352 + @import '~@assets/less/common.less';
  353 +</style>
351 354 <style lang="less">
352 355 .ant-card-body .table-operator {
353 356 margin-bottom: 18px;
... ...
ant-design-vue-jeecg/src/views/modules/online/cgform/OnlCgformHeadList.vue
... ... @@ -6,18 +6,22 @@
6 6 <a-form layout="inline">
7 7 <a-row :gutter="24">
8 8  
9   - <a-col :md="6" :sm="24">
  9 + <a-col :xl="6" :lg="7" :md="8" :sm="24">
10 10 <a-form-item label="表名">
11 11 <a-input placeholder="请输入表名" v-model="queryParam.tableName"></a-input>
12 12 </a-form-item>
13 13 </a-col>
14   - <a-col :md="6" :sm="24">
  14 + <a-col :xl="6" :lg="7" :md="8" :sm="24">
15 15 <a-form-item label="表类型">
16 16 <j-dict-select-tag dictCode="cgform_table_type" v-model="queryParam.tableType"/>
17 17 </a-form-item>
18 18 </a-col>
19   -
20   - <a-col :md="6" :sm="24">
  19 + <a-col :xl="6" :lg="7" :md="8" :sm="24">
  20 + <a-form-item label="表描述">
  21 + <a-input placeholder="请输入表描述" v-model="queryParam.tableTxt"></a-input>
  22 + </a-form-item>
  23 + </a-col>
  24 + <a-col :xl="6" :lg="7" :md="8" :sm="24">
21 25 <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
22 26 <a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
23 27 <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
... ... @@ -31,12 +35,12 @@
31 35 <!-- 操作按钮区域 -->
32 36 <div class="table-operator">
33 37 <a-button @click="handleAdd" type="primary" icon="plus">新增</a-button>
34   - <a-button @click="doCgformButton" type="primary" icon="highlight" style="margin-left:8px">自定义按钮</a-button>
35   - <a-button @click="doEnhanceJs" type="primary" icon="strikethrough" style="margin-left:8px">JS增强</a-button>
36   - <a-button @click="doEnhanceSql" type="primary" icon="filter" v-has="'online:sql'" style="margin-left:8px">SQL增强</a-button>
37   - <a-button @click="doEnhanceJava" type="primary" icon="tool" style="margin-left:8px">Java增强</a-button>
38   - <a-button @click="importOnlineForm" type="primary" icon="database" style="margin-left:8px">从数据库导入表单</a-button>
39   - <a-button @click="goGenerateCode" v-has="'online:goGenerateCode'" type="primary" icon="database" style="margin-left:8px">代码生成</a-button>
  38 + <a-button @click="doCgformButton" type="primary" icon="highlight">自定义按钮</a-button>
  39 + <a-button @click="doEnhanceJs" type="primary" icon="strikethrough">JS增强</a-button>
  40 + <a-button @click="doEnhanceSql" type="primary" icon="filter">SQL增强</a-button>
  41 + <a-button @click="doEnhanceJava" type="primary" icon="tool">Java增强</a-button>
  42 + <a-button @click="importOnlineForm" type="primary" icon="database">从数据库导入表单</a-button>
  43 + <a-button @click="goGenerateCode" type="primary" icon="database">代码生成</a-button>
40 44  
41 45 <a-dropdown v-if="selectedRowKeys.length > 0">
42 46 <a-menu slot="overlay">
... ... @@ -45,7 +49,7 @@
45 49 删除
46 50 </a-menu-item>
47 51 </a-menu>
48   - <a-button style="margin-left: 8px"> 批量操作
  52 + <a-button> 批量操作
49 53 <a-icon type="down"/>
50 54 </a-button>
51 55 </a-dropdown>
... ... @@ -204,8 +208,12 @@
204 208 title: '表类型',
205 209 align: 'center',
206 210 dataIndex: 'tableType',
207   - customRender: (text) => {
208   - return filterDictText(this.tableTypeDictOptions, `${text}`)
  211 + customRender: (text, record) => {
  212 + let tbTypeText = filterDictText(this.tableTypeDictOptions, `${text}`)
  213 + if(record.isTree === 'Y'){
  214 + tbTypeText+='(树)'
  215 + }
  216 + return tbTypeText;
209 217 }
210 218 },
211 219 {
... ... @@ -316,10 +324,14 @@
316 324 this.syncFormId = id
317 325 },
318 326 goPageOnline(rd) {
319   - if(rd.isTree=='Y'){
320   - this.$router.push({ path: '/online/cgformTreeList/' + rd.id })
  327 + if(rd.themeTemplate === 'erp'){
  328 + this.$router.push({ path: '/online/cgformErpList/' + rd.id })
321 329 }else{
322   - this.$router.push({ path: '/online/cgformList/' + rd.id })
  330 + if(rd.isTree=='Y'){
  331 + this.$router.push({ path: '/online/cgformTreeList/' + rd.id })
  332 + }else{
  333 + this.$router.push({ path: '/online/cgformList/' + rd.id })
  334 + }
323 335 }
324 336 },
325 337 handleOnlineUrlClose() {
... ... @@ -438,6 +450,9 @@
438 450 }
439 451 }
440 452 </script>
  453 +<style scoped>
  454 + @import '~@assets/less/common.less';
  455 +</style>
441 456 <style lang="less">
442 457 .ant-card-body .table-operator {
443 458 margin-bottom: 18px;
... ...
ant-design-vue-jeecg/src/views/modules/online/cgform/auto/OnlCgformAutoList.vue
... ... @@ -40,16 +40,15 @@
40 40 <!-- 操作按钮区域 -->
41 41 <div class="table-operator">
42 42 <a-button v-if="buttonSwitch.add" @click="handleAdd" type="primary" icon="plus">新增</a-button>
43   - <a-button v-if="buttonSwitch.import" @click="handleImportXls" type="primary" icon="upload" style="margin-left:8px">导入</a-button>
44   - <a-button v-if="buttonSwitch.export" @click="handleExportXls" type="primary" icon="download" style="margin-left:8px">导出</a-button>
  43 + <a-button v-if="buttonSwitch.import" @click="handleImportXls" type="primary" icon="upload">导入</a-button>
  44 + <a-button v-if="buttonSwitch.export" @click="handleExportXls" type="primary" icon="download">导出</a-button>
45 45 <template v-if="cgButtonList && cgButtonList.length>0" v-for="(item,index) in cgButtonList">
46 46 <a-button
47 47 v-if=" item.optType=='js' "
48 48 :key=" 'cgbtn'+index "
49 49 @click="cgButtonJsHandler(item.buttonCode)"
50 50 type="primary"
51   - :icon="item.buttonIcon"
52   - style="margin-left:8px">
  51 + :icon="item.buttonIcon">
53 52 {{ item.buttonName }}
54 53 </a-button>
55 54 <a-button
... ... @@ -57,8 +56,7 @@
57 56 :key=" 'cgbtn'+index "
58 57 @click="cgButtonActionHandler(item.buttonCode)"
59 58 type="primary"
60   - :icon="item.buttonIcon"
61   - style="margin-left:8px">
  59 + :icon="item.buttonIcon">
62 60 {{ item.buttonName }}
63 61 </a-button>
64 62 </template>
... ... @@ -69,13 +67,11 @@
69 67 :fieldList="superQuery.fieldList"
70 68 :saveCode="$route.fullPath"
71 69 :loading="table.loading"
72   - style="margin-left: 8px;"
73 70 @handleSuperQuery="handleSuperQuery"/>
74 71  
75 72 <a-button
76 73 v-if="buttonSwitch.batch_delete"
77 74 @click="handleDelBatch"
78   - style="margin-left:8px"
79 75 v-show="table.selectedRowKeys.length > 0"
80 76 ghost
81 77 type="primary"
... ... @@ -103,6 +99,16 @@
103 99 :scroll="table.scroll"
104 100 style="min-height: 300px">
105 101  
  102 + <!-- 支持链接href跳转 -->
  103 + <template
  104 + v-for="field of fieldHrefSlots"
  105 + :slot="field.slotName"
  106 + slot-scope="text, record"
  107 + >
  108 + <a @click="handleClickFieldHref(field,record)">{{ text }}</a>
  109 + </template>
  110 +
  111 +
106 112 <template slot="dateSlot" slot-scope="text">
107 113 <span>{{ getFormatDate(text) }}</span>
108 114 </template>
... ... @@ -149,23 +155,17 @@
149 155 更多 <a-icon type="down" />
150 156 </a>
151 157 <a-menu slot="overlay">
152   - <a-menu-item >
  158 + <a-menu-item v-if="buttonSwitch.detail">
153 159 <a href="javascript:;" @click="handleDetail(record)">详情</a>
154 160 </a-menu-item>
155 161 <template v-if="hasBpmStatus">
156 162 <template v-if="record.bpm_status == '1'||record.bpm_status == ''|| record.bpm_status == null">
157   - <a-menu-item>
158   - <a href="javascript:;" @click="startProcess(record)">提交流程</a>
159   - </a-menu-item>
160 163 <a-menu-item v-if="buttonSwitch.delete">
161 164 <a-popconfirm title="确定删除吗?" @confirm="() => handleDeleteOne(record)">
162 165 <a>删除</a>
163 166 </a-popconfirm>
164 167 </a-menu-item>
165 168 </template>
166   - <template v-else>
167   - <a-menu-item @click="handlePreviewPic(record)">审批进度</a-menu-item>
168   - </template>
169 169 </template>
170 170 <template v-else>
171 171 <a-menu-item v-if="buttonSwitch.delete">
... ... @@ -175,7 +175,7 @@
175 175 </a-menu-item>
176 176 </template>
177 177 <template v-if="cgButtonLinkList && cgButtonLinkList.length>0" v-for="(btnItem,btnIndex) in cgButtonLinkList">
178   - <a-menu-item :key=" 'cgbtnLink'+btnIndex ">
  178 + <a-menu-item :key=" 'cgbtnLink'+btnIndex " v-if="showLinkButton(btnItem,record)">
179 179 <a href="javascript:void(0);" @click="cgButtonLinkHandler(record,btnItem.buttonCode,btnItem.optType)">
180 180 <a-icon v-if="btnItem.buttonIcon" :type="btnItem.buttonIcon" />
181 181 {{ btnItem.buttonName }}
... ... @@ -192,20 +192,27 @@
192 192  
193 193 <j-import-modal ref="importModal" :url="getImportUrl()" @ok="importOk"></j-import-modal>
194 194  
  195 + <!-- 跳转Href的动态组件方式 -->
  196 + <a-modal v-bind="hrefComponent.model" v-on="hrefComponent.on">
  197 + <component :is="hrefComponent.is" v-bind="hrefComponent.params"/>
  198 + </a-modal>
  199 +
195 200 </div>
196 201 </a-card>
197 202 </template>
198 203  
199 204 <script>
200 205  
  206 + import { HrefJump } from '@/mixins/OnlAutoListMixin'
201 207 import { postAction,getAction,deleteAction,downFile } from '@/api/manage'
202 208 import { filterMultiDictText } from '@/components/dict/JDictSelectUtil'
203   - import { filterObj } from '@/utils/util';
  209 + import { cloneObject, filterObj } from '@/utils/util'
204 210 import JImportModal from '@/components/jeecg/JImportModal'
205 211 import JSuperQuery from '@comp/jeecg/JSuperQuery'
206 212  
207 213 export default {
208 214 name: 'OnlCgFormAutoList',
  215 + mixins: [HrefJump],
209 216 components: {
210 217 JSuperQuery,
211 218 JImportModal,
... ... @@ -222,7 +229,6 @@
222 229 optPre:"/online/cgform/api/form/",
223 230 exportXls:'/online/cgform/api/exportXls/',
224 231 buttonAction:'/online/cgform/api/doButton',
225   - startProcess: "/process/extActProcess/startMutilProcess",
226 232 },
227 233 flowCodePre:"onl_",
228 234 isorter:{
... ... @@ -282,7 +288,8 @@
282 288 delete:true,
283 289 batch_delete:true,
284 290 import:true,
285   - export:true
  291 + export:true,
  292 + detail:true
286 293 },
287 294 hasBpmStatus:false,
288 295 checkboxFlag:false,
... ... @@ -304,8 +311,7 @@
304 311 this.cgButtonJsHandler('mounted')
305 312 },
306 313 watch: {
307   - '$route.path'(newVal,oldVal) {
308   - console.log('$route.path: ',oldVal)
  314 + '$route'() {
309 315 // 刷新参数放到这里去触发,就可以刷新相同界面了
310 316 this.initAutoList()
311 317 }
... ... @@ -343,30 +349,6 @@
343 349 this.hasBpmStatus = false;
344 350 }
345 351 },
346   - startProcess: function(record){
347   - var that = this;
348   - this.$confirm({
349   - title:"提示",
350   - content:"确认提交流程吗?",
351   - onOk: function(){
352   - var param = {
353   - flowCode:that.flowCodePre+that.currentTableName,
354   - id:record.id,
355   - formUrl:"modules/bpm/task/form/OnlineFormDetail",
356   - formUrlMobile:"modules/bpm/task/form/OnlineFormDetail"
357   - }
358   - postAction(that.url.startProcess,param).then((res)=>{
359   - if(res.success){
360   - that.$message.success(res.message);
361   - that.loadData();
362   - that.onClearSelected();
363   - }else{
364   - that.$message.warning(res.message);
365   - }
366   - });
367   - }
368   - });
369   - },
370 352 initQueryInfo(){
371 353 getAction(`${this.url.getQueryInfo}${this.code}`).then((res)=>{
372 354 console.log("--onlineList-获取查询条件配置",res);
... ... @@ -407,6 +389,7 @@
407 389 this.table.pagination = false
408 390 }
409 391  
  392 + this.fieldHrefSlots = res.result.fieldHrefSlots
410 393 this.dictOptions = res.result.dictOptions
411 394 this.formTemplate = res.result.formTemplate
412 395 this.description = res.result.description
... ... @@ -441,6 +424,8 @@
441 424 this.hasBpmStatusFilter();
442 425 this.loadData();
443 426 this.initQueryInfo();
  427 + //加载新路由,清空checkbox选中
  428 + this.table.selectedRowKeys = [];
444 429 }else{
445 430 this.$message.warning(res.message)
446 431 }
... ... @@ -569,6 +554,10 @@
569 554 this.cgButtonLinkHandler(record,"beforeEdit","js")
570 555 this.$refs.modal.edit(this.formTemplate,record.id);
571 556 },
  557 + showLinkButton(item,record){
  558 + let btn = new ButtonExpHandler(item.exp,record);
  559 + return btn.show;
  560 + },
572 561 handleDetail(record){
573 562 this.$refs.modal.detail(this.formTemplate,record.id);
574 563 },
... ... @@ -604,7 +593,8 @@
604 593 dictCode: field.dictCode,
605 594 dictTable: field.dictTable,
606 595 dictText: field.dictText,
607   - options: field.enum || field.options
  596 + options: field.enum || field.options,
  597 + order: field.order,
608 598 })
609 599 }
610 600 let fieldList = []
... ... @@ -630,6 +620,17 @@
630 620 setField(fieldList, field)
631 621 }
632 622 }
  623 + // 冒泡排序
  624 + for (let i = 0; i < fieldList.length; i++) {
  625 + for (let j = i + 1; j < fieldList.length; j++) {
  626 + let temp1 = fieldList[i]
  627 + let temp2 = fieldList[j]
  628 + if (temp1.order > temp2.order) {
  629 + fieldList[i] = temp2
  630 + fieldList[j] = temp1
  631 + }
  632 + }
  633 + }
633 634 this.superQuery.fieldList = fieldList
634 635 }
635 636 },
... ... @@ -641,7 +642,7 @@
641 642 if(text && text.indexOf(",")>0){
642 643 text = text.substring(0,text.indexOf(","))
643 644 }
644   - return window._CONFIG['imgDomainURL']+"/"+text
  645 + return window._CONFIG['staticDomainURL']+"/"+text
645 646 },
646 647 downloadRowFile(text){
647 648 if(!text){
... ... @@ -651,7 +652,7 @@
651 652 if(text.indexOf(",")>0){
652 653 text = text.substring(0,text.indexOf(","))
653 654 }
654   - window.open(window._CONFIG['downloadUrl']+"/"+text);//TODO 下载的方法
  655 + window.open(window._CONFIG['staticDomainURL']+"/"+text);//TODO 下载的方法
655 656 },
656 657 handleDelBatch(){
657 658 if(this.table.selectedRowKeys.length<=0){
... ... @@ -777,13 +778,15 @@
777 778 }
778 779 },
779 780 initButtonSwitch(hideColumns){
  781 + Object.keys(this.buttonSwitch).forEach(key=>{
  782 + this.buttonSwitch[key]=true
  783 + })
780 784 if(hideColumns && hideColumns.length>0){
781 785 Object.keys(this.buttonSwitch).forEach(key=>{
782 786 if(hideColumns.indexOf(key)>=0){
783 787 this.buttonSwitch[key]=false
784 788 }
785 789 })
786   -
787 790 }
788 791 },
789 792  
... ... @@ -801,6 +804,9 @@
801 804 }
802 805 }
803 806 </script>
  807 +<style scoped>
  808 + @import '~@assets/less/common.less';
  809 +</style>
804 810 <style>
805 811 .ant-card-body .table-operator{
806 812 margin-bottom: 18px;
... ...
ant-design-vue-jeecg/src/views/modules/online/cgform/auto/OnlCgformTreeList.vue
... ... @@ -98,15 +98,6 @@
98 98 <a-menu-item >
99 99 <a @click="handleDetail(record)">详情</a>
100 100 </a-menu-item>
101   -
102   - <a-menu-item v-if="showSubmitFlowButton(record)">
103   - <a @click="startProcess(record)">提交流程</a>
104   - </a-menu-item>
105   -
106   - <template v-if="showViewFlowButton(record)">
107   - <a-menu-item @click="handlePreviewPic(record)">审批进度</a-menu-item>
108   - </template>
109   -
110 101 <a-menu-item v-if="showOptButton('delete',record)">
111 102 <a-popconfirm title="确定删除吗?" @confirm="() => handleDeleteOne(record)">
112 103 <a>删除</a>
... ... @@ -146,7 +137,7 @@
146 137 export default {
147 138 name: 'OnlCgformTreeList',
148 139 components: {
149   - JImportModal
  140 + JImportModal,
150 141 },
151 142 data() {
152 143 return {
... ... @@ -183,7 +174,6 @@
183 174 optPre:"/online/cgform/api/form/",
184 175 exportXls:'/online/cgform/api/exportXls/',
185 176 buttonAction:'/online/cgform/api/doButton',
186   - startProcess: "/process/extActProcess/startMutilProcess"
187 177 },
188 178 isorter:{
189 179 column: 'create_time',
... ... @@ -482,7 +472,7 @@
482 472 if(text && text.indexOf(",")>0){
483 473 text = text.substring(0,text.indexOf(","))
484 474 }
485   - return window._CONFIG['imgDomainURL']+"/"+text
  475 + return window._CONFIG['staticDomainURL']+"/"+text
486 476 },
487 477 downloadRowFile(text){
488 478 if(!text){
... ... @@ -492,7 +482,7 @@
492 482 if(text.indexOf(",")>0){
493 483 text = text.substring(0,text.indexOf(","))
494 484 }
495   - window.open(window._CONFIG['downloadUrl']+"/"+text);
  485 + window.open(window._CONFIG['staticDomainURL']+"/"+text);
496 486 },
497 487 /*-------数据格式化-end----------*/
498 488  
... ... @@ -651,14 +641,6 @@
651 641 }
652 642 return true
653 643 },
654   - showSubmitFlowButton(record){
655   - if(this.hasBpmStatus){
656   - if(record.bpm_status ==null || record.bpm_status =='' || record.bpm_status == '1'){
657   - return true
658   - }
659   - }
660   - return false
661   - },
662 644 showViewFlowButton(record){
663 645 if(this.hasBpmStatus){
664 646 if(record.bpm_status !=null && record.bpm_status !='' && record.bpm_status != '1'){
... ... @@ -691,6 +673,7 @@
691 673 }
692 674 });
693 675 },
  676 +
694 677 }
695 678 }
696 679 </script>
... ...
ant-design-vue-jeecg/src/views/modules/online/cgform/auto/erp/OnlCgformErpList.vue 0 → 100644
  1 +<template>
  2 + <a-card :bordered="false" style="height: 100%">
  3 + <online-common-list
  4 + :ref="'onl_'+mainModel.currentTableName"
  5 + :code="code"
  6 + :model="mainModel"
  7 + @seleted="onSelected">
  8 + </online-common-list>
  9 +
  10 + <a-tabs defaultActiveKey="0">
  11 + <a-tab-pane v-for="(item,index) in subList" :tab="item.description" :key="index+''" :forceRender="true" >
  12 + <online-common-list
  13 + :ref="item.currentTableName"
  14 + :code="item.code"
  15 + :model="item"
  16 + :main="selectedRow">
  17 + </online-common-list>
  18 + </a-tab-pane>
  19 + </a-tabs>
  20 + </a-card>
  21 +</template>
  22 +
  23 +<script>
  24 + import { getAction } from '@/api/manage'
  25 +
  26 + export default {
  27 + name: 'OnlCgformErpList',
  28 + components:{
  29 + },
  30 + data(){
  31 + return {
  32 + code:'',
  33 + url: {
  34 + getColumns: '/online/cgform/api/getErpColumns/',
  35 + },
  36 + mainModel:{},
  37 + subList:[],
  38 + mainId:'',
  39 + selectedRow:{}
  40 +
  41 + }
  42 + },
  43 + watch: {
  44 + '$route'() {
  45 + // 刷新参数放到这里去触发,就可以刷新相同界面了
  46 + this.initColumnConfig()
  47 + }
  48 + },
  49 +
  50 + created() {
  51 + this.initColumnConfig();
  52 + },
  53 + methods:{
  54 + getSubIndex(index){
  55 + return index+1 + ''
  56 + },
  57 + getSubRef(item){
  58 + let ref = item.currentTableName
  59 + console.log("ref string",ref)
  60 + return ref;
  61 + },
  62 + initColumnConfig(){
  63 + if(!this.$route.params.code){
  64 + return false
  65 + }
  66 + this.code = this.$route.params.code
  67 + getAction(`${this.url.getColumns}${this.code}`).then((res)=>{
  68 + console.log("erp表单配置",res)
  69 + if(res.success){
  70 + this.mainModel = res.result.main
  71 + this.subList = res.result.subList
  72 +
  73 + this.$nextTick(()=>{
  74 + this.$refs['onl_'+this.mainModel.currentTableName].initListByModel();
  75 + if(this.subList && this.subList.length>0){
  76 + for(let item of this.subList){
  77 + this.$refs[item.currentTableName][0].initListByModel();
  78 + }
  79 + }
  80 + });
  81 +
  82 + }
  83 + })
  84 + },
  85 + onSelected(row){
  86 + console.log("onSelected",row)
  87 + this.selectedRow = row;
  88 + }
  89 +
  90 +
  91 + }
  92 + }
  93 +</script>
  94 +
  95 +<style>
  96 + .ant-card-body .table-operator{
  97 + margin-bottom: 18px;
  98 + }
  99 + .ant-table-tbody .ant-table-row td{
  100 + padding-top:15px;
  101 + padding-bottom:15px;
  102 + }
  103 + .anty-row-operator button{margin: 0 5px}
  104 + .ant-btn-danger{background-color: #ffffff}
  105 +
  106 + .anty-img-wrap{height:25px;position: relative;}
  107 + .anty-img-wrap > img{max-height:100%;}
  108 + .ant-modal-cust-warp{height: 100%}
  109 + .ant-modal-cust-warp .ant-modal-body{height:calc(100% - 110px) !important;overflow-y: auto}
  110 + .ant-modal-cust-warp .ant-modal-content{height:90% !important;overflow-y: hidden}
  111 +</style>
0 112 \ No newline at end of file
... ...
ant-design-vue-jeecg/src/views/modules/online/cgform/util/TableUtils.js
... ... @@ -109,6 +109,7 @@ export function getMasterTableInitialData() {
109 109 // table2
110 110 isShowForm: '0',
111 111 isShowList: '0',
  112 + isReadOnly: '1',
112 113 fieldShowType: 'text',
113 114 fieldLength: '120',
114 115 queryMode: 'single',
... ...
ant-design-vue-jeecg/src/views/modules/oss/OSSFileList.vue
... ... @@ -31,7 +31,21 @@
31 31 @change="handleChange">
32 32 <a-button>
33 33 <a-icon type="upload"/>
34   - 文件上传
  34 + OSS文件上传
  35 + </a-button>
  36 + </a-upload>
  37 +
  38 + <a-upload
  39 + name="file"
  40 + :multiple="false"
  41 + :action="minioUploadAction"
  42 + :headers="tokenHeader"
  43 + :showUploadList="false"
  44 + :beforeUpload="beforeUpload"
  45 + @change="handleChange">
  46 + <a-button>
  47 + <a-icon type="upload"/>
  48 + MINIO文件上传
35 49 </a-button>
36 50 </a-upload>
37 51 </div>
... ... @@ -58,7 +72,9 @@
58 72 @change="handleTableChange">
59 73  
60 74 <span slot="action" slot-scope="text, record">
61   - <a @click="ossDelete(record.id)">删除</a>
  75 + <a @click="handlePreview(record)">预览</a>
  76 + <a-divider type="vertical"/>
  77 + <a @click="ossDelete(record.id)">删除</a>
62 78 </span>
63 79  
64 80 </a-table>
... ... @@ -108,14 +124,18 @@
108 124 url: {
109 125 upload: "/oss/file/upload",
110 126 list: "/oss/file/list",
111   - delete: "/oss/file/delete"
  127 + delete: "/oss/file/delete",
  128 + minioUpload: "/sys/upload/uploadMinio"
112 129 }
113 130 }
114 131 },
115 132 computed: {
116 133 uploadAction() {
117 134 return window._CONFIG['domianURL'] + this.url.upload;
118   - }
  135 + },
  136 + minioUploadAction() {
  137 + return window._CONFIG['domianURL'] + this.url.minioUpload;
  138 + },
119 139 },
120 140 methods: {
121 141 beforeUpload(file) {
... ... @@ -139,10 +159,10 @@
139 159 this.loadData()
140 160 this.$message.success(`${info.file.name} 上传成功!`);
141 161 } else {
142   - this.$message.error(`${info.file.name} 上传失败.`);
  162 + this.$message.error(`${info.file.response.message}`);
143 163 }
144 164 } else if (info.file.status === 'error') {
145   - this.$message.error(`${info.file.name} 上传失败.`);
  165 + this.$message.error(`${info.file.response.message}`);
146 166 }
147 167 },
148 168 ossDelete(id) {
... ... @@ -154,11 +174,17 @@
154 174 that.handleDelete(id)
155 175 }
156 176 });
  177 + },
  178 + handlePreview(record) {
  179 + if (record && record.url) {
  180 + let url = window._CONFIG['onlinePreviewDomainURL'] + '?url=' + encodeURIComponent(record.url)
  181 + window.open(url, '_blank')
  182 + }
157 183 }
158 184 }
159 185 }
160 186 </script>
161 187  
162 188 <style scoped>
163   - @import '~@assets/less/common.less'
  189 + @import '~@assets/less/common.less';
164 190 </style>
... ...
ant-design-vue-jeecg/src/views/system/DepartList.vue
... ... @@ -17,8 +17,7 @@
17 17 <div style="background: #fff;padding-left:16px;height: 100%; margin-top: 5px">
18 18 <a-alert type="info" :showIcon="true">
19 19 <div slot="message">
20   - 当前选择:
21   - <a v-if="this.currSelected.title">{{ getCurrSelectedTitle() }}</a>
  20 + 当前选择:<span v-if="this.currSelected.title">{{ getCurrSelectedTitle() }}</span>
22 21 <a v-if="this.currSelected.title" style="margin-left: 10px" @click="onClearSelected">取消选择</a>
23 22 </div>
24 23 </a-alert>
... ... @@ -72,82 +71,90 @@
72 71 <!---- author:os_chengtgen -- date:20190827 -- for:切换父子勾选模式 =======------>
73 72 </a-col>
74 73 <a-col :md="12" :sm="24">
75   - <a-card :bordered="false">
76   - <a-form :form="form">
77   - <a-form-item
78   - :labelCol="labelCol"
79   - :wrapperCol="wrapperCol"
80   - label="机构名称">
81   - <a-input placeholder="请输入机构/部门名称" v-decorator="['departName', validatorRules.departName ]"/>
82   - </a-form-item>
83   - <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="上级部门">
84   - <a-tree-select
85   - style="width:100%"
86   - :dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
87   - :treeData="treeData"
88   - :disabled="disable"
89   - v-model="model.parentId"
90   - placeholder="无">
91   - </a-tree-select>
92   - </a-form-item>
93   - <a-form-item
94   - :labelCol="labelCol"
95   - :wrapperCol="wrapperCol"
96   - label="机构编码">
97   - <a-input disabled placeholder="请输入机构编码" v-decorator="['orgCode', validatorRules.orgCode ]"/>
98   - </a-form-item>
99   - <a-form-item
100   - :labelCol="labelCol"
101   - :wrapperCol="wrapperCol"
102   - label="机构类型">
103   - <template v-if="orgCategoryDisabled">
104   - <a-radio-group v-decorator="['orgCategory',validatorRules.orgCategory]" placeholder="请选择机构类型">
105   - <a-radio value="1">
106   - 公司
107   - </a-radio>
108   - </a-radio-group>
109   - </template>
110   - <template v-else>
111   - <a-radio-group v-decorator="['orgCategory',validatorRules.orgCategory]" placeholder="请选择机构类型">
112   - <a-radio value="2">
113   - 部门
114   - </a-radio>
115   - <a-radio value="3">
116   - 岗位
117   - </a-radio>
118   - </a-radio-group>
119   - </template>
120   - </a-form-item>
121   - <a-form-item
122   - :labelCol="labelCol"
123   - :wrapperCol="wrapperCol"
124   - label="排序">
125   - <a-input-number v-decorator="[ 'departOrder',{'initialValue':0}]"/>
126   - </a-form-item>
127   - <a-form-item
128   - :labelCol="labelCol"
129   - :wrapperCol="wrapperCol"
130   - label="手机号">
131   - <a-input placeholder="请输入手机号" v-decorator="['mobile', {'initialValue':''}]"/>
132   - </a-form-item>
133   - <a-form-item
134   - :labelCol="labelCol"
135   - :wrapperCol="wrapperCol"
136   - label="地址">
137   - <a-input placeholder="请输入地址" v-decorator="['address', {'initialValue':''}]"/>
138   - </a-form-item>
139   - <a-form-item
140   - :labelCol="labelCol"
141   - :wrapperCol="wrapperCol"
142   - label="备注">
143   - <a-textarea placeholder="请输入备注" v-decorator="['memo', {'initialValue':''}]"/>
144   - </a-form-item>
145   - </a-form>
146   - <div class="anty-form-btn">
147   - <a-button @click="emptyCurrForm" type="default" htmlType="button" icon="sync">重置</a-button>
148   - <a-button @click="submitCurrForm" type="primary" htmlType="button" icon="form">修改并保存</a-button>
149   - </div>
150   - </a-card>
  74 + <a-tabs defaultActiveKey="1">
  75 + <a-tab-pane tab="基本信息" key="1" >
  76 + <a-card :bordered="false">
  77 + <a-form :form="form">
  78 + <a-form-item
  79 + :labelCol="labelCol"
  80 + :wrapperCol="wrapperCol"
  81 + label="机构名称">
  82 + <a-input placeholder="请输入机构/部门名称" v-decorator="['departName', validatorRules.departName ]"/>
  83 + </a-form-item>
  84 + <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="上级部门">
  85 + <a-tree-select
  86 + style="width:100%"
  87 + :dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
  88 + :treeData="treeData"
  89 + :disabled="disable"
  90 + v-model="model.parentId"
  91 + placeholder="无">
  92 + </a-tree-select>
  93 + </a-form-item>
  94 + <a-form-item
  95 + :labelCol="labelCol"
  96 + :wrapperCol="wrapperCol"
  97 + label="机构编码">
  98 + <a-input disabled placeholder="请输入机构编码" v-decorator="['orgCode', validatorRules.orgCode ]"/>
  99 + </a-form-item>
  100 + <a-form-item
  101 + :labelCol="labelCol"
  102 + :wrapperCol="wrapperCol"
  103 + label="机构类型">
  104 + <template v-if="orgCategoryDisabled">
  105 + <a-radio-group v-decorator="['orgCategory',validatorRules.orgCategory]" placeholder="请选择机构类型">
  106 + <a-radio value="1">
  107 + 公司
  108 + </a-radio>
  109 + </a-radio-group>
  110 + </template>
  111 + <template v-else>
  112 + <a-radio-group v-decorator="['orgCategory',validatorRules.orgCategory]" placeholder="请选择机构类型">
  113 + <a-radio value="2">
  114 + 部门
  115 + </a-radio>
  116 + <a-radio value="3">
  117 + 岗位
  118 + </a-radio>
  119 + </a-radio-group>
  120 + </template>
  121 + </a-form-item>
  122 + <a-form-item
  123 + :labelCol="labelCol"
  124 + :wrapperCol="wrapperCol"
  125 + label="排序">
  126 + <a-input-number v-decorator="[ 'departOrder',{'initialValue':0}]"/>
  127 + </a-form-item>
  128 + <a-form-item
  129 + :labelCol="labelCol"
  130 + :wrapperCol="wrapperCol"
  131 + label="手机号">
  132 + <a-input placeholder="请输入手机号" v-decorator="['mobile', {'initialValue':''}]"/>
  133 + </a-form-item>
  134 + <a-form-item
  135 + :labelCol="labelCol"
  136 + :wrapperCol="wrapperCol"
  137 + label="地址">
  138 + <a-input placeholder="请输入地址" v-decorator="['address', {'initialValue':''}]"/>
  139 + </a-form-item>
  140 + <a-form-item
  141 + :labelCol="labelCol"
  142 + :wrapperCol="wrapperCol"
  143 + label="备注">
  144 + <a-textarea placeholder="请输入备注" v-decorator="['memo', {'initialValue':''}]"/>
  145 + </a-form-item>
  146 + </a-form>
  147 + <div class="anty-form-btn">
  148 + <a-button @click="emptyCurrForm" type="default" htmlType="button" icon="sync">重置</a-button>
  149 + <a-button @click="submitCurrForm" type="primary" htmlType="button" icon="form">修改并保存</a-button>
  150 + </div>
  151 + </a-card>
  152 + </a-tab-pane>
  153 + <a-tab-pane tab="部门权限" key="2" forceRender>
  154 + <depart-auth-modal ref="departAuth"/>
  155 + </a-tab-pane>
  156 + </a-tabs>
  157 +
151 158 </a-col>
152 159 <depart-modal ref="departModal" @ok="loadTree"></depart-modal>
153 160 </a-row>
... ... @@ -158,6 +165,7 @@
158 165 import {queryDepartTreeList, searchByKeywords, deleteByDepartId} from '@/api/api'
159 166 import {httpAction, deleteAction} from '@/api/manage'
160 167 import {JeecgListMixin} from '@/mixins/JeecgListMixin'
  168 + import DepartAuthModal from './modules/DepartAuthModal'
161 169 // 表头
162 170 const columns = [
163 171 {
... ... @@ -201,6 +209,7 @@
201 209 name: 'DepartList',
202 210 mixins: [JeecgListMixin],
203 211 components: {
  212 + DepartAuthModal,
204 213 DepartModal
205 214 },
206 215 data() {
... ... @@ -403,7 +412,7 @@
403 412 this.selectedKeys = [record.key]
404 413 this.model.parentId = record.parentId
405 414 this.setValuesToForm(record)
406   -
  415 + this.$refs.departAuth.show(record.id);
407 416  
408 417 },
409 418 // 触发onSelect事件时,为部门树右侧的form表单赋值
... ... @@ -425,6 +434,7 @@
425 434 this.currSelected = {}
426 435 this.form.resetFields()
427 436 this.selectedKeys = []
  437 + this.$refs.departAuth.departId = ''
428 438 },
429 439 handleNodeTypeChange(val) {
430 440 this.currSelected.nodeType = val
... ...
ant-design-vue-jeecg/src/views/system/DepartUserList.vue
... ... @@ -6,7 +6,7 @@
6 6 <a-input-search @search="onSearch" style="width:100%;margin-top: 10px" placeholder="请输入部门名称"/>
7 7 <!-- 树-->
8 8  
9   - <template>
  9 + <template v-if="userIdentity === '2' && departTree.length>0">
10 10  
11 11 <!--组织机构-->
12 12 <a-tree
... ... @@ -16,10 +16,14 @@
16 16 @select="onSelect"
17 17 :dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
18 18 :treeData="departTree"
  19 + :autoExpandParent="autoExpandParent"
19 20 />
20 21  
21 22 </template>
22   -
  23 + <div style="margin-top: 24px;" v-else-if="userIdentity === '2' && departTree.length==0">
  24 + <h3><span>您的部门下暂无有效部门信息</span></h3>
  25 + </div>
  26 + <div style="margin-top: 24px;" v-else><h3>普通员工暂此权限</h3></div>
23 27 </div>
24 28 </a-card>
25 29 </a-col>
... ... @@ -30,7 +34,10 @@
30 34 <Dept-Base-Info ref="DeptBaseInfo"></Dept-Base-Info>
31 35 </a-tab-pane>
32 36 <a-tab-pane tab="用户信息" key="2">
33   - <Dept-User-Info ref="DeptUserInfo"></Dept-User-Info>
  37 + <Dept-User-Info ref="DeptUserInfo" @clearSelectedDepartKeys="clearSelectedDepartKeys"></Dept-User-Info>
  38 + </a-tab-pane>
  39 + <a-tab-pane tab="部门角色" key="3" forceRender>
  40 + <dept-role-info ref="DeptRoleInfo" @clearSelectedDepartKeys="clearSelectedDepartKeys"/>
34 41 </a-tab-pane>
35 42 </a-tabs>
36 43 </a-card>
... ... @@ -40,13 +47,15 @@
40 47 <script>
41 48 import DeptBaseInfo from './modules/DeptBaseInfo'
42 49 import DeptUserInfo from './modules/DeptUserInfo'
43   - import {queryDepartTreeList, searchByKeywords} from '@/api/api'
  50 + import {queryMyDepartTreeList, searchByKeywords} from '@/api/api'
44 51 import {JeecgListMixin} from '@/mixins/JeecgListMixin'
  52 + import DeptRoleInfo from './modules/DeptRoleInfo'
45 53  
46 54 export default {
47 55 name: 'DepartUserList',
48 56 mixins: [JeecgListMixin],
49 57 components: {
  58 + DeptRoleInfo,
50 59 DeptBaseInfo,
51 60 DeptUserInfo,
52 61 },
... ... @@ -85,6 +94,7 @@
85 94 nodes: [],
86 95 edges: []
87 96 },
  97 + userIdentity:"",
88 98 }
89 99 },
90 100 methods: {
... ... @@ -94,12 +104,19 @@
94 104 loadData() {
95 105 this.refresh();
96 106 },
  107 + clearSelectedDepartKeys() {
  108 + this.checkedKeys = [];
  109 + this.selectedKeys = [];
  110 + this.currentDeptId = '';
  111 + this.$refs.DeptUserInfo.currentDeptId='';
  112 + this.$refs.DeptRoleInfo.currentDeptId='';
  113 + },
97 114 loadTree() {
98 115 var that = this
99 116 that.treeData = []
100 117 that.departTree = []
101   - queryDepartTreeList().then((res) => {
102   - if (res.success) {
  118 + queryMyDepartTreeList().then((res) => {
  119 + if (res.success && res.result ) {
103 120 for (let i = 0; i < res.result.length; i++) {
104 121 let temp = res.result[i]
105 122 that.treeData.push(temp)
... ... @@ -109,6 +126,7 @@
109 126 }
110 127 this.loading = false
111 128 }
  129 + that.userIdentity = res.message
112 130 })
113 131 },
114 132 setThisExpandedKeys(node) {
... ... @@ -161,6 +179,7 @@
161 179  
162 180 this.$refs.DeptBaseInfo.open(record);
163 181 this.$refs.DeptUserInfo.open(record);
  182 + this.$refs.DeptRoleInfo.open(record);
164 183 // }
165 184 // else {
166 185 // this.checkedKeys = [];
... ... @@ -180,6 +199,8 @@
180 199 this.$refs.DeptBaseInfo.open(record);
181 200 this.$refs.DeptUserInfo.onClearSelected();
182 201 this.$refs.DeptUserInfo.open(record);
  202 + this.$refs.DeptRoleInfo.onClearSelected();
  203 + this.$refs.DeptRoleInfo.open(record);
183 204 },
184 205 },
185 206 created() {
... ...
ant-design-vue-jeecg/src/views/system/DictList.vue
... ... @@ -30,6 +30,7 @@
30 30 <a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl" @change="handleImportExcel">
31 31 <a-button type="primary" icon="import">导入</a-button>
32 32 </a-upload>
  33 + <a-button type="primary" icon="sync" @click="refleshCache()">刷新缓存</a-button>
33 34  
34 35 <a-button type="primary" icon="hdd" @click="openDeleteList">回收站</a-button>
35 36 </div>
... ... @@ -70,6 +71,7 @@
70 71 import DictModal from './modules/DictModal'
71 72 import DictItemList from './DictItemList'
72 73 import DictDeleteList from './DictDeleteList'
  74 + import { getAction } from '@/api/manage'
73 75  
74 76 export default {
75 77 name: "DictList",
... ... @@ -132,6 +134,7 @@
132 134 delete: "/sys/dict/delete",
133 135 exportXlsUrl: "sys/dict/exportXls",
134 136 importExcelUrl: "sys/dict/importExcel",
  137 + refleshCache: "sys/dict/refleshCache",
135 138 },
136 139 }
137 140 },
... ... @@ -165,9 +168,18 @@
165 168 that.queryParam.dictCode = "";
166 169 that.loadData(this.ipagination.current);
167 170 },
168   -
169 171 openDeleteList(){
170 172 this.$refs.dictDeleteList.show()
  173 + },
  174 + refleshCache(){
  175 + getAction(this.url.refleshCache).then((res) => {
  176 + if (res.success) {
  177 + this.$message.success("刷新缓存完成!");
  178 + }
  179 + }).catch(e=>{
  180 + this.$message.warn("刷新缓存失败!");
  181 + console.log("刷新失败",e)
  182 + })
171 183 }
172 184 },
173 185 watch: {
... ...
ant-design-vue-jeecg/src/views/system/NewPermissionList.vue
... ... @@ -29,7 +29,9 @@
29 29 :dataSource="dataSource"
30 30 :loading="loading"
31 31 @expand="expandSubmenu"
32   - :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}">
  32 + :expandedRowKeys="expandedRowKeys"
  33 + :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
  34 + @expandedRowsChange="handleExpandedRowsChange">
33 35  
34 36 <span slot="action" slot-scope="text, record">
35 37 <a @click="handleEdit(record)">编辑</a>
... ... @@ -79,7 +81,7 @@
79 81  
80 82 <script>
81 83 import PermissionModal from './modules/PermissionModal'
82   - import { getSystemMenuList,getSystemSubmenu } from '@/api/api'
  84 + import { getSystemMenuList, getSystemSubmenu, getSystemSubmenuBatch } from '@/api/api'
83 85 import { JeecgListMixin } from '@/mixins/JeecgListMixin'
84 86 import PermissionDataRuleList from './PermissionDataRuleList'
85 87 import JEllipsis from '@/components/jeecg/JEllipsis'
... ... @@ -140,7 +142,7 @@
140 142 ]
141 143  
142 144 export default {
143   - name: 'PermissionList',
  145 + name: 'PermissionListAsync',
144 146 mixins: [JeecgListMixin],
145 147 components: {
146 148 PermissionDataRuleList,
... ... @@ -153,6 +155,8 @@
153 155 // 表头
154 156 columns: columns,
155 157 loading: false,
  158 + // 展开的行,受控属性
  159 + expandedRowKeys: [],
156 160 url: {
157 161 list: '/sys/permission/list',
158 162 delete: '/sys/permission/delete',
... ... @@ -162,23 +166,45 @@
162 166 },
163 167 methods: {
164 168 loadData() {
165   - this.dataSource = []
  169 + this.loading = true
166 170 getSystemMenuList().then((res) => {
167 171 if (res.success) {
168   - console.log(res.result)
169 172 this.dataSource = res.result
  173 + return this.loadDataByExpandedRows(this.dataSource)
170 174 }
  175 + }).finally(()=>{
  176 + this.loading = false
171 177 })
172 178 },
173 179 expandSubmenu(expanded, record){
174   - if(expanded){
  180 + if (expanded && (!record.children || record.children.length === 0)) {
175 181 getSystemSubmenu({parentId:record.id}).then((res) => {
176 182 if (res.success) {
177 183 record.children = res.result
178 184 }
179 185 })
180 186 }
181   -
  187 + },
  188 + // 根据已展开的行查询数据(用于保存后刷新时异步加载子级的数据)
  189 + loadDataByExpandedRows(dataList) {
  190 + if (this.expandedRowKeys.length > 0) {
  191 + return getSystemSubmenuBatch({ parentIds: this.expandedRowKeys.join(',') }).then((res) => {
  192 + if (res.success) {
  193 + let childrenMap = res.result
  194 + let fn = (list) => {
  195 + list.forEach(data => {
  196 + if (this.expandedRowKeys.includes(data.id)) {
  197 + data.children = childrenMap[data.id]
  198 + fn(data.children)
  199 + }
  200 + })
  201 + }
  202 + fn(dataList)
  203 + }
  204 + })
  205 + } else {
  206 + return Promise.resolve()
  207 + }
182 208 },
183 209 // 打开数据规则编辑
184 210 handleDataRule(record) {
... ... @@ -189,10 +215,13 @@
189 215 this.$refs.modalForm.localMenuType = 1;
190 216 this.$refs.modalForm.disableSubmit = false;
191 217 this.$refs.modalForm.edit({status:'1',permsType:'1',route:true,'parentId':record.id});
192   - }
  218 + },
  219 + handleExpandedRowsChange(expandedRows) {
  220 + this.expandedRowKeys = expandedRows
  221 + },
193 222 }
194 223 }
195 224 </script>
196 225 <style scoped>
197   - @import '~@assets/less/common.less'
  226 + @import '~@assets/less/common.less';
198 227 </style>
199 228 \ No newline at end of file
... ...
ant-design-vue-jeecg/src/views/system/PermissionList.vue
... ... @@ -28,7 +28,9 @@
28 28 :pagination="false"
29 29 :dataSource="dataSource"
30 30 :loading="loading"
31   - :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}">
  31 + :expandedRowKeys="expandedRowKeys"
  32 + :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
  33 + @expandedRowsChange="handleExpandedRowsChange">
32 34  
33 35 <span slot="action" slot-scope="text, record">
34 36 <a @click="handleEdit(record)">编辑</a>
... ... @@ -152,6 +154,8 @@
152 154 // 表头
153 155 columns: columns,
154 156 loading: false,
  157 + // 展开的行,受控属性
  158 + expandedRowKeys: [],
155 159 url: {
156 160 list: '/sys/permission/list',
157 161 delete: '/sys/permission/delete',
... ... @@ -178,10 +182,13 @@
178 182 this.$refs.modalForm.localMenuType = 1;
179 183 this.$refs.modalForm.disableSubmit = false;
180 184 this.$refs.modalForm.edit({status:'1',permsType:'1',route:true,'parentId':record.id});
181   - }
  185 + },
  186 + handleExpandedRowsChange(expandedRows) {
  187 + this.expandedRowKeys = expandedRows
  188 + },
182 189 }
183 190 }
184 191 </script>
185 192 <style scoped>
186   - @import '~@assets/less/common.less'
  193 + @import '~@assets/less/common.less';
187 194 </style>
188 195 \ No newline at end of file
... ...
ant-design-vue-jeecg/src/views/system/RoleList.vue
... ... @@ -67,8 +67,8 @@
67 67  
68 68 <span slot="action" slot-scope="text, record">
69 69 <a @click="handleEdit(record)">编辑</a>
70   -
71 70 <a-divider type="vertical" />
  71 +
72 72 <a-dropdown>
73 73 <a class="ant-dropdown-link">
74 74 更多 <a-icon type="down" />
... ...
ant-design-vue-jeecg/src/views/system/RoleUserList.vue
... ... @@ -60,26 +60,27 @@
60 60 :rowSelection="{selectedRowKeys: selectedRowKeys1, onChange: onSelectChange1, type:'radio'}"
61 61 @change="handleTableChange">
62 62 <span slot="action" slot-scope="text, record">
63   - <a @click="handleOpen(record)">用户</a>
64   - <a-divider type="vertical"/>
65   - <a-dropdown>
66   - <a class="ant-dropdown-link">
67   - 更多 <a-icon type="down"/>
68   - </a>
69   - <a-menu slot="overlay">
70   - <a-menu-item>
71   - <a @click="handlePerssion(record.id)">授权</a>
72   - </a-menu-item>
73   - <a-menu-item>
74   - <a @click="handleEdit(record)">编辑</a>
75   - </a-menu-item>
76   - <a-menu-item>
77   - <a-popconfirm title="确定删除吗?" @confirm="() => handleDelete1(record.id)">
78   - <a>删除</a>
79   - </a-popconfirm>
80   - </a-menu-item>
81   - </a-menu>
82   - </a-dropdown>
  63 + <a @click="handleOpen(record)">用户</a>
  64 + <a-divider type="vertical"/>
  65 +
  66 + <a-dropdown>
  67 + <a class="ant-dropdown-link">
  68 + 更多 <a-icon type="down"/>
  69 + </a>
  70 + <a-menu slot="overlay">
  71 + <a-menu-item>
  72 + <a @click="handlePerssion(record.id)">授权</a>
  73 + </a-menu-item>
  74 + <a-menu-item>
  75 + <a @click="handleEdit(record)">编辑</a>
  76 + </a-menu-item>
  77 + <a-menu-item>
  78 + <a-popconfirm title="确定删除吗?" @confirm="() => handleDelete1(record.id)">
  79 + <a>删除</a>
  80 + </a-popconfirm>
  81 + </a-menu-item>
  82 + </a-menu>
  83 + </a-dropdown>
83 84 </span>
84 85 </a-table>
85 86 </div>
... ... @@ -530,7 +531,7 @@
530 531 },
531 532 handlePerssion(roleId){
532 533 this.$refs.modalUserRole.show(roleId);
533   - }
  534 + },
534 535 }
535 536 }
536 537 </script>
... ...