@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.
- package/API.md +80 -0
- package/README.md +41 -2
- package/dist/_virtual/_plugin-vue_export-helper.js +10 -0
- package/dist/_virtual/preload-helper.js +88 -0
- package/dist/assets/technology_bg-oWIS2awO.jpg +0 -0
- package/dist/src/Dashboard.js +333 -0
- package/dist/src/DashboardGrid.js +183 -0
- package/dist/src/DesignGrid.js +712 -0
- package/dist/src/DesignItem.js +64 -0
- package/dist/src/Div.js +409 -0
- package/dist/src/Header.js +386 -0
- package/dist/src/Preview.js +28 -0
- package/dist/src/Tabs.js +469 -0
- package/dist/src/Title.js +102 -0
- package/dist/src/Widget.js +267 -0
- package/dist/src/chart-request-scope.js +33 -0
- package/dist/src/design-target.js +58 -0
- package/dist/src/grid/compact.js +69 -0
- package/dist/src/grid/compat/GridItem.js +36 -0
- package/dist/src/grid/compat/GridLayout.js +77 -0
- package/dist/src/grid/designState.js +22 -0
- package/dist/src/grid/engines.js +26 -0
- package/dist/src/grid/geometry.js +45 -0
- package/dist/src/grid/gridstackParams.js +70 -0
- package/dist/src/grid/mixins.js +109 -0
- package/dist/src/grid/model.js +224 -0
- package/dist/src/grid/useViewportGate.js +56 -0
- package/dist/src/index.js +27 -0
- package/dist/src/layout-util.js +109 -0
- package/dist/src/mobile-design.js +51 -0
- package/dist/src/theme-style.js +134 -0
- package/dist/style.css +1009 -0
- package/package.json +86 -3
- package/src/Dashboard.vue +459 -0
- package/src/DashboardGrid.vue +189 -0
- package/src/DesignGrid.vue +851 -0
- package/src/DesignItem.vue +124 -0
- package/src/Div.vue +419 -0
- package/src/Header.vue +501 -0
- package/src/Preview.vue +30 -0
- package/src/Tabs.vue +484 -0
- package/src/Title.vue +107 -0
- package/src/Widget.vue +354 -0
- package/src/chart-request-scope.js +22 -0
- package/src/design-target.js +55 -0
- package/src/grid/compact.js +81 -0
- package/src/grid/compat/GridItem.vue +30 -0
- package/src/grid/compat/GridLayout.vue +76 -0
- package/src/grid/designState.js +17 -0
- package/src/grid/engines.js +22 -0
- package/src/grid/geometry.js +55 -0
- package/src/grid/gridstackParams.js +72 -0
- package/src/grid/mixins.js +114 -0
- package/src/grid/model.js +259 -0
- package/src/grid/useViewportGate.js +57 -0
- package/src/index.js +28 -0
- package/src/layout-util.js +157 -0
- package/src/mobile-design.js +50 -0
- package/src/theme-style.js +185 -0
- package/types/div.d.ts +1 -0
- package/types/index.d.ts +7 -0
- package/types/tabs.d.ts +1 -0
- package/types/title.d.ts +1 -0
- package/types/widget.d.ts +1 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import '../style.css';
|
|
2
|
+
import DashboardGrid from "./DashboardGrid.js";
|
|
3
|
+
import chartRequestScope from "./chart-request-scope.js";
|
|
4
|
+
import { evalWith } from "@epxing/utils/src/eval-with";
|
|
5
|
+
import component from "@epxing/core/src/mixins/component";
|
|
6
|
+
import { pageGridMixin } from "./grid/mixins.js";
|
|
7
|
+
import { stackMobileLayout } from "./layout-util.js";
|
|
8
|
+
import { indexDashboardItems, resolveMobileItem } from "./mobile-design.js";
|
|
9
|
+
import { getPageGrid, isLegacyConfig } from "./grid/model.js";
|
|
10
|
+
import { mapState } from "vuex";
|
|
11
|
+
import { resolveComponent, openBlock, createElementBlock, normalizeStyle, normalizeClass, createElementVNode, createVNode, withCtx, mergeProps, createCommentVNode } from "vue";
|
|
12
|
+
/* empty css */
|
|
13
|
+
import _export_sfc from "../_virtual/_plugin-vue_export-helper.js";
|
|
14
|
+
function refesh() {
|
|
15
|
+
this.resolveChartRequestBroker().invalidate();
|
|
16
|
+
this._epxWalkChildren((child) => {
|
|
17
|
+
if (child.$options.$componentType === "component" && child.$refesh) child.$refesh();
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const _sfc_main = {
|
|
21
|
+
name: "epx-dashboard-widget",
|
|
22
|
+
mixins: [component, pageGridMixin, chartRequestScope],
|
|
23
|
+
components: { [DashboardGrid.name]: DashboardGrid },
|
|
24
|
+
$componentType: "component",
|
|
25
|
+
navConfigs: {
|
|
26
|
+
flag: false
|
|
27
|
+
},
|
|
28
|
+
style: {},
|
|
29
|
+
layout: "grid",
|
|
30
|
+
// 组件列表
|
|
31
|
+
items: [],
|
|
32
|
+
// 组件配置
|
|
33
|
+
itemConfigs: {},
|
|
34
|
+
props: {
|
|
35
|
+
inline: Boolean,
|
|
36
|
+
routeKey: [String, Number],
|
|
37
|
+
routes: {
|
|
38
|
+
type: Array,
|
|
39
|
+
default() {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
data() {
|
|
45
|
+
return {
|
|
46
|
+
headerTitle: null,
|
|
47
|
+
pageParams: {},
|
|
48
|
+
model: {},
|
|
49
|
+
// 组件全局data
|
|
50
|
+
pageEvents: {},
|
|
51
|
+
searchScopeParams: []
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
computed: {
|
|
55
|
+
...mapState({
|
|
56
|
+
showMenu: (state) => state.config.showMenu,
|
|
57
|
+
showAppNav: (state) => state.config.showAppNav
|
|
58
|
+
}),
|
|
59
|
+
pageTitle() {
|
|
60
|
+
return this.headerTitle || this.page && this.page.title || this.meta && this.meta.name;
|
|
61
|
+
},
|
|
62
|
+
components() {
|
|
63
|
+
const { items = [], mItems } = this.$component;
|
|
64
|
+
let list;
|
|
65
|
+
if (this.mobileDashboard) {
|
|
66
|
+
list = items.filter((item) => !item.mobileHidden);
|
|
67
|
+
} else if (this.$isMobile) {
|
|
68
|
+
if (mItems && mItems.length) {
|
|
69
|
+
const pcIndex = indexDashboardItems(items);
|
|
70
|
+
list = mItems.filter((c) => !c.mobileHidden && pcIndex.has(String(c.id))).map((c) => resolveMobileItem(c, pcIndex));
|
|
71
|
+
} else {
|
|
72
|
+
const { mobileAutoStack = true } = this.$configs;
|
|
73
|
+
list = mobileAutoStack ? stackMobileLayout(items, 48) : items;
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
list = items;
|
|
77
|
+
}
|
|
78
|
+
list.forEach((item) => {
|
|
79
|
+
if (item && item.i == null && item.id != null) item.i = item.id;
|
|
80
|
+
});
|
|
81
|
+
return list;
|
|
82
|
+
},
|
|
83
|
+
theme() {
|
|
84
|
+
const { theme } = this.$configs;
|
|
85
|
+
return theme;
|
|
86
|
+
},
|
|
87
|
+
// Widget 原本没有移动端收紧间距的分支(与页面版 Dashboard 不同),按「旧配置像素一致」保留原语义,覆盖 pageGridMixin 的 gridParams
|
|
88
|
+
gridParams() {
|
|
89
|
+
const grid = getPageGrid(this.$configs);
|
|
90
|
+
return this.mobileDashboard ? { ...grid, gap: [8, 8], padding: null } : grid;
|
|
91
|
+
},
|
|
92
|
+
colAttribute() {
|
|
93
|
+
return (component2 = {}) => {
|
|
94
|
+
const { xsspan = 24, ...other } = component2;
|
|
95
|
+
const colAttribute = {
|
|
96
|
+
...other,
|
|
97
|
+
xs: {
|
|
98
|
+
span: xsspan
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
delete colAttribute.id;
|
|
102
|
+
delete colAttribute.style;
|
|
103
|
+
delete colAttribute.title;
|
|
104
|
+
return colAttribute;
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
pageStyle() {
|
|
108
|
+
const { layout = "grid", direction = "row", justify = "flex-start", align = "top" } = this.$configs;
|
|
109
|
+
if (layout === "flex") {
|
|
110
|
+
return { display: "flex", "flex-direction": direction, "justify-content": justify, "align-items": align };
|
|
111
|
+
} else {
|
|
112
|
+
return {};
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
componentStyle() {
|
|
116
|
+
return (component2) => {
|
|
117
|
+
const { layout } = this.$configs;
|
|
118
|
+
const { flex } = component2;
|
|
119
|
+
const compoentStyle = {};
|
|
120
|
+
if (layout === "flex" && flex) {
|
|
121
|
+
compoentStyle.flex = 1;
|
|
122
|
+
}
|
|
123
|
+
return compoentStyle;
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
scopeParams() {
|
|
127
|
+
const { globalScopeParams: configScopeParams = [] } = this.$configs;
|
|
128
|
+
const { scopeParams: queryScopeParams = [] } = this.$configs;
|
|
129
|
+
const _scopeParams = this.$_.cloneDeep([...configScopeParams, ...queryScopeParams, ...this.searchScopeParams]);
|
|
130
|
+
const that = this;
|
|
131
|
+
for (const scopeKey in _scopeParams) {
|
|
132
|
+
const scope = _scopeParams[scopeKey];
|
|
133
|
+
if (!scope) continue;
|
|
134
|
+
let values = scope.values;
|
|
135
|
+
if (/\$\[(.+)\]/.test(values)) {
|
|
136
|
+
values = values.replace(/\$\[(.+)\]/, (match, key) => {
|
|
137
|
+
try {
|
|
138
|
+
return evalWith(`that.${key}`, { that }) || evalWith(`that.$query.${key}`, { that }) || evalWith(`that.$tab.${key}`, { that }) || "";
|
|
139
|
+
} catch (error) {
|
|
140
|
+
return scope.values;
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
if (/#\{(.+)\}/.test(values)) {
|
|
145
|
+
values = values.replace(/#\{(.+)\}/, (match, key) => {
|
|
146
|
+
try {
|
|
147
|
+
return evalWith(`that.$query.${key}`, { that }) || evalWith(`that.$tab.${key}`, { that }) || "";
|
|
148
|
+
} catch (error) {
|
|
149
|
+
return scope.values;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
scope.values = values;
|
|
154
|
+
}
|
|
155
|
+
return _scopeParams;
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
watch: {
|
|
159
|
+
pageTitle: {
|
|
160
|
+
handler(v) {
|
|
161
|
+
this.$emit("change-route", this.routeKey, { title: v });
|
|
162
|
+
},
|
|
163
|
+
immediate: true
|
|
164
|
+
},
|
|
165
|
+
scopeParams: {
|
|
166
|
+
deep: true,
|
|
167
|
+
handler(v) {
|
|
168
|
+
this.pageParams.scopeParams = v;
|
|
169
|
+
},
|
|
170
|
+
immediate: true
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
provide() {
|
|
174
|
+
const gridProvide = {
|
|
175
|
+
getMobileDashboard: () => this.mobileDashboard,
|
|
176
|
+
getDashboardLoadWay: () => this.loadWay,
|
|
177
|
+
getDashboardLegacy: () => isLegacyConfig(this.$configs)
|
|
178
|
+
};
|
|
179
|
+
if (this.isPage) {
|
|
180
|
+
return {
|
|
181
|
+
...gridProvide,
|
|
182
|
+
upPage: this,
|
|
183
|
+
pageId: this.page ? this.page.pageId : "",
|
|
184
|
+
// 当前页面id
|
|
185
|
+
pageMetaId: this.isPage ? this.$metaId || (this.page ? this.page.metaId : "") : null
|
|
186
|
+
// 当前页面对象id
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
return gridProvide;
|
|
190
|
+
},
|
|
191
|
+
methods: {
|
|
192
|
+
$refesh() {
|
|
193
|
+
refesh.apply(this);
|
|
194
|
+
},
|
|
195
|
+
openFullScreen() {
|
|
196
|
+
if (this.$refs.dashboard.requestFullscreen) {
|
|
197
|
+
this.$refs.dashboard.requestFullscreen();
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
search(v) {
|
|
201
|
+
this.searchScopeParams = v;
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
created() {
|
|
205
|
+
const grid = this.gridParams;
|
|
206
|
+
if (grid.rowHeight === "auto" || grid.gap === "auto") {
|
|
207
|
+
window.addEventListener("resize", this.updateBodyHeight);
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
beforeUnmount() {
|
|
211
|
+
window.removeEventListener("resize", this.updateBodyHeight);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
const _hoisted_1 = {
|
|
215
|
+
key: 0,
|
|
216
|
+
class: "dashboard-component"
|
|
217
|
+
};
|
|
218
|
+
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
219
|
+
const _component_epx_component = resolveComponent("epx-component");
|
|
220
|
+
const _component_epx_dashboard_grid = resolveComponent("epx-dashboard-grid");
|
|
221
|
+
return openBlock(), createElementBlock("div", {
|
|
222
|
+
class: normalizeClass(["dashboard", "dashboard--" + $options.theme, { "dashboard--hidden-scroll-bar": _ctx.$configs.scrollBarHidden }]),
|
|
223
|
+
style: normalizeStyle(_ctx.$component.style),
|
|
224
|
+
ref: "container"
|
|
225
|
+
}, [
|
|
226
|
+
createElementVNode("div", {
|
|
227
|
+
ref: "dashboard",
|
|
228
|
+
class: "dashboard-container",
|
|
229
|
+
style: normalizeStyle(_ctx.mobileDashboard ? { width: "100%", maxWidth: (Number(_ctx.$configs.mobileCanvasWidth) || 375) + "px", margin: "0 auto", flex: "none" } : {})
|
|
230
|
+
}, [
|
|
231
|
+
createVNode(_component_epx_dashboard_grid, {
|
|
232
|
+
items: $options.components,
|
|
233
|
+
grid: $options.gridParams,
|
|
234
|
+
"load-way": _ctx.loadWay,
|
|
235
|
+
"body-height": _ctx.bodyHeight,
|
|
236
|
+
"scroll-root-selector": ".dashboard-container"
|
|
237
|
+
}, {
|
|
238
|
+
item: withCtx(({ item: component2 }) => [
|
|
239
|
+
component2.contentType ? (openBlock(), createElementBlock("div", _hoisted_1, [
|
|
240
|
+
createVNode(_component_epx_component, mergeProps(_ctx.isPage ? _ctx.$forwardAttrs(_ctx.$attrs) : {}, {
|
|
241
|
+
class: "component epx-dashboard__component",
|
|
242
|
+
name: component2.contentType,
|
|
243
|
+
"is-dashboard": "",
|
|
244
|
+
"content-type": component2.contentType,
|
|
245
|
+
"dashboard-id": _ctx.$component.id,
|
|
246
|
+
"comp-id": _ctx.getComponentId(component2),
|
|
247
|
+
"comp-title": component2.title,
|
|
248
|
+
items: component2.items,
|
|
249
|
+
itemConfigs: _ctx.$component.itemConfigs,
|
|
250
|
+
"comp-style": component2.style,
|
|
251
|
+
"comp-configs": component2.configs,
|
|
252
|
+
"mobile-appearance": _ctx.mobileRendering ? component2.mobileAppearance : void 0,
|
|
253
|
+
"comp-grid": _ctx.containerGrid(component2),
|
|
254
|
+
"chart-cache": _ctx.$configs.chartCache,
|
|
255
|
+
theme: $options.theme
|
|
256
|
+
}), null, 16, ["name", "content-type", "dashboard-id", "comp-id", "comp-title", "items", "itemConfigs", "comp-style", "comp-configs", "mobile-appearance", "comp-grid", "chart-cache", "theme"])
|
|
257
|
+
])) : createCommentVNode("", true)
|
|
258
|
+
]),
|
|
259
|
+
_: 1
|
|
260
|
+
}, 8, ["items", "grid", "load-way", "body-height"])
|
|
261
|
+
], 4)
|
|
262
|
+
], 6);
|
|
263
|
+
}
|
|
264
|
+
const DashboardWidget = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-d068f7d2"]]);
|
|
265
|
+
export {
|
|
266
|
+
DashboardWidget as default
|
|
267
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import '../style.css';
|
|
2
|
+
import { markRaw } from "vue";
|
|
3
|
+
import { createChartRequestBroker } from "@epxing/utils/src/chart-request-broker";
|
|
4
|
+
const chartRequestScope = {
|
|
5
|
+
inject: { getParentChartRequestBroker: { from: "getChartRequestBroker", default: null } },
|
|
6
|
+
data() {
|
|
7
|
+
return { ownedChartRequestBroker: null };
|
|
8
|
+
},
|
|
9
|
+
provide() {
|
|
10
|
+
return { getChartRequestBroker: () => this.resolveChartRequestBroker() };
|
|
11
|
+
},
|
|
12
|
+
watch: {
|
|
13
|
+
"$route.query.pageId"() {
|
|
14
|
+
var _a;
|
|
15
|
+
(_a = this.ownedChartRequestBroker) == null ? void 0 : _a.dispose();
|
|
16
|
+
this.ownedChartRequestBroker = null;
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
methods: {
|
|
20
|
+
resolveChartRequestBroker() {
|
|
21
|
+
if (this.getParentChartRequestBroker) return this.getParentChartRequestBroker();
|
|
22
|
+
if (!this.ownedChartRequestBroker) this.ownedChartRequestBroker = markRaw(createChartRequestBroker());
|
|
23
|
+
return this.ownedChartRequestBroker;
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
beforeUnmount() {
|
|
27
|
+
var _a;
|
|
28
|
+
(_a = this.ownedChartRequestBroker) == null ? void 0 : _a.dispose();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
export {
|
|
32
|
+
chartRequestScope as default
|
|
33
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import '../style.css';
|
|
2
|
+
import { stackMobileLayout } from "./layout-util.js";
|
|
3
|
+
const DEFAULT_DASHBOARD_THEME = "light";
|
|
4
|
+
function getDashboardDesignTarget(config = {}) {
|
|
5
|
+
return config.designTarget === "mobile" ? "mobile" : "pc";
|
|
6
|
+
}
|
|
7
|
+
function createDashboardConfig(config = {}, target = "pc") {
|
|
8
|
+
var _a;
|
|
9
|
+
const configuredColNum = Number((_a = config.grid) == null ? void 0 : _a.colNum);
|
|
10
|
+
const colNum = Number.isInteger(configuredColNum) && configuredColNum > 0 ? configuredColNum : (config.items || []).length ? 48 : 24;
|
|
11
|
+
const out = {
|
|
12
|
+
...config,
|
|
13
|
+
grid: { ...config.grid, colNum },
|
|
14
|
+
theme: config.theme || DEFAULT_DASHBOARD_THEME,
|
|
15
|
+
designTarget: getDashboardDesignTarget({ designTarget: target }),
|
|
16
|
+
...target === "mobile" ? { mobileCanvasWidth: 375, autoHeight: false, grid: { ...config.grid, colNum, rowHeight: 10, gap: [8, 8], padding: null } } : {}
|
|
17
|
+
};
|
|
18
|
+
if (target === "mobile") {
|
|
19
|
+
delete out.mItems;
|
|
20
|
+
const mobileItems = (items) => {
|
|
21
|
+
const list = JSON.parse(JSON.stringify(items || []));
|
|
22
|
+
for (const item of list) {
|
|
23
|
+
if (item.contentType === "dashboard-tabs") {
|
|
24
|
+
item.mobileExpand = false;
|
|
25
|
+
for (const tab of item.items || []) tab.items = mobileItems(tab.items);
|
|
26
|
+
} else if (item.contentType === "dashboard-div") {
|
|
27
|
+
item.mobileExpand = false;
|
|
28
|
+
item.items = mobileItems(item.items);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return stackMobileLayout(list, colNum, { keepStyle: true });
|
|
32
|
+
};
|
|
33
|
+
out.items = mobileItems(config.items);
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
function removeDashboardItem(items, id, configs) {
|
|
38
|
+
for (let i = 0; i < items.length; i++) {
|
|
39
|
+
const item = items[i];
|
|
40
|
+
if (String(item.id) === String(id)) {
|
|
41
|
+
const clear = (node) => {
|
|
42
|
+
delete configs[node.id];
|
|
43
|
+
for (const child of node.items || []) clear(child);
|
|
44
|
+
};
|
|
45
|
+
clear(item);
|
|
46
|
+
items.splice(i, 1);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
if (item.items && removeDashboardItem(item.items, id, configs)) return true;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
DEFAULT_DASHBOARD_THEME,
|
|
55
|
+
createDashboardConfig,
|
|
56
|
+
getDashboardDesignTarget,
|
|
57
|
+
removeDashboardItem
|
|
58
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import '../../style.css';
|
|
2
|
+
function collides(l1, l2) {
|
|
3
|
+
if (l1 === l2) return false;
|
|
4
|
+
if (l1.x + l1.w <= l2.x) return false;
|
|
5
|
+
if (l1.x >= l2.x + l2.w) return false;
|
|
6
|
+
if (l1.y + l1.h <= l2.y) return false;
|
|
7
|
+
if (l1.y >= l2.y + l2.h) return false;
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
function getFirstCollision(layout, item) {
|
|
11
|
+
for (let i = 0, len = layout.length; i < len; i++) {
|
|
12
|
+
if (collides(layout[i], item)) return layout[i];
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function sortLayoutItemsByRowCol(layout) {
|
|
16
|
+
return Array.from(layout).sort((a, b) => {
|
|
17
|
+
if (a.y === b.y && a.x === b.x) return 0;
|
|
18
|
+
if (a.y > b.y || a.y === b.y && a.x > b.x) return 1;
|
|
19
|
+
return -1;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function compactItem(compareWith, item, verticalCompact = true) {
|
|
23
|
+
if (verticalCompact) {
|
|
24
|
+
while (item.y > 0 && !getFirstCollision(compareWith, item)) item.y--;
|
|
25
|
+
}
|
|
26
|
+
let collision;
|
|
27
|
+
while (collision = getFirstCollision(compareWith, item)) item.y = collision.y + collision.h;
|
|
28
|
+
return item;
|
|
29
|
+
}
|
|
30
|
+
function cloneItem(item) {
|
|
31
|
+
return {
|
|
32
|
+
...item,
|
|
33
|
+
x: Number(item.x) || 0,
|
|
34
|
+
y: Number(item.y) || 0,
|
|
35
|
+
w: Math.max(1, Number(item.w) || 1),
|
|
36
|
+
h: Math.max(1, Number(item.h) || 1)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function compact(layout, verticalCompact = true) {
|
|
40
|
+
const source = Array.isArray(layout) ? layout.filter(Boolean) : [];
|
|
41
|
+
const out = new Array(source.length);
|
|
42
|
+
const indexOf = /* @__PURE__ */ new Map();
|
|
43
|
+
const flow = [];
|
|
44
|
+
source.forEach((item, idx) => {
|
|
45
|
+
const copy = cloneItem(item);
|
|
46
|
+
if (copy.float) {
|
|
47
|
+
out[idx] = copy;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
indexOf.set(copy, idx);
|
|
51
|
+
flow.push(copy);
|
|
52
|
+
});
|
|
53
|
+
const compareWith = flow.filter((item) => item.static);
|
|
54
|
+
for (const item of sortLayoutItemsByRowCol(flow)) {
|
|
55
|
+
if (!item.static) {
|
|
56
|
+
compactItem(compareWith, item, verticalCompact);
|
|
57
|
+
compareWith.push(item);
|
|
58
|
+
}
|
|
59
|
+
out[indexOf.get(item)] = item;
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
collides,
|
|
65
|
+
compact,
|
|
66
|
+
compactItem,
|
|
67
|
+
getFirstCollision,
|
|
68
|
+
sortLayoutItemsByRowCol
|
|
69
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import '../../../style.css';
|
|
2
|
+
import { rectStyle, itemRect } from "../geometry.js";
|
|
3
|
+
import { openBlock, createElementBlock, normalizeStyle, renderSlot } from "vue";
|
|
4
|
+
import _export_sfc from "../../../_virtual/_plugin-vue_export-helper.js";
|
|
5
|
+
const _sfc_main = {
|
|
6
|
+
// 组件 name 走仓库命名规范(epx- 前缀);全局注册名仍用 'GridItem'(见 index.js)
|
|
7
|
+
name: "epx-grid-compat-grid-item",
|
|
8
|
+
inheritAttrs: false,
|
|
9
|
+
props: {
|
|
10
|
+
x: { type: Number, default: 0 },
|
|
11
|
+
y: { type: Number, default: 0 },
|
|
12
|
+
w: { type: Number, default: 1 },
|
|
13
|
+
h: { type: Number, default: 1 },
|
|
14
|
+
i: { type: [String, Number], default: "" }
|
|
15
|
+
},
|
|
16
|
+
inject: { epxCompatGrid: { default: null } },
|
|
17
|
+
computed: {
|
|
18
|
+
style() {
|
|
19
|
+
if (!this.epxCompatGrid) return void 0;
|
|
20
|
+
const pos = this.epxCompatGrid.positions[String(this.i)] || { x: this.x, y: this.y, w: this.w, h: this.h };
|
|
21
|
+
return { position: "absolute", ...rectStyle(itemRect(pos, this.epxCompatGrid.resolved)) };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
26
|
+
return openBlock(), createElementBlock("div", {
|
|
27
|
+
class: "vgl-item vgl-item--static",
|
|
28
|
+
style: normalizeStyle($options.style)
|
|
29
|
+
}, [
|
|
30
|
+
renderSlot(_ctx.$slots, "default")
|
|
31
|
+
], 4);
|
|
32
|
+
}
|
|
33
|
+
const CompatGridItem = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
|
|
34
|
+
export {
|
|
35
|
+
CompatGridItem as default
|
|
36
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import '../../../style.css';
|
|
2
|
+
import { layoutHeight, resolveGrid } from "../geometry.js";
|
|
3
|
+
import { compact } from "../compact.js";
|
|
4
|
+
import { openBlock, createElementBlock, normalizeStyle, renderSlot } from "vue";
|
|
5
|
+
/* empty css */
|
|
6
|
+
import _export_sfc from "../../../_virtual/_plugin-vue_export-helper.js";
|
|
7
|
+
const _sfc_main = {
|
|
8
|
+
// 组件 name 走仓库命名规范(epx- 前缀);全局注册名仍用 'GridLayout'(见 index.js),
|
|
9
|
+
// 保证旧模板里直接写的 <grid-layout> 标签能解析到——标签解析走注册名,不走这里的 name
|
|
10
|
+
name: "epx-grid-compat-grid-layout",
|
|
11
|
+
inheritAttrs: false,
|
|
12
|
+
props: {
|
|
13
|
+
layout: { type: Array, default: () => [] },
|
|
14
|
+
colNum: { type: Number, default: 12 },
|
|
15
|
+
rowHeight: { type: Number, default: 150 },
|
|
16
|
+
margin: { type: Array, default: () => [10, 10] },
|
|
17
|
+
// 显式宽度(测试 / 无 ResizeObserver 环境);缺省由 ResizeObserver 测量
|
|
18
|
+
width: { type: Number, default: 0 }
|
|
19
|
+
},
|
|
20
|
+
provide() {
|
|
21
|
+
return { epxCompatGrid: this };
|
|
22
|
+
},
|
|
23
|
+
data() {
|
|
24
|
+
return { measuredWidth: 0 };
|
|
25
|
+
},
|
|
26
|
+
computed: {
|
|
27
|
+
resolved() {
|
|
28
|
+
const gap = [Number(this.margin[0]) || 0, Number(this.margin[1]) || 0];
|
|
29
|
+
return resolveGrid({ colNum: this.colNum, rowHeight: this.rowHeight, gap, padding: null }, { containerWidth: this.width || this.measuredWidth });
|
|
30
|
+
},
|
|
31
|
+
// 与旧布局库一致:layout 先 compact(空隙上移、重叠推下),grid-item 按 i 取压紧后的坐标;不写回 layout
|
|
32
|
+
compacted() {
|
|
33
|
+
return compact(this.layout);
|
|
34
|
+
},
|
|
35
|
+
// i(缺省用 id)→ 压紧后的项,供子 grid-item 取坐标;键统一转字符串,避免数字 / 字符串 id 不等
|
|
36
|
+
positions() {
|
|
37
|
+
const map = {};
|
|
38
|
+
for (const item of this.compacted) {
|
|
39
|
+
const key = item.i != null ? item.i : item.id;
|
|
40
|
+
if (key != null) map[String(key)] = item;
|
|
41
|
+
}
|
|
42
|
+
return map;
|
|
43
|
+
},
|
|
44
|
+
height() {
|
|
45
|
+
return layoutHeight(this.compacted, this.resolved);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
methods: {
|
|
49
|
+
measure() {
|
|
50
|
+
if (this.$refs.root) this.measuredWidth = this.$refs.root.offsetWidth;
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
mounted() {
|
|
54
|
+
if (this.width) return;
|
|
55
|
+
this.measure();
|
|
56
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
57
|
+
this.resizeObserver = new ResizeObserver(() => this.measure());
|
|
58
|
+
this.resizeObserver.observe(this.$refs.root);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
beforeUnmount() {
|
|
62
|
+
if (this.resizeObserver) this.resizeObserver.disconnect();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
66
|
+
return openBlock(), createElementBlock("div", {
|
|
67
|
+
ref: "root",
|
|
68
|
+
class: "vgl-layout epx-dashboard-grid-compat",
|
|
69
|
+
style: normalizeStyle({ height: $options.height + "px" })
|
|
70
|
+
}, [
|
|
71
|
+
renderSlot(_ctx.$slots, "default")
|
|
72
|
+
], 4);
|
|
73
|
+
}
|
|
74
|
+
const CompatGridLayout = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
|
|
75
|
+
export {
|
|
76
|
+
CompatGridLayout as default
|
|
77
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import '../../style.css';
|
|
2
|
+
import { reactive } from "vue";
|
|
3
|
+
function createDesignState() {
|
|
4
|
+
const state = reactive({ activeId: null, activeItem: null, activeOwner: null });
|
|
5
|
+
return {
|
|
6
|
+
state,
|
|
7
|
+
select(item, owner) {
|
|
8
|
+
state.activeId = item ? item.id : null;
|
|
9
|
+
state.activeItem = item || null;
|
|
10
|
+
state.activeOwner = owner || null;
|
|
11
|
+
},
|
|
12
|
+
clear() {
|
|
13
|
+
this.select(null, null);
|
|
14
|
+
},
|
|
15
|
+
isActive(item) {
|
|
16
|
+
return !!item && state.activeId === item.id;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
createDesignState
|
|
22
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import '../../style.css';
|
|
2
|
+
import { GridStackEngine } from "gridstack";
|
|
3
|
+
class NoCollideEngine extends GridStackEngine {
|
|
4
|
+
collide() {
|
|
5
|
+
return void 0;
|
|
6
|
+
}
|
|
7
|
+
collideAll() {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
// 基类在有 maxRow 时会用「基类引擎」克隆一份模拟移动(碰撞检测随之回来:覆盖过半的项被推挤、不足半的移动被拒),
|
|
11
|
+
// 无碰撞时不存在「推挤后超出 maxRow」的情形,直接走基类无 maxRow 的简单路径:moveNode 里的 nodeBoundFix 把节点夹在 maxRow 内即可
|
|
12
|
+
moveNodeCheck(node, o) {
|
|
13
|
+
if (!this.changedPosConstrain(node, o)) return false;
|
|
14
|
+
o.pack = true;
|
|
15
|
+
return this.moveNode(node, o);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
class PushOnlyEngine extends GridStackEngine {
|
|
19
|
+
swap() {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
NoCollideEngine,
|
|
25
|
+
PushOnlyEngine
|
|
26
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import '../../style.css';
|
|
2
|
+
function resolveGrid(grid, { containerWidth = 0, bodyHeight = 0 } = {}) {
|
|
3
|
+
const colNum = grid.colNum;
|
|
4
|
+
const rowHeight = grid.rowHeight === "auto" ? Math.max(1, bodyHeight / 1e3 * 10) : grid.rowHeight;
|
|
5
|
+
const autoGap = [rowHeight * 2, rowHeight * 2];
|
|
6
|
+
const gap = grid.gap === "auto" ? autoGap : grid.gap;
|
|
7
|
+
const padding = grid.padding == null ? gap : grid.padding === "auto" ? autoGap : grid.padding;
|
|
8
|
+
const [gapX, gapY] = gap;
|
|
9
|
+
const [padX, padY] = padding;
|
|
10
|
+
const rawColWidth = padX === gapX ? (containerWidth - gapX * (colNum + 1)) / colNum : (containerWidth - padX * 2 - gapX * (colNum - 1)) / colNum;
|
|
11
|
+
const colWidth = containerWidth > 0 ? rawColWidth : 0;
|
|
12
|
+
return { colNum, rowHeight, gapX, gapY, padX, padY, colWidth, containerWidth };
|
|
13
|
+
}
|
|
14
|
+
function itemRect(item, r) {
|
|
15
|
+
const x = Number(item.x) || 0;
|
|
16
|
+
const y = Number(item.y) || 0;
|
|
17
|
+
const w = Math.max(1, Number(item.w) || 1);
|
|
18
|
+
const h = Math.max(1, Number(item.h) || 1);
|
|
19
|
+
return {
|
|
20
|
+
left: Math.round(r.colWidth * x + (x + 1) * r.gapX + (r.padX - r.gapX)),
|
|
21
|
+
top: Math.round(r.rowHeight * y + (y + 1) * r.gapY + (r.padY - r.gapY)),
|
|
22
|
+
width: Math.round(r.colWidth * w + Math.max(0, w - 1) * r.gapX),
|
|
23
|
+
height: Math.round(r.rowHeight * h + Math.max(0, h - 1) * r.gapY)
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function layoutHeight(items, r) {
|
|
27
|
+
let bottom = 0;
|
|
28
|
+
for (const item of items || []) {
|
|
29
|
+
if (!item) continue;
|
|
30
|
+
const b = (Number(item.y) || 0) + Math.max(1, Number(item.h) || 1);
|
|
31
|
+
if (b > bottom) bottom = b;
|
|
32
|
+
}
|
|
33
|
+
if (!bottom) return 0;
|
|
34
|
+
if (r.padY === r.gapY) return bottom * (r.rowHeight + r.gapY) + r.gapY;
|
|
35
|
+
return r.padY * 2 + bottom * (r.rowHeight + r.gapY) - r.gapY;
|
|
36
|
+
}
|
|
37
|
+
function rectStyle(rect) {
|
|
38
|
+
return { left: rect.left + "px", top: rect.top + "px", width: rect.width + "px", height: rect.height + "px" };
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
itemRect,
|
|
42
|
+
layoutHeight,
|
|
43
|
+
rectStyle,
|
|
44
|
+
resolveGrid
|
|
45
|
+
};
|