@rockn/vue-formula-editor 1.0.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/dist/index.js ADDED
@@ -0,0 +1,1537 @@
1
+ import { defineComponent as mt, ref as m, watch as z, computed as I, openBlock as r, createElementBlock as u, normalizeStyle as Y, normalizeClass as N, createElementVNode as n, toDisplayString as b, createTextVNode as p, createCommentVNode as g, createVNode as re, Transition as ue, withCtx as ce, Fragment as T, renderList as C, unref as De, withDirectives as de, vModelText as pe, withModifiers as fe, nextTick as P } from "vue";
2
+ import { tokenize as ve, formatValue as Ue, valueTypeName as gt, BUILTIN_FUNCTIONS as Me, evaluateFormula as Pe, validateFormula as ht, formatFormula as Et } from "./engine/index.js";
3
+ import { ParseError as jl, RuntimeError as Kl, evaluate as ql, lexErrors as Gl, parse as Xl, parseFormula as zl } from "./engine/index.js";
4
+ const Tt = { class: "fe-header" }, St = { class: "fe-title-wrap" }, It = { class: "fe-title-box" }, Nt = { class: "fe-title" }, Ot = { class: "fe-subtitle" }, Lt = { class: "fe-header-actions" }, xt = ["disabled", "title"], Ct = ["disabled", "title"], Rt = ["disabled"], At = ["disabled"], Ft = ["disabled"], $t = ["disabled"], _t = ["disabled", "title"], wt = { class: "fe-btn-ico" }, Dt = {
5
+ key: 0,
6
+ class: "fe-help"
7
+ }, Ut = { class: "fe-help-col" }, Mt = { class: "fe-help-fns" }, Pt = {
8
+ key: 0,
9
+ class: "fe-toolbar"
10
+ }, Vt = { class: "fe-toolbar-group" }, Ht = ["disabled", "title", "onClick"], Bt = { class: "fe-body" }, Wt = {
11
+ key: 0,
12
+ class: "fe-vars"
13
+ }, Yt = { class: "fe-vars-head" }, jt = { class: "fe-vars-count" }, Kt = { class: "fe-vars-search" }, qt = ["disabled"], Gt = { class: "fe-vars-list" }, Xt = ["title", "onClick"], zt = { class: "fe-var-line1" }, Qt = ["title", "onClick"], Zt = { class: "fe-var-name" }, Jt = {
14
+ key: 1,
15
+ class: "fe-var-req",
16
+ title: "必填"
17
+ }, el = { class: "fe-var-desc" }, tl = {
18
+ key: 0,
19
+ class: "fe-var-sub"
20
+ }, ll = ["title", "onClick"], nl = {
21
+ key: 1,
22
+ class: "fe-var-sub"
23
+ }, sl = ["title", "onClick"], al = {
24
+ key: 0,
25
+ class: "fe-var-sub-empty"
26
+ }, ol = {
27
+ key: 0,
28
+ class: "fe-vars-empty"
29
+ }, il = { key: 0 }, rl = { key: 1 }, ul = {
30
+ key: 1,
31
+ class: "fe-fns"
32
+ }, cl = { class: "fe-vars-head" }, dl = { class: "fe-vars-count" }, pl = { class: "fe-vars-search" }, fl = ["disabled"], vl = { class: "fe-vars-list" }, bl = ["title", "onClick"], yl = { class: "fe-fn-line1" }, kl = { class: "fe-fn-name" }, ml = { class: "fe-fn-group" }, gl = { class: "fe-fn-sig" }, hl = { class: "fe-fn-desc" }, El = {
33
+ key: 0,
34
+ class: "fe-vars-empty"
35
+ }, Tl = { class: "fe-editor" }, Sl = { class: "fe-code-shell" }, Il = { class: "fe-gutter" }, Nl = ["innerHTML"], Ol = ["placeholder", "disabled", "readonly"], Ll = {
36
+ key: 0,
37
+ class: "fe-errors"
38
+ }, xl = ["onClick"], Cl = { class: "fe-error-msg" }, Rl = { class: "fe-error-kind" }, Al = { class: "fe-footer" }, Fl = { class: "fe-foot-left" }, $l = {
39
+ key: 0,
40
+ class: "fe-foot-err-count"
41
+ }, _l = ["title"], wl = {
42
+ key: 0,
43
+ class: "fe-result-type"
44
+ }, Dl = {
45
+ key: 1,
46
+ class: "fe-result-value empty"
47
+ }, Ul = { class: "fe-foot-caret" }, Ml = /* @__PURE__ */ mt({
48
+ __name: "FormulaEditor",
49
+ props: {
50
+ modelValue: { default: "" },
51
+ variables: { default: () => [] },
52
+ context: { default: () => ({}) },
53
+ functions: { default: () => ({}) },
54
+ functionSpecs: { default: () => ({}) },
55
+ title: { default: "公式编辑器" },
56
+ subtitle: { default: "FORMULA EDITOR" },
57
+ placeholder: { default: '例如:IF {订单金额} >= 199 THEN "免运费" ELSE "收运费"' },
58
+ height: { default: "340px" },
59
+ disabled: { type: Boolean, default: !1 },
60
+ readonly: { type: Boolean, default: !1 },
61
+ showVariablesPanel: { type: Boolean, default: !0 },
62
+ showToolbar: { type: Boolean, default: !0 },
63
+ showPreview: { type: Boolean, default: !0 },
64
+ theme: { default: "dark" }
65
+ },
66
+ emits: ["update:modelValue", "change", "error", "insert", "update:theme"],
67
+ setup(y, { expose: Q, emit: j }) {
68
+ const f = y, L = j, v = m(f.modelValue), $ = m(f.theme), V = m(f.showVariablesPanel), H = m(!1), K = m(!1), Z = m(!1), J = m(""), ee = m(""), q = m(0), be = m(0), G = m({ line: 1, col: 1 });
69
+ z(
70
+ () => f.modelValue,
71
+ (e) => {
72
+ e !== v.value && (v.value = e);
73
+ }
74
+ ), z(
75
+ () => f.theme,
76
+ (e) => {
77
+ e && ($.value = e);
78
+ }
79
+ );
80
+ const h = m(null), Ve = m(null), ye = m(null), x = m(null), ke = /* @__PURE__ */ new Set(["IF", "ELSE", "THEN", "ELSEIF", "如果", "那么", "否则", "否则如果"]), me = /* @__PURE__ */ new Set(["AND", "OR"]);
81
+ function te(e) {
82
+ return ke.has(e) ? !0 : /^[A-Za-z]+$/.test(e) ? ke.has(e.toUpperCase()) : !1;
83
+ }
84
+ function ge(e) {
85
+ return me.has(e) ? !0 : /^[A-Za-z]+$/.test(e) ? me.has(e.toUpperCase()) : !1;
86
+ }
87
+ const le = I(() => ve(v.value)), B = m({ ok: !0, errors: [], ast: null }), _ = m(null);
88
+ let he;
89
+ function He() {
90
+ window.clearTimeout(he), he = window.setTimeout(() => {
91
+ B.value = ht(
92
+ v.value,
93
+ f.variables,
94
+ f.functions,
95
+ Object.keys(f.context)
96
+ ), _.value = v.value.trim() ? Pe(v.value, f.context, f.functions, f.variables) : null;
97
+ }, 120);
98
+ }
99
+ z(
100
+ [v, () => f.variables, () => f.context, () => f.functions],
101
+ He,
102
+ { immediate: !0 }
103
+ );
104
+ const Be = I(() => v.value.split(`
105
+ `).length), We = I(() => 12 + (G.value.line - 1) * 21.25 - q.value), Ee = I(() => {
106
+ const e = J.value.trim().toLowerCase();
107
+ return e ? f.variables.filter(
108
+ (t) => (t.name + " " + (t.label ?? "") + " " + (t.description ?? "")).toLowerCase().includes(e)
109
+ ) : f.variables;
110
+ }), M = I(() => B.value.errors), Ye = I(() => {
111
+ const e = /* @__PURE__ */ new Set();
112
+ for (const t of M.value) {
113
+ const l = v.value.slice(0, t.start).split(`
114
+ `).length;
115
+ e.add(l);
116
+ }
117
+ return e;
118
+ }), je = I(() => {
119
+ const e = M.value;
120
+ return e.length === 0 ? { label: "公式有效", cls: "ok" } : { label: `${e.length} 个问题`, cls: "bad" };
121
+ }), Ke = I(() => {
122
+ const e = _.value;
123
+ return e ? e.ok ? Ue(e.value) : e.error.message : null;
124
+ }), Te = I(() => {
125
+ const e = _.value;
126
+ return e && e.ok ? gt(e.value) : "";
127
+ }), qe = I(() => {
128
+ const e = _.value;
129
+ return e ? e.ok ? "ok" : "bad" : "empty";
130
+ });
131
+ function ne(e) {
132
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
133
+ }
134
+ const Se = m(0);
135
+ let w = null;
136
+ function Ge(e) {
137
+ const t = Se.value;
138
+ let l = -1;
139
+ for (let i = 0; i < e.length; i++) {
140
+ const c = e[i];
141
+ if (c.type === "eof") break;
142
+ if (t >= c.start && t <= c.end) {
143
+ l = i;
144
+ break;
145
+ }
146
+ }
147
+ if (l < 0) return null;
148
+ const a = { lparen: "rparen", lbracket: "rbracket" }, s = { rparen: "lparen", rbracket: "lbracket" }, o = e[l];
149
+ if (o.type in a) {
150
+ let i = 0;
151
+ for (let c = l; c < e.length; c++) {
152
+ const d = e[c];
153
+ if (d.type === "eof") break;
154
+ if (d.type === o.type) i++;
155
+ else if (d.type === a[o.type] && (i--, i === 0))
156
+ return { a: l, b: c };
157
+ }
158
+ } else if (o.type in s) {
159
+ let i = 0;
160
+ for (let c = l; c >= 0; c--) {
161
+ const d = e[c];
162
+ if (d.type === "eof") break;
163
+ if (d.type === o.type) i++;
164
+ else if (d.type === s[o.type] && (i--, i === 0))
165
+ return { a: c, b: l };
166
+ }
167
+ }
168
+ return null;
169
+ }
170
+ function Xe(e, t, l) {
171
+ if (l !== void 0 && (l === (w == null ? void 0 : w.a) || l === (w == null ? void 0 : w.b)))
172
+ return "tok-pair";
173
+ switch (e.type) {
174
+ case "number":
175
+ return "tok-num";
176
+ case "string":
177
+ return e.closed === !1 ? "tok-err" : "tok-str";
178
+ case "variable":
179
+ return e.closed === !1 ? "tok-err" : "tok-var";
180
+ case "identifier":
181
+ return te(e.value) && (t == null ? void 0 : t.type) !== "lparen" ? "tok-kw" : ge(e.value) && (t == null ? void 0 : t.type) !== "lparen" ? "tok-op" : "tok-fn";
182
+ case "operator":
183
+ return "tok-op";
184
+ case "lparen":
185
+ case "rparen":
186
+ case "lbracket":
187
+ case "rbracket":
188
+ case "comma":
189
+ case "question":
190
+ case "colon":
191
+ return "tok-paren";
192
+ case "error":
193
+ return "tok-err";
194
+ default:
195
+ return "";
196
+ }
197
+ }
198
+ const ze = I(() => {
199
+ let e = "", t = 0;
200
+ const l = le.value;
201
+ w = Ge(l);
202
+ for (let a = 0; a < l.length; a++) {
203
+ const s = l[a];
204
+ if (s.type === "eof") break;
205
+ e += ne(v.value.slice(t, s.start)), e += `<span class="${Xe(s, l[a + 1], a)}">${ne(s.raw)}</span>`, t = s.end;
206
+ }
207
+ return e += ne(v.value.slice(t)), e + `
208
+ `;
209
+ });
210
+ function Qe() {
211
+ var t;
212
+ const e = ((t = h.value) == null ? void 0 : t.value) ?? v.value;
213
+ e !== v.value && (v.value = e), L("update:modelValue", e), L("change", e);
214
+ }
215
+ z(B, (e) => L("error", e));
216
+ function O() {
217
+ const e = h.value;
218
+ if (!e) return;
219
+ const t = e.selectionStart;
220
+ Se.value = t;
221
+ const l = v.value.slice(0, t), a = l.split(`
222
+ `).length, s = t - l.lastIndexOf(`
223
+ `);
224
+ G.value = { line: a, col: s };
225
+ }
226
+ function Ze() {
227
+ const e = h.value;
228
+ e && (q.value = e.scrollTop, be.value = e.scrollLeft);
229
+ }
230
+ function Je(e, t) {
231
+ var l, a;
232
+ if (e.type === "variable" && e.closed !== !1) {
233
+ const s = f.variables.find((A) => A.name === e.value), o = s != null && s.type ? `[${Fe(s.type)}]` : "", i = s != null && s.required ? " 必填" : "", c = s != null && s.description ? `
234
+ ${s.description}` : "", d = s != null && s.label && s.label !== s.name ? `(${s.label})` : "", E = f.context[e.value];
235
+ let k = "";
236
+ E !== void 0 && (k = `
237
+ 当前值: ${Ue(E, 60)}`);
238
+ let R = "";
239
+ (s == null ? void 0 : s.type) === "enum" && s.options && (R = `
240
+ 标签: ${s.options.map((U) => typeof U == "string" ? U : U.label).join(" / ")}`);
241
+ let D = "";
242
+ return (s == null ? void 0 : s.type) === "object" && s.fields && s.fields.length > 0 && (D = `
243
+ 字段: ${s.fields.map((A) => A.key).join(" / ")}`), `变量 {${e.value}}${d} ${o}${i}${c}${k}${R}${D}`;
244
+ }
245
+ if (e.type === "identifier") {
246
+ if ((t == null ? void 0 : t.type) === "lparen") {
247
+ const i = e.value.toUpperCase(), c = Me[i];
248
+ if (c) {
249
+ const d = (l = c.params) != null && l.length ? `
250
+ ` + c.params.map((k, R) => `${R + 1}. ${k}`).join(`
251
+ `) : "", E = c.example ? `
252
+ 示例: ${c.example}` : "";
253
+ return `${c.signature}
254
+ ${c.description}${d}${E}`;
255
+ }
256
+ if (e.value in f.functions) {
257
+ const d = f.functionSpecs[e.value];
258
+ if (d) {
259
+ const E = (a = d.params) != null && a.length ? `
260
+ ` + d.params.map((R, D) => `${D + 1}. ${R}`).join(`
261
+ `) : "", k = d.example ? `
262
+ 示例: ${d.example}` : "";
263
+ return `${d.signature}
264
+ ${d.description}${E}${k}`;
265
+ }
266
+ return `自定义函数 ${e.value}(...)`;
267
+ }
268
+ return null;
269
+ }
270
+ const s = e.value.toUpperCase(), o = {
271
+ IF: `IF 条件 THEN 真值 ELSE IF 条件 THEN 真值 … ELSE 假值
272
+ 块状条件判断,条件满足即返回对应分支,支持多级 ELSE IF`,
273
+ ELSEIF: `ELSE IF 条件 THEN 真值
274
+ 在前一个条件不满足时继续判断`,
275
+ THEN: `IF 条件 THEN 真值
276
+ 条件与结果之间的分隔关键字`,
277
+ ELSE: `ELSE 假值
278
+ 所有条件都不满足时的兜底分支`,
279
+ 如果: `如果 条件 那么 真值 否则 假值
280
+ 块状条件判断(中文关键字写法)`,
281
+ 那么: `如果 条件 那么 真值
282
+ 条件与结果之间的分隔关键字(中文)`,
283
+ 否则: `否则 假值
284
+ 所有条件都不满足时的兜底分支(中文)`,
285
+ 否则如果: `否则如果 条件 那么 真值
286
+ 在前一个条件不满足时继续判断(中文)`
287
+ };
288
+ if (te(e.value)) return o[s] ?? o[e.value] ?? null;
289
+ if (ge(e.value))
290
+ return e.value.toUpperCase() === "AND" ? "AND — 逻辑与(两边同时为真)" : "OR — 逻辑或(任一边为真)";
291
+ }
292
+ return e.type === "operator" ? {
293
+ "&&": "逻辑与(同时满足)",
294
+ "||": "逻辑或(任一满足)",
295
+ "==": '宽松等于(数字 5 与字符串 "5" 视为相等)',
296
+ "===": "严格等于(类型也必须相同)",
297
+ "!=": "宽松不等于",
298
+ "!==": "严格不等于",
299
+ "<": "小于",
300
+ "<=": "小于等于",
301
+ ">": "大于",
302
+ ">=": "大于等于",
303
+ "+": "加(数字相加;含字符串时拼接)",
304
+ "-": "减",
305
+ "*": "乘",
306
+ "/": "除(除数不能为 0)",
307
+ "%": "取余",
308
+ "**": "幂运算",
309
+ "!": "逻辑非"
310
+ }[e.value] ?? null : null;
311
+ }
312
+ function et(e) {
313
+ const t = h.value, l = ye.value;
314
+ if (!t || !l) {
315
+ x.value = null;
316
+ return;
317
+ }
318
+ const a = document;
319
+ let s = null;
320
+ if (a.caretRangeFromPoint) {
321
+ const k = a.caretRangeFromPoint(e.clientX, e.clientY);
322
+ k && (k.startContainer === t || t.contains(k.startContainer)) && (s = k.startOffset);
323
+ } else if (a.caretPositionFromPoint) {
324
+ const k = a.caretPositionFromPoint(e.clientX, e.clientY);
325
+ k && (s = k.offset);
326
+ }
327
+ if (s == null) {
328
+ x.value = null;
329
+ return;
330
+ }
331
+ const o = le.value, i = o.find((k) => k.type !== "eof" && s >= k.start && s < k.end);
332
+ if (!i) {
333
+ x.value = null;
334
+ return;
335
+ }
336
+ const c = o.indexOf(i), d = Je(i, o[c + 1]);
337
+ if (!d) {
338
+ x.value = null;
339
+ return;
340
+ }
341
+ const E = l.getBoundingClientRect();
342
+ x.value = { text: d, x: e.clientX - E.left, y: e.clientY - E.top };
343
+ }
344
+ function tt() {
345
+ x.value = null;
346
+ }
347
+ function lt() {
348
+ const e = h.value;
349
+ if (!e || f.disabled || f.readonly) return;
350
+ const t = e.selectionStart, l = Ne(t);
351
+ l && (e.setSelectionRange(l.start, l.end), O());
352
+ }
353
+ function Ie(e) {
354
+ let t = null, l = !1;
355
+ const a = v.value, s = Math.min(e, a.length);
356
+ for (let o = 0; o < s; o++) {
357
+ const i = a[o];
358
+ t ? l ? l = !1 : i === "\\" ? l = !0 : i === t && (t = null) : (i === "'" || i === '"') && (t = i);
359
+ }
360
+ return t !== null;
361
+ }
362
+ function Ne(e) {
363
+ const t = v.value, l = t.length;
364
+ if (e < l && t[e] === "{") {
365
+ const o = t.indexOf("}", e + 1);
366
+ return o >= 0 ? { start: e, end: o + 1 } : null;
367
+ }
368
+ if (e > 0 && t[e - 1] === "}") {
369
+ const o = e - 1;
370
+ for (let i = o - 1; i >= 0; i--)
371
+ if (t[i] === "{") return { start: i, end: o + 1 };
372
+ return null;
373
+ }
374
+ let a = -1;
375
+ for (let o = e - 1; o >= 0; o--) {
376
+ const i = t[o];
377
+ if (i === "}") break;
378
+ if (i === "{") {
379
+ a = o;
380
+ break;
381
+ }
382
+ }
383
+ if (a < 0) return null;
384
+ const s = t.indexOf("}", a + 1);
385
+ return s >= 0 ? { start: a, end: s + 1 } : null;
386
+ }
387
+ function W(e, t, l, a = "insertText") {
388
+ const s = h.value;
389
+ if (!s || e > t) return;
390
+ s.focus(), s.setSelectionRange(e, t);
391
+ const o = s.value;
392
+ let i = !1;
393
+ try {
394
+ i = l === "" ? document.execCommand("delete") : document.execCommand("insertText", !1, l);
395
+ } catch {
396
+ i = !1;
397
+ }
398
+ if (!i || s.value === o) {
399
+ if (s.value === o && s.value.slice(e, t) === l) return;
400
+ s.setRangeText(l, e, t), s.dispatchEvent(
401
+ new InputEvent("input", {
402
+ inputType: a,
403
+ data: l === "" ? null : l,
404
+ bubbles: !0
405
+ })
406
+ );
407
+ }
408
+ }
409
+ function nt(e, t, l) {
410
+ W(e, t, l, "deleteContent");
411
+ }
412
+ function Oe(e) {
413
+ var i;
414
+ const t = v.value, l = t.lastIndexOf(`
415
+ `, e - 1) + 1, a = t.slice(l, e), s = ((i = /^[ \t]*/.exec(a)) == null ? void 0 : i[0]) ?? "", o = a.trim();
416
+ return /THEN$/i.test(o) || /那么$/.test(o) || /^ELSE$/i.test(o) || o === "否则" ? s + " " : s;
417
+ }
418
+ function st(e, t) {
419
+ const l = le.value;
420
+ let a = null;
421
+ for (const s of l) {
422
+ if (s.type === "eof") break;
423
+ s.start < e || s.end > t || s.type === "identifier" && te(s.value) && /^(ELSE|否则)$/i.test(s.value) && (a = s);
424
+ }
425
+ return a;
426
+ }
427
+ function at(e) {
428
+ var _e;
429
+ const t = v.value, l = t.lastIndexOf(`
430
+ `, e - 1) + 1, a = t.indexOf(`
431
+ `, e), s = a === -1 ? t.length : a, o = st(l, s);
432
+ if (!o || e < o.start - 1 || e > o.end) return !1;
433
+ const c = t.slice(l, o.start).trimEnd();
434
+ if (c.trim() === "") return !1;
435
+ const d = ((_e = /^[ \t]*/.exec(c)) == null ? void 0 : _e[0]) ?? "", E = /^(IF|如果)(?=\s)/i.test(c.trim()) ? "" : d.length >= 2 ? d.slice(0, -2) : "", k = t.slice(o.end, s), R = E + " ", D = c + `
436
+ ` + E + o.raw + `
437
+ ` + R;
438
+ let A = s, U = k.trim();
439
+ if (U === "" && t[s] === `
440
+ `) {
441
+ let F = s + 1;
442
+ for (; F < t.length && (t[F] === " " || t[F] === " "); ) F++;
443
+ A = F, U = "";
444
+ }
445
+ W(l, A, D + U, "insertText");
446
+ const $e = l + D.length;
447
+ return P(() => {
448
+ var F, we;
449
+ (F = h.value) == null || F.focus(), (we = h.value) == null || we.setSelectionRange($e, $e), O();
450
+ }), !0;
451
+ }
452
+ function ot(e) {
453
+ const t = h.value;
454
+ if (!t || f.disabled || f.readonly) return;
455
+ if (e.key === "Tab") {
456
+ e.preventDefault(), S(" ");
457
+ return;
458
+ }
459
+ if (t.selectionStart !== t.selectionEnd) {
460
+ if (e.key === "Enter" && !e.isComposing) {
461
+ const a = t.selectionStart, s = t.selectionEnd, o = v.value.slice(a, s);
462
+ if (o.length > 2 && o.startsWith("{") && o.endsWith("}") && !o.slice(1, -1).includes("{") && !o.slice(1, -1).includes("}")) {
463
+ e.preventDefault(), t.setSelectionRange(s, s), S(`
464
+ ` + Oe(s));
465
+ return;
466
+ }
467
+ }
468
+ return;
469
+ }
470
+ const l = t.selectionStart;
471
+ if (e.key === "Backspace" || e.key === "Delete") {
472
+ if (!Ie(l)) {
473
+ const a = Ne(l);
474
+ a && l > a.start && l < a.end && (e.preventDefault(), nt(a.start, a.end, ""), P(() => {
475
+ t.focus(), t.setSelectionRange(a.start, a.start), O();
476
+ }));
477
+ }
478
+ return;
479
+ }
480
+ if (e.key === "Enter" && !e.isComposing) {
481
+ if (e.preventDefault(), at(l)) return;
482
+ S(`
483
+ ` + Oe(l));
484
+ return;
485
+ }
486
+ if ((e.key === "{" || e.key === '"' || e.key === "'") && !e.ctrlKey && !e.metaKey && !e.altKey && !e.isComposing) {
487
+ if (e.preventDefault(), e.key === "{") {
488
+ S("{}"), P(() => {
489
+ t.focus(), t.setSelectionRange(l + 1, l + 1), O();
490
+ });
491
+ return;
492
+ }
493
+ const a = e.key, s = v.value[l];
494
+ Ie(l) || s === a ? S(a) : (S(a + a), P(() => {
495
+ t.focus(), t.setSelectionRange(l + 1, l + 1), O();
496
+ }));
497
+ return;
498
+ }
499
+ }
500
+ function se() {
501
+ var e;
502
+ (e = h.value) == null || e.focus();
503
+ }
504
+ function it() {
505
+ const e = $.value === "dark" ? "light" : "dark";
506
+ $.value = e, L("update:theme", e);
507
+ }
508
+ function S(e, t) {
509
+ const l = h.value;
510
+ if (!l) return;
511
+ const a = l.selectionStart ?? v.value.length, s = l.selectionEnd ?? a;
512
+ W(a, s, e, "insertText"), P(() => {
513
+ if (l.focus(), t) {
514
+ const i = t.exec(e);
515
+ if (i && i.index >= 0) {
516
+ l.setSelectionRange(a + i.index, a + i.index + i[0].length);
517
+ return;
518
+ }
519
+ }
520
+ const o = a + e.length;
521
+ l.setSelectionRange(o, o), O();
522
+ });
523
+ }
524
+ function rt(e) {
525
+ S(`{${e}}`), L("insert", { type: "variable", value: e });
526
+ }
527
+ function Le(e) {
528
+ S(e.snippet, e.select);
529
+ }
530
+ function ut(e, t) {
531
+ const l = h.value;
532
+ l && (l.focus(), l.setSelectionRange(e, t), l.scrollTop = l.scrollTop, O());
533
+ }
534
+ function ct() {
535
+ W(0, v.value.length, "", "deleteContent"), se();
536
+ }
537
+ function dt() {
538
+ var i;
539
+ const e = v.value;
540
+ if (!e.trim()) return;
541
+ const t = Et(e);
542
+ if (t === e) {
543
+ se();
544
+ return;
545
+ }
546
+ const l = ((i = h.value) == null ? void 0 : i.selectionStart) ?? e.length, a = ve(e);
547
+ let s = null;
548
+ for (let c = 0; c < a.length; c++) {
549
+ const d = a[c];
550
+ if (d.type === "eof") break;
551
+ if (l >= d.start && l <= d.end) {
552
+ s = d;
553
+ break;
554
+ }
555
+ }
556
+ W(0, e.length, t, "insertText");
557
+ let o = t.length;
558
+ if (s) {
559
+ const d = ve(t).find(
560
+ (E) => E.type !== "eof" && E.type === s.type && E.raw === s.raw
561
+ );
562
+ d && (o = d.start);
563
+ }
564
+ P(() => {
565
+ var c, d;
566
+ (c = h.value) == null || c.focus(), (d = h.value) == null || d.setSelectionRange(o, o), O();
567
+ });
568
+ }
569
+ async function pt() {
570
+ try {
571
+ await navigator.clipboard.writeText(v.value);
572
+ } catch {
573
+ const e = h.value;
574
+ e && (e.select(), document.execCommand("copy"), e.setSelectionRange(e.value.length, e.value.length));
575
+ }
576
+ }
577
+ const ae = [
578
+ {
579
+ key: "if",
580
+ label: "IF…ELSE",
581
+ title: "块状 IF:IF 条件 THEN 真值 ELSE 假值(支持多级 ELSE IF)",
582
+ group: "logic",
583
+ snippet: `IF 条件 THEN
584
+ 真值
585
+ ELSE
586
+ 假值`,
587
+ select: /条件/
588
+ },
589
+ {
590
+ key: "elseif",
591
+ label: "ELSE IF",
592
+ title: "追加条件分支:ELSE IF 条件 THEN 真值",
593
+ group: "logic",
594
+ snippet: `ELSE IF 条件 THEN
595
+ 真值`,
596
+ select: /条件/
597
+ },
598
+ {
599
+ key: "ifs",
600
+ label: "IFS",
601
+ title: "IFS(条件1, 值1, 条件2, 值2, 默认值) — 多条件判断",
602
+ group: "logic",
603
+ snippet: "IFS(条件1, 值1, 条件2, 值2, 默认值)",
604
+ select: /条件1/
605
+ },
606
+ {
607
+ key: "ternary",
608
+ label: "a?b:c",
609
+ title: "三元表达式:条件 ? 真值 : 假值",
610
+ group: "logic",
611
+ snippet: "条件 ? 真值 : 假值",
612
+ select: /条件/
613
+ },
614
+ {
615
+ key: "and",
616
+ label: "AND",
617
+ title: "AND(条件1, 条件2) — 全部满足",
618
+ group: "logic",
619
+ snippet: "AND(条件1, 条件2)",
620
+ select: /条件1/
621
+ },
622
+ {
623
+ key: "or",
624
+ label: "OR",
625
+ title: "OR(条件1, 条件2) — 任一满足",
626
+ group: "logic",
627
+ snippet: "OR(条件1, 条件2)",
628
+ select: /条件1/
629
+ },
630
+ {
631
+ key: "not",
632
+ label: "NOT",
633
+ title: "NOT(条件) — 取反",
634
+ group: "logic",
635
+ snippet: "NOT(条件)",
636
+ select: /条件/
637
+ },
638
+ { key: "add", label: "+", title: "加法", group: "math", snippet: " + " },
639
+ { key: "sub", label: "−", title: "减法", group: "math", snippet: " - " },
640
+ { key: "mul", label: "×", title: "乘法", group: "math", snippet: " * " },
641
+ { key: "div", label: "÷", title: "除法", group: "math", snippet: " / " },
642
+ {
643
+ key: "round",
644
+ label: "ROUND",
645
+ title: "ROUND(数字, 小数位)",
646
+ group: "math",
647
+ snippet: "ROUND(数字, 2)",
648
+ select: /数字/
649
+ },
650
+ {
651
+ key: "sum",
652
+ label: "SUM",
653
+ title: "SUM(数字1, 数字2, …) — 数值总和(忽略非数字参数)",
654
+ group: "math",
655
+ snippet: "SUM(数字1, 数字2)",
656
+ select: /数字1/
657
+ },
658
+ {
659
+ key: "average",
660
+ label: "AVERAGE",
661
+ title: "AVERAGE(数字1, 数字2, …) — 平均值(忽略非数字参数)",
662
+ group: "math",
663
+ snippet: "AVERAGE(数字1, 数字2)",
664
+ select: /数字1/
665
+ },
666
+ {
667
+ key: "abs",
668
+ label: "ABS",
669
+ title: "ABS(数字) — 取绝对值",
670
+ group: "math",
671
+ snippet: "ABS(数字)",
672
+ select: /数字/
673
+ },
674
+ {
675
+ key: "floor",
676
+ label: "FLOOR",
677
+ title: "FLOOR(数字) — 向下取整",
678
+ group: "math",
679
+ snippet: "FLOOR(数字)",
680
+ select: /数字/
681
+ },
682
+ {
683
+ key: "ceil",
684
+ label: "CEIL",
685
+ title: "CEIL(数字) — 向上取整",
686
+ group: "math",
687
+ snippet: "CEIL(数字)",
688
+ select: /数字/
689
+ },
690
+ {
691
+ key: "min",
692
+ label: "MIN",
693
+ title: "MIN(数字1, 数字2, …) — 取最小值",
694
+ group: "math",
695
+ snippet: "MIN(数字1, 数字2)",
696
+ select: /数字1/
697
+ },
698
+ {
699
+ key: "max",
700
+ label: "MAX",
701
+ title: "MAX(数字1, 数字2, …) — 取最大值",
702
+ group: "math",
703
+ snippet: "MAX(数字1, 数字2)",
704
+ select: /数字1/
705
+ },
706
+ { key: "eq", label: "=", title: "等于 ==", group: "compare", snippet: " == " },
707
+ { key: "neq", label: "≠", title: "不等于 !=", group: "compare", snippet: " != " },
708
+ { key: "lt", label: "<", title: "小于", group: "compare", snippet: " < " },
709
+ { key: "gt", label: ">", title: "大于", group: "compare", snippet: " > " },
710
+ { key: "andop", label: "&&", title: "逻辑与", group: "compare", snippet: " && " },
711
+ { key: "orop", label: "||", title: "逻辑或", group: "compare", snippet: " || " },
712
+ {
713
+ key: "between",
714
+ label: "BETWEEN",
715
+ title: "BETWEEN(值, 下限, 上限) — 是否在 [下限, 上限] 区间内(含边界)",
716
+ group: "compare",
717
+ snippet: "BETWEEN(值, 下限, 上限)",
718
+ select: /值/
719
+ },
720
+ {
721
+ key: "isnull",
722
+ label: "ISNULL",
723
+ title: "ISNULL(值) — 是否为空值(NULL / 未定义)",
724
+ group: "compare",
725
+ snippet: "ISNULL(值)",
726
+ select: /值/
727
+ },
728
+ {
729
+ key: "isempty",
730
+ label: "ISEMPTY",
731
+ title: "ISEMPTY(值) — 是否为空(NULL、空字符串、空数组/对象)",
732
+ group: "compare",
733
+ snippet: "ISEMPTY(值)",
734
+ select: /值/
735
+ },
736
+ {
737
+ key: "isnumeric",
738
+ label: "ISNUMERIC",
739
+ title: "ISNUMERIC(值) — 是否为数字(含可转换的数字字符串)",
740
+ group: "compare",
741
+ snippet: "ISNUMERIC(值)",
742
+ select: /值/
743
+ },
744
+ {
745
+ key: "concat",
746
+ label: "拼接",
747
+ title: "CONCAT(文本1, 文本2) — 字符串拼接",
748
+ group: "text",
749
+ snippet: "CONCAT(文本1, 文本2)",
750
+ select: /文本1/
751
+ },
752
+ {
753
+ key: "len",
754
+ label: "LEN",
755
+ title: "LEN(文本) — 返回文本长度(按字符数)",
756
+ group: "text",
757
+ snippet: "LEN(文本)",
758
+ select: /文本/
759
+ },
760
+ {
761
+ key: "upper",
762
+ label: "UPPER",
763
+ title: "UPPER(文本) — 转大写",
764
+ group: "text",
765
+ snippet: "UPPER(文本)",
766
+ select: /文本/
767
+ },
768
+ {
769
+ key: "lower",
770
+ label: "LOWER",
771
+ title: "LOWER(文本) — 转小写",
772
+ group: "text",
773
+ snippet: "LOWER(文本)",
774
+ select: /文本/
775
+ },
776
+ {
777
+ key: "trim",
778
+ label: "TRIM",
779
+ title: "TRIM(文本) — 去除首尾空白",
780
+ group: "text",
781
+ snippet: "TRIM(文本)",
782
+ select: /文本/
783
+ },
784
+ {
785
+ key: "substr",
786
+ label: "SUBSTR",
787
+ title: "SUBSTR(文本, 起始下标[, 长度]) — 截取子串(0 起始)",
788
+ group: "text",
789
+ snippet: "SUBSTR(文本, 起始下标)",
790
+ select: /文本/
791
+ },
792
+ {
793
+ key: "mid",
794
+ label: "MID",
795
+ title: "MID(文本, 起始位, 长度) — Excel 风格截取(1 起始)",
796
+ group: "text",
797
+ snippet: "MID(文本, 起始位, 长度)",
798
+ select: /文本/
799
+ },
800
+ {
801
+ key: "left",
802
+ label: "LEFT",
803
+ title: "LEFT(文本, 长度) — 取左侧 N 个字符",
804
+ group: "text",
805
+ snippet: "LEFT(文本, 长度)",
806
+ select: /文本/
807
+ },
808
+ {
809
+ key: "right",
810
+ label: "RIGHT",
811
+ title: "RIGHT(文本, 长度) — 取右侧 N 个字符",
812
+ group: "text",
813
+ snippet: "RIGHT(文本, 长度)",
814
+ select: /文本/
815
+ },
816
+ {
817
+ key: "contains",
818
+ label: "CONTAINS",
819
+ title: "CONTAINS(文本, 子串) — 是否包含子串(大小写敏感)",
820
+ group: "text",
821
+ snippet: "CONTAINS(文本, 子串)",
822
+ select: /文本/
823
+ },
824
+ {
825
+ key: "startswith",
826
+ label: "STARTSWITH",
827
+ title: "STARTSWITH(文本, 前缀) — 是否以指定前缀开头",
828
+ group: "text",
829
+ snippet: "STARTSWITH(文本, 前缀)",
830
+ select: /文本/
831
+ },
832
+ {
833
+ key: "endswith",
834
+ label: "ENDSWITH",
835
+ title: "ENDSWITH(文本, 后缀) — 是否以指定后缀结尾",
836
+ group: "text",
837
+ snippet: "ENDSWITH(文本, 后缀)",
838
+ select: /文本/
839
+ },
840
+ {
841
+ key: "indexof",
842
+ label: "INDEXOF",
843
+ title: "INDEXOF(文本, 子串) — 子串位置(0 起始),未找到 -1",
844
+ group: "text",
845
+ snippet: "INDEXOF(文本, 子串)",
846
+ select: /文本/
847
+ },
848
+ {
849
+ key: "replace",
850
+ label: "REPLACE",
851
+ title: "REPLACE(文本, 被替换, 替换为) — 替换所有匹配",
852
+ group: "text",
853
+ snippet: "REPLACE(文本, 被替换, 替换为)",
854
+ select: /文本/
855
+ },
856
+ {
857
+ key: "split",
858
+ label: "SPLIT",
859
+ title: "SPLIT(文本, 分隔符) — 拆分为数组",
860
+ group: "text",
861
+ snippet: "SPLIT(文本, 分隔符)",
862
+ select: /文本/
863
+ },
864
+ {
865
+ key: "ifnull",
866
+ label: "IFNULL",
867
+ title: "IFNULL(值, 兜底值) — 为空时返回兜底值",
868
+ group: "logic",
869
+ snippet: "IFNULL(值, 兜底值)",
870
+ select: /值/
871
+ },
872
+ {
873
+ key: "coalesce",
874
+ label: "COALESCE",
875
+ title: "COALESCE(值1, 值2, …) — 取第一个非空值",
876
+ group: "logic",
877
+ snippet: "COALESCE(值1, 值2, 值3)",
878
+ select: /值1/
879
+ },
880
+ {
881
+ key: "switch",
882
+ label: "SWITCH",
883
+ title: "SWITCH(值, 情况1, 结果1, …, 默认值) — 按值匹配",
884
+ group: "logic",
885
+ snippet: "SWITCH(值, 情况1, 结果1, 默认值)",
886
+ select: /值/
887
+ },
888
+ {
889
+ key: "tostring",
890
+ label: "TOSTRING",
891
+ title: "TOSTRING(值) — 转字符串",
892
+ group: "convert",
893
+ snippet: "TOSTRING(值)",
894
+ select: /值/
895
+ },
896
+ {
897
+ key: "tonumber",
898
+ label: "TONUMBER",
899
+ title: "TONUMBER(值) — 转数字",
900
+ group: "convert",
901
+ snippet: "TONUMBER(值)",
902
+ select: /值/
903
+ },
904
+ {
905
+ key: "toboolean",
906
+ label: "TOBOOLEAN",
907
+ title: "TOBOOLEAN(值) — 转布尔",
908
+ group: "convert",
909
+ snippet: "TOBOOLEAN(值)",
910
+ select: /值/
911
+ },
912
+ {
913
+ key: "now",
914
+ label: "NOW",
915
+ title: "NOW() — 当前时间戳(毫秒)",
916
+ group: "date",
917
+ snippet: "NOW()"
918
+ },
919
+ {
920
+ key: "today",
921
+ label: "TODAY",
922
+ title: "TODAY() — 今天零点时间戳",
923
+ group: "date",
924
+ snippet: "TODAY()"
925
+ },
926
+ {
927
+ key: "dateadd",
928
+ label: "DATEADD",
929
+ title: "DATEADD(时间戳, 天数) — 日期加 N 天",
930
+ group: "date",
931
+ snippet: "DATEADD(TODAY(), 7)",
932
+ select: /TODAY\(\)/
933
+ },
934
+ {
935
+ key: "datedif",
936
+ label: "DATEDIF",
937
+ title: "DATEDIF(起始, 结束) — 相隔整天数",
938
+ group: "date",
939
+ snippet: "DATEDIF(TODAY(), DATEADD(TODAY(), 7))",
940
+ select: /TODAY\(\)/
941
+ },
942
+ {
943
+ key: "count",
944
+ label: "COUNT",
945
+ title: "COUNT(数组) — 数组长度",
946
+ group: "agg",
947
+ snippet: "COUNT(数组)",
948
+ select: /数组/
949
+ },
950
+ {
951
+ key: "countif",
952
+ label: "COUNTIF",
953
+ title: "COUNTIF(数组, 目标值) — 统计相等元素个数",
954
+ group: "agg",
955
+ snippet: "COUNTIF(数组, 目标值)",
956
+ select: /数组/
957
+ },
958
+ {
959
+ key: "sumif",
960
+ label: "SUMIF",
961
+ title: "SUMIF(数组, 目标值) — 相等元素求和",
962
+ group: "agg",
963
+ snippet: "SUMIF(数组, 目标值)",
964
+ select: /数组/
965
+ },
966
+ {
967
+ key: "keys",
968
+ label: "KEYS",
969
+ title: "KEYS(对象) — 对象的键名数组",
970
+ group: "agg",
971
+ snippet: "KEYS(对象)",
972
+ select: /对象/
973
+ },
974
+ {
975
+ key: "values",
976
+ label: "VALUES",
977
+ title: "VALUES(对象) — 对象的键值数组",
978
+ group: "agg",
979
+ snippet: "VALUES(对象)",
980
+ select: /对象/
981
+ }
982
+ ], xe = [
983
+ { key: "logic", label: "逻辑" },
984
+ { key: "math", label: "运算" },
985
+ { key: "compare", label: "比较" },
986
+ { key: "text", label: "文本" },
987
+ { key: "convert", label: "转换" },
988
+ { key: "date", label: "日期" },
989
+ { key: "agg", label: "聚合" }
990
+ ];
991
+ function ft(e) {
992
+ var t;
993
+ return ((t = xe.find((l) => l.key === e)) == null ? void 0 : t.label) ?? e;
994
+ }
995
+ const X = Object.values(Me).map((e) => {
996
+ var t;
997
+ return {
998
+ name: e.name,
999
+ signature: e.signature,
1000
+ description: e.description,
1001
+ params: e.params ?? [],
1002
+ example: e.example,
1003
+ /** 在模板中的分组(用于函数面板归类) */
1004
+ group: ((t = ae.find((l) => l.label.toUpperCase() === e.name)) == null ? void 0 : t.group) ?? "logic"
1005
+ };
1006
+ }), Ce = I(() => {
1007
+ const e = ee.value.trim().toLowerCase();
1008
+ return e ? X.filter(
1009
+ (t) => (t.name + " " + t.signature + " " + t.description).toLowerCase().includes(e)
1010
+ ) : X;
1011
+ }), oe = m(/* @__PURE__ */ new Set());
1012
+ function vt(e) {
1013
+ const t = new Set(oe.value);
1014
+ t.has(e) ? t.delete(e) : t.add(e), oe.value = t;
1015
+ }
1016
+ function ie(e) {
1017
+ return oe.value.has(e);
1018
+ }
1019
+ function Re(e) {
1020
+ if (e.fields && e.fields.length > 0) return e.fields.map((l) => l.key);
1021
+ const t = f.context[e.name];
1022
+ return t && typeof t == "object" && !Array.isArray(t) ? Object.keys(t) : [];
1023
+ }
1024
+ const Ae = {
1025
+ number: "数字",
1026
+ string: "文本",
1027
+ boolean: "布尔",
1028
+ enum: "枚举",
1029
+ object: "键值对",
1030
+ any: "任意"
1031
+ };
1032
+ function Fe(e) {
1033
+ return e && Ae[e] ? Ae[e] : "任意";
1034
+ }
1035
+ function bt(e, t) {
1036
+ S(`{${e.name}} == '${t}'`), L("insert", { type: "text", value: `{${e.name}} == '${t}'` });
1037
+ }
1038
+ function yt(e, t) {
1039
+ S(`{${e.name}}.${t}`), L("insert", { type: "text", value: `{${e.name}}.${t}` });
1040
+ }
1041
+ function kt(e) {
1042
+ const t = ae.find((l) => l.label.toUpperCase() === e.name);
1043
+ t ? Le(t) : S(e.signature), L("insert", { type: "function", value: e.name });
1044
+ }
1045
+ return Q({
1046
+ /** 手动校验,返回 { ok, errors, ast } */
1047
+ validate: () => B.value,
1048
+ /** 手动求值(可传自定义测试值,缺省用 props.context) */
1049
+ evaluate: (e) => Pe(v.value, e ?? f.context, f.functions, f.variables),
1050
+ /** 在光标处插入文本并聚焦 */
1051
+ insertText: (e) => S(e),
1052
+ /** 聚焦编辑器 */
1053
+ focus: se
1054
+ }), (e, t) => (r(), u("div", {
1055
+ class: N(["fe", { "theme-light": $.value === "light" }]),
1056
+ style: Y({ "--fe-height": y.height })
1057
+ }, [
1058
+ n("header", Tt, [
1059
+ n("div", St, [
1060
+ t[7] || (t[7] = n("span", { class: "fe-mark" }, "Σ", -1)),
1061
+ n("div", It, [
1062
+ n("div", Nt, b(y.title), 1),
1063
+ n("div", Ot, b(y.subtitle), 1)
1064
+ ])
1065
+ ]),
1066
+ n("div", Lt, [
1067
+ y.showVariablesPanel ? (r(), u("button", {
1068
+ key: 0,
1069
+ class: N(["fe-btn", { active: V.value }]),
1070
+ type: "button",
1071
+ disabled: y.disabled,
1072
+ title: V.value ? "收起变量面板" : "展开变量面板",
1073
+ onClick: t[0] || (t[0] = (l) => V.value = !V.value)
1074
+ }, [...t[8] || (t[8] = [
1075
+ n("span", { class: "fe-btn-ico" }, "☰", -1),
1076
+ p("变量 ", -1)
1077
+ ])], 10, xt)) : g("", !0),
1078
+ n("button", {
1079
+ class: N(["fe-btn", { active: H.value }]),
1080
+ type: "button",
1081
+ disabled: y.disabled,
1082
+ title: H.value ? "收起函数面板" : "展开函数面板(点选插入函数)",
1083
+ onClick: t[1] || (t[1] = (l) => H.value = !H.value)
1084
+ }, [...t[9] || (t[9] = [
1085
+ n("span", { class: "fe-btn-ico" }, "ƒ", -1),
1086
+ p("函数 ", -1)
1087
+ ])], 10, Ct),
1088
+ n("button", {
1089
+ class: "fe-btn",
1090
+ type: "button",
1091
+ disabled: y.disabled,
1092
+ title: "一键格式化(按块状 IF 层级对齐缩进)",
1093
+ onClick: dt
1094
+ }, [...t[10] || (t[10] = [
1095
+ n("span", { class: "fe-btn-ico" }, "⇄", -1),
1096
+ p("格式化 ", -1)
1097
+ ])], 8, Rt),
1098
+ n("button", {
1099
+ class: "fe-btn",
1100
+ type: "button",
1101
+ disabled: y.disabled,
1102
+ title: "复制公式",
1103
+ onClick: pt
1104
+ }, [...t[11] || (t[11] = [
1105
+ n("span", { class: "fe-btn-ico" }, "⧉", -1),
1106
+ p("复制 ", -1)
1107
+ ])], 8, At),
1108
+ n("button", {
1109
+ class: "fe-btn",
1110
+ type: "button",
1111
+ disabled: y.disabled,
1112
+ title: "清空公式",
1113
+ onClick: ct
1114
+ }, [...t[12] || (t[12] = [
1115
+ n("span", { class: "fe-btn-ico" }, "✕", -1),
1116
+ p("清空 ", -1)
1117
+ ])], 8, Ft),
1118
+ n("button", {
1119
+ class: N(["fe-btn", { active: K.value }]),
1120
+ type: "button",
1121
+ disabled: y.disabled,
1122
+ title: "语法帮助",
1123
+ onClick: t[2] || (t[2] = (l) => K.value = !K.value)
1124
+ }, [...t[13] || (t[13] = [
1125
+ n("span", { class: "fe-btn-ico" }, "?", -1),
1126
+ p("帮助 ", -1)
1127
+ ])], 10, $t),
1128
+ n("button", {
1129
+ class: "fe-btn",
1130
+ type: "button",
1131
+ disabled: y.disabled,
1132
+ title: $.value === "dark" ? "切换到浅色主题" : "切换到深色主题",
1133
+ onClick: it
1134
+ }, [
1135
+ n("span", wt, b($.value === "dark" ? "☀" : "🌙"), 1),
1136
+ p(" " + b($.value === "dark" ? "浅色" : "深色"), 1)
1137
+ ], 8, _t)
1138
+ ])
1139
+ ]),
1140
+ re(ue, { name: "fe-drop" }, {
1141
+ default: ce(() => [
1142
+ K.value ? (r(), u("div", Dt, [
1143
+ t[15] || (t[15] = n("div", { class: "fe-help-col" }, [
1144
+ n("div", { class: "fe-help-title" }, "语法速查"),
1145
+ n("table", { class: "fe-help-table" }, [
1146
+ n("tbody", null, [
1147
+ n("tr", null, [
1148
+ n("td", { class: "fe-help-k" }, "变量引用"),
1149
+ n("td", null, [
1150
+ n("code", null, "{变量名}")
1151
+ ])
1152
+ ]),
1153
+ n("tr", null, [
1154
+ n("td", { class: "fe-help-k" }, "成员访问"),
1155
+ n("td", null, [
1156
+ n("code", null, "{订单}.金额"),
1157
+ p(" / "),
1158
+ n("code", null, "{订单}['金额']"),
1159
+ p(" / "),
1160
+ n("code", null, "{订单}.商品[0]")
1161
+ ])
1162
+ ]),
1163
+ n("tr", null, [
1164
+ n("td", { class: "fe-help-k" }, "字符串"),
1165
+ n("td", null, [
1166
+ n("code", null, "'文本'"),
1167
+ p(" 或 "),
1168
+ n("code", null, '"文本"')
1169
+ ])
1170
+ ]),
1171
+ n("tr", null, [
1172
+ n("td", { class: "fe-help-k" }, "布尔/空"),
1173
+ n("td", null, [
1174
+ n("code", null, "true"),
1175
+ p(),
1176
+ n("code", null, "false"),
1177
+ p(),
1178
+ n("code", null, "null"),
1179
+ p(),
1180
+ n("code", null, "undefined")
1181
+ ])
1182
+ ]),
1183
+ n("tr", null, [
1184
+ n("td", { class: "fe-help-k" }, "块状条件"),
1185
+ n("td", null, [
1186
+ n("code", null, "IF 条件 THEN 真值 ELSE 假值"),
1187
+ p("(可多级 "),
1188
+ n("code", null, "ELSE IF"),
1189
+ p(")")
1190
+ ])
1191
+ ]),
1192
+ n("tr", null, [
1193
+ n("td", { class: "fe-help-k" }, "中文关键字"),
1194
+ n("td", null, [
1195
+ n("code", null, "如果 条件 那么 真值 否则 假值"),
1196
+ p(" / "),
1197
+ n("code", null, "否则如果")
1198
+ ])
1199
+ ]),
1200
+ n("tr", null, [
1201
+ n("td", { class: "fe-help-k" }, "函数条件"),
1202
+ n("td", null, [
1203
+ n("code", null, "IF(条件, 真值, 假值)"),
1204
+ p("、"),
1205
+ n("code", null, "IFS(…)"),
1206
+ p("(兼容写法)")
1207
+ ])
1208
+ ]),
1209
+ n("tr", null, [
1210
+ n("td", { class: "fe-help-k" }, "枚举比较"),
1211
+ n("td", null, [
1212
+ p("公式写标签 "),
1213
+ n("code", null, "{会员等级} == '金卡'"),
1214
+ p(",求值用代码值,标签/值双向可匹配")
1215
+ ])
1216
+ ]),
1217
+ n("tr", null, [
1218
+ n("td", { class: "fe-help-k" }, "比较"),
1219
+ n("td", null, [
1220
+ n("code", null, "=="),
1221
+ p(),
1222
+ n("code", null, "!="),
1223
+ p(),
1224
+ n("code", null, "<"),
1225
+ p(),
1226
+ n("code", null, ">"),
1227
+ p(),
1228
+ n("code", null, "<="),
1229
+ p(),
1230
+ n("code", null, ">=")
1231
+ ])
1232
+ ]),
1233
+ n("tr", null, [
1234
+ n("td", { class: "fe-help-k" }, "逻辑"),
1235
+ n("td", null, [
1236
+ n("code", null, "&&"),
1237
+ p(),
1238
+ n("code", null, "||"),
1239
+ p(),
1240
+ n("code", null, "!"),
1241
+ p(),
1242
+ n("code", null, "AND"),
1243
+ p(),
1244
+ n("code", null, "OR"),
1245
+ p(),
1246
+ n("code", null, "NOT()")
1247
+ ])
1248
+ ]),
1249
+ n("tr", null, [
1250
+ n("td", { class: "fe-help-k" }, "三元"),
1251
+ n("td", null, [
1252
+ n("code", null, "条件 ? 真值 : 假值")
1253
+ ])
1254
+ ])
1255
+ ])
1256
+ ]),
1257
+ n("p", { class: "fe-help-tip" }, [
1258
+ p(" 提示:插入的模板会选中占位文字(如“条件”),直接输入即可替换;光标在变量 "),
1259
+ n("code", null, "{…}"),
1260
+ p(" 内按退格/删除键可整体删除该变量。 ")
1261
+ ])
1262
+ ], -1)),
1263
+ n("div", Ut, [
1264
+ t[14] || (t[14] = n("div", { class: "fe-help-title" }, "内置函数", -1)),
1265
+ n("ul", Mt, [
1266
+ (r(!0), u(T, null, C(De(X), (l) => (r(), u("li", {
1267
+ key: l.name
1268
+ }, [
1269
+ n("code", null, b(l.signature), 1),
1270
+ n("span", null, b(l.description), 1)
1271
+ ]))), 128))
1272
+ ])
1273
+ ])
1274
+ ])) : g("", !0)
1275
+ ]),
1276
+ _: 1
1277
+ }),
1278
+ y.showToolbar ? (r(), u("div", Pt, [
1279
+ (r(), u(T, null, C(xe, (l) => (r(), u(T, {
1280
+ key: l.key
1281
+ }, [
1282
+ n("span", Vt, b(l.label), 1),
1283
+ (r(!0), u(T, null, C(ae.filter((a) => a.group === l.key), (a) => (r(), u("button", {
1284
+ key: a.key,
1285
+ class: "fe-chip",
1286
+ type: "button",
1287
+ disabled: y.disabled,
1288
+ title: a.title,
1289
+ onClick: (s) => Le(a)
1290
+ }, b(a.label), 9, Ht))), 128))
1291
+ ], 64))), 64)),
1292
+ t[16] || (t[16] = n("span", { class: "fe-toolbar-hint" }, "插入的占位文字可直接输入替换", -1))
1293
+ ])) : g("", !0),
1294
+ n("div", Bt, [
1295
+ V.value ? (r(), u("aside", Wt, [
1296
+ n("div", Yt, [
1297
+ t[17] || (t[17] = n("span", null, "可选变量", -1)),
1298
+ n("span", jt, b(y.variables.length), 1)
1299
+ ]),
1300
+ n("div", Kt, [
1301
+ de(n("input", {
1302
+ "onUpdate:modelValue": t[3] || (t[3] = (l) => J.value = l),
1303
+ type: "text",
1304
+ placeholder: "搜索变量…",
1305
+ disabled: y.disabled
1306
+ }, null, 8, qt), [
1307
+ [pe, J.value]
1308
+ ])
1309
+ ]),
1310
+ n("div", Gt, [
1311
+ (r(!0), u(T, null, C(Ee.value, (l) => (r(), u(T, {
1312
+ key: l.name
1313
+ }, [
1314
+ n("div", {
1315
+ class: N(["fe-var-row", { disabled: y.disabled }]),
1316
+ title: `点击插入 {${l.name}}`,
1317
+ onClick: (a) => rt(l.name)
1318
+ }, [
1319
+ n("div", zt, [
1320
+ l.type === "enum" || l.type === "object" ? (r(), u("span", {
1321
+ key: 0,
1322
+ class: N(["fe-var-expand", { open: ie(l.name) }]),
1323
+ title: l.type === "enum" ? "展开标签" : "展开字段",
1324
+ onClick: fe((a) => vt(l.name), ["stop"])
1325
+ }, "▸", 10, Qt)) : g("", !0),
1326
+ n("span", Zt, "{" + b(l.name) + "}", 1),
1327
+ n("span", {
1328
+ class: N(["fe-var-badge", `vt-${l.type ?? "any"}`])
1329
+ }, b(Fe(l.type)), 3),
1330
+ l.required ? (r(), u("span", Jt, "*")) : g("", !0)
1331
+ ]),
1332
+ n("div", el, [
1333
+ p(b(l.label ?? l.name), 1),
1334
+ l.description ? (r(), u(T, { key: 0 }, [
1335
+ p(" — " + b(l.description), 1)
1336
+ ], 64)) : g("", !0)
1337
+ ])
1338
+ ], 10, Xt),
1339
+ ie(l.name) && l.type === "enum" ? (r(), u("div", tl, [
1340
+ (r(!0), u(T, null, C(l.options ?? [], (a) => (r(), u("div", {
1341
+ key: typeof a == "string" ? a : a.label,
1342
+ class: "fe-var-chip",
1343
+ title: `插入 {${l.name}} == '${typeof a == "string" ? a : a.label}'`,
1344
+ onClick: fe((s) => bt(l, typeof a == "string" ? a : a.label), ["stop"])
1345
+ }, b(typeof a == "string" ? a : a.label), 9, ll))), 128))
1346
+ ])) : g("", !0),
1347
+ ie(l.name) && l.type === "object" ? (r(), u("div", nl, [
1348
+ (r(!0), u(T, null, C(Re(l), (a) => (r(), u("div", {
1349
+ key: a,
1350
+ class: "fe-var-chip",
1351
+ title: `插入 {${l.name}}.${a}`,
1352
+ onClick: fe((s) => yt(l, a), ["stop"])
1353
+ }, b(a), 9, sl))), 128)),
1354
+ Re(l).length === 0 ? (r(), u("div", al, " 无字段(context 中对象为空) ")) : g("", !0)
1355
+ ])) : g("", !0)
1356
+ ], 64))), 128)),
1357
+ Ee.value.length === 0 ? (r(), u("div", ol, [
1358
+ t[18] || (t[18] = n("div", { class: "fe-vars-empty-ico" }, "∅", -1)),
1359
+ y.variables.length === 0 ? (r(), u("div", il, "暂无可用变量")) : (r(), u("div", rl, "没有匹配的变量"))
1360
+ ])) : g("", !0)
1361
+ ]),
1362
+ t[19] || (t[19] = n("div", { class: "fe-vars-foot" }, "点击变量插入;枚举/键值对可点 ▸ 展开选标签/字段", -1))
1363
+ ])) : g("", !0),
1364
+ H.value ? (r(), u("aside", ul, [
1365
+ n("div", cl, [
1366
+ t[20] || (t[20] = n("span", null, "内置函数", -1)),
1367
+ n("span", dl, b(De(X).length), 1)
1368
+ ]),
1369
+ n("div", pl, [
1370
+ de(n("input", {
1371
+ "onUpdate:modelValue": t[4] || (t[4] = (l) => ee.value = l),
1372
+ type: "text",
1373
+ placeholder: "搜索函数…",
1374
+ disabled: y.disabled
1375
+ }, null, 8, fl), [
1376
+ [pe, ee.value]
1377
+ ])
1378
+ ]),
1379
+ n("div", vl, [
1380
+ (r(!0), u(T, null, C(Ce.value, (l) => (r(), u("div", {
1381
+ key: l.name,
1382
+ class: N(["fe-fn-row", { disabled: y.disabled }]),
1383
+ title: l.example ? `${l.signature}
1384
+ 示例: ${l.example}` : l.signature,
1385
+ onClick: (a) => kt(l)
1386
+ }, [
1387
+ n("div", yl, [
1388
+ n("span", kl, b(l.name), 1),
1389
+ n("span", ml, b(ft(l.group)), 1)
1390
+ ]),
1391
+ n("div", gl, b(l.signature), 1),
1392
+ n("div", hl, b(l.description), 1)
1393
+ ], 10, bl))), 128)),
1394
+ Ce.value.length === 0 ? (r(), u("div", El, [...t[21] || (t[21] = [
1395
+ n("div", { class: "fe-vars-empty-ico" }, "∅", -1),
1396
+ n("div", null, "没有匹配的函数", -1)
1397
+ ])])) : g("", !0)
1398
+ ]),
1399
+ t[22] || (t[22] = n("div", { class: "fe-vars-foot" }, "点击函数插入到光标处", -1))
1400
+ ])) : g("", !0),
1401
+ n("div", Tl, [
1402
+ n("div", Sl, [
1403
+ n("div", Il, [
1404
+ n("div", {
1405
+ class: "fe-gutter-inner",
1406
+ style: Y({ transform: `translateY(${-q.value}px)` })
1407
+ }, [
1408
+ (r(!0), u(T, null, C(Be.value, (l) => (r(), u("span", {
1409
+ key: l,
1410
+ class: N(["fe-gutter-line", { err: Ye.value.has(l) }])
1411
+ }, b(l), 3))), 128))
1412
+ ], 4)
1413
+ ]),
1414
+ n("div", {
1415
+ ref_key: "wrapRef",
1416
+ ref: ye,
1417
+ class: "fe-code-wrap"
1418
+ }, [
1419
+ n("div", {
1420
+ class: "fe-line-marker",
1421
+ style: Y({ top: We.value + "px" })
1422
+ }, null, 4),
1423
+ n("pre", {
1424
+ ref_key: "preRef",
1425
+ ref: Ve,
1426
+ class: "fe-highlight",
1427
+ "aria-hidden": "true",
1428
+ style: Y({ transform: `translate(${-be.value}px, ${-q.value}px)` })
1429
+ }, [
1430
+ n("code", { innerHTML: ze.value }, null, 8, Nl)
1431
+ ], 4),
1432
+ de(n("textarea", {
1433
+ ref_key: "taRef",
1434
+ ref: h,
1435
+ "onUpdate:modelValue": t[5] || (t[5] = (l) => v.value = l),
1436
+ class: "fe-textarea",
1437
+ placeholder: y.placeholder,
1438
+ disabled: y.disabled,
1439
+ readonly: y.readonly,
1440
+ spellcheck: !1,
1441
+ autocapitalize: "off",
1442
+ autocomplete: "off",
1443
+ wrap: "off",
1444
+ onInput: Qe,
1445
+ onScroll: Ze,
1446
+ onKeydown: ot,
1447
+ onClick: O,
1448
+ onDblclick: lt,
1449
+ onKeyup: O,
1450
+ onSelect: O,
1451
+ onMousemove: et,
1452
+ onMouseleave: tt
1453
+ }, null, 40, Ol), [
1454
+ [pe, v.value]
1455
+ ]),
1456
+ re(ue, { name: "fe-fade" }, {
1457
+ default: ce(() => [
1458
+ x.value ? (r(), u("div", {
1459
+ key: 0,
1460
+ class: "fe-tooltip",
1461
+ style: Y({ left: x.value.x + "px", top: x.value.y + "px" })
1462
+ }, b(x.value.text), 5)) : g("", !0)
1463
+ ]),
1464
+ _: 1
1465
+ })
1466
+ ], 512)
1467
+ ]),
1468
+ re(ue, { name: "fe-drop" }, {
1469
+ default: ce(() => [
1470
+ Z.value && M.value.length > 0 ? (r(), u("div", Ll, [
1471
+ (r(!0), u(T, null, C(M.value, (l, a) => (r(), u("div", {
1472
+ key: a,
1473
+ class: "fe-error-row",
1474
+ onClick: (s) => ut(l.start, l.end)
1475
+ }, [
1476
+ n("span", {
1477
+ class: N(["fe-error-dot", l.severity])
1478
+ }, null, 2),
1479
+ n("span", Cl, b(l.message), 1),
1480
+ n("span", Rl, b(l.kind), 1)
1481
+ ], 8, xl))), 128))
1482
+ ])) : g("", !0)
1483
+ ]),
1484
+ _: 1
1485
+ })
1486
+ ])
1487
+ ]),
1488
+ n("footer", Al, [
1489
+ n("div", Fl, [
1490
+ n("span", {
1491
+ class: N(["fe-status", B.value.ok && v.value.trim() ? "ok" : "bad"]),
1492
+ onClick: t[6] || (t[6] = (l) => Z.value = !Z.value)
1493
+ }, [
1494
+ t[23] || (t[23] = n("span", { class: "fe-status-dot" }, null, -1)),
1495
+ p(" " + b(v.value.trim() ? je.value.label : "等待输入"), 1)
1496
+ ], 2),
1497
+ M.value.length > 0 ? (r(), u("span", $l, b(M.value.length) + " 处 ", 1)) : g("", !0)
1498
+ ]),
1499
+ y.showPreview ? (r(), u("div", {
1500
+ key: 0,
1501
+ class: N(["fe-foot-result", qe.value])
1502
+ }, [
1503
+ t[24] || (t[24] = n("span", { class: "fe-result-label" }, "实时求值", -1)),
1504
+ _.value ? (r(), u(T, { key: 0 }, [
1505
+ n("span", {
1506
+ class: "fe-result-value",
1507
+ title: _.value.ok ? String(_.value.value) : ""
1508
+ }, b(Ke.value), 9, _l),
1509
+ Te.value ? (r(), u("span", wl, b(Te.value), 1)) : g("", !0)
1510
+ ], 64)) : (r(), u("span", Dl, "—"))
1511
+ ], 2)) : g("", !0),
1512
+ n("div", Ul, "Ln " + b(G.value.line) + ", Col " + b(G.value.col), 1)
1513
+ ])
1514
+ ], 6));
1515
+ }
1516
+ }), Pl = (y, Q) => {
1517
+ const j = y.__vccOpts || y;
1518
+ for (const [f, L] of Q)
1519
+ j[f] = L;
1520
+ return j;
1521
+ }, Bl = /* @__PURE__ */ Pl(Ml, [["__scopeId", "data-v-10da7669"]]);
1522
+ export {
1523
+ Me as BUILTIN_FUNCTIONS,
1524
+ Bl as FormulaEditor,
1525
+ jl as ParseError,
1526
+ Kl as RuntimeError,
1527
+ ql as evaluate,
1528
+ Pe as evaluateFormula,
1529
+ Et as formatFormula,
1530
+ Ue as formatValue,
1531
+ Gl as lexErrors,
1532
+ Xl as parse,
1533
+ zl as parseFormula,
1534
+ ve as tokenize,
1535
+ ht as validateFormula,
1536
+ gt as valueTypeName
1537
+ };