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,167 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
进度滑块(官方 module 1981 / 504)。
|
|
3
|
+
结构对齐在线编辑器渲染产物:
|
|
4
|
+
ce_slider > ce_slider_rect(bg) > ce_slider_bar(fg) + ce_slider_con > ce_slider_block
|
|
5
|
+
支持点击 / 拖动改变值(按 step 吸附),与在线交互一致。
|
|
6
|
+
-->
|
|
7
|
+
<template>
|
|
8
|
+
<div class="ce_slider" :style="rootStyle">
|
|
9
|
+
<div
|
|
10
|
+
ref="rect"
|
|
11
|
+
class="ce_slider_rect"
|
|
12
|
+
:style="rectStyle"
|
|
13
|
+
@pointerdown="onDown"
|
|
14
|
+
>
|
|
15
|
+
<div class="ce_slider_bar" :style="barStyle" />
|
|
16
|
+
<div class="ce_slider_con" :style="{ left: conLeft }">
|
|
17
|
+
<div
|
|
18
|
+
class="ce_slider_block"
|
|
19
|
+
:class="{ ce_dragging: dragging }"
|
|
20
|
+
:style="blockStyleObj"
|
|
21
|
+
/>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script>
|
|
28
|
+
import base from './base.js'
|
|
29
|
+
|
|
30
|
+
export default {
|
|
31
|
+
name: 'CeProgressSlider',
|
|
32
|
+
mixins: [base],
|
|
33
|
+
props: {
|
|
34
|
+
styleProps: { type: Object, default: () => ({}) },
|
|
35
|
+
value: { type: [String, Number], default: 0 },
|
|
36
|
+
minProgress: { type: [String, Number], default: 0 },
|
|
37
|
+
maxProgress: { type: [String, Number], default: 100 },
|
|
38
|
+
step: { type: [String, Number], default: 1 },
|
|
39
|
+
backgroundColor: { type: String, default: '#e6e6e6' },
|
|
40
|
+
foregroundColor: { type: String, default: '#0a59f5' },
|
|
41
|
+
processHeight: { type: [String, Number], default: 5 },
|
|
42
|
+
blockStyle: { type: String, default: 'circle' },
|
|
43
|
+
blockColor: { type: String, default: '#ffffff' },
|
|
44
|
+
blockImage: { type: String, default: '' },
|
|
45
|
+
blockWidth: { type: [String, Number], default: 15 },
|
|
46
|
+
blockHeight: { type: [String, Number], default: 15 },
|
|
47
|
+
},
|
|
48
|
+
emits: ['change', 'selectChange'],
|
|
49
|
+
data() {
|
|
50
|
+
return { dragging: false }
|
|
51
|
+
},
|
|
52
|
+
computed: {
|
|
53
|
+
rootStyle() {
|
|
54
|
+
// 注意:滑块根不裁切自身内容。滑块块在 0%/100% 时会落在轨道端点、且
|
|
55
|
+
// 按下时整体放大 1.2×,若这里 overflow:hidden 会把块裁掉。
|
|
56
|
+
return { height: 'auto', boxSizing: 'border-box' }
|
|
57
|
+
},
|
|
58
|
+
min() {
|
|
59
|
+
const n = Number(this.minProgress)
|
|
60
|
+
return Number.isFinite(n) ? n : 0
|
|
61
|
+
},
|
|
62
|
+
max() {
|
|
63
|
+
const n = Number(this.maxProgress)
|
|
64
|
+
return Number.isFinite(n) ? n : 100
|
|
65
|
+
},
|
|
66
|
+
stepVal() {
|
|
67
|
+
const n = Number(this.step)
|
|
68
|
+
return n > 0 ? n : 1
|
|
69
|
+
},
|
|
70
|
+
numValue() {
|
|
71
|
+
const n = Number(this.value)
|
|
72
|
+
return Number.isFinite(n) ? n : this.min
|
|
73
|
+
},
|
|
74
|
+
percent() {
|
|
75
|
+
const span = this.max - this.min || 1
|
|
76
|
+
const p = ((this.numValue - this.min) / span) * 100
|
|
77
|
+
return Math.max(0, Math.min(100, p))
|
|
78
|
+
},
|
|
79
|
+
conLeft() {
|
|
80
|
+
// 滑块块定位:轨道两侧各内缩半个滑块宽,使块在 0%/100% 时仍完整落在节点盒内
|
|
81
|
+
// (对齐线上滑块「滑块块不出轨」的呈现,不被节点 overflow:hidden 裁切)。
|
|
82
|
+
const bw = Number(this.blockWidth) || 15
|
|
83
|
+
const half = bw / 2
|
|
84
|
+
return `calc(${half}px + (100% - ${bw}px) * ${this.percent} / 100)`
|
|
85
|
+
},
|
|
86
|
+
rectStyle() {
|
|
87
|
+
const h = Number(this.processHeight) || 5
|
|
88
|
+
return {
|
|
89
|
+
backgroundColor: this.adapter.doColor(this.backgroundColor),
|
|
90
|
+
height: this.adapter.doSize(h),
|
|
91
|
+
borderRadius: this.adapter.doSize(h),
|
|
92
|
+
marginTop: this.adapter.doSize(8),
|
|
93
|
+
marginBottom: this.adapter.doSize(8),
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
barStyle() {
|
|
97
|
+
const h = Number(this.processHeight) || 5
|
|
98
|
+
const bw = Number(this.blockWidth) || 15
|
|
99
|
+
// 填充条与滑块块两端对齐:从半个滑块宽起,宽度按「(满宽 - 滑块宽) × 百分比」计算,
|
|
100
|
+
// 使 0%/100% 时填充条与滑块块始终落在轨道内,不被节点 overflow:hidden 裁切。
|
|
101
|
+
return {
|
|
102
|
+
backgroundColor: this.adapter.doColor(this.foregroundColor),
|
|
103
|
+
left: this.adapter.doSize(bw / 2),
|
|
104
|
+
width: `calc((100% - ${bw}px) * ${this.percent} / 100)`,
|
|
105
|
+
borderTopLeftRadius: this.adapter.doSize(h),
|
|
106
|
+
borderBottomLeftRadius: this.adapter.doSize(h),
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
blockStyleObj() {
|
|
110
|
+
const w = Number(this.blockWidth) || 15
|
|
111
|
+
const h = Number(this.blockHeight) || 15
|
|
112
|
+
const out = {
|
|
113
|
+
width: this.adapter.doSize(w),
|
|
114
|
+
height: this.adapter.doSize(h),
|
|
115
|
+
boxShadow: 'rgba(0,0,0,0.3) 0 0 3px 0',
|
|
116
|
+
}
|
|
117
|
+
if (this.blockStyle === 'image' && this.blockImage) {
|
|
118
|
+
out.backgroundImage = `url(${this.adapter.doReplaceUrl(this.doI18n(this.blockImage))})`
|
|
119
|
+
out.borderRadius = '0'
|
|
120
|
+
} else if (this.blockStyle === 'circle') {
|
|
121
|
+
out.backgroundColor = this.adapter.doColor(this.blockColor)
|
|
122
|
+
out.borderRadius = this.adapter.doSize(Math.min(w, h) / 2)
|
|
123
|
+
} else {
|
|
124
|
+
out.backgroundColor = this.adapter.doColor(this.blockColor)
|
|
125
|
+
out.borderRadius = this.adapter.doSize(2)
|
|
126
|
+
}
|
|
127
|
+
return out
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
methods: {
|
|
131
|
+
clampStep(v) {
|
|
132
|
+
const clamped = Math.max(this.min, Math.min(this.max, v))
|
|
133
|
+
const stepped = Math.round((clamped - this.min) / this.stepVal) * this.stepVal + this.min
|
|
134
|
+
return Math.max(this.min, Math.min(this.max, stepped))
|
|
135
|
+
},
|
|
136
|
+
valueFromEvent(ev) {
|
|
137
|
+
const el = this.$refs.rect
|
|
138
|
+
if (!el) return this.numValue
|
|
139
|
+
const rect = el.getBoundingClientRect()
|
|
140
|
+
const ratio = (ev.clientX - rect.left) / rect.width
|
|
141
|
+
return this.clampStep(this.min + ratio * (this.max - this.min))
|
|
142
|
+
},
|
|
143
|
+
onDown(ev) {
|
|
144
|
+
this.dragging = true
|
|
145
|
+
this.setValue(this.valueFromEvent(ev))
|
|
146
|
+
window.addEventListener('pointermove', this.onMove)
|
|
147
|
+
window.addEventListener('pointerup', this.onUp)
|
|
148
|
+
},
|
|
149
|
+
onMove(ev) {
|
|
150
|
+
if (!this.dragging) return
|
|
151
|
+
this.setValue(this.valueFromEvent(ev))
|
|
152
|
+
},
|
|
153
|
+
onUp() {
|
|
154
|
+
this.dragging = false
|
|
155
|
+
window.removeEventListener('pointermove', this.onMove)
|
|
156
|
+
window.removeEventListener('pointerup', this.onUp)
|
|
157
|
+
},
|
|
158
|
+
setValue(v) {
|
|
159
|
+
this.$emit('change', v)
|
|
160
|
+
this.$emit('selectChange', v)
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
beforeUnmount() {
|
|
164
|
+
this.onUp()
|
|
165
|
+
},
|
|
166
|
+
}
|
|
167
|
+
</script>
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
类型化属性控件(meta 驱动的分派注册表)。
|
|
3
|
+
|
|
4
|
+
参照 ubomp-h5-sub 的「控件注册 + itemValueFrom 切换」范式:
|
|
5
|
+
- 按 meta.itemType 渲染对应类型化设置器(颜色/数字/枚举/开关/文本/图标/表达式…);
|
|
6
|
+
- 按 meta.itemTypeRange 决定是否显示「值来源」切换:const('0') / var('1') / image('2');
|
|
7
|
+
- 写回:直接 mutate 传入的 Property 实例的 itemValue / itemValueFrom,
|
|
8
|
+
再由父级(PropTree / NodeInspector)统一刷新预览或定点并入样式实例树。
|
|
9
|
+
|
|
10
|
+
本组件只渲染「单个属性」,不负责字段发现(那是 PropTree / NodeInspector 的事)。
|
|
11
|
+
|
|
12
|
+
所有设置器统一 props:value / options / watches / itemInfo / disabled,
|
|
13
|
+
统一 emit:input(新值) / change(触发预览) / sync(批量样式,仅 CustomSelect)。
|
|
14
|
+
-->
|
|
15
|
+
<template>
|
|
16
|
+
<div class="pc" :class="{ 'pc--readonly': disabled }">
|
|
17
|
+
<!-- 值来源切换(仅当 meta 声明了多于一种来源 且 允许切换时显示) -->
|
|
18
|
+
<div class="pc-srcs" v-if="sources.length > 1 && bExchange">
|
|
19
|
+
<button
|
|
20
|
+
v-for="s in sources"
|
|
21
|
+
:key="s.key"
|
|
22
|
+
type="button"
|
|
23
|
+
class="pc-src"
|
|
24
|
+
:class="{ 'is-active': property.itemValueFrom === s.key }"
|
|
25
|
+
:disabled="disabled"
|
|
26
|
+
@click="setSource(s.key)"
|
|
27
|
+
>{{ s.label }}</button>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="pc-body">
|
|
31
|
+
<!-- 图片素材来源 -->
|
|
32
|
+
<SetImage
|
|
33
|
+
v-if="property.itemValueFrom === '2'"
|
|
34
|
+
:value="property.itemValue"
|
|
35
|
+
:options="property.itemOption"
|
|
36
|
+
:item-info="property"
|
|
37
|
+
:disabled="disabled"
|
|
38
|
+
@input="onInput"
|
|
39
|
+
@change="onChange"
|
|
40
|
+
/>
|
|
41
|
+
|
|
42
|
+
<!-- 变量 / 表达式来源:带语法高亮、可拖拽调高度 -->
|
|
43
|
+
<SetExpr
|
|
44
|
+
v-else-if="property.itemValueFrom === '1'"
|
|
45
|
+
:value="property.itemValue"
|
|
46
|
+
:disabled="disabled"
|
|
47
|
+
:item-info="property"
|
|
48
|
+
@input="onInput"
|
|
49
|
+
@change="onChange"
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
<!-- 常量来源:按 itemType 分派 -->
|
|
53
|
+
<component
|
|
54
|
+
v-else
|
|
55
|
+
:is="setterComp.comp"
|
|
56
|
+
:value="property.itemValue"
|
|
57
|
+
:options="property.itemOption"
|
|
58
|
+
:watches="watches"
|
|
59
|
+
:item-info="property"
|
|
60
|
+
:disabled="disabled"
|
|
61
|
+
:kind="setterComp.kind"
|
|
62
|
+
:multiline="setterComp.multiline"
|
|
63
|
+
:boolean="setterComp.boolean"
|
|
64
|
+
@input="onInput"
|
|
65
|
+
@change="onChange"
|
|
66
|
+
@sync="onSync"
|
|
67
|
+
/>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</template>
|
|
71
|
+
|
|
72
|
+
<script>
|
|
73
|
+
import SetText from './setters/SetText.vue'
|
|
74
|
+
import SetNumber from './setters/SetNumber.vue'
|
|
75
|
+
import SetSelect from './setters/SetSelect.vue'
|
|
76
|
+
import SetSegment from './setters/SetSegment.vue'
|
|
77
|
+
import SetSwitch from './setters/SetSwitch.vue'
|
|
78
|
+
import SetColor from './setters/SetColor.vue'
|
|
79
|
+
import SetIconSelect from './setters/SetIconSelect.vue'
|
|
80
|
+
import SetIfForm from './setters/SetIfForm.vue'
|
|
81
|
+
import SetNumberStyle from './setters/SetNumberStyle.vue'
|
|
82
|
+
import SetPadding from './setters/SetPadding.vue'
|
|
83
|
+
import SetGrid9 from './setters/SetGrid9.vue'
|
|
84
|
+
import SetBgPosition from './setters/SetBgPosition.vue'
|
|
85
|
+
import SetBgSize from './setters/SetBgSize.vue'
|
|
86
|
+
import SetGradient from './setters/SetGradient.vue'
|
|
87
|
+
import SetCustomSelect from './setters/SetCustomSelect.vue'
|
|
88
|
+
import SetList from './setters/SetList.vue'
|
|
89
|
+
import SetImage from './setters/SetImage.vue'
|
|
90
|
+
import SetCode from './setters/SetCode.vue'
|
|
91
|
+
import SetExpr from './setters/SetExpr.vue'
|
|
92
|
+
|
|
93
|
+
const FROM_LABEL = { '0': '常量', '1': '变量', '2': '图片' }
|
|
94
|
+
// meta.itemTypeRange 字符串 -> 允许的来源 key
|
|
95
|
+
const RANGE_KEYS = { const: '0', var: '1', image: '2' }
|
|
96
|
+
|
|
97
|
+
/** itemType -> 设置器组件 + 附加 props(kind / multiline / boolean) */
|
|
98
|
+
function resolveSetter(itemType) {
|
|
99
|
+
switch (itemType) {
|
|
100
|
+
case 'InputNumber': return { comp: SetNumber }
|
|
101
|
+
case 'Color': return { comp: SetColor }
|
|
102
|
+
case 'Select': return { comp: SetSelect }
|
|
103
|
+
case 'TabSelect': return { comp: SetSegment, kind: 'TabSelect' }
|
|
104
|
+
case 'Input': return { comp: SetText }
|
|
105
|
+
case 'Textarea': return { comp: SetText, multiline: true }
|
|
106
|
+
case 'Switcher': return { comp: SetSwitch }
|
|
107
|
+
case 'BooleanForm': return { comp: SetSwitch, boolean: true }
|
|
108
|
+
case 'PaddingStyle': return { comp: SetPadding }
|
|
109
|
+
case 'NumberStyle': return { comp: SetNumberStyle }
|
|
110
|
+
case 'PositionStyle': return { comp: SetGrid9, kind: 'PositionStyle' }
|
|
111
|
+
case 'BackgroundPositionStyle': return { comp: SetBgPosition }
|
|
112
|
+
case 'BackgroundSizeStyle': return { comp: SetBgSize }
|
|
113
|
+
case 'GradientStyle': return { comp: SetGradient }
|
|
114
|
+
case 'FontStyle': return { comp: SetSegment, kind: 'FontStyle' }
|
|
115
|
+
case 'TextDecorationStyle': return { comp: SetSegment, kind: 'TextDecorationStyle' }
|
|
116
|
+
case 'JustifyContentStyle': return { comp: SetSegment, kind: 'JustifyContentStyle' }
|
|
117
|
+
case 'AlignItemsStyle': return { comp: SetSegment, kind: 'AlignItemsStyle' }
|
|
118
|
+
case 'IconSelect': return { comp: SetIconSelect }
|
|
119
|
+
case 'CustomSelect': return { comp: SetCustomSelect }
|
|
120
|
+
case 'List': return { comp: SetList }
|
|
121
|
+
case 'IfForm': return { comp: SetIfForm }
|
|
122
|
+
case 'Code': return { comp: SetCode }
|
|
123
|
+
// 未识别 / (none) / 缺省:普通文本框,保证可回填、可编辑
|
|
124
|
+
default: return { comp: SetText }
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export default {
|
|
129
|
+
name: 'PropControl',
|
|
130
|
+
components: {
|
|
131
|
+
SetText, SetNumber, SetSelect, SetSegment, SetSwitch, SetColor,
|
|
132
|
+
SetIconSelect, SetIfForm, SetNumberStyle, SetPadding, SetGrid9,
|
|
133
|
+
SetBgPosition, SetBgSize, SetGradient, SetCustomSelect, SetList, SetImage, SetCode, SetExpr,
|
|
134
|
+
},
|
|
135
|
+
props: {
|
|
136
|
+
property: { type: Object, required: true },
|
|
137
|
+
watches: { type: Object, default: () => ({}) },
|
|
138
|
+
disabled: { type: Boolean, default: false },
|
|
139
|
+
/**
|
|
140
|
+
* 是否允许显示「常量 / 变量 / 图片」来源切换。
|
|
141
|
+
* 对齐线上编辑器 editorOptions.bExchange:指令(directives)区块固定为 false,
|
|
142
|
+
* 故显隐设置 / 具体条件这类指令子项不渲染切换 tab,直接以常量形态呈现。
|
|
143
|
+
*/
|
|
144
|
+
bExchange: { type: Boolean, default: true },
|
|
145
|
+
},
|
|
146
|
+
emits: ['change', 'sync'],
|
|
147
|
+
computed: {
|
|
148
|
+
itemType() {
|
|
149
|
+
return (this.property && this.property.itemType) || ''
|
|
150
|
+
},
|
|
151
|
+
sources() {
|
|
152
|
+
const raw = (this.property && this.property.itemTypeRange) || 'const'
|
|
153
|
+
const keys = String(raw).split(',').map((s) => s.trim()).filter(Boolean)
|
|
154
|
+
let list = keys
|
|
155
|
+
.map((k) => ({ key: RANGE_KEYS[k], label: FROM_LABEL[RANGE_KEYS[k]] }))
|
|
156
|
+
.filter((x) => x.key)
|
|
157
|
+
// 当前 itemValueFrom 若不在声明来源内(schema 越界或缺失),也纳入以免丢值;
|
|
158
|
+
// 但仅当它是合法来源键('0'/'1'/'2')时才塞入,忽略 undefined/非法值,
|
|
159
|
+
// 避免单一来源属性(如 Code 脚本)被误渲染出脏来源按钮。
|
|
160
|
+
const cur = String(this.property.itemValueFrom)
|
|
161
|
+
if (/^[012]$/.test(cur) && !list.some((s) => s.key === cur)) {
|
|
162
|
+
list = list.concat([{ key: cur, label: FROM_LABEL[cur] || '来源' + cur }])
|
|
163
|
+
}
|
|
164
|
+
return list
|
|
165
|
+
},
|
|
166
|
+
setterComp() {
|
|
167
|
+
return resolveSetter(this.itemType)
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
methods: {
|
|
171
|
+
onInput(v) {
|
|
172
|
+
this.property.itemValue = v
|
|
173
|
+
// 输入即触发预览刷新:NodeInspector 的 scheduleApply 自带 250ms 防抖,
|
|
174
|
+
// 故高频输入只会触发一次(约 250ms 后)重建预览,满足「即时预览」预期(问题 #6)。
|
|
175
|
+
this.$emit('change')
|
|
176
|
+
},
|
|
177
|
+
onChange() {
|
|
178
|
+
this.$emit('change')
|
|
179
|
+
},
|
|
180
|
+
onSync(map) {
|
|
181
|
+
this.$emit('sync', map)
|
|
182
|
+
},
|
|
183
|
+
setSource(k) {
|
|
184
|
+
if (this.disabled) return
|
|
185
|
+
const prop = this.property
|
|
186
|
+
const to = String(k)
|
|
187
|
+
if (prop.itemValueFrom === to) return
|
|
188
|
+
// 切换来源:先把「当前来源」的实时值归档进缓存,再切到目标来源;
|
|
189
|
+
// 目标来源有缓存值则原样回填,没有(从未编辑过)才给默认值。
|
|
190
|
+
// 这样每种来源(常量/变量/图片)各存各的,互不复写 —— 修复「切走再切回内容被清空」。
|
|
191
|
+
if (!prop._valuesByFrom) {
|
|
192
|
+
prop._valuesByFrom = { '0': undefined, '1': undefined, '2': undefined }
|
|
193
|
+
}
|
|
194
|
+
prop._valuesByFrom[prop.itemValueFrom] = prop.itemValue
|
|
195
|
+
prop.itemValueFrom = to
|
|
196
|
+
if (prop._valuesByFrom[to] !== undefined) {
|
|
197
|
+
prop.itemValue = prop._valuesByFrom[to]
|
|
198
|
+
} else if (to === '0') {
|
|
199
|
+
const d = prop.itemDefaultValue
|
|
200
|
+
prop.itemValue = d === undefined || d === null ? this.defaultFor() : d
|
|
201
|
+
prop._valuesByFrom[to] = prop.itemValue
|
|
202
|
+
} else {
|
|
203
|
+
// 变量('1') / 图片('2') 首次进入时给空串作为起点(非清空,后续切回会回填)
|
|
204
|
+
prop.itemValue = ''
|
|
205
|
+
prop._valuesByFrom[to] = ''
|
|
206
|
+
}
|
|
207
|
+
this.$emit('change')
|
|
208
|
+
},
|
|
209
|
+
defaultFor() {
|
|
210
|
+
switch (this.itemType) {
|
|
211
|
+
case 'Color': return '#000000'
|
|
212
|
+
case 'InputNumber': return 0
|
|
213
|
+
case 'Switcher': return '0'
|
|
214
|
+
case 'BooleanForm': return false
|
|
215
|
+
case 'TabSelect':
|
|
216
|
+
case 'Select': {
|
|
217
|
+
const opt = this.property.itemOption || {}
|
|
218
|
+
const list = opt.iParamList || opt.iTypeList || []
|
|
219
|
+
return list.length ? list[0].key : ''
|
|
220
|
+
}
|
|
221
|
+
default: return ''
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
}
|
|
226
|
+
</script>
|
|
227
|
+
|
|
228
|
+
<style scoped>
|
|
229
|
+
.pc { display: flex; flex-direction: column; gap: 6px; }
|
|
230
|
+
/* .pc-body 是块级容器,唯一子节点 .st-switch 是 inline-flex(内联级原子盒子)。
|
|
231
|
+
行盒高度由继承的 line-height(body 默认 16px / normal ≈ 21.5px)决定,而非 18px 的开关本身,
|
|
232
|
+
导致 .pc-body 被撑到 21.5px 且开关按 baseline 对齐而非垂直居中。置 0 让行盒坍缩为 18px。 */
|
|
233
|
+
.pc-body { line-height: 0; }
|
|
234
|
+
.pc-srcs { display: flex; gap: 4px; line-height: 1.5; }
|
|
235
|
+
.pc-src {
|
|
236
|
+
flex: 1; font-size: 12px; padding: 3px 0; border: 1px solid var(--border);
|
|
237
|
+
background: transparent; color: var(--text-dim); border-radius: 6px; cursor: pointer;
|
|
238
|
+
}
|
|
239
|
+
.pc-src.is-active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
|
|
240
|
+
.pc--readonly .pc-src { opacity: .6; cursor: not-allowed; }
|
|
241
|
+
</style>
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
属性树递归渲染器。
|
|
3
|
+
|
|
4
|
+
职责(与编辑器右侧面板的字段组织一致):
|
|
5
|
+
- group 类型 / 含 itemChildren 的属性 → 渲染可折叠分组头,递归渲染其子属性;
|
|
6
|
+
- 叶子属性 → 「标签 + 控件」一行,控件交给全局 <prop-control>;
|
|
7
|
+
- 把「从根到当前叶子的链」(Property[]) 透传给父级,父级据此决定:
|
|
8
|
+
* style 模式:调用 NodeEditModel.applyStyleChange(链首分组, 链身);
|
|
9
|
+
* property / directive 模式:整树导出(applyToSchema)。
|
|
10
|
+
|
|
11
|
+
本组件通过全局注册 self-reference(prop-tree)+ 全局 prop-control 实现递归,
|
|
12
|
+
避免与 PropControl 互相 import 造成的循环依赖。
|
|
13
|
+
-->
|
|
14
|
+
<template>
|
|
15
|
+
<div class="ptree" :class="{ 'ptree--plain': plain }">
|
|
16
|
+
<template v-for="p in properties" :key="p.itemId">
|
|
17
|
+
<!-- 分组(自身可见 且 存在可见后代才渲染,避免空分组) -->
|
|
18
|
+
<div v-if="isGroup(p) && groupVisible(p)" class="ptree__group">
|
|
19
|
+
<div class="ptree__group-head" type="button" @click="toggle(p)">
|
|
20
|
+
<span class="ptree__caret" :class="{ 'is-open': isOpen(p) }">▸</span>
|
|
21
|
+
<span class="ptree__group-name">{{ p.itemName || p.itemKey }}</span>
|
|
22
|
+
</div>
|
|
23
|
+
<div v-show="isOpen(p)" class="ptree__group-body">
|
|
24
|
+
<prop-tree
|
|
25
|
+
v-if="p.itemChildren && p.itemChildren.properties.length"
|
|
26
|
+
:properties="p.itemChildren.properties"
|
|
27
|
+
:mode="mode"
|
|
28
|
+
:depth="depth + 1"
|
|
29
|
+
:chain="chain.concat(p)"
|
|
30
|
+
:watches="watches"
|
|
31
|
+
:disabled="disabled"
|
|
32
|
+
:b-exchange="bExchange"
|
|
33
|
+
:plain="plain"
|
|
34
|
+
@leaf-change="$emit('leaf-change', $event)"
|
|
35
|
+
@leaf-sync="$emit('leaf-sync', $event)"
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- 叶子(itemCondition 判定为不可见时不渲染,与线上编辑器一致) -->
|
|
41
|
+
<div
|
|
42
|
+
v-else-if="p.itemVisible !== false"
|
|
43
|
+
class="ptree__row"
|
|
44
|
+
:class="{ 'ptree__row--disabled': disabled, 'ptree__row--block': isBlock(p) }"
|
|
45
|
+
>
|
|
46
|
+
<label class="ptree__label" :title="p.itemDesc || p.itemName || p.itemKey">{{ p.itemName || p.itemKey || p.itemType || '字段' }}</label>
|
|
47
|
+
<div class="ptree__control">
|
|
48
|
+
<prop-control
|
|
49
|
+
:property="p"
|
|
50
|
+
:watches="watches"
|
|
51
|
+
:disabled="disabled"
|
|
52
|
+
:b-exchange="bExchange"
|
|
53
|
+
@change="onLeaf(p)"
|
|
54
|
+
@sync="onSync"
|
|
55
|
+
/>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</template>
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
61
|
+
|
|
62
|
+
<script>
|
|
63
|
+
export default {
|
|
64
|
+
name: 'PropTree',
|
|
65
|
+
props: {
|
|
66
|
+
properties: { type: Array, default: () => [] },
|
|
67
|
+
mode: { type: String, default: 'property' },
|
|
68
|
+
/** 嵌套深度:顶层 ptree 为 1,每深入一层 +1(用于决定是否显示分组计数) */
|
|
69
|
+
depth: { type: Number, default: 1 },
|
|
70
|
+
/** 从根到当前层父分组的链(Property[]),叶子变更时拼接上自身 */
|
|
71
|
+
chain: { type: Array, default: () => [] },
|
|
72
|
+
/** 同级联动快照,供 itemWatch 使用(如 flexDirection 决定对齐图标方向) */
|
|
73
|
+
watches: { type: Object, default: () => ({}) },
|
|
74
|
+
disabled: { type: Boolean, default: false },
|
|
75
|
+
/** 是否允许显示来源切换 tab(指令区块由父级传 false) */
|
|
76
|
+
bExchange: { type: Boolean, default: true },
|
|
77
|
+
/** 无 hover 高亮模式(事件抽屉脚本/参数区用,深/浅主题一致) */
|
|
78
|
+
plain: { type: Boolean, default: false },
|
|
79
|
+
},
|
|
80
|
+
emits: ['leaf-change', 'leaf-sync'],
|
|
81
|
+
data() {
|
|
82
|
+
// 分组默认展开
|
|
83
|
+
return { openMap: {} }
|
|
84
|
+
},
|
|
85
|
+
methods: {
|
|
86
|
+
isGroup(p) {
|
|
87
|
+
if (!p) return false
|
|
88
|
+
if (p.itemType === 'group') return true
|
|
89
|
+
const kids = p.itemChildren && p.itemChildren.properties
|
|
90
|
+
return !!(kids && kids.length)
|
|
91
|
+
},
|
|
92
|
+
childCount(p) {
|
|
93
|
+
const kids = p.itemChildren && p.itemChildren.properties
|
|
94
|
+
return kids ? kids.length : 0
|
|
95
|
+
},
|
|
96
|
+
/** 需要整行铺开的控件(代码编辑器这类高控件,横排会被挤扁) */
|
|
97
|
+
isBlock(p) {
|
|
98
|
+
return p && p.itemType === 'Code'
|
|
99
|
+
},
|
|
100
|
+
/** 分组是否应当渲染:自身可见 且 至少存在一个可见后代(叶子或子分组) */
|
|
101
|
+
groupVisible(p) {
|
|
102
|
+
if (p.itemVisible === false) return false
|
|
103
|
+
const kids = p.itemChildren && p.itemChildren.properties
|
|
104
|
+
if (!kids || !kids.length) return true
|
|
105
|
+
return kids.some((c) => {
|
|
106
|
+
if (c.itemVisible === false) return false
|
|
107
|
+
if (this.isGroup(c)) return this.groupVisible(c)
|
|
108
|
+
return true
|
|
109
|
+
})
|
|
110
|
+
},
|
|
111
|
+
isOpen(p) {
|
|
112
|
+
// 默认展开;仅当显式置为 false 时收起
|
|
113
|
+
return this.openMap[p.itemId] !== false
|
|
114
|
+
},
|
|
115
|
+
toggle(p) {
|
|
116
|
+
this.openMap[p.itemId] = this.openMap[p.itemId] === false
|
|
117
|
+
// 触发响应式
|
|
118
|
+
this.openMap = Object.assign({}, this.openMap)
|
|
119
|
+
},
|
|
120
|
+
onLeaf(p) {
|
|
121
|
+
this.$emit('leaf-change', this.chain.concat(p))
|
|
122
|
+
},
|
|
123
|
+
onSync(map) {
|
|
124
|
+
this.$emit('leaf-sync', map)
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
</script>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
单选按钮(官方 module 1979 / 502)。
|
|
3
|
+
结构对齐在线编辑器渲染产物:
|
|
4
|
+
ce_radio_button > ce_radio_item > ce_radio_icon(svg) + span(text)
|
|
5
|
+
RadioButton 在 DSL 里是「单个独立单选」,无分组 key;
|
|
6
|
+
selected 状态由本地维护(点击切换),选中色用 selectColor、未选中用 unSelectColor。
|
|
7
|
+
-->
|
|
8
|
+
<template>
|
|
9
|
+
<span class="ce_radio_button" :style="rootStyle">
|
|
10
|
+
<div class="ce_radio_item" @click="toggle">
|
|
11
|
+
<span
|
|
12
|
+
v-if="buttonStyle !== 'Text'"
|
|
13
|
+
class="ce_radio_icon"
|
|
14
|
+
:style="{ width: iconSize + 'px', height: iconSize + 'px', marginRight: textSpacing + 'px' }"
|
|
15
|
+
>
|
|
16
|
+
<svg class="ce_svgradiostyle" :viewBox="'0 0 1024 1024'" :style="{ fill: color, stroke: color }">
|
|
17
|
+
<path v-for="(d, i) in radioPaths" :key="i" :d="d" />
|
|
18
|
+
</svg>
|
|
19
|
+
</span>
|
|
20
|
+
<span v-if="showText === '1' || showText === 1" :style="{ color, fontSize: fontSize + 'px' }">{{ doI18n(text) }}</span>
|
|
21
|
+
</div>
|
|
22
|
+
</span>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script>
|
|
26
|
+
import base from './base.js'
|
|
27
|
+
import { RADIO_PATHS } from './icons.js'
|
|
28
|
+
|
|
29
|
+
export default {
|
|
30
|
+
name: 'CeRadioButton',
|
|
31
|
+
mixins: [base],
|
|
32
|
+
props: {
|
|
33
|
+
styleProps: { type: Object, default: () => ({}) },
|
|
34
|
+
value: { type: [String, Number], default: '' },
|
|
35
|
+
selectColor: { type: String, default: '#0082ff' },
|
|
36
|
+
unSelectColor: { type: String, default: '#333333' },
|
|
37
|
+
buttonStyle: { type: String, default: 'Circle' },
|
|
38
|
+
textSpacing: { type: [String, Number], default: '3' },
|
|
39
|
+
fontSize: { type: [String, Number], default: '14' },
|
|
40
|
+
iconSize: { type: [String, Number], default: '14' },
|
|
41
|
+
showText: { type: [String, Number], default: '1' },
|
|
42
|
+
text: { type: [String, Number], default: '' },
|
|
43
|
+
},
|
|
44
|
+
emits: ['change', 'selectChange'],
|
|
45
|
+
computed: {
|
|
46
|
+
rootStyle() {
|
|
47
|
+
return { overflow: 'hidden', boxSizing: 'border-box' }
|
|
48
|
+
},
|
|
49
|
+
selected() {
|
|
50
|
+
const v = this.value
|
|
51
|
+
return v !== '' && v != null && v !== false && v !== '0'
|
|
52
|
+
},
|
|
53
|
+
color() {
|
|
54
|
+
return this.selected ? this.selectColor : this.unSelectColor
|
|
55
|
+
},
|
|
56
|
+
radioPaths() {
|
|
57
|
+
if (this.selected && this.buttonStyle === 'CircleEmpty') {
|
|
58
|
+
return RADIO_PATHS.RadioButtonCircleEmptyCheck
|
|
59
|
+
}
|
|
60
|
+
if (this.buttonStyle === 'CircleEmpty') {
|
|
61
|
+
return RADIO_PATHS.RadioButtonCircleEmpty
|
|
62
|
+
}
|
|
63
|
+
return RADIO_PATHS.RadioButtonCircle
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
methods: {
|
|
67
|
+
toggle() {
|
|
68
|
+
const next = this.selected ? '' : (this.value || '1')
|
|
69
|
+
this.$emit('change', next)
|
|
70
|
+
this.$emit('selectChange', !!next)
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
</script>
|