@epxing/dashboard 0.0.0-stage → 5.3.0-beta.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.
Files changed (64) hide show
  1. package/API.md +79 -0
  2. package/README.md +40 -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 +332 -0
  7. package/dist/src/DashboardGrid.js +182 -0
  8. package/dist/src/DesignGrid.js +711 -0
  9. package/dist/src/DesignItem.js +63 -0
  10. package/dist/src/Div.js +408 -0
  11. package/dist/src/Header.js +385 -0
  12. package/dist/src/Preview.js +27 -0
  13. package/dist/src/Tabs.js +468 -0
  14. package/dist/src/Title.js +101 -0
  15. package/dist/src/Widget.js +266 -0
  16. package/dist/src/chart-request-scope.js +32 -0
  17. package/dist/src/design-target.js +57 -0
  18. package/dist/src/grid/compact.js +68 -0
  19. package/dist/src/grid/compat/GridItem.js +35 -0
  20. package/dist/src/grid/compat/GridLayout.js +76 -0
  21. package/dist/src/grid/designState.js +21 -0
  22. package/dist/src/grid/engines.js +25 -0
  23. package/dist/src/grid/geometry.js +44 -0
  24. package/dist/src/grid/gridstackParams.js +69 -0
  25. package/dist/src/grid/mixins.js +108 -0
  26. package/dist/src/grid/model.js +223 -0
  27. package/dist/src/grid/useViewportGate.js +55 -0
  28. package/dist/src/index.js +26 -0
  29. package/dist/src/layout-util.js +108 -0
  30. package/dist/src/mobile-design.js +50 -0
  31. package/dist/src/theme-style.js +133 -0
  32. package/dist/style.css +1009 -0
  33. package/package.json +85 -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
@@ -0,0 +1,124 @@
1
+ <template>
2
+ <div :class="['epx-dashboard-design-item', { 'epx-dashboard-design-item--active': active }]" @click.stop="$emit('select', item)">
3
+ <!-- 整卡可拖:gridstack 的拖拽把手是壳的内容盒,这里不设起手区;内容区哪些元素吃指针由画布的 .drag-component 规则决定 -->
4
+ <div class="epx-dashboard-design-item__body"><slot></slot></div>
5
+ <!-- 8 向缩放把手的视觉圆点(沿用旧 resize-point 规格):不接收指针,命中区是 gridstack 生成的 .ui-resizable-* -->
6
+ <i v-for="dir in handles" :key="dir" :class="['epx-dashboard-design-item__handle', 'epx-dashboard-design-item__handle--' + dir]"></i>
7
+ <div class="epx-dashboard-design-item__operates">
8
+ <!-- 悬浮切换(所有项通用,容器内子项也有):点一下在悬浮层 / 碰撞层之间切换,高亮表示当前悬浮中;
9
+ 整页悬浮模式下逐项悬浮没有意义,宿主传 floatable=false 隐藏 -->
10
+ <i v-if="floatable" :class="['epx-dashboard-design-item__float', 'el-icon-top', { 'is-on': !!item.float }]"
11
+ :title="item.float ? '悬浮中,点击恢复默认' : '设为悬浮(可叠放、不推挤)'" @click="toggleFloat"></i>
12
+ <slot name="operates"></slot>
13
+ </div>
14
+ </div>
15
+ </template>
16
+
17
+ <script>
18
+ // 设计态项 chrome:选中边框、缩放把手圆点、操作条。拖拽 / 缩放交互由 gridstack(内容盒 + .ui-resizable-* 把手)负责,这里只重样式。
19
+ const HANDLES = ['n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw']
20
+
21
+ export default {
22
+ name: 'epx-dashboard-design-item',
23
+ props: {
24
+ item: { type: Object, required: true },
25
+ active: Boolean,
26
+ // 是否显示悬浮切换图标:整页悬浮(grid.mode === 'float')与移动端布局不显示
27
+ floatable: { type: Boolean, default: true }
28
+ },
29
+ emits: ['select'],
30
+ data () {
31
+ return { handles: HANDLES }
32
+ },
33
+ methods: {
34
+ // 悬浮 ↔ 默认:字段直接落到 item 上(与 DesignGrid.setFloat / 属性面板 changeItemConfigs 'component' 同一路径),
35
+ // 画布按 item.float 把壳搬到悬浮层 / 碰撞层,外壳 deep watch 自动保存;关掉时层级 z 一并删掉、不留脏值。
36
+ // 点击会冒泡到根元素的 select,所以切换的同时该项也被选中
37
+ toggleFloat () {
38
+ const item = this.item
39
+ if (item.float) {
40
+ delete item.float
41
+ delete item.z
42
+ } else {
43
+ item.float = true
44
+ }
45
+ }
46
+ }
47
+ }
48
+ </script>
49
+
50
+ <style lang="scss">
51
+ @include b(dashboard-design-item) {
52
+ position: relative;
53
+ height: 100%;
54
+ box-sizing: border-box;
55
+ // 选中 / 悬停边框已移到 grid-stack-item 壳的 ::after(不受 overflow 裁切),此处只控制把手和操作条
56
+ &:hover,
57
+ &.epx-dashboard-design-item--active {
58
+ > .epx-dashboard-design-item__handle {
59
+ display: block;
60
+ }
61
+ }
62
+ &:hover > .epx-dashboard-design-item__operates,
63
+ &.epx-dashboard-design-item--active > .epx-dashboard-design-item__operates {
64
+ display: flex;
65
+ }
66
+ // 内容盒本身要能接住指针(主题「悬浮预览」靠 .drag-component:hover 命中),子元素是否吃指针由画布规则决定
67
+ @include e(body) {
68
+ height: 100%;
69
+ }
70
+ @include e(handle) {
71
+ position: absolute;
72
+ display: none;
73
+ width: 6px;
74
+ height: 6px;
75
+ border-radius: 50%;
76
+ pointer-events: none;
77
+ z-index: 2;
78
+ @include bg_color_primary;
79
+ }
80
+ // 悬浮切换图标:规格同宿主操作条按钮(宿主的 .component-operate__item 是各自文件里的非 scoped 样式,这里自带一份不依赖它);
81
+ // 悬浮中反色高亮(白底蓝标)表示开关状态
82
+ @include e(float) {
83
+ display: block;
84
+ width: 20px;
85
+ font-size: var(--epx-font-size-base);
86
+ line-height: 20px;
87
+ text-align: center;
88
+ color: #fff;
89
+ cursor: pointer;
90
+ border-radius: 2px;
91
+ @include when(on) {
92
+ color: #0874E7;
93
+ background: #fff;
94
+ }
95
+ }
96
+ // 操作条:避开 gridstack 的 ne 角把手(内容盒右上 20×20)与 e 边把手(右侧 10px),并压过把手的 z-index(100 / 101);
97
+ // 它同时是 draggable.cancel 选择器,按钮上按下不会起手拖拽
98
+ @include e(operates) {
99
+ position: absolute;
100
+ top: 24px;
101
+ right: 12px;
102
+ display: none;
103
+ align-items: center;
104
+ gap: 4px;
105
+ padding: 0 4px;
106
+ background: #0874E7;
107
+ border-radius: 4px;
108
+ z-index: 200;
109
+ }
110
+ }
111
+ .epx-dashboard-design-item__handle--n { top: -3px; left: 50%; margin-left: -3px; }
112
+ .epx-dashboard-design-item__handle--s { bottom: -3px; left: 50%; margin-left: -3px; }
113
+ .epx-dashboard-design-item__handle--e { right: -3px; top: 50%; margin-top: -3px; }
114
+ .epx-dashboard-design-item__handle--w { left: -3px; top: 50%; margin-top: -3px; }
115
+ .epx-dashboard-design-item__handle--ne { top: -3px; right: -3px; }
116
+ .epx-dashboard-design-item__handle--nw { top: -3px; left: -3px; }
117
+ .epx-dashboard-design-item__handle--se { bottom: -3px; right: -3px; }
118
+ .epx-dashboard-design-item__handle--sw { bottom: -3px; left: -3px; }
119
+ // gridstack 把手只保留命中区(几何用库默认值),去掉库自带的角把手箭头图标与旋转;视觉由上面的圆点负责
120
+ .epx-dashboard-design-grid .grid-stack-item > .ui-resizable-handle {
121
+ background-image: none;
122
+ transform: none;
123
+ }
124
+ </style>
package/src/Div.vue ADDED
@@ -0,0 +1,419 @@
1
+ <template>
2
+ <div ref="container" :class="['container','epx-dashboard-div', `epx-dashboard-div--${compTheme}`,{ 'drag-element': isDragElement }, { 'is-design': mode==='design'}]" :style="style">
3
+ <div :class="['epx-chart__header', {'epx-chart__header--no-icon': $configs.iconFlag===false,}]" :style="titleStyle" v-if="showTitle && titleText">
4
+ <div class="epx-chart__header-left" :style="{color:$configs.iconColor}">
5
+ <template v-if="$configs.iconFlag!==false" >
6
+ <i v-if="$configs.icon" :style="$configs.textStyle" class="epx-chart__header-icon--icon" :class="$configs.icon || 'epx-icon-kapianmorentubiao'"></i>
7
+ <span v-else class="epx-chart__header-icon"></span>
8
+ </template>
9
+ <span class="epx-chart__title" :style="$configs.textStyle">{{titleText}}</span>
10
+ </div>
11
+ </div>
12
+ <!-- 运行态:headless 网格,不依赖布局库;移动端(真机 / 手机框预览)子项按堆叠重排(containerGridMixin.mobileItems) -->
13
+ <epx-dashboard-grid
14
+ v-if="mode !== 'design'"
15
+ class="epx-dashboard-div__grid"
16
+ :items="mobileItems(items)"
17
+ :grid="gridParams"
18
+ :load-way="loadWay"
19
+ :body-height="bodyHeight"
20
+ scroll-root-selector=".epx-dashboard__container, .dashboard-container">
21
+ <template #item="{ item }">
22
+ <div class="drag-component">
23
+ <component :is="'epx-'+ item.contentType" :ref="`component_${item.i}`"
24
+ class="epx-dashboard__component"
25
+ is-dashboard
26
+ :key="item.id"
27
+ :comp-id="item.id"
28
+ :comp-title="item.title"
29
+ :comp-style="item.style"
30
+ :comp-configs="getCompConfigs(item, item.id)"
31
+ :comp-grid="childGrid(item)"
32
+ :chart-cache="chartCache"
33
+ :mobile-appearance="mobileStacked ? item.mobileAppearance : undefined"
34
+ :items="item.items"
35
+ :item-configs="itemConfigs"
36
+ :theme="theme" ></component>
37
+ </div>
38
+ </template>
39
+ </epx-dashboard-grid>
40
+ <!-- 设计态:子网格(gridstack 封装)挂在页面级根实例下,跨网格拖入 / 拖出由 transfer 事件搬数据。
41
+ 内容区单独一层:flex 撑满、内容超高时容器内滚动,其可视高度传给子网格做 min-height(指针到网格底部才不触发库的「离开网格」) -->
42
+ <div v-else ref="designBody" class="epx-dashboard-div__design-body">
43
+ <epx-dashboard-design-grid
44
+ class="epx-dashboard-div__design-grid"
45
+ :sub-grid-of="epxDashboardDesignGrid"
46
+ :items="items"
47
+ :grid="gridParams"
48
+ :body-height="bodyHeight"
49
+ :min-height="designMinHeight"
50
+ @change="onDesignChange"
51
+ @transfer-out="onTransferOut"
52
+ @transfer-in="onTransferIn">
53
+ <template #item="{ item }">
54
+ <epx-dashboard-design-item :item="item" :active="!!design && design.isActive(item)" :floatable="gridParams.mode !== 'float'" @select="openConfigPanel">
55
+ <!-- 渲染组件:嵌套容器同样进设计态(items / changeItems / open-config 的传法与页面级画布对容器项一致) -->
56
+ <div v-if="item.contentType && !reloadComponent[item.id]" class="drag-component">
57
+ <epx-component :name="item.contentType" :ref="`component_${item.i}`"
58
+ class="epx-dashboard__component"
59
+ is-dashboard
60
+ :key="item.id"
61
+ :comp-id="item.id"
62
+ :comp-title="item.title"
63
+ :comp-style="item.style"
64
+ :comp-configs="getCompConfigs(item, item.id)"
65
+ :comp-grid="childGrid(item)"
66
+ :comp-item="item"
67
+ :items="item.items"
68
+ :item-configs="itemConfigs"
69
+ :dashboard-id="dashboardId"
70
+ :all-component-list="allComponentList"
71
+ :theme="compTheme"
72
+ mode="design"
73
+ :mobile-appearance="mobileStacked ? item.mobileAppearance : undefined"
74
+ @changeItems="v => changeSubItems(item, v)"
75
+ @open-config="(...args) => $emit('open-config', ...args)"></epx-component>
76
+ </div>
77
+ <span v-else>{{item.i}}</span>
78
+ <template #operates>
79
+ <i class="component-operate__item el-icon-edit-outline" v-if="item.contentType==='report-chart'" @click="designReport(item)"></i>
80
+ <i class="component-operate__item el-icon-setting" @click="editComponent(item)"></i>
81
+ <!-- <i class="component-operate__item el-icon-copy-document" v-if="item.contentType==='report-chart'" @click="$emit('copy-report', item)"></i> -->
82
+ <el-dropdown class="componnet-dropdown" @command="v=>deleteComponent(item, v)" v-if="item.contentType==='report-chart'">
83
+ <i class="component-operate__item el-icon-delete"></i>
84
+ <template v-slot:dropdown><el-dropdown-menu>
85
+ <el-dropdown-item command="only">仅从仪表盘中删除</el-dropdown-item>
86
+ <el-dropdown-item command="all">从仪表盘和报表库中删除</el-dropdown-item>
87
+ </el-dropdown-menu></template>
88
+ </el-dropdown>
89
+ <i class="component-operate__item el-icon-delete" v-else @click="deleteComponent(item)"></i>
90
+ </template>
91
+ </epx-dashboard-design-item>
92
+ </template>
93
+ </epx-dashboard-design-grid>
94
+ </div>
95
+ </div>
96
+ </template>
97
+
98
+ <script>
99
+ import { defineAsyncComponent } from 'vue'
100
+ import { normalizeGridItem } from './layout-util'
101
+ import DashboardGrid from './DashboardGrid.vue'
102
+ import { containerGridMixin } from './grid/mixins'
103
+ export default {
104
+ name: 'epx-dashboard-div',
105
+ // 运行态网格参数 / 门控策略 / 嵌套容器网格透传(compGrid prop、gridParams / loadWay / childGrid)来自 containerGridMixin
106
+ mixins: [containerGridMixin],
107
+ components: {
108
+ [DashboardGrid.name]: DashboardGrid,
109
+ // 设计态画布(gridstack 封装)与项 chrome 按需加载,运行态不拉 gridstack
110
+ 'epx-dashboard-design-grid': defineAsyncComponent(() => import('./DesignGrid.vue')),
111
+ 'epx-dashboard-design-item': defineAsyncComponent(() => import('./DesignItem.vue'))
112
+ },
113
+ inject: {
114
+ // 父网格 api(页面级画布或上级容器的 DesignGrid 提供):本容器的子网格挂到它下面;独立挂载时为空,子网格退化为独立网格
115
+ epxDashboardDesignGrid: { default: null },
116
+ // 设计态共享选中态(页面级画布提供):容器内与页面级只有一个选中项
117
+ epxDashboardDesign: { default: null }
118
+ },
119
+ props: {
120
+ theme: String,
121
+ compId: String,
122
+ compStyle: {
123
+ type: Object,
124
+ default () {
125
+ return {}
126
+ }
127
+ },
128
+ compItem: {
129
+ type: Object,
130
+ default () {
131
+ return {}
132
+ }
133
+ },
134
+ compConfigs: {
135
+ type: Object,
136
+ default () {
137
+ return {}
138
+ }
139
+ },
140
+ items: {
141
+ type: Array,
142
+ default () {
143
+ return []
144
+ }
145
+ },
146
+ itemConfigs: {
147
+ type: Object,
148
+ default () {
149
+ return {}
150
+ }
151
+ },
152
+ mode: String,
153
+ allComponentList: Array,
154
+ chartCache: Boolean,
155
+ dashboardId: String
156
+ },
157
+ data () {
158
+ return {
159
+ reloadComponent: {},
160
+ isDragElement: false,
161
+ // 设计态内容区可视高度(壳内容盒高 − 标题栏高),传给子网格做 min-height
162
+ designMinHeight: 0
163
+ }
164
+ },
165
+ computed: {
166
+ compTheme () {
167
+ return this.$configs.theme || this.theme
168
+ },
169
+ getCompConfigs () {
170
+ return (component) => {
171
+ // 如果是页面 查找页面默认配置
172
+ const { configs = {}, id, title, contentType, ...otherConfigs } = component
173
+ const compId = this.getDashboardLegacy() && this.dashboardId ? this.dashboardId + '-' + id : id
174
+ const itemConfig = this.itemConfigs[compId] || {}
175
+ const _configs = { ...otherConfigs, ...configs, ...itemConfig }
176
+ if (title) {
177
+ _configs.componentName = title
178
+ }
179
+ return _configs
180
+ }
181
+ },
182
+ $configs () {
183
+ const itemConfig = this.itemConfigs[this.compId] || {}
184
+ const _configs = { ...this.compConfigs, ...itemConfig }
185
+ return _configs
186
+ },
187
+ style () {
188
+ const style = { ...this.compStyle }
189
+ if (style.backgroundImage) {
190
+ style.backgroundImage = style.backgroundImage.replace(/url\s*\(([\w+-/.\s]+)\)/g, (str, $1) => {
191
+ return `url(${this.$util.getFileUrl($1)})`
192
+ })
193
+ }
194
+ return style
195
+ },
196
+ showTitle () {
197
+ const { showTitle } = this.$configs
198
+ return showTitle
199
+ },
200
+ title () {
201
+ const { title = '' } = this.$configs
202
+ return title
203
+ },
204
+ titleStyle () {
205
+ const { align, backgroundStyle, backgroundColor, backgroundImage } = this.titleFontStyle || {}
206
+ const style = {}
207
+ if (align) {
208
+ style.justifyContent = align
209
+ }
210
+ if (backgroundStyle === 'image') {
211
+ style.backgroundImage = `url(${this.$util.getFileUrl(backgroundImage)})`
212
+ } else if (backgroundColor) {
213
+ style.backgroundColor = backgroundColor
214
+ }
215
+ return style
216
+ },
217
+ titleText () {
218
+ return this.$configs.title
219
+ },
220
+ design () {
221
+ return this.epxDashboardDesign
222
+ },
223
+ activeComponent () {
224
+ return this.design ? this.design.state.activeItem : null
225
+ }
226
+ },
227
+ watch: {
228
+ items: {
229
+ handler (v = []) {
230
+ for (const item of v) {
231
+ if (!item) continue
232
+ // i 与 id 兜底(i 不再持久化)+ x/y/w/h 必须是数字(网格引擎按数值布局)
233
+ normalizeGridItem(item)
234
+ }
235
+ },
236
+ deep: true,
237
+ immediate: true
238
+ }
239
+ },
240
+ methods: {
241
+ addGridItem (v) {
242
+ this.$emit('changeItems', [...this.items, v])
243
+ },
244
+ // 库改了子项坐标(拖拽 / 缩放 / 推挤 / 首次对账):画布已就地写回,只需通知上层保存
245
+ onDesignChange () {
246
+ this.$emit('changeItems', [...this.items])
247
+ },
248
+ // 跨网格搬出(子项被拖到页面或别的容器):源侧必须同步移出,否则子网格下一次同步会把壳重新加回来
249
+ onTransferOut (item) {
250
+ this.$emit('changeItems', this.items.filter(i => i.id !== item.id))
251
+ },
252
+ // 跨网格搬入:画布已按本网格换算过 w/h
253
+ onTransferIn (item) {
254
+ this.$emit('changeItems', [...this.items, item])
255
+ },
256
+ // 嵌套容器的子项变化:替换该容器项对象后上报(与页面级画布 changeSubItems 同手法)
257
+ changeSubItems (item, v) {
258
+ const index = this.items.findIndex(i => i.id === item.id)
259
+ if (index === -1) return
260
+ const items = [...this.items]
261
+ items.splice(index, 1, { ...items[index], items: v })
262
+ this.$emit('changeItems', items)
263
+ },
264
+ openConfigPanel (component) {
265
+ if (this.design) this.design.select(component, this.compId)
266
+ },
267
+ // 设计态内容区可视高度:壳内容盒高 − 标题栏高(内容区是 flex 撑满的一层,量它即可;无标题时等于壳内容盒高)
268
+ measureDesignHeight () {
269
+ if (this.$refs.designBody) this.designMinHeight = this.$refs.designBody.clientHeight
270
+ },
271
+ designReport (component) {
272
+ const configs = this.getCompConfigs(component, component.id)
273
+ this.goPage({
274
+ component: 'report-design',
275
+ params: {
276
+ id: configs.id,
277
+ callback: () => {
278
+ this.reloadComponent[component.id] = true
279
+ this.$nextTick(() => {
280
+ this.reloadComponent[component.id] = false
281
+ })
282
+ }
283
+ }
284
+
285
+ })
286
+ },
287
+ editComponent (activeComponent) {
288
+ const componentInfo = this.allComponentList.find(item => item.CODE === activeComponent.contentType) || { CODE: activeComponent.contentType }
289
+ const configs = this.getCompConfigs(activeComponent, activeComponent.id)
290
+ this.$emit('open-config', activeComponent.id, componentInfo, configs)
291
+ },
292
+ deleteComponent (component, command) {
293
+ const activeComponent = component || this.activeComponent
294
+ const index = this.items.findIndex(item => item.id === activeComponent.id)
295
+ if (activeComponent.contentType === 'report-chat' && command === 'all') {
296
+ const configs = this.getCompConfigs(activeComponent, activeComponent.id)
297
+ this.$http.removeMulti({
298
+ entityId: 'FM_REPORT_BASE',
299
+ ids: configs.id
300
+ })
301
+ }
302
+ if (index !== -1) {
303
+ const items = [...this.items]
304
+ items.splice(index, 1)
305
+ this.$emit('changeItems', items)
306
+ // 被删的正是选中项:清掉共享选中态,操作条 / 属性面板不再指向已删除的项
307
+ if (this.design && this.design.isActive(activeComponent)) this.design.clear()
308
+ }
309
+ }
310
+ },
311
+ mounted () {
312
+ if (this.mode !== 'design') return
313
+ this.measureDesignHeight()
314
+ if (typeof ResizeObserver !== 'undefined' && this.$refs.designBody) {
315
+ this.designResizeObserver = new ResizeObserver(() => this.measureDesignHeight())
316
+ this.designResizeObserver.observe(this.$refs.designBody)
317
+ }
318
+ },
319
+ beforeUnmount () {
320
+ if (this.designResizeObserver) this.designResizeObserver.disconnect()
321
+ }
322
+ }
323
+ </script>
324
+
325
+ <style lang="scss" scoped>
326
+ div.container {
327
+ height: 100%;
328
+ pointer-events: all!important;
329
+ }
330
+ // 设计态:标题栏 + 内容区纵向排布,内容区撑满剩余高度、内容超高时容器内滚动(运行态布局不受影响)。
331
+ // 容器没有专门的起手区:按在子网格空白处即拖容器(DesignGrid 只对锁定子项打「按下」标记)
332
+ div.container.is-design {
333
+ display: flex;
334
+ flex-direction: column;
335
+ > .epx-chart__header {
336
+ flex: none;
337
+ }
338
+ }
339
+ .epx-dashboard-div__design-body {
340
+ flex: 1;
341
+ min-height: 0;
342
+ overflow: auto;
343
+ }
344
+ .epx-dashboard-div__grid {
345
+ min-height: 100%;
346
+ }
347
+ .drag-component {
348
+ width: 100%;
349
+ height: 100%;
350
+ overflow: auto;
351
+ }
352
+ .is-design {
353
+ .drag-component {
354
+ >* {
355
+ pointer-events: none;
356
+ }
357
+ }
358
+ }
359
+ // 操作条按钮:横排在 epx-dashboard-design-item__operates 里
360
+ .component-operate__item {
361
+ display: block;
362
+ font-size: var(--epx-font-size-base);
363
+ color: #fff;
364
+ line-height: 20px;
365
+ text-align: center;
366
+ cursor: pointer;
367
+ margin: 2px 0;
368
+ }
369
+ .componnet-dropdown {
370
+ .component-operate__item {
371
+ width: 20px;
372
+ margin-top: 0;
373
+ }
374
+ }
375
+ </style>
376
+ <style lang="scss">
377
+ @include b(dashboard-div) {
378
+ background: var(--epx-bg-card);
379
+ @include m(dark) {
380
+ background: rgba(255, 255, 255, 0.1);
381
+ @include e(title) {
382
+ color: rgba(255, 255, 255, 0.8);
383
+ }
384
+ @include e(num) {
385
+ color: #fff;
386
+ >font {
387
+ color: #fff;
388
+ }
389
+ }
390
+ }
391
+ @include m(technology) {
392
+ background: rgba(255, 255, 255, 0.1);
393
+ backdrop-filter: blur(4px);
394
+ position: relative;
395
+ // 设计态不用毛玻璃:backdrop-filter 让容器根自成堆叠上下文,子网格里的选中壳(R31 的 z 600)会被关在容器里、压不过页面级悬浮层
396
+ &.is-design {
397
+ backdrop-filter: none;
398
+ }
399
+ &::before {
400
+ content: '';
401
+ position: absolute;
402
+ top: 0;
403
+ display: block;
404
+ width: 100%;
405
+ height: 1px;
406
+ background: linear-gradient(90deg, rgba(45, 164, 247, 0) 0%, #2DA4F7 50%, rgba(45, 164, 247, 0) 100%);
407
+ opacity: 0.5;
408
+ }
409
+ .epx-chart__header-icon {
410
+ width: 40px;
411
+ background: url('@/assets/images/dashboard/theme_header.png') no-repeat center;
412
+ background-size: 100% 100%;
413
+ &::after {
414
+ display: none;
415
+ }
416
+ }
417
+ }
418
+ }
419
+ </style>