@epxing/dashboard 0.0.0-stage → 5.3.0-beta.2

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.
Files changed (64) hide show
  1. package/API.md +80 -0
  2. package/README.md +41 -2
  3. package/dist/_virtual/_plugin-vue_export-helper.js +10 -0
  4. package/dist/_virtual/preload-helper.js +88 -0
  5. package/dist/assets/technology_bg-oWIS2awO.jpg +0 -0
  6. package/dist/src/Dashboard.js +333 -0
  7. package/dist/src/DashboardGrid.js +183 -0
  8. package/dist/src/DesignGrid.js +712 -0
  9. package/dist/src/DesignItem.js +64 -0
  10. package/dist/src/Div.js +409 -0
  11. package/dist/src/Header.js +386 -0
  12. package/dist/src/Preview.js +28 -0
  13. package/dist/src/Tabs.js +469 -0
  14. package/dist/src/Title.js +102 -0
  15. package/dist/src/Widget.js +267 -0
  16. package/dist/src/chart-request-scope.js +33 -0
  17. package/dist/src/design-target.js +58 -0
  18. package/dist/src/grid/compact.js +69 -0
  19. package/dist/src/grid/compat/GridItem.js +36 -0
  20. package/dist/src/grid/compat/GridLayout.js +77 -0
  21. package/dist/src/grid/designState.js +22 -0
  22. package/dist/src/grid/engines.js +26 -0
  23. package/dist/src/grid/geometry.js +45 -0
  24. package/dist/src/grid/gridstackParams.js +70 -0
  25. package/dist/src/grid/mixins.js +109 -0
  26. package/dist/src/grid/model.js +224 -0
  27. package/dist/src/grid/useViewportGate.js +56 -0
  28. package/dist/src/index.js +27 -0
  29. package/dist/src/layout-util.js +109 -0
  30. package/dist/src/mobile-design.js +51 -0
  31. package/dist/src/theme-style.js +134 -0
  32. package/dist/style.css +1009 -0
  33. package/package.json +86 -3
  34. package/src/Dashboard.vue +459 -0
  35. package/src/DashboardGrid.vue +189 -0
  36. package/src/DesignGrid.vue +851 -0
  37. package/src/DesignItem.vue +124 -0
  38. package/src/Div.vue +419 -0
  39. package/src/Header.vue +501 -0
  40. package/src/Preview.vue +30 -0
  41. package/src/Tabs.vue +484 -0
  42. package/src/Title.vue +107 -0
  43. package/src/Widget.vue +354 -0
  44. package/src/chart-request-scope.js +22 -0
  45. package/src/design-target.js +55 -0
  46. package/src/grid/compact.js +81 -0
  47. package/src/grid/compat/GridItem.vue +30 -0
  48. package/src/grid/compat/GridLayout.vue +76 -0
  49. package/src/grid/designState.js +17 -0
  50. package/src/grid/engines.js +22 -0
  51. package/src/grid/geometry.js +55 -0
  52. package/src/grid/gridstackParams.js +72 -0
  53. package/src/grid/mixins.js +114 -0
  54. package/src/grid/model.js +259 -0
  55. package/src/grid/useViewportGate.js +57 -0
  56. package/src/index.js +28 -0
  57. package/src/layout-util.js +157 -0
  58. package/src/mobile-design.js +50 -0
  59. package/src/theme-style.js +185 -0
  60. package/types/div.d.ts +1 -0
  61. package/types/index.d.ts +7 -0
  62. package/types/tabs.d.ts +1 -0
  63. package/types/title.d.ts +1 -0
  64. package/types/widget.d.ts +1 -0
package/src/Header.vue ADDED
@@ -0,0 +1,501 @@
1
+ <template>
2
+ <!-- 移动端导航栏 -->
3
+ <div class="mobile-header" v-if="$isMobile" :style="$component.style">
4
+ <div class="title">
5
+ <i class="el-icon-arrow-left" @click="backPage"></i>
6
+ <span>{{pageTitle}}</span>
7
+ </div>
8
+ <!-- <div v-if="$configs.moreFlag"> -->
9
+ <!-- 多选操作>1时显示more图标
10
+ 多选操作=1时如果flag为true显示more图标,为false直接显示操作名称 -->
11
+ <div class="mobile-opera" v-if="operates.length>1">
12
+ <el-popover
13
+ popper-class="popper"
14
+ placement="bottom"
15
+ trigger="click"
16
+ >
17
+ <ul class="setting-list">
18
+ <li v-for="operate in operates" :key="operate.text" @click="listHandler(operate)">
19
+ <i :class="['fa',operate.icon]"></i>
20
+ {{operate.text}}
21
+ </li>
22
+ <!-- <li @click="pageSetting"><i class="el-icon-document-copy"></i>页面设置</li>
23
+ <li @click="metaSetting"><i class="el-icon-coin"></i>对象设置</li> -->
24
+ </ul>
25
+ <template v-slot:reference>
26
+ <i :class="[$configs.icon]" :style="{transform:'rotate(90deg)'}" v-if="$configs.icon"></i>
27
+ <i class="el-icon-more" :style="{transform:'rotate(90deg)'}" v-else></i>
28
+ </template>
29
+ </el-popover>
30
+ <i class="menu-icon el-icon-s-fold" v-if="menu" @click="isMenuShow=true"></i>
31
+ <!-- P2a Task3:van-popup → MobileSheet(el-drawer 底座)。size="60%" 对应原 width:60%;
32
+ 保留的 height/marginTop 是原补 van-popup 边缘 1px 缝的 hack,换底座后是否仍需要——
33
+ 未能在真实浏览器核实(本机无可达 dev server + 有并行会话在改同一工作树,未敢自起
34
+ 服务),按 brief 的三步走 Step 1 原样保留,未删。详见 task-3-report.md。 -->
35
+ <mobile-sheet v-model="isMenuShow" position="right" size="60%">
36
+ <div class="menu-container">
37
+ <epx-menu class="menu" v-if="menu" :modules="menu" :moduleId="module.ID" :moduleName="module.NAME" >
38
+ </epx-menu>
39
+ <div class="to-index" @click="backIndex">
40
+ <i class="el-icon-house"></i>
41
+ {{$t('return home')}}
42
+ </div>
43
+ </div>
44
+ </mobile-sheet>
45
+
46
+ <!-- <i class="el-icon-more" :style="{transform:'rotate(90deg)'}"></i> -->
47
+ </div>
48
+ <div class="mobile-opera" v-else>
49
+ <li v-for="operate in operates" class="mobile-operate-btn" :key="operate.text" @click="listHandler(operate)">
50
+ <i :class="['fa',operate.icon]"></i>
51
+ {{operate.text}}
52
+ </li>
53
+ </div>
54
+ </div>
55
+ <!-- pc端 -->
56
+ <div class="header" v-else :style="$component.style">
57
+ <div class="header-inner">
58
+ <div class="title">
59
+ <!-- 返回箭头:页内下钻(栈 > 1)、调用方显式传 showBack(同基础 Header)、常用报表看板预览
60
+ (previewReport 用 openType '1' 新开页签,看板是该页签第一页、栈恒为 1,而智能体应用没有页签行,
61
+ 不显示就没法回列表;返回会销毁本页 → 关掉页签 → 回到上一个页签)。AI 面板内预览由面板自带关闭 -->
62
+ <i class="el-icon-arrow-left" @click="backPage" v-if="(routeLength!==1 || showBack || reportBoardId) && !isAiAgentPage"></i>
63
+ <span class="">{{ pageTitle}}</span>
64
+ </div>
65
+ <ul class="tabs" v-if="tabs && tabs.length>0">
66
+ <!-- T30b COMPILER_FILTERS:同 header/Breadcrumb.vue —— tabTitle 是 extends 来的 method,非 filter。 -->
67
+ <li :class="['tab-item', { 'active': tab.active}]" v-for="(tab, index) in tabs" :key="index" @click="change(tab, index)">{{ tabTitle(tab, index) }}</li>
68
+ </ul>
69
+ <div class="opera" >
70
+ <el-switch
71
+ class="seach-flag"
72
+ v-if="scopeFlag && globalSearchFields.length>0"
73
+ v-model="showSearchFields"
74
+ inactive-text="查询筛选:"
75
+ >
76
+ </el-switch>
77
+ <el-button class="opera-item" type="primary" link @click="reloadPageConfig">
78
+ <i class="el-icon-refresh"></i>
79
+ {{'刷新页面'}}
80
+ </el-button>
81
+ <el-button class="opera-item" type="primary" link @click="switchFullscreen">
82
+ <i class="el-icon-full-screen"></i>
83
+ {{fullscreen? '取消全屏': '全屏'}}
84
+ </el-button>
85
+ <el-button v-if="(showPageConfigBtn && !isAiAgentPage) || reportBoardId" class="opera-item" type="primary" link @click="newPageSettingVisual">
86
+ <i class="el-icon-edit-outline"></i>
87
+ 设计
88
+ </el-button>
89
+
90
+ <el-dialog
91
+ v-if="$configs.showHelp && $configs.help"
92
+ class="help-dialog"
93
+ v-model="showHelpDialog"
94
+ :modal="false"
95
+ width="70%"
96
+ :fullscreen="helpDialogFullscreen"
97
+ >
98
+ <template v-slot:header><div class="help-header">
99
+ <span>{{ this.$t('helptext') }}</span>
100
+ <i class="full-btn el-icon-full-screen" @click="helpDialogFullscreen=!helpDialogFullscreen"></i>
101
+ </div></template>
102
+ <div class="help-content" v-html="$configs.help"></div>
103
+ </el-dialog>
104
+ </div>
105
+ </div>
106
+ <div class="search-fields" v-show="showSearchFields">
107
+ <epx-search-fields :metaId="$metaId" :fields="globalSearchFields" :showFilter="true" :showKeyword="false" :showSearchLabel="true" @search="search"></epx-search-fields>
108
+ </div>
109
+ </div>
110
+ </template>
111
+
112
+ <script>
113
+ import Header from '@epxing/components/src/header'
114
+ import { evalWith } from '@epxing/utils/src/eval-with'
115
+ import { MobileSheet } from '@epxing/components/src/mobile'
116
+ export default {
117
+ name: 'epx-dashboard-header',
118
+ extends: Header,
119
+ components: { MobileSheet },
120
+ data () {
121
+ return {
122
+ fullscreen: false,
123
+ showSearchFields: !!this.searchDefaultFlag
124
+ }
125
+ },
126
+ props: {
127
+ scopeFlag: Boolean,
128
+ globalSearchFields: {
129
+ type: Array,
130
+ default () {
131
+ return []
132
+ }
133
+ },
134
+ searchDefaultFlag: Boolean,
135
+ title: {
136
+ type: String,
137
+ default: ''
138
+ }
139
+ },
140
+ computed: {
141
+ // AI 助手右侧预览(AiChatPage 打开看板时传 isAiAgentPage):隐藏返回和设计按钮,
142
+ // 面板自带关闭按钮,设计入口对临时注入的看板页面无意义
143
+ isAiAgentPage () {
144
+ return !!this.$query.isAiAgentPage
145
+ },
146
+ // 常用报表看板(FM_CA_REPORT_BOARD)的报表记录 id:
147
+ // 1) previewReport 透传的 $query.reportBoardId;
148
+ // 2) 兜底从临时页面 id(report-board-<报表ID>)解析,避免依赖配置是否重新应用。
149
+ // 有它就把"设计"按钮放开(即便在 AI 面板里),并走报表设计模式。
150
+ reportBoardId () {
151
+ if (this.$query.reportBoardId) return this.$query.reportBoardId
152
+ const pid = this.upPage && this.upPage.$pageId
153
+ if (pid && /^report-board-/.test(pid)) return pid.replace(/^report-board-/, '')
154
+ return ''
155
+ },
156
+ pageTitle () {
157
+ const _this = this
158
+ const { config: pageConfigs = {} } = this.$pageInfo
159
+ const { title = '' } = pageConfigs
160
+ const pageTitle = title.replace(/\$\[([^[]+)\]/g, (macth, key) => {
161
+ try {
162
+ return evalWith(`_this.${key}`, { _this }) || evalWith(`_this.$query.${key}`, { _this }) || ''
163
+ } catch (error) {
164
+ console.error(error)
165
+ return ''
166
+ }
167
+ })
168
+
169
+ return pageTitle || this.$query.title || this.defaultTitle || this.$query.menuTitle
170
+ }
171
+ },
172
+ methods: {
173
+ newPageSettingVisual () {
174
+ // 常用报表看板:打开报表设计模式,设计器保存写回 FM_CA_REPORT_BOARD.DASHBOARD_CONFIG,
175
+ // 不依赖 FM_PAGE_CONFIG 的 pageId(预览用的是临时注入页面)
176
+ if (this.reportBoardId) {
177
+ const routePath = this.$util.getRootPath()
178
+ window.open(`${routePath}/config/reportboard/${this.appConfig['CLIENT|CODE']}/${this.appConfig['SYSTEM|CODE']}/${this.reportBoardId}`)
179
+ // 设计在新标签页保存到 FM_CA_REPORT_BOARD;切回本预览页时重新拉取配置刷新看板
180
+ document.removeEventListener('visibilitychange', this.onReportBoardReturn)
181
+ document.addEventListener('visibilitychange', this.onReportBoardReturn)
182
+ return
183
+ }
184
+ if (!this.upPage.$pageId) {
185
+ this.$message.error(this.$t('The default page cannot be edited'))
186
+ return
187
+ }
188
+ const pageId = this.upPage.$pageId
189
+ // const route = {
190
+ // metaId: 'FM_PAGE_CONFIG',
191
+ // flag: 'v_visual_page_config',
192
+ // component: 'visual-config-dashboard',
193
+ // id: pageId,
194
+ // entityId: this.upPage.$metaId,
195
+ // config: this.upPage.pageConfig,
196
+ // pageInstance: this.upPage,
197
+ // title: this.title || this.appConfig.TITLE,
198
+ // pageType: 'EDIT'
199
+ // }
200
+ // this.$createPage(route)
201
+ setTimeout(() => {
202
+ const routePath = this.$util.getRootPath()
203
+ window.open(`${routePath}/config/dashboard/${this.appConfig['CLIENT|CODE']}/${this.appConfig['SYSTEM|CODE']}/${pageId}`)
204
+ window.removeEventListener('message', this.reloadPageConfig)
205
+ window.addEventListener('message', this.reloadPageConfig)
206
+ }, 200)
207
+ },
208
+ switchFullscreen () {
209
+ this.$emit('fullscreen')
210
+ },
211
+ search (v) {
212
+ if (this.timer) {
213
+ clearTimeout(this.timer)
214
+ this.timer = null
215
+ }
216
+ this.timer = setTimeout(() => {
217
+ this.$emit('search', v)
218
+ clearTimeout(this.timer)
219
+ this.timer = null
220
+ }, 1000)
221
+ },
222
+ reloadPageConfig (e) {
223
+ if (this.timer) {
224
+ this.timer = null
225
+ clearTimeout(this.timer)
226
+ }
227
+ this.timer = setTimeout(() => {
228
+ this.$store.commit('DELETE_PAGE', { pageId: this.upPage.$pageId })
229
+ this.reloadPage()
230
+ this.timer = null
231
+ clearTimeout(this.timer)
232
+ }, 1000)
233
+ },
234
+ // 从报表设计标签页切回预览时:重新拉取 FM_CA_REPORT_BOARD 配置,重建临时看板页面并刷新
235
+ onReportBoardReturn () {
236
+ if (document.visibilityState !== 'visible') return
237
+ // 只刷新一次(reloadPage 会重建头部,监听随之失效;要再次同步可再点"设计")
238
+ document.removeEventListener('visibilitychange', this.onReportBoardReturn)
239
+ const reportId = this.reportBoardId
240
+ const pid = this.upPage && this.upPage.$pageId
241
+ if (!reportId || !pid) return
242
+ this.$http.get({ entityId: 'FM_CA_REPORT_BOARD', id: reportId })
243
+ .then(res => {
244
+ const record = (res && res.data) || {}
245
+ let config = {}
246
+ try {
247
+ const raw = record.DASHBOARD_CONFIG
248
+ config = raw ? (typeof raw === 'string' ? JSON.parse(raw) : raw) : {}
249
+ } catch (err) {
250
+ config = {}
251
+ }
252
+ // 兼容信封写法 { title, metaId, config: {...} }
253
+ if (config && config.config && (config.config.items || config.config.itemConfigs)) {
254
+ config = Object.assign({ title: config.title, metaId: config.metaId }, config.config)
255
+ }
256
+ this.$store.commit('DELETE_PAGE', { pageId: pid })
257
+ this.$store.commit('ADD_PAGE', {
258
+ pageId: pid,
259
+ pageCode: 'dashboard',
260
+ metaId: config.metaId || '',
261
+ pageMetaId: config.metaId || '',
262
+ title: record.REPORT_NAME || config.title || '报表预览',
263
+ label: record.REPORT_NAME || config.title || '报表预览',
264
+ config: config
265
+ })
266
+ this.reloadPage()
267
+ })
268
+ .catch(() => {})
269
+ }
270
+ },
271
+ beforeUnmount () {
272
+ document.removeEventListener('visibilitychange', this.onReportBoardReturn)
273
+ }
274
+ }
275
+ </script>
276
+ <style scoped lang="scss">
277
+ .header {
278
+ margin-bottom: 10px;
279
+ }
280
+ .header-inner {
281
+ display: flex;
282
+ justify-content: space-between;
283
+ align-items: center;
284
+ background: var(--epx-bg-card);
285
+ width: 100%;
286
+ height: 50px;
287
+ padding: 0 16px;
288
+ box-sizing: border-box;
289
+ .title {
290
+ font-size: var(--epx-font-size-l);
291
+ color: #333333;
292
+ i {
293
+ font-size: 22px;
294
+ position: relative;
295
+ top: 2px;
296
+ cursor: pointer;
297
+ vertical-align: baseline;
298
+ }
299
+ }
300
+ .tabs {
301
+ display: flex;
302
+ flex: 1;
303
+ padding-left: 20px;
304
+ }
305
+ .tab-item {
306
+ cursor: pointer;
307
+ font-size: var(--epx-font-size-l);
308
+ line-height: 1;
309
+ color: #666;
310
+ padding: 0 12px;
311
+ border-right: 1px solid #ccc;
312
+ &.active {
313
+ @include font_color_primary;
314
+ }
315
+ &:last-child {
316
+ border: 0
317
+ }
318
+ }
319
+ .opera {
320
+
321
+ :deep(.el-button) {
322
+ padding: 8px 16px;
323
+ }
324
+
325
+ .more {
326
+ font-size: var(--epx-font-size-xl);
327
+ padding: 5px 14px;
328
+ }
329
+ .opera-text {
330
+ border: 1px solid #999;
331
+ padding: 0 12px;
332
+ font-size: var(--epx-font-size-xl);
333
+ color: #8395a7;
334
+ line-height: 1;
335
+ cursor: pointer;
336
+ i {
337
+ margin-right: 4px;
338
+ }
339
+ margin-left: 24px;
340
+ }
341
+ }
342
+ }
343
+ .opera-item {
344
+ color: #444;
345
+ margin: 0;
346
+ }
347
+ .epx-header {
348
+ margin-bottom: 10px;
349
+ padding: 6px 12px;
350
+ display: flex;
351
+ }
352
+ .mobile-header {
353
+ background: var(--epx-bg-card);
354
+ display: flex;
355
+ align-items: center;
356
+ justify-content: space-between;
357
+ padding: 12px;
358
+ font-size: var(--epx-font-size-l);
359
+ color: #333;
360
+ border-bottom: 1px solid #e3e3e3;
361
+ .title {
362
+ line-height: 20px;
363
+ i {
364
+ font-size: var(--epx-font-size-xl);
365
+ margin: 0;
366
+ }
367
+ }
368
+ i {
369
+ font-size: 18px;
370
+ color: #666;
371
+ margin-left: 8px;
372
+ }
373
+ }
374
+ .setting-list {
375
+ li {
376
+ padding: 6px 8px;
377
+ border-bottom: 1px solid #e3e3e3 ;
378
+ i {
379
+ margin-right: 6px;
380
+ }
381
+ &:last-child {
382
+ border:0
383
+ }
384
+ }
385
+ }
386
+ .help-dialog {
387
+
388
+ :deep(.el-dialog__header) {
389
+ padding: 15px 20px;
390
+ font-size: var(--epx-font-size-l);
391
+ line-height: 20px;
392
+ }
393
+ :deep(.el-dialog__headerbtn) {
394
+ top: 14px;
395
+ }
396
+ :deep(.el-dialog__body) {
397
+ position: relative;
398
+ max-height: 70vh;
399
+ overflow: auto;
400
+ padding: 10px;
401
+ }
402
+ :deep(.is-fullscreen .el-dialog__body) {
403
+ box-sizing: border-box;
404
+ height: calc(100% - 52px);
405
+ max-height: none;
406
+
407
+ }
408
+
409
+ }
410
+ .full-btn {
411
+ float: right;
412
+ font-size: var(--epx-font-size-base);
413
+ margin-right: 30px;
414
+ margin-top: 2px;
415
+ color: #999;
416
+ cursor: pointer;
417
+ }
418
+ .help-content {
419
+
420
+ }
421
+ @media screen and (max-width: 992px){
422
+ .mobile-header {
423
+ margin: 10px!important;
424
+ margin-bottom: 0!important;
425
+ }
426
+ .epx-header {
427
+ margin-bottom: 0;
428
+ position: relative;
429
+ &::after {
430
+ content: '';
431
+ position: absolute;
432
+ bottom: 0;
433
+ left: 0;
434
+ width: 100%;
435
+ height: 1px;
436
+ background: #e3e3e3;
437
+ transform: scaleY(0.5);
438
+ }
439
+
440
+ :deep(.el-page-header__title) {
441
+ display: none;
442
+ }
443
+ :deep(.el-page-header__left) {
444
+ margin-right: 0;
445
+ &::after {
446
+ display: none;
447
+ }
448
+ }
449
+ :deep(.el-page-header__content) {
450
+ flex: 1;
451
+ text-align: center;
452
+ font-size: var(--epx-font-size-l);
453
+ }
454
+
455
+ }
456
+ }
457
+ .menu-container {
458
+ display: flex;
459
+ flex-direction: column;
460
+ height: 100%;
461
+ .menu {
462
+ flex: 1;
463
+ }
464
+ .to-index {
465
+ flex-shrink: 0;
466
+ width: 100%;
467
+ height: 44px;
468
+ border-top: 1px solid #e3e3e3;
469
+ font-size: var(--epx-font-size-base);
470
+ @include font_color_primary;
471
+ text-align: center;
472
+ line-height: 44px;
473
+ i {
474
+ @include font_color_primary;
475
+ position: relative;
476
+ top: 1px;
477
+ margin-right: 4px;
478
+ }
479
+ }
480
+ }
481
+ .allow-click {
482
+ cursor: pointer;
483
+ &:hover {
484
+ @include font_color_primary;
485
+ }
486
+ }
487
+ .mobile-operate-btn {
488
+ @include font_color_primary;
489
+ font-size: var(--epx-font-size-base);
490
+ }
491
+ .seach-flag {
492
+
493
+ :deep(.el-switch__label.is-active) {
494
+ color: #333;
495
+ }
496
+
497
+ }
498
+ .search-fields {
499
+ border-top: 1px solid #F2F3F7;
500
+ }
501
+ </style>
@@ -0,0 +1,30 @@
1
+ <template>
2
+ <div>
3
+ 仪表盘预览
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ name: 'epx-dashboard--preview',
10
+ props: {
11
+ compConfigs: {
12
+ type: Object,
13
+ default () {
14
+ return {}
15
+ }
16
+ }
17
+ },
18
+ inject: ['pageMetaId'],
19
+ computed: {
20
+ currentMetaId () {
21
+ const { metaId = this.pageMetaId } = this.compConfigs
22
+ return metaId
23
+ }
24
+ }
25
+ }
26
+ </script>
27
+
28
+ <style>
29
+
30
+ </style>