mooho-base-admin-plus 0.1.60 → 0.1.63

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": "0.1.60",
4
+ "version": "0.1.63",
5
5
  "author": "jinyifan <jinyifan@mooho.com.cn>",
6
6
  "dotnetVersion": "1.3.97",
7
7
  "license": "MIT",
@@ -20,6 +20,7 @@
20
20
  "@fortawesome/fontawesome-free": "^5.13.0",
21
21
  "axios": "^0.26.0",
22
22
  "date-fns": "^2.9.0",
23
+ "ckeditor4-integrations-common": "^1.0.0",
23
24
  "echarts": "^5.3.2",
24
25
  "file-saver": "^2.0.2",
25
26
  "js-cookie": "^3.0.1",
@@ -1,22 +1,20 @@
1
1
  <template>
2
- <ckeditor
3
- :model-value="value"
4
- @update:model-value="$event => $emit('input', $event)"
5
- :readOnly="readonly"
6
- :style="{ width: width == null ? null : width + 'px' }"
7
- :editorUrl="editorUrl"
8
- :config="config"
9
- ></ckeditor>
2
+ <div id="editor"></div>
10
3
  </template>
4
+
11
5
  <script>
6
+ /* global CKEDITOR */
7
+
12
8
  import mixinPage from '../../mixins/page';
13
9
  import util from '../../libs/util';
10
+ import { debounce, getEditorNamespace } from 'ckeditor4-integrations-common';
14
11
 
15
12
  export default {
16
13
  mixins: [mixinPage],
17
14
  props: {
18
- value: {
19
- type: String
15
+ modelValue: {
16
+ type: String,
17
+ default: ''
20
18
  },
21
19
  // 只读
22
20
  readonly: {
@@ -31,9 +29,15 @@
31
29
  },
32
30
  editorUrl: {
33
31
  type: String,
34
- default: 'https://cdn.ckeditor.com/4.13.0/full/ckeditor.js'
32
+ default: 'https://cdn.ckeditor.com/4.19.0/standard-all/ckeditor.js'
35
33
  }
36
34
  },
35
+ data() {
36
+ return {
37
+ instance: null, // 当前实例
38
+ $_destroyed: false // 是否销毁
39
+ };
40
+ },
37
41
  computed: {
38
42
  config() {
39
43
  const token = util.cookies.get('token');
@@ -81,6 +85,107 @@
81
85
  };
82
86
  }
83
87
  },
84
- methods: {}
88
+ mounted() {
89
+ getEditorNamespace(this.editorUrl, namespace => {
90
+ this.$emit('namespaceloaded', namespace);
91
+ }).then(() => {
92
+ if (this.$_destroyed) {
93
+ return;
94
+ }
95
+
96
+ const config = this.prepareConfig();
97
+
98
+ CKEDITOR.replace('editor', config);
99
+ });
100
+ },
101
+ beforeUnmount() {
102
+ if (this.instance) {
103
+ this.instance.destroy();
104
+ }
105
+
106
+ this.$_destroyed = true;
107
+ },
108
+ watch: {
109
+ modelValue(val) {
110
+ if (this.instance && this.instance.getData() !== val) {
111
+ this.instance.setData(val);
112
+ }
113
+ },
114
+
115
+ readonly(val) {
116
+ if (this.instance) {
117
+ this.instance.setReadOnly(val);
118
+ }
119
+ }
120
+ },
121
+ methods: {
122
+ prepareConfig() {
123
+ const config = this.config || {};
124
+ config.on = config.on || {};
125
+
126
+ if (config.delayIfDetached === undefined) {
127
+ config.delayIfDetached = true;
128
+ }
129
+ if (this.readonly !== null) {
130
+ config.readOnly = this.readonly;
131
+ }
132
+
133
+ const userInstanceReadyCallback = config.on.instanceReady;
134
+
135
+ config.on.instanceReady = evt => {
136
+ this.instance = evt.editor;
137
+
138
+ this.$nextTick().then(() => {
139
+ this.prepareComponentData();
140
+
141
+ if (userInstanceReadyCallback) {
142
+ userInstanceReadyCallback(evt);
143
+ }
144
+ });
145
+ };
146
+
147
+ return config;
148
+ },
149
+ prepareComponentData() {
150
+ const data = this.modelValue;
151
+
152
+ this.instance.fire('lockSnapshot');
153
+
154
+ this.instance.setData(data, {
155
+ callback: () => {
156
+ this.$_setUpEditorEvents();
157
+
158
+ const newData = this.instance.getData();
159
+
160
+ // Locking the snapshot prevents the 'change' event.
161
+ // Trigger it manually to update the bound data.
162
+ if (data !== newData) {
163
+ // this.$once('input', () => {
164
+ // this.$emit('ready', this.instance);
165
+ // });
166
+
167
+ this.$emit('update:model-value', newData);
168
+ }
169
+
170
+ this.instance.fire('unlockSnapshot');
171
+ }
172
+ });
173
+ },
174
+ $_setUpEditorEvents() {
175
+ const editor = this.instance;
176
+
177
+ const onChange = debounce(evt => {
178
+ const data = editor.getData();
179
+
180
+ // Editor#change event might be fired without an actual data change.
181
+ if (this.modelValue !== data) {
182
+ // The compatibility with the v-model and general Vue.js concept of input–like components.
183
+ this.$emit('update:model-value', data, evt, editor);
184
+ }
185
+ }, 80);
186
+
187
+ editor.on('change', onChange);
188
+ }
189
+ }
85
190
  };
86
191
  </script>
@@ -42,6 +42,7 @@
42
42
  :filter-enable="filterEnable"
43
43
  :page-enable="pageEnable"
44
44
  :embedded="embedded"
45
+ :before-load-data="beforeLoadData"
45
46
  :on-search="onSearch"
46
47
  :summary-method="summaryMethod"
47
48
  :page-size-opts="pageSizeOpts"
@@ -275,6 +276,12 @@
275
276
  type: Boolean,
276
277
  default: false
277
278
  },
279
+ /**
280
+ * 加载数据前
281
+ */
282
+ beforeLoadData: {
283
+ type: Function
284
+ },
278
285
  /**
279
286
  * 查询方法
280
287
  */
@@ -177,7 +177,7 @@
177
177
  *
178
178
  * @public
179
179
  */
180
- loadChart(data) {
180
+ async loadChart(data) {
181
181
  if (data) {
182
182
  this.data = data;
183
183
  }
@@ -199,6 +199,11 @@
199
199
  }
200
200
  };
201
201
 
202
+ // 预先加载枚举值
203
+ if (this.setting.chartGroupCodeType && this.setting.chartGroupCodeType.indexOf('Enum:') === 0) {
204
+ await this.loadEnum(this.setting.chartGroupCodeType.replace('Enum:', ''));
205
+ }
206
+
202
207
  if (!this.setting.chartCustomSeries) {
203
208
  if (this.setting.chartType != 'Pie') {
204
209
  let xAxis = Array.from(
@@ -222,7 +227,13 @@
222
227
  group = Array.from(
223
228
  new Set(
224
229
  this.data.map(item => {
225
- return this.parseData(item, this.setting.chartGroupCode);
230
+ let value = this.parseData(item, this.setting.chartGroupCode);
231
+
232
+ if (this.setting.chartGroupCodeType && this.setting.chartGroupCodeType.indexOf('Enum:') === 0) {
233
+ value = this.getEnum(this.setting.chartGroupCodeType.replace('Enum:', ''), value);
234
+ }
235
+
236
+ return value;
226
237
  })
227
238
  )
228
239
  );
@@ -234,7 +245,13 @@
234
245
  let keyData = {};
235
246
  this.data
236
247
  .filter(data => {
237
- return (item == null && group.length == 1) || this.parseData(data, this.setting.chartGroupCode) == item;
248
+ let value = this.parseData(data, this.setting.chartGroupCode);
249
+
250
+ if (this.setting.chartGroupCodeType && this.setting.chartGroupCodeType.indexOf('Enum:') === 0) {
251
+ value = this.getEnum(this.setting.chartGroupCodeType.replace('Enum:', ''), value);
252
+ }
253
+
254
+ return (item == null && group.length == 1) || value == item;
238
255
  })
239
256
  .forEach(data => {
240
257
  let key = this.parseData(data, this.setting.chartXCode);
@@ -265,7 +282,13 @@
265
282
  group = Array.from(
266
283
  new Set(
267
284
  this.data.map(item => {
268
- return this.parseData(item, this.setting.chartGroupCode);
285
+ let value = this.parseData(item, this.setting.chartGroupCode);
286
+
287
+ if (this.setting.chartGroupCodeType && this.setting.chartGroupCodeType.indexOf('Enum:') === 0) {
288
+ value = this.getEnum(this.setting.chartGroupCodeType.replace('Enum:', ''), value);
289
+ }
290
+
291
+ return value;
269
292
  })
270
293
  )
271
294
  );
@@ -277,7 +300,13 @@
277
300
  let keyData = {};
278
301
  this.data
279
302
  .filter(data => {
280
- return (item == null && group.length == 1) || this.parseData(data, this.setting.chartGroupCode) == item;
303
+ let value = this.parseData(data, this.setting.chartGroupCode);
304
+
305
+ if (this.setting.chartGroupCodeType && this.setting.chartGroupCodeType.indexOf('Enum:') === 0) {
306
+ value = this.getEnum(this.setting.chartGroupCodeType.replace('Enum:', ''), value);
307
+ }
308
+
309
+ return (item == null && group.length == 1) || value == item;
281
310
  })
282
311
  .forEach(data => {
283
312
  let key = this.parseData(data, this.setting.chartXCode);
@@ -411,7 +440,7 @@
411
440
  row[code] = checked.code;
412
441
  row[code + 'Type'] = checked.dataType;
413
442
 
414
- //this.$forceUpdate();
443
+ this.$forceUpdate();
415
444
 
416
445
  if (table) {
417
446
  table.loadData();
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div>
2
+ <div :class="{ 'full-screen': isFullScreen }">
3
3
  <Divider :plain="true" v-if="title != null" dashed orientation="left" size="small">
4
4
  <span class="title">{{ title }}</span>
5
5
  </Divider>
@@ -8,66 +8,69 @@
8
8
  -->
9
9
  <slot name="top"></slot>
10
10
  <Form v-if="tableView.filterEnable && filterEnable" :model="filter" :label-width="125" label-colon=":" label-position="right" class="filter" @submit.prevent>
11
- <Row :gutter="24" type="flex" justify="end">
12
- <!--
11
+ <div ref="filterCommand">
12
+ <Row :gutter="24" type="flex" justify="end">
13
+ <!--
13
14
  @slot 筛选栏
14
15
  @binding {object} table 表格对象
15
16
  @binding {object} filter 筛选对象
16
17
  -->
17
- <slot name="filter" :table="this" :filter="filter">
18
- <!--
18
+ <slot name="filter" :table="this" :filter="filter">
19
+ <!--
19
20
  @slot 自定义筛选栏
20
21
  -->
21
- <slot name="customFilter"></slot>
22
- <table-filter
23
- ref="tableFilter"
24
- :data="filter"
25
- :columns="filterColumns"
26
- :isDataSource="tableView.isDataSource"
27
- :keywordEnable="tableView.keywordEnable"
28
- @on-keyup="onKeyup"
29
- >
30
- <template #column="{ filter, column }">
31
- <!--
22
+ <slot name="customFilter"></slot>
23
+ <table-filter
24
+ ref="tableFilter"
25
+ :data="filter"
26
+ :columns="filterColumns"
27
+ :isDataSource="tableView.isDataSource"
28
+ :keywordEnable="tableView.keywordEnable"
29
+ @on-keyup="onKeyup"
30
+ >
31
+ <template #column="{ filter, column }">
32
+ <!--
32
33
  @slot 筛选栏自定义列
33
34
  @binding {object} filter 筛选条件对象
34
35
  @binding {object} column 列对象
35
36
  @binding {string} code 列代码
36
37
  -->
37
- <slot name="filterColumn" :filter="filter" :column="column" :code="column.code"></slot>
38
- </template>
39
- </table-filter>
40
- <Col v-bind="getGrid(tableView.filterWidth)" :style="{ 'text-align': tableView.filterAlign == null ? 'left' : tableView.filterAlign.toLowerCase() }">
41
- <FormItem :label-width="0">
42
- <Button type="info" v-if="tableView.keywordEnable || filterColumns.length > 0" custom-icon="fa fa-search" @click="search()" size="small">查询</Button>
43
- <Button type="primary" v-if="tableView.createEnable && createEnable" custom-icon="fa fa-plus" @click="create()" size="small">新建</Button>
44
- <!--
38
+ <slot name="filterColumn" :filter="filter" :column="column" :code="column.code"></slot>
39
+ </template>
40
+ </table-filter>
41
+ <Col v-bind="getGrid(tableView.filterWidth)" :style="{ 'text-align': tableView.filterAlign == null ? 'left' : tableView.filterAlign.toLowerCase() }">
42
+ <FormItem :label-width="0">
43
+ <Button type="info" v-if="tableView.keywordEnable || filterColumns.length > 0" custom-icon="fa fa-search" @click="search()" size="small">查询</Button>
44
+ <Button type="primary" v-if="tableView.createEnable && createEnable" custom-icon="fa fa-plus" @click="create()" size="small">新建</Button>
45
+ <!--
45
46
  @slot 筛选栏按钮
46
47
  -->
47
- <slot name="filterCommand"></slot>
48
- <Dropdown
49
- v-if="tableView.exportEnable || tableView.exportPdfEnable || tableView.printEnable || tableView.batchEditEnable"
50
- style="margin-left: 4px"
51
- @on-click="moreClick"
52
- >
53
- <Button type="info" size="small">
54
- 更多...
55
- <Icon type="ios-arrow-down" />
56
- </Button>
57
- <template #list>
58
- <DropdownMenu>
59
- <DropdownItem v-if="tableView.exportEnable" name="exportExcel">导出 Excel</DropdownItem>
60
- <DropdownItem v-if="tableView.exportPdfEnable" name="exportPdf">导出 PDF</DropdownItem>
61
- <DropdownItem v-if="tableView.printEnable" name="print">打印</DropdownItem>
62
- <DropdownItem v-if="tableView.batchEditEnable" name="batchEdit">批量编辑</DropdownItem>
63
- </DropdownMenu>
64
- </template>
65
- </Dropdown>
66
- <Button type="error" title="筛选设置" custom-icon="fa fa-filter" size="small" v-if="settingEnable && allow('permission/tableView')" @click="filterSettingOpen" />
67
- </FormItem>
68
- </Col>
69
- </slot>
70
- </Row>
48
+ <slot name="filterCommand"></slot>
49
+ <Dropdown
50
+ v-if="tableView.exportEnable || tableView.exportPdfEnable || tableView.printEnable || tableView.batchEditEnable"
51
+ style="margin-left: 4px"
52
+ @on-click="moreClick"
53
+ >
54
+ <Button type="info" size="small">
55
+ 更多...
56
+ <Icon type="ios-arrow-down" />
57
+ </Button>
58
+ <template #list>
59
+ <DropdownMenu>
60
+ <DropdownItem v-if="tableView.exportEnable" name="exportExcel">导出 Excel</DropdownItem>
61
+ <DropdownItem v-if="tableView.exportPdfEnable" name="exportPdf">导出 PDF</DropdownItem>
62
+ <DropdownItem v-if="tableView.printEnable" name="print">打印</DropdownItem>
63
+ <DropdownItem v-if="tableView.batchEditEnable" name="batchEdit">批量编辑</DropdownItem>
64
+ </DropdownMenu>
65
+ </template>
66
+ </Dropdown>
67
+ <Button type="error" title="筛选设置" custom-icon="fa fa-filter" size="small" v-if="settingEnable && allow('permission/tableView')" @click="filterSettingOpen" />
68
+ <Button v-if="tableView.fullEnable" type="info" title="全屏" custom-icon="fa fa-expand-arrows-alt" size="small" @click="isFullScreen = !isFullScreen" />
69
+ </FormItem>
70
+ </Col>
71
+ </slot>
72
+ </Row>
73
+ </div>
71
74
  </Form>
72
75
  <!--
73
76
  @slot 中部
@@ -541,7 +544,8 @@
541
544
  itemSelectActive: false,
542
545
  commandButtons: [],
543
546
  preview: false,
544
- imageUrl: null
547
+ imageUrl: null,
548
+ isFullScreen: false
545
549
  };
546
550
  },
547
551
  async created() {
@@ -713,6 +717,12 @@
713
717
  type: Boolean,
714
718
  default: false
715
719
  },
720
+ /**
721
+ * 加载数据前
722
+ */
723
+ beforeLoadData: {
724
+ type: Function
725
+ },
716
726
  /**
717
727
  * 查询方法
718
728
  */
@@ -736,7 +746,11 @@
736
746
  // 表格最大高度,固定表头
737
747
  maxHeight() {
738
748
  if (this.height == null) {
739
- return document.body.offsetHeight - 400;
749
+ if (this.isFullScreen) {
750
+ return document.body.offsetHeight - 100 - this.$refs.filterCommand.offsetHeight;
751
+ } else {
752
+ return document.body.offsetHeight - 400;
753
+ }
740
754
  } else {
741
755
  return this.height;
742
756
  }
@@ -969,6 +983,11 @@
969
983
  }
970
984
  }
971
985
 
986
+ // 加载前动作
987
+ if (typeof this.beforeLoadData == 'function') {
988
+ this.beforeLoadData();
989
+ }
990
+
972
991
  if (staticData) {
973
992
  this.staticData = this.copy(staticData);
974
993
  }
@@ -19,7 +19,7 @@
19
19
  <i-menu-head v-if="headerMenu && isMobile" />
20
20
  <i-header-log v-if="isDesktop && showLog" />
21
21
  <i-header-fullscreen v-if="isDesktop && showFullscreen" />
22
- <i-header-notice v-if="showNotice" />
22
+ <i-header-notice ref="notice" v-if="showNotice" />
23
23
  <i-header-user />
24
24
  <i-header-i18n v-if="showI18n" />
25
25
  <i-header-setting v-if="enableSetting && !isMobile" />
@@ -269,6 +269,29 @@
269
269
  },
270
270
  mounted() {
271
271
  document.addEventListener('scroll', this.handleScroll, { passive: true });
272
+
273
+ // 非移动端,根据系统缩放比率调整界面大小
274
+ //console.log('navigator.userAgent', navigator.userAgent);
275
+ if (
276
+ Setting.layout.autoFixRatio == true &&
277
+ window.screen.width < 1920 &&
278
+ window.screen.width * window.devicePixelRatio <= 1920 &&
279
+ !navigator.userAgent.match(/AppleWebKit.*Mobile.*/) &&
280
+ !navigator.userAgent.match(/.*Android.*/)
281
+ ) {
282
+ document.body.style.zoom = 1 / window.devicePixelRatio;
283
+
284
+ setTimeout(() => {
285
+ let sider = document.getElementsByClassName('i-layout-sider');
286
+ if (sider.length > 0) {
287
+ sider[0].style.height = window.devicePixelRatio * 100 + 'vh';
288
+ }
289
+ var menu = document.getElementsByClassName('i-layout-menu-side');
290
+ if (menu.length > 0) {
291
+ menu[0].style.height = 'calc(' + window.devicePixelRatio * 100 + 'vh - 64px)';
292
+ }
293
+ });
294
+ }
272
295
  },
273
296
  beforeUnmount() {
274
297
  document.removeEventListener('scroll', this.handleScroll);
@@ -309,7 +309,7 @@ export default {
309
309
  let data = [];
310
310
  let value = this.parseData(model, expression);
311
311
 
312
- if (value != null && value != '') {
312
+ if (this.isJSON(value)) {
313
313
  data = JSON.parse(value);
314
314
  }
315
315
 
@@ -329,7 +329,7 @@ export default {
329
329
  let data = [];
330
330
  let value = this.parseData(model, expression);
331
331
 
332
- if (value != null && value != '') {
332
+ if (this.isJSON(value)) {
333
333
  data = JSON.parse(value);
334
334
  data[0] = new Date(data[0]);
335
335
  data[1] = new Date(data[1]);
@@ -628,7 +628,8 @@ export default {
628
628
  },
629
629
  // 重置通知栏数字
630
630
  resetNotice() {
631
- this.getNotice(this).init();
631
+ let notice = this.getNotice(this);
632
+ notice.init();
632
633
  },
633
634
  // 获取通知栏
634
635
  getNotice(node) {
@@ -641,6 +642,23 @@ export default {
641
642
  }
642
643
 
643
644
  return null;
645
+ },
646
+ // 判断是否JSON
647
+ isJSON(str) {
648
+ if (!!(str || '').trim()) {
649
+ try {
650
+ var obj = JSON.parse(str);
651
+ if (typeof obj == 'object' && obj) {
652
+ return true;
653
+ } else {
654
+ return false;
655
+ }
656
+ } catch (e) {
657
+ return false;
658
+ }
659
+ } else {
660
+ return false;
661
+ }
644
662
  }
645
663
  }
646
664
  };
@@ -32,7 +32,7 @@
32
32
 
33
33
  /* 页面头部 */
34
34
  .ivu-page-header {
35
- height: 150px;
35
+ height: 135px;
36
36
  background: #078dcf;
37
37
  }
38
38
 
@@ -295,8 +295,12 @@
295
295
  }
296
296
 
297
297
  /* 按钮图标 */
298
- .ivu-btn {
298
+ /* .ivu-btn {
299
299
  line-height: 30px;
300
+ } */
301
+
302
+ .ivu-btn > span {
303
+ width: 2px;
300
304
  }
301
305
 
302
306
  .ivu-btn > .ivu-icon {
@@ -309,12 +313,16 @@
309
313
  margin-left: 0;
310
314
  }
311
315
 
312
- .ivu-btn-small {
316
+ /* .ivu-btn-small {
313
317
  line-height: 20px;
314
- }
318
+ } */
315
319
 
316
- .ivu-btn-small > .ivu-icon {
320
+ /* .ivu-btn-small > .ivu-icon {
317
321
  margin-bottom: 2px;
322
+ } */
323
+
324
+ .ivu-btn.ivu-btn-small > span {
325
+ width: 0px;
318
326
  }
319
327
 
320
328
  .ivu-btn-primary {
@@ -508,3 +516,15 @@
508
516
  .i-layout-sider {
509
517
  min-height: 100%;
510
518
  }
519
+
520
+ /* 全屏 */
521
+ .full-screen {
522
+ position: fixed;
523
+ top: 0;
524
+ left: 0;
525
+ right: 0;
526
+ bottom: 0;
527
+ padding: 10px;
528
+ background: #fff;
529
+ z-index: 1000;
530
+ }