mooho-base-admin-plus 2.9.5 → 2.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mooho-base-admin-plus",
3
3
  "description": "MOOHO basic framework for admin by Vue3",
4
- "version": "2.9.5",
4
+ "version": "2.10.1",
5
5
  "author": "jinyifan <jinyifan@mooho.com.cn>",
6
6
  "license": "MIT",
7
7
  "private": false,
@@ -47,5 +47,14 @@ export default {
47
47
  id
48
48
  }
49
49
  });
50
+ },
51
+ recall(id) {
52
+ return request({
53
+ url: `api/${res}/recall`,
54
+ method: 'post',
55
+ data: {
56
+ id
57
+ }
58
+ });
50
59
  }
51
60
  };
@@ -1,4 +1,6 @@
1
1
  import request from '../libs/request';
2
+ import dateFormat from 'date-fns/format';
3
+ import saveAs from 'file-saver';
2
4
 
3
5
  const res = 'I18nText';
4
6
 
@@ -29,5 +31,20 @@ export default {
29
31
  url: `api/${res}/getAll`,
30
32
  method: 'get'
31
33
  });
34
+ },
35
+ async exportFile(ids) {
36
+ const result = await request({
37
+ url: `api/${res}/exportFile`,
38
+ method: 'post',
39
+ responseType: 'blob',
40
+ data: {
41
+ ids
42
+ }
43
+ });
44
+
45
+ const blob = new Blob([result], {
46
+ type: 'application/octet-stream'
47
+ });
48
+ saveAs(blob, 'I18nText-' + dateFormat(new Date(), 'yyyyMMddHHmmss') + '.i18n');
32
49
  }
33
50
  };
package/src/api/task.js CHANGED
@@ -35,6 +35,16 @@ export default {
35
35
  }
36
36
  });
37
37
  },
38
+ recall(id, comment) {
39
+ return request({
40
+ url: `api/${res}/recall`,
41
+ method: 'post',
42
+ data: {
43
+ id,
44
+ comment
45
+ }
46
+ });
47
+ },
38
48
  redirect(id, targetID, comment) {
39
49
  return request({
40
50
  url: `api/${res}/redirect`,
@@ -139,8 +139,6 @@
139
139
  filter[this.sourceDataCode] = JSON.parse(this.modelValue).join(',');
140
140
 
141
141
  this.loadDataView(this.source).then(async view => {
142
- console.log('xxx');
143
-
144
142
  if (view.dataView.isCustom) {
145
143
  let res = await customModelApi.query(view.dataView.model, filter, [this.sourceDataCode, this.sourceDisplayCode]);
146
144
  if (res.data.length > 0) {
@@ -0,0 +1,197 @@
1
+ <template>
2
+ <Input type="text" :readonly="true" :model-value="modelValue" @click="open()">
3
+ <template #prepend>
4
+ <Button custom-icon="fa fa-edit" @click="open()"></Button>
5
+ </template>
6
+ </Input>
7
+ <modal-table ref="table" view-code="TableDefaultFiltering" :static="true" :footer-enable="true" @edit="({ row, index }) => edit(row, index)">
8
+ <template #footer>
9
+ <Button type="primary" ghost custom-icon="fa fa-plus" @click="edit()">
10
+ {{ $t('Front_Btn_Add') }}
11
+ </Button>
12
+ <Button type="primary" custom-icon="fa fa-check" @click="saveFiltering">{{ $t('Front_Btn_OK') }}</Button>
13
+ </template>
14
+ </modal-table>
15
+ <modal-form ref="form" view-code="TableDefaultFilteringEdit">
16
+ <template #column="{ data, code }">
17
+ <template v-if="code == 'column'">
18
+ <Input type="text" readonly v-model="data[code]" @on-focus="openSelect()">
19
+ <template #prepend>
20
+ <Button custom-icon="fa fa-search" @click="openSelect()"></Button>
21
+ </template>
22
+ </Input>
23
+ </template>
24
+ </template>
25
+ <template #footer>
26
+ <Button type="primary" custom-icon="fa fa-check" @click="save()">{{ $t('Front_Btn_OK') }}</Button>
27
+ </template>
28
+ </modal-form>
29
+ <column-check ref="columnCheck" />
30
+ </template>
31
+
32
+ <script>
33
+ import mixinPage from '../../mixins/page';
34
+ import columnCheck from './column-check.vue';
35
+
36
+ export default {
37
+ mixins: [mixinPage],
38
+ components: { columnCheck },
39
+ props: {
40
+ /**
41
+ * @model
42
+ */
43
+ modelValue: {
44
+ type: String
45
+ },
46
+ /**
47
+ * 是否数据源
48
+ */
49
+ isDataSource: {
50
+ type: Boolean
51
+ },
52
+ /**
53
+ * 模型
54
+ */
55
+ model: {
56
+ type: String
57
+ },
58
+ /**
59
+ * 数据源
60
+ */
61
+ dataSource: {
62
+ type: String
63
+ }
64
+ },
65
+ data() {
66
+ return {
67
+ index: null
68
+ };
69
+ },
70
+ computed: {},
71
+ async created() {},
72
+ methods: {
73
+ open() {
74
+ if (this.isDataSource && !(this.dataSource || '').trim()) {
75
+ this.error('Front_Msg_Please_Select_Model_First');
76
+ return;
77
+ } else if (!this.isDataSource && !(this.model || '').trim()) {
78
+ this.error('Front_Msg_Please_Select_Model_First');
79
+ return;
80
+ }
81
+
82
+ let array = [];
83
+
84
+ if (!!(this.modelValue || '').trim()) {
85
+ let json = JSON.parse(this.modelValue);
86
+ for (let key in json) {
87
+ let operatorType = null;
88
+ let suffix = null;
89
+
90
+ let column = key.split('_')[0];
91
+
92
+ if (key.split('_').length > 1) {
93
+ suffix = key.split('_')[1];
94
+ }
95
+
96
+ if (suffix == 'g') {
97
+ operatorType = '大于';
98
+ } else if (suffix == 'l') {
99
+ operatorType = '小于';
100
+ } else if (suffix == 'ge') {
101
+ operatorType = '大于等于';
102
+ } else if (suffix == 'le') {
103
+ operatorType = '小于等于';
104
+ } else if (suffix == 'n') {
105
+ operatorType = '不等于';
106
+ } else if (suffix == 'c') {
107
+ operatorType = '文字包含';
108
+ } else {
109
+ operatorType = '等于';
110
+ }
111
+
112
+ array.push({
113
+ column,
114
+ operatorType,
115
+ keyValue: json[key]
116
+ });
117
+ }
118
+ }
119
+
120
+ this.$refs.table.loadData(array);
121
+ this.$refs.table.open();
122
+ },
123
+ edit(row, index) {
124
+ this.index = index;
125
+ this.$refs.form.open(row);
126
+ },
127
+ async save() {
128
+ let isOK = await this.$refs.form.validate();
129
+
130
+ if (!isOK) {
131
+ this.error('Front_Msg_Form_Validate_Fail');
132
+ return;
133
+ }
134
+
135
+ let array = this.$refs.table.data;
136
+ let data = this.$refs.form.data;
137
+
138
+ if (this.index == null) {
139
+ // 新增
140
+ array.push(data);
141
+ } else {
142
+ // 编辑
143
+ array[this.index].column = data.column;
144
+ array[this.index].operatorType = data.operatorType;
145
+ array[this.index].keyValue = data.keyValue;
146
+ }
147
+
148
+ this.$refs.table.loadData(array);
149
+ this.$refs.form.close();
150
+ },
151
+ saveFiltering() {
152
+ let obj = {};
153
+ let json = null;
154
+
155
+ if (this.$refs.table.data.length > 0) {
156
+ this.$refs.table.data.forEach(item => {
157
+ let key = item.column;
158
+
159
+ if ((item.operatorType = '大于')) {
160
+ key += '_g';
161
+ } else if ((item.operatorType = '小于')) {
162
+ key += '_l';
163
+ } else if ((item.operatorType = '大于等于')) {
164
+ key += '_ge';
165
+ } else if ((item.operatorType = '小于等于')) {
166
+ key += '_le';
167
+ } else if ((item.operatorType = '不等于')) {
168
+ key += '_n';
169
+ } else if ((item.operatorType = '文字包含')) {
170
+ key += '_c';
171
+ }
172
+
173
+ obj[key] = item.keyValue;
174
+ });
175
+
176
+ json = JSON.stringify(obj);
177
+ }
178
+
179
+ this.$emit('update:modelValue', json);
180
+
181
+ this.$refs.table.close();
182
+ },
183
+ // 打开字段选择界面
184
+ openSelect() {
185
+ if (this.isDataSource) {
186
+ this.$refs.columnCheck.openDataSourceFilter(this.dataSource, checked => {
187
+ this.$refs.form.data.column = checked.code;
188
+ });
189
+ } else {
190
+ this.$refs.columnCheck.open(this.model, checked => {
191
+ this.$refs.form.data.column = checked.code;
192
+ });
193
+ }
194
+ }
195
+ }
196
+ };
197
+ </script>
@@ -0,0 +1,166 @@
1
+ <template>
2
+ <Input type="text" :readonly="true" :model-value="modelValue" @click="open()">
3
+ <template #prepend>
4
+ <Button custom-icon="fa fa-edit" @click="open()"></Button>
5
+ </template>
6
+ </Input>
7
+ <modal-table ref="table" view-code="TableDefaultSorting" :static="true" :footer-enable="true" @edit="({ row, index }) => edit(row, index)">
8
+ <template #footer>
9
+ <Button type="primary" ghost custom-icon="fa fa-plus" @click="edit()">
10
+ {{ $t('Front_Btn_Add') }}
11
+ </Button>
12
+ <Button type="primary" custom-icon="fa fa-check" @click="saveSorting">{{ $t('Front_Btn_OK') }}</Button>
13
+ </template>
14
+ </modal-table>
15
+ <modal-form ref="form" view-code="TableDefaultSortingEdit">
16
+ <template #column="{ data, code }">
17
+ <template v-if="code == 'column'">
18
+ <Input type="text" readonly v-model="data[code]" @on-focus="openSelect()">
19
+ <template #prepend>
20
+ <Button custom-icon="fa fa-search" @click="openSelect()"></Button>
21
+ </template>
22
+ </Input>
23
+ </template>
24
+ </template>
25
+ <template #footer>
26
+ <Button type="primary" custom-icon="fa fa-check" @click="save()">{{ $t('Front_Btn_OK') }}</Button>
27
+ </template>
28
+ </modal-form>
29
+ <column-check ref="columnCheck" />
30
+ </template>
31
+
32
+ <script>
33
+ import mixinPage from '../../mixins/page';
34
+ import columnCheck from './column-check.vue';
35
+
36
+ export default {
37
+ mixins: [mixinPage],
38
+ components: { columnCheck },
39
+ props: {
40
+ /**
41
+ * @model
42
+ */
43
+ modelValue: {
44
+ type: String
45
+ },
46
+ /**
47
+ * 是否数据源
48
+ */
49
+ isDataSource: {
50
+ type: Boolean
51
+ },
52
+ /**
53
+ * 模型
54
+ */
55
+ model: {
56
+ type: String
57
+ },
58
+ /**
59
+ * 数据源
60
+ */
61
+ dataSource: {
62
+ type: String
63
+ }
64
+ },
65
+ data() {
66
+ return {
67
+ index: null
68
+ };
69
+ },
70
+ computed: {},
71
+ async created() {},
72
+ methods: {
73
+ open() {
74
+ if (this.isDataSource && !(this.dataSource || '').trim()) {
75
+ this.error('Front_Msg_Please_Select_Model_First');
76
+ return;
77
+ } else if (!this.isDataSource && !(this.model || '').trim()) {
78
+ this.error('Front_Msg_Please_Select_Model_First');
79
+ return;
80
+ }
81
+
82
+ let array = [];
83
+
84
+ if (!!(this.modelValue || '').trim()) {
85
+ let json = JSON.parse(this.modelValue);
86
+ for (let key in json) {
87
+ array.push({
88
+ column: key,
89
+ rule: json[key]
90
+ });
91
+ }
92
+ }
93
+
94
+ this.$refs.table.loadData(array);
95
+ this.$refs.table.open();
96
+ },
97
+ edit(row, index) {
98
+ this.index = index;
99
+ this.$refs.form.open(row);
100
+ },
101
+ async save() {
102
+ let isOK = await this.$refs.form.validate();
103
+
104
+ if (!isOK) {
105
+ this.error('Front_Msg_Form_Validate_Fail');
106
+ return;
107
+ }
108
+
109
+ let array = this.$refs.table.data;
110
+ let data = this.$refs.form.data;
111
+
112
+ if (this.index == null) {
113
+ // 新增
114
+ if (array.some(item => item.column == data.column)) {
115
+ this.error('排序字段已存在!');
116
+ return;
117
+ }
118
+
119
+ array.push(data);
120
+ } else {
121
+ // 编辑
122
+ for (let i = 0; i < data.length; i++) {
123
+ if (i != this.index && data[i].column == data.column) {
124
+ this.error('排序字段不能重复!');
125
+ return;
126
+ }
127
+ }
128
+
129
+ array[this.index].column = data.column;
130
+ array[this.index].rule = data.rule;
131
+ }
132
+
133
+ this.$refs.table.loadData(array);
134
+ this.$refs.form.close();
135
+ },
136
+ saveSorting() {
137
+ let obj = {};
138
+ let json = null;
139
+
140
+ if (this.$refs.table.data.length > 0) {
141
+ this.$refs.table.data.forEach(item => {
142
+ obj[item.column] = item.rule;
143
+ });
144
+
145
+ json = JSON.stringify(obj);
146
+ }
147
+
148
+ this.$emit('update:modelValue', json);
149
+
150
+ this.$refs.table.close();
151
+ },
152
+ // 打开字段选择界面
153
+ openSelect() {
154
+ if (this.isDataSource) {
155
+ this.$refs.columnCheck.openDataSourceFilter(this.dataSource, checked => {
156
+ this.$refs.form.data.column = checked.code;
157
+ });
158
+ } else {
159
+ this.$refs.columnCheck.open(this.model, checked => {
160
+ this.$refs.form.data.column = checked.code;
161
+ });
162
+ }
163
+ }
164
+ }
165
+ };
166
+ </script>
@@ -23,18 +23,10 @@
23
23
  <Input type="text" :readonly="true" v-model="data.keywordColumn" @click="keywordColumnOpen()"></Input>
24
24
  </div>
25
25
  <div v-if="code == 'sorting'">
26
- <Input type="text" :readonly="true" v-model="sort" @click="$refs.modal_sort.open()">
27
- <template #prepend>
28
- <Button custom-icon="fa fa-edit" @click="$refs.modal_sort.open()"></Button>
29
- </template>
30
- </Input>
26
+ <modal-setting-sorting ref="settingSorting" v-model="data.sorting" :is-data-source="data.isDataSource" :model="data.model" :data-source="data.dataSource" />
31
27
  </div>
32
28
  <div v-if="code == 'filtering'">
33
- <Input type="text" :readonly="true" v-model="filter" @click="$refs.modal_filter.open()">
34
- <template #prepend>
35
- <Button custom-icon="fa fa-edit" @click="$refs.modal_filter.open()"></Button>
36
- </template>
37
- </Input>
29
+ <modal-setting-filtering ref="settingFiltering" v-model="data.filtering" :is-data-source="data.isDataSource" :model="data.model" :data-source="data.dataSource" />
38
30
  </div>
39
31
  <div v-if="code == 'batchSelectDataCode'">
40
32
  <Input type="text" :readonly="true" v-model="$refs.setting.data.batchSelectDataCode" @click="openColumnCheck($refs.setting.data, 'batchSelectDataCode')">
@@ -183,8 +175,7 @@
183
175
  <Button type="primary" custom-icon="fa fa-save" @click="saveParams">{{ $t('Front_Btn_Save') }}</Button>
184
176
  </template>
185
177
  </modal-table>
186
- <modal-form-sort ref="modal_sort" :sort="sort" :dataView="dataView" @bindSort="bindSort" />
187
- <modal-form-filter ref="modal_filter" :filter="filter" :dataView="dataView" @bindFilter="bindFilter" />
178
+
188
179
  <column-check ref="columnCheck" />
189
180
  <group-method ref="groupMethod" />
190
181
  <group-column ref="groupColumn" />
@@ -197,24 +188,20 @@
197
188
  import columnSelect from './column-select.vue';
198
189
  import columnEdit from './column-edit.vue';
199
190
  import { mapActions } from 'vuex';
200
- import modalFormSort from './modal-form-sort.vue';
201
- import modalFormFilter from './modal-form-filter.vue';
191
+ import modalSettingSorting from './modal-setting-sorting.vue';
192
+ import modalSettingFiltering from './modal-setting-filtering.vue';
202
193
  import groupMethod from './group-method.vue';
203
194
  import groupColumn from './group-column.vue';
204
195
  import conditionEdit from './condition-edit.vue';
205
196
 
206
197
  export default {
207
198
  mixins: [mixinPage],
208
- components: { columnCheck, columnSelect, columnEdit, modalFormSort, modalFormFilter, groupMethod, groupColumn, conditionEdit },
199
+ components: { columnCheck, columnSelect, columnEdit, conditionEdit, groupMethod, groupColumn, modalSettingFiltering, modalSettingSorting },
209
200
  data() {
210
201
  return {
211
202
  active: false,
212
203
  dataView: {},
213
204
  data: {},
214
- tablename: '',
215
- sort: '',
216
- filter: '',
217
- tableid: '',
218
205
  commandButton: {}
219
206
  };
220
207
  },
@@ -418,10 +405,6 @@
418
405
  },
419
406
  // 设置
420
407
  setting() {
421
- // 排序字段参数赋值
422
- this.sort = this.dataView.sorting;
423
- this.filter = this.dataView.filtering;
424
-
425
408
  if (!!(this.dataView.commandButton || '').trim()) {
426
409
  this.$refs.commandButton.loadData(JSON.parse(this.dataView.commandButton));
427
410
  } else {
@@ -455,16 +438,6 @@
455
438
  });
456
439
  }
457
440
  },
458
- //绑定排序组件返回的排序json
459
- bindSort(value) {
460
- this.sort = value;
461
- this.$refs.setting.data.sorting = value;
462
- },
463
- //绑定筛选组件返回的排序json
464
- bindFilter(value) {
465
- this.filter = value;
466
- this.$refs.setting.data.filtering = value;
467
- },
468
441
  // 打开操作栏按钮参数设置界面
469
442
  openParams(row) {
470
443
  this.commandButton = row;
@@ -1466,10 +1466,12 @@
1466
1466
  */
1467
1467
  async edit(row, index) {
1468
1468
  let data = row;
1469
- if (this.tableView.isCustom) {
1470
- data = await customModelApi.get(this.tableView.model, row.id);
1471
- } else if (!this.tableView.isDataSource) {
1472
- data = await modelApi.get(this.tableView.model, row.id);
1469
+ if (!this.static && !this.embedded) {
1470
+ if (this.tableView.isCustom) {
1471
+ data = await customModelApi.get(this.tableView.model, row.id);
1472
+ } else if (!this.tableView.isDataSource) {
1473
+ data = await modelApi.get(this.tableView.model, row.id);
1474
+ }
1473
1475
  }
1474
1476
 
1475
1477
  /**
@@ -1487,10 +1489,12 @@
1487
1489
  */
1488
1490
  async show(row, index) {
1489
1491
  let data = row;
1490
- if (this.tableView.isCustom) {
1491
- data = await customModelApi.get(this.tableView.model, row.id);
1492
- } else if (!this.tableView.isDataSource) {
1493
- data = await modelApi.get(this.tableView.model, row.id);
1492
+ if (!this.static && !this.embedded) {
1493
+ if (this.tableView.isCustom) {
1494
+ data = await customModelApi.get(this.tableView.model, row.id);
1495
+ } else if (!this.tableView.isDataSource) {
1496
+ data = await modelApi.get(this.tableView.model, row.id);
1497
+ }
1494
1498
  }
1495
1499
 
1496
1500
  /**
@@ -1508,7 +1512,7 @@
1508
1512
  */
1509
1513
  remove(row, index) {
1510
1514
  this.confirm('Front_Msg_Sure_To_Delete_Item', async () => {
1511
- if (this.embedded) {
1515
+ if (this.static || this.embedded) {
1512
1516
  this.staticData.splice((this.current - 1) * this.size + index, 1);
1513
1517
  this.loadData();
1514
1518
 
@@ -24,6 +24,7 @@
24
24
  <Button type="warning" v-if="isRejectable" custom-icon="fa fa-reply" @click="reject()">{{ $t('Front_Btn_Reject') }}</Button>
25
25
  <Button type="primary" v-if="isBackable" custom-icon="fa fa-share" @click="back()">{{ $t('Front_Btn_Back_Previous') }}</Button>
26
26
  <Button type="primary" v-if="isRedirectable" custom-icon="fa fa-share" @click="$refs.redirectTaskForm.open()">{{ $t('Front_Btn_Redirect') }}</Button>
27
+ <Button type="primary" v-if="isRecallable" custom-icon="fa fa-reply-all" @click="recall()">{{ $t('Front_Btn_Recall') }}</Button>
27
28
  <Button type="primary" v-if="isMessageEnable" custom-icon="fa fa-comment-dots" @click="openMessage()">{{ $t('Front_Btn_Leave_Message') }}</Button>
28
29
  <Button type="info" custom-icon="fa fa-history" @click="$refs.approvalHistoryTable.open({ processInstID: task.processInstID })">{{ $t('Front_Btn_History') }}</Button>
29
30
  <Button type="default" custom-icon="fa fa-times" @click="opened = false">{{ $t('Front_Btn_Close') }}</Button>
@@ -92,6 +93,7 @@
92
93
  isBackable: false,
93
94
  isRedirectable: false,
94
95
  isMessageEnable: false,
96
+ isRecallable: false,
95
97
  messageData: {
96
98
  message: null
97
99
  },
@@ -117,6 +119,7 @@
117
119
  this.isBackable = this.task.activityInst.activity.isBackable && this.task.status == 'Pending';
118
120
  this.isRedirectable = this.task.activityInst.activity.isRedirectable && this.task.status == 'Pending';
119
121
  this.isMessageEnable = this.task.processInst.isMessageEnable && this.task.status == 'Pending';
122
+ this.isRecallable = this.task.activityInst.activity.isRecallable && this.task.status == 'Finished' && !this.task.isCallbacked;
120
123
  this.isCustom = false;
121
124
 
122
125
  setTimeout(async () => {
@@ -309,6 +312,29 @@
309
312
  }
310
313
  );
311
314
  },
315
+ // 撤回
316
+ async recall() {
317
+ this.confirmInput(
318
+ 'Front_Msg_Sure_To_Recall_Task',
319
+ this.$t('Front_Label_Comment'),
320
+ async () => {
321
+ await taskApi.recall(this.task.id, this.comment);
322
+ this.success('Front_Msg_Success');
323
+ this.resetNotice();
324
+ this.$emit('on-after-action');
325
+ this.opened = false;
326
+ },
327
+ comment => {
328
+ if (!(comment || '').trim()) {
329
+ this.warning('Front_Msg_Please_Input_Recall_Reason');
330
+ return false;
331
+ } else {
332
+ this.comment = comment;
333
+ return true;
334
+ }
335
+ }
336
+ );
337
+ },
312
338
  // 退回上一步
313
339
  async back() {
314
340
  this.confirmInput(
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div>
3
3
  <Row :gutter="12" class="ivu-mt">
4
- <Col :xxl="8" :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
4
+ <!-- <Col :xxl="8" :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
5
5
  <div>
6
6
  <Row :gutter="12">
7
7
  <Col v-bind="grid" class="ivu-mb">
@@ -86,7 +86,7 @@
86
86
  </Col>
87
87
  </Row>
88
88
  </div>
89
- </Col>
89
+ </Col> -->
90
90
  <Col :xl="10" :lg="8" :md="8" :sm="12" :xs="24">
91
91
  <notice-list />
92
92
  </Col>