cardbuddy 0.6.0
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/README.md +211 -0
- package/dist/cli.js +76 -0
- package/dist/config.js +63 -0
- package/dist/core.js +2 -0
- package/dist/preview-editor/assets/apple-touch-icon-BQcES9QK.png +0 -0
- package/dist/preview-editor/assets/default-image-wzZYQ86K.png +0 -0
- package/dist/preview-editor/assets/favicon-16-Cg6Nrosk.png +0 -0
- package/dist/preview-editor/assets/favicon-32-B_1744sp.png +0 -0
- package/dist/preview-editor/assets/index-BYB0hngG.js +72 -0
- package/dist/preview-editor/assets/index-CpBZEQDy.css +1 -0
- package/dist/preview-editor/assets/site-icon-CFMif8z4.png +0 -0
- package/dist/preview-editor/assets/svg-symbols-B2exBrAr.svg +118 -0
- package/dist/preview-editor/index.html +17 -0
- package/dist/server-core/_vendor/finmall-env.mjs +96 -0
- package/dist/server-core/assetProxy.js +154 -0
- package/dist/server-core/bbox.js +153 -0
- package/dist/server-core/browser.js +108 -0
- package/dist/server-core/buildDataFromSchema.js +50 -0
- package/dist/server-core/cardFiles.js +188 -0
- package/dist/server-core/cardHistory.js +156 -0
- package/dist/server-core/cardImage.js +85 -0
- package/dist/server-core/cardImportMeta.js +139 -0
- package/dist/server-core/cardListCache.js +68 -0
- package/dist/server-core/cardOpenFolder.js +135 -0
- package/dist/server-core/cardSave.js +127 -0
- package/dist/server-core/cardSidecar.js +72 -0
- package/dist/server-core/cardSync.js +259 -0
- package/dist/server-core/cardValidate.js +134 -0
- package/dist/server-core/index.js +82 -0
- package/dist/server-core/logger.js +241 -0
- package/dist/server-core/nodeModels.js +52 -0
- package/dist/server-core/renderStatus.js +207 -0
- package/dist/server-core/screenshot.js +123 -0
- package/dist/server-core/sliceStatic.js +44 -0
- package/dist/standalone.js +447 -0
- package/dist/vendored/App.vue +2330 -0
- package/dist/vendored/api.js +142 -0
- package/dist/vendored/assets/apple-touch-icon.png +0 -0
- package/dist/vendored/assets/default-image.png +0 -0
- package/dist/vendored/assets/favicon-16.png +0 -0
- package/dist/vendored/assets/favicon-32.png +0 -0
- package/dist/vendored/assets/logo.png +0 -0
- package/dist/vendored/assets/site-icon.png +0 -0
- package/dist/vendored/assets/svg-symbols.svg +118 -0
- package/dist/vendored/assets/svgIcons/icons.svg +54 -0
- package/dist/vendored/assets/svgIcons/index.js +39 -0
- package/dist/vendored/components/AbsoluteLayout.vue +42 -0
- package/dist/vendored/components/AlertCardInstance.vue +58 -0
- package/dist/vendored/components/AlertCardLayer.vue +59 -0
- package/dist/vendored/components/ApiPayloadTree.vue +168 -0
- package/dist/vendored/components/AppToast.vue +37 -0
- package/dist/vendored/components/Button.vue +37 -0
- package/dist/vendored/components/Canvas.vue +65 -0
- package/dist/vendored/components/CardGallery.vue +243 -0
- package/dist/vendored/components/CardPreview.vue +72 -0
- package/dist/vendored/components/CardSelect.vue +200 -0
- package/dist/vendored/components/CheckGroup.vue +131 -0
- package/dist/vendored/components/Column.vue +25 -0
- package/dist/vendored/components/ComponentPalette.vue +294 -0
- package/dist/vendored/components/ConfirmDialog.vue +39 -0
- package/dist/vendored/components/CustomPlaceholder.vue +33 -0
- package/dist/vendored/components/DatePickerOverlay.vue +345 -0
- package/dist/vendored/components/DebugPanel.vue +806 -0
- package/dist/vendored/components/DeviceOverlay.vue +70 -0
- package/dist/vendored/components/Divider.vue +22 -0
- package/dist/vendored/components/Dropdown.vue +191 -0
- package/dist/vendored/components/EventEditor.vue +247 -0
- package/dist/vendored/components/GridLayout.vue +67 -0
- package/dist/vendored/components/HoverGuides.vue +122 -0
- package/dist/vendored/components/Icon.vue +34 -0
- package/dist/vendored/components/Image.vue +34 -0
- package/dist/vendored/components/Input.vue +90 -0
- package/dist/vendored/components/JsonTree.vue +516 -0
- package/dist/vendored/components/ListLayout.vue +68 -0
- package/dist/vendored/components/LogValue.vue +51 -0
- package/dist/vendored/components/Map.vue +110 -0
- package/dist/vendored/components/MultiColumn.vue +49 -0
- package/dist/vendored/components/MultiText.vue +58 -0
- package/dist/vendored/components/NodeInspector.vue +295 -0
- package/dist/vendored/components/PickerOverlay.vue +131 -0
- package/dist/vendored/components/ProgressSlider.vue +167 -0
- package/dist/vendored/components/PropControl.vue +241 -0
- package/dist/vendored/components/PropTree.vue +128 -0
- package/dist/vendored/components/RadioButton.vue +74 -0
- package/dist/vendored/components/RemotePreview.vue +171 -0
- package/dist/vendored/components/RenderTree.vue +437 -0
- package/dist/vendored/components/Root.vue +30 -0
- package/dist/vendored/components/Row.vue +25 -0
- package/dist/vendored/components/SelectionOverlay.vue +105 -0
- package/dist/vendored/components/StructureTree.vue +949 -0
- package/dist/vendored/components/SvgCode.vue +19 -0
- package/dist/vendored/components/SvgSymbols.vue +30 -0
- package/dist/vendored/components/Switch.vue +38 -0
- package/dist/vendored/components/Text.vue +32 -0
- package/dist/vendored/components/Video.vue +70 -0
- package/dist/vendored/components/ViewContainer.vue +52 -0
- package/dist/vendored/components/base.js +12 -0
- package/dist/vendored/components/icons.js +47 -0
- package/dist/vendored/components/index.js +58 -0
- package/dist/vendored/components/setters/SetBgPosition.vue +64 -0
- package/dist/vendored/components/setters/SetBgSize.vue +90 -0
- package/dist/vendored/components/setters/SetCode.vue +90 -0
- package/dist/vendored/components/setters/SetColor.vue +244 -0
- package/dist/vendored/components/setters/SetCustomSelect.vue +58 -0
- package/dist/vendored/components/setters/SetExpr.vue +107 -0
- package/dist/vendored/components/setters/SetGradient.vue +125 -0
- package/dist/vendored/components/setters/SetGrid9.vue +68 -0
- package/dist/vendored/components/setters/SetIconSelect.vue +67 -0
- package/dist/vendored/components/setters/SetIfForm.vue +61 -0
- package/dist/vendored/components/setters/SetImage.vue +43 -0
- package/dist/vendored/components/setters/SetList.vue +139 -0
- package/dist/vendored/components/setters/SetNumber.vue +118 -0
- package/dist/vendored/components/setters/SetNumberStyle.vue +113 -0
- package/dist/vendored/components/setters/SetPadding.vue +77 -0
- package/dist/vendored/components/setters/SetSegment.vue +160 -0
- package/dist/vendored/components/setters/SetSelect.vue +51 -0
- package/dist/vendored/components/setters/SetSwitch.vue +49 -0
- package/dist/vendored/components/setters/SetText.vue +52 -0
- package/dist/vendored/components/setters/props.js +23 -0
- package/dist/vendored/fidelity/baseline-messageOverview.json +87 -0
- package/dist/vendored/fidelity/calibration.json +29 -0
- package/dist/vendored/fidelity/fidelity-messageOverview-after.html +114 -0
- package/dist/vendored/fidelity/fidelity-messageOverview-after.json +272 -0
- package/dist/vendored/fidelity/fidelity-messageOverview.html +185 -0
- package/dist/vendored/fidelity/fidelity-messageOverview.json +336 -0
- package/dist/vendored/fidelity/fidelity-messageOverview_v2.html +262 -0
- package/dist/vendored/fidelity/fidelity-messageOverview_v2.json +502 -0
- package/dist/vendored/fidelity/fidelity-messageOverview_v3.html +201 -0
- package/dist/vendored/fidelity/fidelity-messageOverview_v3.json +417 -0
- package/dist/vendored/fidelity/fidelity-messageOverview_visual.html +125 -0
- package/dist/vendored/fidelity/fidelity-messageOverview_visual.json +338 -0
- package/dist/vendored/index.html +16 -0
- package/dist/vendored/main.js +36 -0
- package/dist/vendored/meta/action-meta.json +141 -0
- package/dist/vendored/meta/component-meta.json +2714 -0
- package/dist/vendored/meta/directive-meta.json +40 -0
- package/dist/vendored/meta/style-meta.json +1086 -0
- package/dist/vendored/runtime/StyleParser.js +179 -0
- package/dist/vendored/runtime/adapter.js +166 -0
- package/dist/vendored/runtime/alert-card.js +125 -0
- package/dist/vendored/runtime/app-toast.js +33 -0
- package/dist/vendored/runtime/assets.js +16 -0
- package/dist/vendored/runtime/canvas-context.js +141 -0
- package/dist/vendored/runtime/card-runtime.js +289 -0
- package/dist/vendored/runtime/clipboard-node.js +83 -0
- package/dist/vendored/runtime/clipboard.js +29 -0
- package/dist/vendored/runtime/evaluate.js +152 -0
- package/dist/vendored/runtime/event-model.js +300 -0
- package/dist/vendored/runtime/hoverGuide.js +31 -0
- package/dist/vendored/runtime/jsapi-mock.js +256 -0
- package/dist/vendored/runtime/node-model.js +217 -0
- package/dist/vendored/runtime/property.js +364 -0
- package/dist/vendored/runtime/selectionOverlay.js +53 -0
- package/dist/vendored/runtime/toPreview.js +648 -0
- package/dist/vendored/standalone-main.js +36 -0
- package/dist/vendored/standalone.html +12 -0
- package/dist/vendored/styles/app.css +2565 -0
- package/dist/vendored/styles/card-gallery.css +234 -0
- package/dist/vendored/styles/card-select.css +281 -0
- package/dist/vendored/styles/components.css +383 -0
- package/dist/vendored/styles/dropdown.css +337 -0
- package/dist/vendored/styles/json-tree.css +308 -0
- package/dist/vendored/styles/node-inspector.css +1003 -0
- package/dist/vendored/styles/setters.css +962 -0
- package/dist/vendored/styles/standalone.css +27 -0
- package/dist/vendored/styles/structure-tree.css +320 -0
- package/package.json +42 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 节点编辑模型:复刻编辑器属性面板(ModelComponent + ModelStyle + EditorStyleForm)。
|
|
3
|
+
* ①组件属性:表单树 == 实例树,meta 造默认树再被 schema item 覆盖,导出整树写回;
|
|
4
|
+
* ②样式:表单树(styleForm,完整 meta 裁剪)≠ 实例树(style,仅 styleState + schema 出现过的),
|
|
5
|
+
* 定点写入避免整棵默认树进 schema;③可见性:itemCondition 统一由 updatePropertyVisible 计算。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import Property, { PropertyList } from './property.js';
|
|
9
|
+
import { COMPONENT_META, STYLE_META_LIST, DIRECTIVE_META, Style } from './toPreview.js';
|
|
10
|
+
|
|
11
|
+
const clone = (v) => (v === undefined ? v : JSON.parse(JSON.stringify(v)));
|
|
12
|
+
|
|
13
|
+
/** 根节点 className "Root" 不在组件 meta 里,这里合成一份:无属性、无插槽、样式全开 */
|
|
14
|
+
const ROOT_META = {
|
|
15
|
+
className: 'Root',
|
|
16
|
+
name: '根节点',
|
|
17
|
+
styleState: {},
|
|
18
|
+
styleVisible: {
|
|
19
|
+
rect: true,
|
|
20
|
+
flex: true,
|
|
21
|
+
background: true,
|
|
22
|
+
padding: true,
|
|
23
|
+
margin: true,
|
|
24
|
+
borderRadius: true,
|
|
25
|
+
border: true,
|
|
26
|
+
shadow: true,
|
|
27
|
+
transform: true,
|
|
28
|
+
other: true,
|
|
29
|
+
},
|
|
30
|
+
styleMeta: {},
|
|
31
|
+
properties: [],
|
|
32
|
+
slots: [],
|
|
33
|
+
events: [{ eventName: '初始化完成', eventKey: 'onReady' }],
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function getComponentMeta(className) {
|
|
37
|
+
if (className === 'Root') return ROOT_META;
|
|
38
|
+
return COMPONENT_META[className] || null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 按 styleVisible 裁剪 + styleMeta 覆盖,产出面板用的样式表单树。
|
|
43
|
+
* styleVisible 形如 `{font: true, flex: {flexShrink: true}}`:
|
|
44
|
+
* true → 该分组全部子字段可见;对象 → 只保留列出的子字段。
|
|
45
|
+
*/
|
|
46
|
+
function buildStyleForm(styleVisible = {}, styleMeta = {}, styleState = {}) {
|
|
47
|
+
const out = [];
|
|
48
|
+
|
|
49
|
+
STYLE_META_LIST.forEach((groupMeta) => {
|
|
50
|
+
const vis = styleVisible[groupMeta.itemKey];
|
|
51
|
+
if (!vis) return;
|
|
52
|
+
|
|
53
|
+
const prop = new Property(groupMeta);
|
|
54
|
+
const children = (prop.itemChildren && prop.itemChildren.properties) || [];
|
|
55
|
+
|
|
56
|
+
// 对象形态的 visible:裁掉未列出的子字段
|
|
57
|
+
if (vis && typeof vis === 'object') {
|
|
58
|
+
for (let i = 0; i < children.length; i++) {
|
|
59
|
+
if (!vis[children[i].itemKey]) children.splice(i--, 1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (!children.length) return;
|
|
63
|
+
|
|
64
|
+
// styleMeta 覆盖(改名 / 改选项),如 Row 把 justifyContent 显示成「水平对齐」
|
|
65
|
+
const overrides = styleMeta[groupMeta.itemKey] || {};
|
|
66
|
+
children.forEach((c) => {
|
|
67
|
+
const ov = overrides[c.itemKey];
|
|
68
|
+
if (!ov) return;
|
|
69
|
+
if (ov.itemName) c.itemName = ov.itemName;
|
|
70
|
+
if (ov.itemOption) c.itemOption = Object.assign({}, c.itemOption, ov.itemOption);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// styleState 默认值打底(组件自带的初始样式,schema 里往往并未显式写出)
|
|
74
|
+
if (styleState[groupMeta.itemKey]) {
|
|
75
|
+
PropertyList.setPropertyValue(children, styleState[groupMeta.itemKey]);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
prop.itemValueFrom = '0';
|
|
79
|
+
out.push(prop);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export class NodeEditModel {
|
|
86
|
+
/**
|
|
87
|
+
* @param {Object} schemaNode 原始 schema 节点(与 currentSchema 同引用)
|
|
88
|
+
*/
|
|
89
|
+
constructor(schemaNode = {}) {
|
|
90
|
+
this.schemaNode = schemaNode;
|
|
91
|
+
this.className = schemaNode.className || 'Root';
|
|
92
|
+
this.isRoot = this.className === 'Root';
|
|
93
|
+
|
|
94
|
+
const meta = getComponentMeta(this.className);
|
|
95
|
+
this.metaMissing = !meta;
|
|
96
|
+
this.meta = meta || { properties: [], events: [], styleVisible: {}, styleState: {}, styleMeta: {} };
|
|
97
|
+
|
|
98
|
+
this.name = this.meta.name || this.className;
|
|
99
|
+
this.events = this.meta.events || [];
|
|
100
|
+
|
|
101
|
+
/* -------- 组件属性:表单树 == 实例树 -------- */
|
|
102
|
+
this.properties = (this.meta.properties || []).map((m) => new Property(m));
|
|
103
|
+
Property.restoreProperty(schemaNode.properties || [], this.properties);
|
|
104
|
+
Property.updatePropertyVisible(this.properties);
|
|
105
|
+
// meta 未知、但 schema 里存在的属性:原样保留,避免写回时丢失(回填保真)
|
|
106
|
+
this.extraProperties = this._orphans(schemaNode.properties, this.properties);
|
|
107
|
+
|
|
108
|
+
/* -------- 指令:根节点没有指令 -------- */
|
|
109
|
+
this.hasDirectives = !this.isRoot;
|
|
110
|
+
this.directives = this.hasDirectives ? DIRECTIVE_META.map((m) => new Property(m)) : [];
|
|
111
|
+
if (this.hasDirectives) {
|
|
112
|
+
Property.restoreProperty(schemaNode.directives || [], this.directives);
|
|
113
|
+
Property.updatePropertyVisible(this.directives);
|
|
114
|
+
this.extraDirectives = this._orphans(schemaNode.directives, this.directives);
|
|
115
|
+
} else {
|
|
116
|
+
this.extraDirectives = [];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* -------- 样式:实例树(精简,用于导出) -------- */
|
|
120
|
+
this.style = new Style({
|
|
121
|
+
state: this.meta.styleState,
|
|
122
|
+
visible: this.meta.styleVisible,
|
|
123
|
+
meta: this.meta.styleMeta,
|
|
124
|
+
});
|
|
125
|
+
this.style.onRestore(schemaNode.style || {});
|
|
126
|
+
|
|
127
|
+
/* -------- 样式:表单树(完整 meta,用于展示与编辑) -------- */
|
|
128
|
+
this.styleForm = buildStyleForm(
|
|
129
|
+
this.meta.styleVisible || {},
|
|
130
|
+
this.meta.styleMeta || {},
|
|
131
|
+
this.meta.styleState || {},
|
|
132
|
+
);
|
|
133
|
+
// 用节点当前实际样式覆盖表单树(不传 mode:只填充命中的字段,其余保留默认)
|
|
134
|
+
Property.restoreProperty(this.style.onExport().properties, this.styleForm);
|
|
135
|
+
Property.updatePropertyVisible(this.styleForm);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** 重算 itemCondition 可见性(任一控件变更后调用) */
|
|
139
|
+
refreshVisible() {
|
|
140
|
+
Property.updatePropertyVisible(this.properties);
|
|
141
|
+
if (this.hasDirectives) Property.updatePropertyVisible(this.directives);
|
|
142
|
+
Property.updatePropertyVisible(this.styleForm);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 样式表单某字段变更 → 定点并入实例树。
|
|
147
|
+
* @param {Property} group 顶层分组属性
|
|
148
|
+
* @param {Property[]} chain 从分组下一层到被改字段的属性链
|
|
149
|
+
*/
|
|
150
|
+
applyStyleChange(group, chain = []) {
|
|
151
|
+
let head = null;
|
|
152
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
153
|
+
head = { itemId: chain[i].itemId, child: head };
|
|
154
|
+
}
|
|
155
|
+
// 先按新值重算可见性,避免把刚被条件隐藏的字段一起导出
|
|
156
|
+
Property.updatePropertyVisible(this.styleForm);
|
|
157
|
+
this.style.applyChange(group, head);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
exportProperties() {
|
|
161
|
+
const base = this.properties.reduce((ret, p) => {
|
|
162
|
+
const e = p.onExport();
|
|
163
|
+
if (e) ret.push(e);
|
|
164
|
+
return ret;
|
|
165
|
+
}, []);
|
|
166
|
+
return base.concat(this.extraProperties.map((p) => clone(p)));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
exportDirectives() {
|
|
170
|
+
const base = this.directives.reduce((ret, p) => {
|
|
171
|
+
const e = p.onExport();
|
|
172
|
+
if (e) ret.push(e);
|
|
173
|
+
return ret;
|
|
174
|
+
}, []);
|
|
175
|
+
return base.concat(this.extraDirectives.map((p) => clone(p)));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** 收集 schema 中存在、但 meta 模型里没有对应项的属性(按 itemId / itemKey 判定) */
|
|
179
|
+
_orphans(schemaItems, modelProps) {
|
|
180
|
+
const known = new Set();
|
|
181
|
+
(modelProps || []).forEach((p) => {
|
|
182
|
+
if (p.itemId) known.add(p.itemId);
|
|
183
|
+
if (p.itemKey) known.add(p.itemKey);
|
|
184
|
+
});
|
|
185
|
+
return (schemaItems || [])
|
|
186
|
+
.filter((s) => s && s.itemId && !known.has(s.itemId) && !(s.itemKey && known.has(s.itemKey)))
|
|
187
|
+
.map((s) => clone(s));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
exportStyle() {
|
|
191
|
+
return this.style.onExport();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 写回原始 schema 节点(就地 mutate,保持与 currentSchema 的引用关系)。
|
|
196
|
+
* 只覆盖 properties / directives / style 三块;slots、eventactions、componentId 等不动。
|
|
197
|
+
*/
|
|
198
|
+
applyToSchema() {
|
|
199
|
+
const node = this.schemaNode;
|
|
200
|
+
node.properties = this.exportProperties();
|
|
201
|
+
node.style = this.exportStyle();
|
|
202
|
+
// 根节点原本就没有 directives 字段,不要凭空加上
|
|
203
|
+
if (this.hasDirectives) node.directives = this.exportDirectives();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** 导出成 schema 片段(用于往返一致性自检,不落盘) */
|
|
207
|
+
toSchemaFragment() {
|
|
208
|
+
const out = {
|
|
209
|
+
properties: this.exportProperties(),
|
|
210
|
+
style: this.exportStyle(),
|
|
211
|
+
};
|
|
212
|
+
if (this.hasDirectives) out.directives = this.exportDirectives();
|
|
213
|
+
return clone(out);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export default NodeEditModel;
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Property / PropertyList 模型
|
|
3
|
+
*
|
|
4
|
+
* 忠实移植自编辑器 chunk c9 中的 `Property`(mod 196) 与 `PropertyList`(mod 215 宿主) 两个类。
|
|
5
|
+
* 这是 schema(item 数组序列化格式) → 预览数据(普通 JS 对象) 的核心。
|
|
6
|
+
*
|
|
7
|
+
* 关键语义(逐条对应原实现,勿随意"优化"):
|
|
8
|
+
* - 属性树先由 **meta 定义**构造出一棵带默认值的完整树;
|
|
9
|
+
* - 再用 schema 里导出的 item 覆盖(restoreProperty);
|
|
10
|
+
* - 最后 onPreview 把树摊平成普通对象,变量表达式收集到 __vars__。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
let __seq = 0;
|
|
14
|
+
const nextVal = (p) => `${p}_${(++__seq).toString(36)}`;
|
|
15
|
+
|
|
16
|
+
export const ValueFrom = { const: '0', var: '1', image: '2' };
|
|
17
|
+
export const RestoreMode = { Delete: '1', Append: '2' };
|
|
18
|
+
|
|
19
|
+
const clone = (v) => (v === undefined ? v : JSON.parse(JSON.stringify(v)));
|
|
20
|
+
|
|
21
|
+
/** 一组属性(对应 meta 的 itemChildren 数组,或 itemList 的一个元素) */
|
|
22
|
+
export class PropertyList {
|
|
23
|
+
/**
|
|
24
|
+
* @param {Array} metaList meta 定义数组
|
|
25
|
+
* @param {Object} defaults 父级 itemDefaultValue(对象形态),用于给子项填初值
|
|
26
|
+
*/
|
|
27
|
+
constructor(metaList = [], defaults = {}) {
|
|
28
|
+
this.id = nextVal('propgroup');
|
|
29
|
+
this.properties = (metaList || []).map((m) => new Property(m));
|
|
30
|
+
PropertyList.setPropertyValue(this.properties, defaults);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** 用一个普通对象给同级属性批量填值(仅 itemValueFrom==="0" 的常量项) */
|
|
34
|
+
static setPropertyValue(props = [], data = {}) {
|
|
35
|
+
if (!data) return;
|
|
36
|
+
props.forEach((p) => {
|
|
37
|
+
if (p.itemVisible && p.itemKey && data[p.itemKey] !== undefined && p.itemValueFrom === '0') {
|
|
38
|
+
p.itemValue = data[p.itemKey];
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
onRestore(schemaGroup = {}, mode, hooks, paths) {
|
|
44
|
+
Property.restoreProperty(schemaGroup.properties || [], this.properties, mode, hooks, paths);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
onPreview(ctx) {
|
|
48
|
+
return this.properties.reduce((acc, p) => {
|
|
49
|
+
const r = p.onPreview(ctx);
|
|
50
|
+
if (r) Object.assign(acc, r);
|
|
51
|
+
return acc;
|
|
52
|
+
}, {});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 序列化回 schema item 数组(onRestore 的逆运算)。
|
|
57
|
+
* @param {{itemId:string, child?:Object}} [child] 只导出指定 itemId 的那一支(用于样式的定点回写)
|
|
58
|
+
*/
|
|
59
|
+
onExport(child) {
|
|
60
|
+
const properties = this.properties.reduce((ret, p) => {
|
|
61
|
+
if (!child || child.itemId === p.itemId) {
|
|
62
|
+
const e = p.onExport(child && child.child);
|
|
63
|
+
if (e) ret.push(e);
|
|
64
|
+
}
|
|
65
|
+
return ret;
|
|
66
|
+
}, []);
|
|
67
|
+
return { properties };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export class Property {
|
|
72
|
+
constructor(meta = {}) {
|
|
73
|
+
this.id = nextVal('prop');
|
|
74
|
+
const m = clone(meta) || {};
|
|
75
|
+
|
|
76
|
+
this.itemId = m.itemId;
|
|
77
|
+
this.itemName = m.itemName;
|
|
78
|
+
this.itemType = m.itemType;
|
|
79
|
+
this.itemKey = m.itemKey;
|
|
80
|
+
this.itemOption = m.itemOption || {};
|
|
81
|
+
this.itemParam = m.itemParam || {};
|
|
82
|
+
this.itemDefaultValue = m.itemDefaultValue === undefined ? null : m.itemDefaultValue;
|
|
83
|
+
this.itemDesc = m.itemDesc;
|
|
84
|
+
this.itemCondition = m.itemCondition;
|
|
85
|
+
this.itemWatch = m.itemWatch || {};
|
|
86
|
+
this.itemTypeRange = m.itemTypeRange || 'const,var';
|
|
87
|
+
this.itemDirection = m.itemDirection;
|
|
88
|
+
this.itemVisible = true;
|
|
89
|
+
this.itemUpdated = false;
|
|
90
|
+
|
|
91
|
+
// itemTypeRange 的第一段决定初始取值来源:const→"0" var→"1" image→"2"
|
|
92
|
+
this.itemValueFrom = ValueFrom[String(this.itemTypeRange).split(/,/)[0]];
|
|
93
|
+
this._itemValueFrom = this.itemValueFrom;
|
|
94
|
+
|
|
95
|
+
const dv = clone(this.itemDefaultValue);
|
|
96
|
+
this.itemChildren = new PropertyList(m.itemChildren || [], dv);
|
|
97
|
+
this.itemTemplate = m.itemTemplate || m.itemList || [];
|
|
98
|
+
this.itemList = [];
|
|
99
|
+
|
|
100
|
+
if (!this.itemChildren.properties.length && Object.keys(this.itemTemplate).length) {
|
|
101
|
+
this.initItemList(dv);
|
|
102
|
+
}
|
|
103
|
+
this.initItemValue(dv);
|
|
104
|
+
|
|
105
|
+
// 各来源(常量/变量/图片)独立值缓存:切换来源 Tab 时按 itemValueFrom 归档/回填,
|
|
106
|
+
// 互不覆盖,修复「切到常量再切回变量,变量内容被清空」的问题。
|
|
107
|
+
// 仅作内存态编辑缓冲,不参与序列化(schema 仍只保留当前激活来源的值)。
|
|
108
|
+
this._valuesByFrom = { '0': undefined, '1': undefined, '2': undefined };
|
|
109
|
+
this._valuesByFrom[this.itemValueFrom] = this.itemValue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
initItemValue(v, force = false) {
|
|
113
|
+
if (this.itemValueFrom === '1' || this.itemValueFrom === '2') {
|
|
114
|
+
this.itemValue = force ? v : '';
|
|
115
|
+
} else if (this.itemChildren.properties.length) {
|
|
116
|
+
this.itemValue = {};
|
|
117
|
+
} else if (Object.keys(this.itemTemplate).length) {
|
|
118
|
+
this.itemValue = [];
|
|
119
|
+
} else {
|
|
120
|
+
this.itemValue = v;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
initItemList(v) {
|
|
125
|
+
const arr = v || [];
|
|
126
|
+
if (Array.isArray(arr)) arr.forEach((it) => this.addItems(it));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** 往 itemList 追加一个元素(按 templateField 选择模板) */
|
|
130
|
+
addItems(data, cb) {
|
|
131
|
+
const templateField = this.itemOption?.templateField || '';
|
|
132
|
+
if (this.itemChildren.properties.length) return undefined;
|
|
133
|
+
const key = data ? data[templateField] : '';
|
|
134
|
+
const tpl = templateField ? this.itemTemplate[key] || [] : this.itemTemplate || [];
|
|
135
|
+
const list = new PropertyList(tpl, data || {});
|
|
136
|
+
if (cb) cb(list);
|
|
137
|
+
this.itemList.push(list);
|
|
138
|
+
return list;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** 移除 itemList 第 idx 项(List 类型编辑用) */
|
|
142
|
+
removeAt(idx) {
|
|
143
|
+
if (this.itemChildren.properties.length) return;
|
|
144
|
+
if (idx >= 0 && idx < this.itemList.length) this.itemList.splice(idx, 1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** 调整 itemList 顺序(List 类型编辑用) */
|
|
148
|
+
moveItem(from, to) {
|
|
149
|
+
if (this.itemChildren.properties.length) return;
|
|
150
|
+
if (from < 0 || from >= this.itemList.length) return;
|
|
151
|
+
to = Math.max(0, Math.min(this.itemList.length - 1, to));
|
|
152
|
+
if (from === to) return;
|
|
153
|
+
const [it] = this.itemList.splice(from, 1);
|
|
154
|
+
this.itemList.splice(to, 0, it);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** 用 schema 导出的 item 覆盖本属性 */
|
|
158
|
+
onRestore(item = {}, mode, hooks = {}, paths = []) {
|
|
159
|
+
if (!Object.keys(item).length) return;
|
|
160
|
+
const nextPaths = [].concat(paths);
|
|
161
|
+
nextPaths.push(this.itemId);
|
|
162
|
+
|
|
163
|
+
// schema 缺省 itemValueFrom 时保留 meta 推导的默认值(itemTypeRange 首段),
|
|
164
|
+
// 不要覆盖成 undefined —— 否则单一来源属性(如 Code 类型脚本)会被 PropControl
|
|
165
|
+
// 当成「越界来源」而误渲染来源切换条(见 EventEditor 脚本参数误显来源按钮)。
|
|
166
|
+
if (item.itemValueFrom !== undefined) this.itemValueFrom = item.itemValueFrom;
|
|
167
|
+
this.initItemValue(item.itemValue, true);
|
|
168
|
+
this._valuesByFrom = { '0': undefined, '1': undefined, '2': undefined };
|
|
169
|
+
this._valuesByFrom[this.itemValueFrom] = this.itemValue;
|
|
170
|
+
this.itemChildren.onRestore(item.itemChildren || {}, mode, hooks, nextPaths);
|
|
171
|
+
|
|
172
|
+
this.itemList = [];
|
|
173
|
+
// List 类型的真实数据行在 item.itemValue(数组,元素是 {字段:值} 的纯数据对象);
|
|
174
|
+
// item.itemList 只是「每一行字段」的模板(如 dataList 的 key/desc 字段定义),
|
|
175
|
+
// 不可误当成数据行。必须用「数据行 + 字段模板」重建 itemList,否则会把字段模板
|
|
176
|
+
// 当成数据行,导致所有行共用同一模板默认值(如 key 全部为 "1"),复选框因此只能
|
|
177
|
+
// 全选/全不选。对齐构造函数里的 initItemList(同样以默认数据行 + 模板重建)。
|
|
178
|
+
const rows = Array.isArray(item.itemValue) ? item.itemValue : [];
|
|
179
|
+
if (rows.length || !item.itemList) {
|
|
180
|
+
rows.forEach((row) => this.addItems(row || {}));
|
|
181
|
+
} else if (Array.isArray(item.itemList)) {
|
|
182
|
+
// 兼容极个别卡片:数据行直接放在 itemList(每行带 properties)的场景,
|
|
183
|
+
// 仍按模板逐行重建并 onRestore 其嵌套结构。
|
|
184
|
+
item.itemList.forEach((li) => {
|
|
185
|
+
const created = this.addItems({});
|
|
186
|
+
if (created) created.onRestore(li, mode, hooks, nextPaths);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* 摊平成预览数据。
|
|
193
|
+
* @param {{paths:string[], vars:Object}} ctx 顶层调用不传;嵌套调用透传 paths/vars
|
|
194
|
+
*/
|
|
195
|
+
onPreview(ctx) {
|
|
196
|
+
const out = {};
|
|
197
|
+
const paths = ctx ? ctx.paths : [];
|
|
198
|
+
const isNested = !!(paths && paths.length);
|
|
199
|
+
const vars = isNested ? ctx.vars : {};
|
|
200
|
+
const cur = [].concat(paths || []);
|
|
201
|
+
|
|
202
|
+
if (this.itemVisible && this.itemKey) {
|
|
203
|
+
if (this.itemValueFrom === '0') {
|
|
204
|
+
if (this.itemChildren.properties.length > 0) {
|
|
205
|
+
cur.push(this.itemKey);
|
|
206
|
+
out[this.itemKey] = this.itemChildren.onPreview({ paths: cur, vars });
|
|
207
|
+
} else if (this.itemList.length > 0) {
|
|
208
|
+
cur.push(this.itemKey);
|
|
209
|
+
out[this.itemKey] = this.itemList.map((el, idx) => {
|
|
210
|
+
const p = [].concat(cur);
|
|
211
|
+
p.push(idx);
|
|
212
|
+
return el.onPreview({ paths: p, vars });
|
|
213
|
+
});
|
|
214
|
+
} else {
|
|
215
|
+
out[this.itemKey] = this.itemValue;
|
|
216
|
+
}
|
|
217
|
+
} else if (this.itemValueFrom === '1') {
|
|
218
|
+
// 变量绑定:值先取 meta 默认值,表达式登记到 vars["点号路径"]
|
|
219
|
+
out[this.itemKey] = this.itemDefaultValue;
|
|
220
|
+
if (this.itemValue) {
|
|
221
|
+
cur.push(this.itemKey);
|
|
222
|
+
vars[cur.join('.')] = this.itemValue;
|
|
223
|
+
}
|
|
224
|
+
} else if (this.itemValueFrom === '2') {
|
|
225
|
+
out[this.itemKey] = this.itemValue;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!isNested) out.__vars__ = vars;
|
|
230
|
+
return out;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* 序列化回 schema item(onRestore 的逆运算)。
|
|
235
|
+
* 忠实移植自编辑器 ModelProperty.onExport:
|
|
236
|
+
* - itemVisible 为假的属性**不导出**(itemCondition 隐藏的字段不会污染 schema);
|
|
237
|
+
* - itemChildren 与 itemList 互斥,且都只在非空时才出现;
|
|
238
|
+
* - 变量场景(itemValueFrom==='1')额外带上 itemDefaultValue。
|
|
239
|
+
* @param {{itemId:string, child?:Object}} [child] 定点导出用的子链
|
|
240
|
+
*/
|
|
241
|
+
onExport(child) {
|
|
242
|
+
if (!this.itemVisible) return undefined;
|
|
243
|
+
// 动作型设置器(如 CustomSelect「常用样式」)无 itemKey,不承载可序列化的值,
|
|
244
|
+
// 跳过导出可避免把 {itemKey:undefined} 脏项写回 schema.properties(见问题 #1/#5)。
|
|
245
|
+
if (this.itemKey === undefined || this.itemKey === null) return undefined;
|
|
246
|
+
|
|
247
|
+
const itemChildren = this.itemChildren.onExport(child);
|
|
248
|
+
const itemList = this.itemList.map((g) => g.onExport(child));
|
|
249
|
+
const extra = {};
|
|
250
|
+
|
|
251
|
+
if (this.itemValueFrom === '1') extra.itemDefaultValue = this.itemDefaultValue;
|
|
252
|
+
|
|
253
|
+
if (itemChildren && itemChildren.properties.length) extra.itemChildren = itemChildren;
|
|
254
|
+
else if (itemList.length) extra.itemList = itemList;
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
itemId: this.itemId,
|
|
258
|
+
itemKey: this.itemKey,
|
|
259
|
+
itemValueFrom: this.itemValueFrom,
|
|
260
|
+
itemValue: this.itemValue,
|
|
261
|
+
...extra,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* ------------------------------ 静态工具 ------------------------------ */
|
|
266
|
+
|
|
267
|
+
/** 合并两个 preview 结果,__vars__ 单独浅合并 */
|
|
268
|
+
static mergePreview(a = {}, b = {}) {
|
|
269
|
+
const av = a.__vars__;
|
|
270
|
+
const bv = b.__vars__;
|
|
271
|
+
return bv ? Object.assign(a, b, { __vars__: Object.assign({}, av, bv) }) : Object.assign(a, bv);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* 用 schema items 覆盖 meta 属性树。
|
|
276
|
+
* 注意:schemaItems 为空时**整棵默认树原样保留**(原实现的 `if (e.length && i.length)`)。
|
|
277
|
+
* mode 缺省时既不删除未匹配项、也不追加多余项 —— 组件 properties/directives 走的就是这条路径。
|
|
278
|
+
*/
|
|
279
|
+
static restoreProperty(schemaItems = [], metaProps = [], mode, hooks = {}, paths) {
|
|
280
|
+
hooks = hooks || {};
|
|
281
|
+
if (!schemaItems.length || !metaProps.length) return;
|
|
282
|
+
|
|
283
|
+
const rest = {};
|
|
284
|
+
const index = (schemaItems || []).reduce((acc, it) => {
|
|
285
|
+
const k = it.itemId || it.itemKey;
|
|
286
|
+
if (k) {
|
|
287
|
+
acc[k] = it;
|
|
288
|
+
rest[k] = it;
|
|
289
|
+
}
|
|
290
|
+
return acc;
|
|
291
|
+
}, {});
|
|
292
|
+
|
|
293
|
+
for (let i = 0; i < metaProps.length; i++) {
|
|
294
|
+
const p = metaProps[i];
|
|
295
|
+
const matched = index[p.itemId] || index[p.itemKey];
|
|
296
|
+
if (matched) {
|
|
297
|
+
delete rest[p.itemId];
|
|
298
|
+
delete rest[p.itemKey];
|
|
299
|
+
p.onRestore(matched, mode, {}, paths);
|
|
300
|
+
p.itemUpdated = true;
|
|
301
|
+
if (hooks.restore) hooks.restore(p);
|
|
302
|
+
} else if (mode === RestoreMode.Delete) {
|
|
303
|
+
metaProps.splice(i--, 1);
|
|
304
|
+
if (hooks.delete) hooks.delete(p);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (mode === RestoreMode.Append) {
|
|
309
|
+
for (const k in rest) {
|
|
310
|
+
if (hooks.append) hooks.append(rest[k], metaProps, [].concat(paths || []));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* 依据 itemCondition 重算同级属性可见性(不可见的属性不会出现在 preview 产物里)。
|
|
317
|
+
* 语法:`a.b` → 同级中 itemCondition==="a" 的属性其值须等于 "b";
|
|
318
|
+
* 支持 `a.!b`(取反)与 `a.b|c`(多选其一);段数为奇数的项自身是"条件源"。
|
|
319
|
+
*/
|
|
320
|
+
static updatePropertyVisible(props = []) {
|
|
321
|
+
const list = props || [];
|
|
322
|
+
const data = Property._conditionData(list);
|
|
323
|
+
list.forEach((p) => {
|
|
324
|
+
p.itemVisible = !/\./.test(p.itemCondition) || Property._conditionResult(p.itemCondition, data);
|
|
325
|
+
Property.updatePropertyVisible(p.itemChildren.properties);
|
|
326
|
+
p.itemList.forEach((l) => Property.updatePropertyVisible(l.properties));
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
static _conditionData(props) {
|
|
331
|
+
return props.reduce((acc, p) => {
|
|
332
|
+
const c = `${p.itemCondition || ''}`;
|
|
333
|
+
if (c && c.split('.').length % 2 === 1) acc[c] = p.itemValue;
|
|
334
|
+
return acc;
|
|
335
|
+
}, {});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
static _conditionResult(cond = '', data = {}) {
|
|
339
|
+
let ok = true;
|
|
340
|
+
const seg = cond.split('.');
|
|
341
|
+
for (let i = 0; i < seg.length - 1; i += 2) {
|
|
342
|
+
const path = seg.slice(0, i + 1).join('.');
|
|
343
|
+
let expect = seg[i + 1];
|
|
344
|
+
let negate = false;
|
|
345
|
+
if (/^!/.test(expect)) {
|
|
346
|
+
negate = true;
|
|
347
|
+
expect = expect.substring(1);
|
|
348
|
+
}
|
|
349
|
+
const options = expect.split('|');
|
|
350
|
+
ok = false;
|
|
351
|
+
for (let j = 0; j < options.length; j++) {
|
|
352
|
+
if (data[path] == options[j]) {
|
|
353
|
+
ok = true;
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (negate) ok = !ok;
|
|
358
|
+
if (!ok) break;
|
|
359
|
+
}
|
|
360
|
+
return ok;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export default Property;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// 节点选中遮罩:DevTools 风格的「覆盖层」方案。
|
|
2
|
+
//
|
|
3
|
+
// 旧实现把高亮做成 `.ce-highlight::after` 伪元素,直接挂在组件根 DOM(this.$el)上。
|
|
4
|
+
// 问题:CSS 规范下 `::before/::after` 不会为「可替换元素」(replaced elements:canvas、
|
|
5
|
+
// input、img、video、select、textarea、audio) 生成盒子。一旦某组件的根就是可替换元素
|
|
6
|
+
// (本工程里的 Canvas→<canvas>、Input→<input>),this.$el 落在它身上,遮罩整块消失。
|
|
7
|
+
//
|
|
8
|
+
// 新方案:不再依赖伪元素,而是在手机屏幕滚动容器内放一个绝对定位的覆盖层 div,选中时
|
|
9
|
+
// 把它的位置/尺寸算成与选中节点根元素完全重合(content 坐标,随滚动自然移动)。对任意
|
|
10
|
+
// 元素类型(含 canvas/input/button)100% 生效,且零布局影响、零额外逐节点 DOM。
|
|
11
|
+
import { reactive } from 'vue'
|
|
12
|
+
|
|
13
|
+
export const selectionOverlay = reactive({
|
|
14
|
+
// 当前选中节点的根 DOM 元素(RenderTree 在选中时写入 this.$el)
|
|
15
|
+
el: null,
|
|
16
|
+
// 当前选中节点的 id:用于列表(ListLayout/GridLayout)重复项的「先到先得」判定——
|
|
17
|
+
// 同一模板节点会被渲染成多个共享 id 的 DOM 实例,全部满足选中条件,
|
|
18
|
+
// 必须靠 id 区分「同一选区」与「新选区」,避免互相覆盖导致高亮落在末项。
|
|
19
|
+
id: null,
|
|
20
|
+
visible: false,
|
|
21
|
+
// 覆盖层位置/尺寸(content 坐标,由 SelectionOverlay 组件计算后写入)
|
|
22
|
+
top: 0,
|
|
23
|
+
left: 0,
|
|
24
|
+
width: 0,
|
|
25
|
+
height: 0,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 标记某节点为选中:写入根元素并置可见。
|
|
30
|
+
* 列表重复项共享同一 id,所有实例都会调用本函数;同一选区(id 相同)只认第一个调用者
|
|
31
|
+
* (先到先得),使高亮落在第一个(与 Ctrl/⌘ 点击定位一致);不同 id 的新选区则正常覆盖。
|
|
32
|
+
* @param {HTMLElement} el 目标根元素(建议传 DOM 中第一个匹配元素,见 RenderTree.updateHighlight)
|
|
33
|
+
* @param {string} id 选中节点的 id(与 selectedNodeId 同源)
|
|
34
|
+
*/
|
|
35
|
+
export function setSelectionEl(el, id) {
|
|
36
|
+
if (selectionOverlay.id === id) return
|
|
37
|
+
selectionOverlay.el = el
|
|
38
|
+
selectionOverlay.id = id
|
|
39
|
+
selectionOverlay.visible = true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 取消选中:仅当传入 id 正是当前高亮选区时才清除,避免误清其它节点的高亮。
|
|
44
|
+
* 列表场景下所有重复实例共享同一 id,首个实例清除后即置空,其余实例因 id 已不符而不再重复清除。
|
|
45
|
+
* @param {string} id 取消选中节点的 id
|
|
46
|
+
*/
|
|
47
|
+
export function clearSelectionEl(id) {
|
|
48
|
+
if (selectionOverlay.id === id) {
|
|
49
|
+
selectionOverlay.el = null
|
|
50
|
+
selectionOverlay.id = null
|
|
51
|
+
selectionOverlay.visible = false
|
|
52
|
+
}
|
|
53
|
+
}
|