@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.
@@ -0,0 +1,2 @@
1
+ import type { Token } from './types';
2
+ export declare function tokenize(src: string): Token[];
@@ -0,0 +1,21 @@
1
+ import type { ExprNode } from './types';
2
+ /**
3
+ * 递归下降解析器(优先级爬升)。
4
+ * 语法:
5
+ * expr := ternary
6
+ * ternary := or ('?' expr ':' expr)?
7
+ * or := and ('||' and)*
8
+ * and := equality ('&&' equality)*
9
+ * equality := relational (('=='|'!='|'==='|'!==') relational)*
10
+ * relational := additive (('<'|'<='|'>'|'>=') additive)*
11
+ * additive := multiplicative (('+'|'-') multiplicative)*
12
+ * multiplicative := unary (('*'|'/'|'%'|'**') unary)*
13
+ * unary := ('-'|'+'|'!') unary | primary
14
+ * primary := number | string | boolean | null | variable | call | '(' expr ')'
15
+ */
16
+ export declare class ParseError extends Error {
17
+ start: number;
18
+ end: number;
19
+ constructor(message: string, start: number, end: number);
20
+ }
21
+ export declare function parse(src: string): ExprNode;
@@ -0,0 +1,182 @@
1
+ /**
2
+ * 公式引擎共享类型定义
3
+ * 纯 TypeScript、零依赖,可脱离 Vue 独立复用。
4
+ */
5
+ /** 变量类型 */
6
+ export type ValueType = 'number' | 'string' | 'boolean' | 'object' | 'enum' | 'any';
7
+ /** 枚举选项:label 为公式中书写/显示的标签,value 为求值/比较使用的代码值 */
8
+ export interface EnumOption {
9
+ label: string;
10
+ value: unknown;
11
+ }
12
+ /** 键值对变量的字段定义(可选;未定义时以测试值对象的键兜底) */
13
+ export interface FieldDef {
14
+ /** 字段名(公式中作为成员访问的标签) */
15
+ key: string;
16
+ /** 显示名称(默认取 key) */
17
+ label?: string;
18
+ /** 字段值类型 */
19
+ type?: ValueType;
20
+ /** 字段描述 */
21
+ description?: string;
22
+ }
23
+ /** 变量定义(传给编辑器的可选变量列表) */
24
+ export interface VariableDef {
25
+ /** 变量名,公式中以 {名称} 引用 */
26
+ name: string;
27
+ /** 显示名称(默认取 name) */
28
+ label?: string;
29
+ /** 变量描述,展示在变量面板中 */
30
+ description?: string;
31
+ /** 值类型,用于测试值输入与类型徽标 */
32
+ type?: ValueType;
33
+ /** 是否必填 */
34
+ required?: boolean;
35
+ /** 默认值(用于测试值面板初始化) */
36
+ defaultValue?: unknown;
37
+ /** 可选值(下拉选择),string 或 {label, value};type='enum' 时作为枚举标签-值映射 */
38
+ options?: Array<string | {
39
+ label: string;
40
+ value: unknown;
41
+ }>;
42
+ /** 键值对变量的字段定义(type='object' 时生效) */
43
+ fields?: FieldDef[];
44
+ }
45
+ /** 词法单元类型 */
46
+ export type TokenType = 'number' | 'string' | 'identifier' | 'variable' | 'operator' | 'lparen' | 'rparen' | 'lbracket' | 'rbracket' | 'dot' | 'comma' | 'question' | 'colon' | 'eof' | 'error';
47
+ /** 词法单元 */
48
+ export interface Token {
49
+ type: TokenType;
50
+ /** 语义值:number 为数值文本、string 为解转义后的内容、variable 为变量名、identifier 为标识符文本等 */
51
+ value: string;
52
+ /** 起始偏移(含) */
53
+ start: number;
54
+ /** 结束偏移(不含) */
55
+ end: number;
56
+ /** 原始文本 */
57
+ raw: string;
58
+ /** 仅 string/variable:是否闭合(引号/右花括号存在) */
59
+ closed?: boolean;
60
+ }
61
+ export interface NumberNode {
62
+ type: 'number';
63
+ value: number;
64
+ start: number;
65
+ end: number;
66
+ }
67
+ export interface StringNode {
68
+ type: 'string';
69
+ value: string;
70
+ start: number;
71
+ end: number;
72
+ }
73
+ export interface BooleanNode {
74
+ type: 'boolean';
75
+ value: boolean;
76
+ start: number;
77
+ end: number;
78
+ }
79
+ export interface NullNode {
80
+ type: 'null';
81
+ start: number;
82
+ end: number;
83
+ }
84
+ export interface VariableNode {
85
+ type: 'variable';
86
+ name: string;
87
+ start: number;
88
+ end: number;
89
+ }
90
+ /** 成员访问:{订单}.金额 / {订单}['金额'] / {订单}.商品[0] */
91
+ export interface MemberNode {
92
+ type: 'member';
93
+ /** 被访问的表达式(变量、函数调用、括号表达式或另一成员访问) */
94
+ object: ExprNode;
95
+ /** 点访问的成员名(.金额)或字符串下标(['金额']);下标表达式访问时为空字符串 */
96
+ prop: string;
97
+ /** 下标表达式([expr]);点访问/字符串下标时为 null */
98
+ index: ExprNode | null;
99
+ start: number;
100
+ end: number;
101
+ }
102
+ export interface CallNode {
103
+ type: 'call';
104
+ name: string;
105
+ args: ExprNode[];
106
+ start: number;
107
+ end: number;
108
+ }
109
+ export interface BinaryNode {
110
+ type: 'binary';
111
+ op: string;
112
+ left: ExprNode;
113
+ right: ExprNode;
114
+ start: number;
115
+ end: number;
116
+ }
117
+ export interface UnaryNode {
118
+ type: 'unary';
119
+ op: string;
120
+ operand: ExprNode;
121
+ start: number;
122
+ end: number;
123
+ }
124
+ export interface ConditionalNode {
125
+ type: 'conditional';
126
+ test: ExprNode;
127
+ consequent: ExprNode;
128
+ alternate: ExprNode;
129
+ start: number;
130
+ end: number;
131
+ }
132
+ /** IF 分支:condition 满足则求值 body */
133
+ export interface IfBranch {
134
+ condition: ExprNode;
135
+ body: ExprNode;
136
+ }
137
+ /** 块状 IF / ELSE IF / ELSE 节点(IF 条件 THEN 真值 ELSE 假值) */
138
+ export interface IfNode {
139
+ type: 'if';
140
+ /** 至少一个分支,按书写顺序依次判断 */
141
+ branches: IfBranch[];
142
+ /** 兜底分支,可省略(省略时条件全不满足返回 null) */
143
+ elseBody: ExprNode | null;
144
+ start: number;
145
+ end: number;
146
+ }
147
+ export type ExprNode = NumberNode | StringNode | BooleanNode | NullNode | VariableNode | MemberNode | CallNode | BinaryNode | UnaryNode | ConditionalNode | IfNode;
148
+ export interface FormulaError {
149
+ message: string;
150
+ /** 出错区间(对应原公式字符串偏移) */
151
+ start: number;
152
+ end: number;
153
+ severity: 'error' | 'warning';
154
+ kind: 'lex' | 'parse' | 'semantic' | 'runtime';
155
+ }
156
+ /** 内置函数规格 */
157
+ export interface FunctionSpec {
158
+ name: string;
159
+ signature: string;
160
+ description: string;
161
+ minArgs: number;
162
+ maxArgs: number;
163
+ /** 参数说明(按序),用于悬停提示与函数面板 */
164
+ params?: string[];
165
+ /** 示例(完整调用文本),用于悬停提示与函数面板 */
166
+ example?: string;
167
+ fn: (args: unknown[]) => unknown;
168
+ }
169
+ /** 校验结果 */
170
+ export interface ValidationResult {
171
+ ok: boolean;
172
+ errors: FormulaError[];
173
+ ast: ExprNode | null;
174
+ }
175
+ /** 求值结果 */
176
+ export type EvalResult = {
177
+ ok: true;
178
+ value: unknown;
179
+ } | {
180
+ ok: false;
181
+ error: FormulaError;
182
+ };
package/dist/index.cjs ADDED
@@ -0,0 +1,39 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("vue"),m=require("./engine/index.cjs"),Ge={class:"fe-header"},Xe={class:"fe-title-wrap"},Qe={class:"fe-title-box"},Ze={class:"fe-title"},Je={class:"fe-subtitle"},et={class:"fe-header-actions"},tt=["disabled","title"],lt=["disabled","title"],nt=["disabled"],ot=["disabled"],at=["disabled"],st=["disabled"],rt=["disabled","title"],it={class:"fe-btn-ico"},ct={key:0,class:"fe-help"},ut={class:"fe-help-col"},dt={class:"fe-help-fns"},pt={key:0,class:"fe-toolbar"},mt={class:"fe-toolbar-group"},ft=["disabled","title","onClick"],Et={class:"fe-body"},kt={key:0,class:"fe-vars"},Nt={class:"fe-vars-head"},vt={class:"fe-vars-count"},yt={class:"fe-vars-search"},bt=["disabled"],gt={class:"fe-vars-list"},Vt=["title","onClick"],Tt={class:"fe-var-line1"},ht=["title","onClick"],St={class:"fe-var-name"},xt={key:1,class:"fe-var-req",title:"必填"},Ct={class:"fe-var-desc"},Bt={key:0,class:"fe-var-sub"},It=["title","onClick"],Lt={key:1,class:"fe-var-sub"},Ot=["title","onClick"],Ft={key:0,class:"fe-var-sub-empty"},Dt={key:0,class:"fe-vars-empty"},Rt={key:0},wt={key:1},_t={key:1,class:"fe-fns"},At={class:"fe-vars-head"},$t={class:"fe-vars-count"},Ut={class:"fe-vars-search"},Mt=["disabled"],Pt={class:"fe-vars-list"},Ht=["title","onClick"],zt={class:"fe-fn-line1"},Wt={class:"fe-fn-name"},Yt={class:"fe-fn-group"},jt={class:"fe-fn-sig"},qt={class:"fe-fn-desc"},Kt={key:0,class:"fe-vars-empty"},Gt={class:"fe-editor"},Xt={class:"fe-code-shell"},Qt={class:"fe-gutter"},Zt=["innerHTML"],Jt=["placeholder","disabled","readonly"],el={key:0,class:"fe-errors"},tl=["onClick"],ll={class:"fe-error-msg"},nl={class:"fe-error-kind"},ol={class:"fe-footer"},al={class:"fe-foot-left"},sl={key:0,class:"fe-foot-err-count"},rl=["title"],il={key:0,class:"fe-result-type"},cl={key:1,class:"fe-result-value empty"},ul={class:"fe-foot-caret"},dl=e.defineComponent({__name:"FormulaEditor",props:{modelValue:{default:""},variables:{default:()=>[]},context:{default:()=>({})},functions:{default:()=>({})},functionSpecs:{default:()=>({})},title:{default:"公式编辑器"},subtitle:{default:"FORMULA EDITOR"},placeholder:{default:'例如:IF {订单金额} >= 199 THEN "免运费" ELSE "收运费"'},height:{default:"340px"},disabled:{type:Boolean,default:!1},readonly:{type:Boolean,default:!1},showVariablesPanel:{type:Boolean,default:!0},showToolbar:{type:Boolean,default:!0},showPreview:{type:Boolean,default:!0},theme:{default:"dark"}},emits:["update:modelValue","change","error","insert","update:theme"],setup(p,{expose:U,emit:R}){const u=p,y=R,d=e.ref(u.modelValue),h=e.ref(u.theme),L=e.ref(u.showVariablesPanel),O=e.ref(!1),w=e.ref(!1),M=e.ref(!1),P=e.ref(""),H=e.ref(""),_=e.ref(0),X=e.ref(0),A=e.ref({line:1,col:1});e.watch(()=>u.modelValue,t=>{t!==d.value&&(d.value=t)}),e.watch(()=>u.theme,t=>{t&&(h.value=t)});const E=e.ref(null),Ne=e.ref(null),Q=e.ref(null),b=e.ref(null),Z=new Set(["IF","ELSE","THEN","ELSEIF","如果","那么","否则","否则如果"]),J=new Set(["AND","OR"]);function z(t){return Z.has(t)?!0:/^[A-Za-z]+$/.test(t)?Z.has(t.toUpperCase()):!1}function ee(t){return J.has(t)?!0:/^[A-Za-z]+$/.test(t)?J.has(t.toUpperCase()):!1}const W=e.computed(()=>m.tokenize(d.value)),F=e.ref({ok:!0,errors:[],ast:null}),S=e.ref(null);let te;function ve(){window.clearTimeout(te),te=window.setTimeout(()=>{F.value=m.validateFormula(d.value,u.variables,u.functions,Object.keys(u.context)),S.value=d.value.trim()?m.evaluateFormula(d.value,u.context,u.functions,u.variables):null},120)}e.watch([d,()=>u.variables,()=>u.context,()=>u.functions],ve,{immediate:!0});const ye=e.computed(()=>d.value.split(`
2
+ `).length),be=e.computed(()=>12+(A.value.line-1)*21.25-_.value),le=e.computed(()=>{const t=P.value.trim().toLowerCase();return t?u.variables.filter(l=>(l.name+" "+(l.label??"")+" "+(l.description??"")).toLowerCase().includes(t)):u.variables}),I=e.computed(()=>F.value.errors),ge=e.computed(()=>{const t=new Set;for(const l of I.value){const n=d.value.slice(0,l.start).split(`
3
+ `).length;t.add(n)}return t}),Ve=e.computed(()=>{const t=I.value;return t.length===0?{label:"公式有效",cls:"ok"}:{label:`${t.length} 个问题`,cls:"bad"}}),Te=e.computed(()=>{const t=S.value;return t?t.ok?m.formatValue(t.value):t.error.message:null}),ne=e.computed(()=>{const t=S.value;return t&&t.ok?m.valueTypeName(t.value):""}),he=e.computed(()=>{const t=S.value;return t?t.ok?"ok":"bad":"empty"});function Y(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}const oe=e.ref(0);let x=null;function Se(t){const l=oe.value;let n=-1;for(let r=0;r<t.length;r++){const i=t[r];if(i.type==="eof")break;if(l>=i.start&&l<=i.end){n=r;break}}if(n<0)return null;const a={lparen:"rparen",lbracket:"rbracket"},o={rparen:"lparen",rbracket:"lbracket"},s=t[n];if(s.type in a){let r=0;for(let i=n;i<t.length;i++){const c=t[i];if(c.type==="eof")break;if(c.type===s.type)r++;else if(c.type===a[s.type]&&(r--,r===0))return{a:n,b:i}}}else if(s.type in o){let r=0;for(let i=n;i>=0;i--){const c=t[i];if(c.type==="eof")break;if(c.type===s.type)r++;else if(c.type===o[s.type]&&(r--,r===0))return{a:i,b:n}}}return null}function xe(t,l,n){if(n!==void 0&&(n===(x==null?void 0:x.a)||n===(x==null?void 0:x.b)))return"tok-pair";switch(t.type){case"number":return"tok-num";case"string":return t.closed===!1?"tok-err":"tok-str";case"variable":return t.closed===!1?"tok-err":"tok-var";case"identifier":return z(t.value)&&(l==null?void 0:l.type)!=="lparen"?"tok-kw":ee(t.value)&&(l==null?void 0:l.type)!=="lparen"?"tok-op":"tok-fn";case"operator":return"tok-op";case"lparen":case"rparen":case"lbracket":case"rbracket":case"comma":case"question":case"colon":return"tok-paren";case"error":return"tok-err";default:return""}}const Ce=e.computed(()=>{let t="",l=0;const n=W.value;x=Se(n);for(let a=0;a<n.length;a++){const o=n[a];if(o.type==="eof")break;t+=Y(d.value.slice(l,o.start)),t+=`<span class="${xe(o,n[a+1],a)}">${Y(o.raw)}</span>`,l=o.end}return t+=Y(d.value.slice(l)),t+`
4
+ `});function Be(){var l;const t=((l=E.value)==null?void 0:l.value)??d.value;t!==d.value&&(d.value=t),y("update:modelValue",t),y("change",t)}e.watch(F,t=>y("error",t));function v(){const t=E.value;if(!t)return;const l=t.selectionStart;oe.value=l;const n=d.value.slice(0,l),a=n.split(`
5
+ `).length,o=l-n.lastIndexOf(`
6
+ `);A.value={line:a,col:o}}function Ie(){const t=E.value;t&&(_.value=t.scrollTop,X.value=t.scrollLeft)}function Le(t,l){var n,a;if(t.type==="variable"&&t.closed!==!1){const o=u.variables.find(V=>V.name===t.value),s=o!=null&&o.type?`[${me(o.type)}]`:"",r=o!=null&&o.required?" 必填":"",i=o!=null&&o.description?`
7
+ ${o.description}`:"",c=o!=null&&o.label&&o.label!==o.name?`(${o.label})`:"",k=u.context[t.value];let f="";k!==void 0&&(f=`
8
+ 当前值: ${m.formatValue(k,60)}`);let g="";(o==null?void 0:o.type)==="enum"&&o.options&&(g=`
9
+ 标签: ${o.options.map(B=>typeof B=="string"?B:B.label).join(" / ")}`);let C="";return(o==null?void 0:o.type)==="object"&&o.fields&&o.fields.length>0&&(C=`
10
+ 字段: ${o.fields.map(V=>V.key).join(" / ")}`),`变量 {${t.value}}${c} ${s}${r}${i}${f}${g}${C}`}if(t.type==="identifier"){if((l==null?void 0:l.type)==="lparen"){const r=t.value.toUpperCase(),i=m.BUILTIN_FUNCTIONS[r];if(i){const c=(n=i.params)!=null&&n.length?`
11
+ `+i.params.map((f,g)=>`${g+1}. ${f}`).join(`
12
+ `):"",k=i.example?`
13
+ 示例: ${i.example}`:"";return`${i.signature}
14
+ ${i.description}${c}${k}`}if(t.value in u.functions){const c=u.functionSpecs[t.value];if(c){const k=(a=c.params)!=null&&a.length?`
15
+ `+c.params.map((g,C)=>`${C+1}. ${g}`).join(`
16
+ `):"",f=c.example?`
17
+ 示例: ${c.example}`:"";return`${c.signature}
18
+ ${c.description}${k}${f}`}return`自定义函数 ${t.value}(...)`}return null}const o=t.value.toUpperCase(),s={IF:`IF 条件 THEN 真值 ELSE IF 条件 THEN 真值 … ELSE 假值
19
+ 块状条件判断,条件满足即返回对应分支,支持多级 ELSE IF`,ELSEIF:`ELSE IF 条件 THEN 真值
20
+ 在前一个条件不满足时继续判断`,THEN:`IF 条件 THEN 真值
21
+ 条件与结果之间的分隔关键字`,ELSE:`ELSE 假值
22
+ 所有条件都不满足时的兜底分支`,如果:`如果 条件 那么 真值 否则 假值
23
+ 块状条件判断(中文关键字写法)`,那么:`如果 条件 那么 真值
24
+ 条件与结果之间的分隔关键字(中文)`,否则:`否则 假值
25
+ 所有条件都不满足时的兜底分支(中文)`,否则如果:`否则如果 条件 那么 真值
26
+ 在前一个条件不满足时继续判断(中文)`};if(z(t.value))return s[o]??s[t.value]??null;if(ee(t.value))return t.value.toUpperCase()==="AND"?"AND — 逻辑与(两边同时为真)":"OR — 逻辑或(任一边为真)"}return t.type==="operator"?{"&&":"逻辑与(同时满足)","||":"逻辑或(任一满足)","==":'宽松等于(数字 5 与字符串 "5" 视为相等)',"===":"严格等于(类型也必须相同)","!=":"宽松不等于","!==":"严格不等于","<":"小于","<=":"小于等于",">":"大于",">=":"大于等于","+":"加(数字相加;含字符串时拼接)","-":"减","*":"乘","/":"除(除数不能为 0)","%":"取余","**":"幂运算","!":"逻辑非"}[t.value]??null:null}function Oe(t){const l=E.value,n=Q.value;if(!l||!n){b.value=null;return}const a=document;let o=null;if(a.caretRangeFromPoint){const f=a.caretRangeFromPoint(t.clientX,t.clientY);f&&(f.startContainer===l||l.contains(f.startContainer))&&(o=f.startOffset)}else if(a.caretPositionFromPoint){const f=a.caretPositionFromPoint(t.clientX,t.clientY);f&&(o=f.offset)}if(o==null){b.value=null;return}const s=W.value,r=s.find(f=>f.type!=="eof"&&o>=f.start&&o<f.end);if(!r){b.value=null;return}const i=s.indexOf(r),c=Le(r,s[i+1]);if(!c){b.value=null;return}const k=n.getBoundingClientRect();b.value={text:c,x:t.clientX-k.left,y:t.clientY-k.top}}function Fe(){b.value=null}function De(){const t=E.value;if(!t||u.disabled||u.readonly)return;const l=t.selectionStart,n=se(l);n&&(t.setSelectionRange(n.start,n.end),v())}function ae(t){let l=null,n=!1;const a=d.value,o=Math.min(t,a.length);for(let s=0;s<o;s++){const r=a[s];l?n?n=!1:r==="\\"?n=!0:r===l&&(l=null):(r==="'"||r==='"')&&(l=r)}return l!==null}function se(t){const l=d.value,n=l.length;if(t<n&&l[t]==="{"){const s=l.indexOf("}",t+1);return s>=0?{start:t,end:s+1}:null}if(t>0&&l[t-1]==="}"){const s=t-1;for(let r=s-1;r>=0;r--)if(l[r]==="{")return{start:r,end:s+1};return null}let a=-1;for(let s=t-1;s>=0;s--){const r=l[s];if(r==="}")break;if(r==="{"){a=s;break}}if(a<0)return null;const o=l.indexOf("}",a+1);return o>=0?{start:a,end:o+1}:null}function D(t,l,n,a="insertText"){const o=E.value;if(!o||t>l)return;o.focus(),o.setSelectionRange(t,l);const s=o.value;let r=!1;try{r=n===""?document.execCommand("delete"):document.execCommand("insertText",!1,n)}catch{r=!1}if(!r||o.value===s){if(o.value===s&&o.value.slice(t,l)===n)return;o.setRangeText(n,t,l),o.dispatchEvent(new InputEvent("input",{inputType:a,data:n===""?null:n,bubbles:!0}))}}function Re(t,l,n){D(t,l,n,"deleteContent")}function re(t){var r;const l=d.value,n=l.lastIndexOf(`
27
+ `,t-1)+1,a=l.slice(n,t),o=((r=/^[ \t]*/.exec(a))==null?void 0:r[0])??"",s=a.trim();return/THEN$/i.test(s)||/那么$/.test(s)||/^ELSE$/i.test(s)||s==="否则"?o+" ":o}function we(t,l){const n=W.value;let a=null;for(const o of n){if(o.type==="eof")break;o.start<t||o.end>l||o.type==="identifier"&&z(o.value)&&/^(ELSE|否则)$/i.test(o.value)&&(a=o)}return a}function _e(t){var Ee;const l=d.value,n=l.lastIndexOf(`
28
+ `,t-1)+1,a=l.indexOf(`
29
+ `,t),o=a===-1?l.length:a,s=we(n,o);if(!s||t<s.start-1||t>s.end)return!1;const i=l.slice(n,s.start).trimEnd();if(i.trim()==="")return!1;const c=((Ee=/^[ \t]*/.exec(i))==null?void 0:Ee[0])??"",k=/^(IF|如果)(?=\s)/i.test(i.trim())?"":c.length>=2?c.slice(0,-2):"",f=l.slice(s.end,o),g=k+" ",C=i+`
30
+ `+k+s.raw+`
31
+ `+g;let V=o,B=f.trim();if(B===""&&l[o]===`
32
+ `){let T=o+1;for(;T<l.length&&(l[T]===" "||l[T]===" ");)T++;V=T,B=""}D(n,V,C+B,"insertText");const fe=n+C.length;return e.nextTick(()=>{var T,ke;(T=E.value)==null||T.focus(),(ke=E.value)==null||ke.setSelectionRange(fe,fe),v()}),!0}function Ae(t){const l=E.value;if(!l||u.disabled||u.readonly)return;if(t.key==="Tab"){t.preventDefault(),N(" ");return}if(l.selectionStart!==l.selectionEnd){if(t.key==="Enter"&&!t.isComposing){const a=l.selectionStart,o=l.selectionEnd,s=d.value.slice(a,o);if(s.length>2&&s.startsWith("{")&&s.endsWith("}")&&!s.slice(1,-1).includes("{")&&!s.slice(1,-1).includes("}")){t.preventDefault(),l.setSelectionRange(o,o),N(`
33
+ `+re(o));return}}return}const n=l.selectionStart;if(t.key==="Backspace"||t.key==="Delete"){if(!ae(n)){const a=se(n);a&&n>a.start&&n<a.end&&(t.preventDefault(),Re(a.start,a.end,""),e.nextTick(()=>{l.focus(),l.setSelectionRange(a.start,a.start),v()}))}return}if(t.key==="Enter"&&!t.isComposing){if(t.preventDefault(),_e(n))return;N(`
34
+ `+re(n));return}if((t.key==="{"||t.key==='"'||t.key==="'")&&!t.ctrlKey&&!t.metaKey&&!t.altKey&&!t.isComposing){if(t.preventDefault(),t.key==="{"){N("{}"),e.nextTick(()=>{l.focus(),l.setSelectionRange(n+1,n+1),v()});return}const a=t.key,o=d.value[n];ae(n)||o===a?N(a):(N(a+a),e.nextTick(()=>{l.focus(),l.setSelectionRange(n+1,n+1),v()}));return}}function j(){var t;(t=E.value)==null||t.focus()}function $e(){const t=h.value==="dark"?"light":"dark";h.value=t,y("update:theme",t)}function N(t,l){const n=E.value;if(!n)return;const a=n.selectionStart??d.value.length,o=n.selectionEnd??a;D(a,o,t,"insertText"),e.nextTick(()=>{if(n.focus(),l){const r=l.exec(t);if(r&&r.index>=0){n.setSelectionRange(a+r.index,a+r.index+r[0].length);return}}const s=a+t.length;n.setSelectionRange(s,s),v()})}function Ue(t){N(`{${t}}`),y("insert",{type:"variable",value:t})}function ie(t){N(t.snippet,t.select)}function Me(t,l){const n=E.value;n&&(n.focus(),n.setSelectionRange(t,l),n.scrollTop=n.scrollTop,v())}function Pe(){D(0,d.value.length,"","deleteContent"),j()}function He(){var r;const t=d.value;if(!t.trim())return;const l=m.formatFormula(t);if(l===t){j();return}const n=((r=E.value)==null?void 0:r.selectionStart)??t.length,a=m.tokenize(t);let o=null;for(let i=0;i<a.length;i++){const c=a[i];if(c.type==="eof")break;if(n>=c.start&&n<=c.end){o=c;break}}D(0,t.length,l,"insertText");let s=l.length;if(o){const c=m.tokenize(l).find(k=>k.type!=="eof"&&k.type===o.type&&k.raw===o.raw);c&&(s=c.start)}e.nextTick(()=>{var i,c;(i=E.value)==null||i.focus(),(c=E.value)==null||c.setSelectionRange(s,s),v()})}async function ze(){try{await navigator.clipboard.writeText(d.value)}catch{const t=E.value;t&&(t.select(),document.execCommand("copy"),t.setSelectionRange(t.value.length,t.value.length))}}const q=[{key:"if",label:"IF…ELSE",title:"块状 IF:IF 条件 THEN 真值 ELSE 假值(支持多级 ELSE IF)",group:"logic",snippet:`IF 条件 THEN
35
+ 真值
36
+ ELSE
37
+ 假值`,select:/条件/},{key:"elseif",label:"ELSE IF",title:"追加条件分支:ELSE IF 条件 THEN 真值",group:"logic",snippet:`ELSE IF 条件 THEN
38
+ 真值`,select:/条件/},{key:"ifs",label:"IFS",title:"IFS(条件1, 值1, 条件2, 值2, 默认值) — 多条件判断",group:"logic",snippet:"IFS(条件1, 值1, 条件2, 值2, 默认值)",select:/条件1/},{key:"ternary",label:"a?b:c",title:"三元表达式:条件 ? 真值 : 假值",group:"logic",snippet:"条件 ? 真值 : 假值",select:/条件/},{key:"and",label:"AND",title:"AND(条件1, 条件2) — 全部满足",group:"logic",snippet:"AND(条件1, 条件2)",select:/条件1/},{key:"or",label:"OR",title:"OR(条件1, 条件2) — 任一满足",group:"logic",snippet:"OR(条件1, 条件2)",select:/条件1/},{key:"not",label:"NOT",title:"NOT(条件) — 取反",group:"logic",snippet:"NOT(条件)",select:/条件/},{key:"add",label:"+",title:"加法",group:"math",snippet:" + "},{key:"sub",label:"−",title:"减法",group:"math",snippet:" - "},{key:"mul",label:"×",title:"乘法",group:"math",snippet:" * "},{key:"div",label:"÷",title:"除法",group:"math",snippet:" / "},{key:"round",label:"ROUND",title:"ROUND(数字, 小数位)",group:"math",snippet:"ROUND(数字, 2)",select:/数字/},{key:"sum",label:"SUM",title:"SUM(数字1, 数字2, …) — 数值总和(忽略非数字参数)",group:"math",snippet:"SUM(数字1, 数字2)",select:/数字1/},{key:"average",label:"AVERAGE",title:"AVERAGE(数字1, 数字2, …) — 平均值(忽略非数字参数)",group:"math",snippet:"AVERAGE(数字1, 数字2)",select:/数字1/},{key:"abs",label:"ABS",title:"ABS(数字) — 取绝对值",group:"math",snippet:"ABS(数字)",select:/数字/},{key:"floor",label:"FLOOR",title:"FLOOR(数字) — 向下取整",group:"math",snippet:"FLOOR(数字)",select:/数字/},{key:"ceil",label:"CEIL",title:"CEIL(数字) — 向上取整",group:"math",snippet:"CEIL(数字)",select:/数字/},{key:"min",label:"MIN",title:"MIN(数字1, 数字2, …) — 取最小值",group:"math",snippet:"MIN(数字1, 数字2)",select:/数字1/},{key:"max",label:"MAX",title:"MAX(数字1, 数字2, …) — 取最大值",group:"math",snippet:"MAX(数字1, 数字2)",select:/数字1/},{key:"eq",label:"=",title:"等于 ==",group:"compare",snippet:" == "},{key:"neq",label:"≠",title:"不等于 !=",group:"compare",snippet:" != "},{key:"lt",label:"<",title:"小于",group:"compare",snippet:" < "},{key:"gt",label:">",title:"大于",group:"compare",snippet:" > "},{key:"andop",label:"&&",title:"逻辑与",group:"compare",snippet:" && "},{key:"orop",label:"||",title:"逻辑或",group:"compare",snippet:" || "},{key:"between",label:"BETWEEN",title:"BETWEEN(值, 下限, 上限) — 是否在 [下限, 上限] 区间内(含边界)",group:"compare",snippet:"BETWEEN(值, 下限, 上限)",select:/值/},{key:"isnull",label:"ISNULL",title:"ISNULL(值) — 是否为空值(NULL / 未定义)",group:"compare",snippet:"ISNULL(值)",select:/值/},{key:"isempty",label:"ISEMPTY",title:"ISEMPTY(值) — 是否为空(NULL、空字符串、空数组/对象)",group:"compare",snippet:"ISEMPTY(值)",select:/值/},{key:"isnumeric",label:"ISNUMERIC",title:"ISNUMERIC(值) — 是否为数字(含可转换的数字字符串)",group:"compare",snippet:"ISNUMERIC(值)",select:/值/},{key:"concat",label:"拼接",title:"CONCAT(文本1, 文本2) — 字符串拼接",group:"text",snippet:"CONCAT(文本1, 文本2)",select:/文本1/},{key:"len",label:"LEN",title:"LEN(文本) — 返回文本长度(按字符数)",group:"text",snippet:"LEN(文本)",select:/文本/},{key:"upper",label:"UPPER",title:"UPPER(文本) — 转大写",group:"text",snippet:"UPPER(文本)",select:/文本/},{key:"lower",label:"LOWER",title:"LOWER(文本) — 转小写",group:"text",snippet:"LOWER(文本)",select:/文本/},{key:"trim",label:"TRIM",title:"TRIM(文本) — 去除首尾空白",group:"text",snippet:"TRIM(文本)",select:/文本/},{key:"substr",label:"SUBSTR",title:"SUBSTR(文本, 起始下标[, 长度]) — 截取子串(0 起始)",group:"text",snippet:"SUBSTR(文本, 起始下标)",select:/文本/},{key:"mid",label:"MID",title:"MID(文本, 起始位, 长度) — Excel 风格截取(1 起始)",group:"text",snippet:"MID(文本, 起始位, 长度)",select:/文本/},{key:"left",label:"LEFT",title:"LEFT(文本, 长度) — 取左侧 N 个字符",group:"text",snippet:"LEFT(文本, 长度)",select:/文本/},{key:"right",label:"RIGHT",title:"RIGHT(文本, 长度) — 取右侧 N 个字符",group:"text",snippet:"RIGHT(文本, 长度)",select:/文本/},{key:"contains",label:"CONTAINS",title:"CONTAINS(文本, 子串) — 是否包含子串(大小写敏感)",group:"text",snippet:"CONTAINS(文本, 子串)",select:/文本/},{key:"startswith",label:"STARTSWITH",title:"STARTSWITH(文本, 前缀) — 是否以指定前缀开头",group:"text",snippet:"STARTSWITH(文本, 前缀)",select:/文本/},{key:"endswith",label:"ENDSWITH",title:"ENDSWITH(文本, 后缀) — 是否以指定后缀结尾",group:"text",snippet:"ENDSWITH(文本, 后缀)",select:/文本/},{key:"indexof",label:"INDEXOF",title:"INDEXOF(文本, 子串) — 子串位置(0 起始),未找到 -1",group:"text",snippet:"INDEXOF(文本, 子串)",select:/文本/},{key:"replace",label:"REPLACE",title:"REPLACE(文本, 被替换, 替换为) — 替换所有匹配",group:"text",snippet:"REPLACE(文本, 被替换, 替换为)",select:/文本/},{key:"split",label:"SPLIT",title:"SPLIT(文本, 分隔符) — 拆分为数组",group:"text",snippet:"SPLIT(文本, 分隔符)",select:/文本/},{key:"ifnull",label:"IFNULL",title:"IFNULL(值, 兜底值) — 为空时返回兜底值",group:"logic",snippet:"IFNULL(值, 兜底值)",select:/值/},{key:"coalesce",label:"COALESCE",title:"COALESCE(值1, 值2, …) — 取第一个非空值",group:"logic",snippet:"COALESCE(值1, 值2, 值3)",select:/值1/},{key:"switch",label:"SWITCH",title:"SWITCH(值, 情况1, 结果1, …, 默认值) — 按值匹配",group:"logic",snippet:"SWITCH(值, 情况1, 结果1, 默认值)",select:/值/},{key:"tostring",label:"TOSTRING",title:"TOSTRING(值) — 转字符串",group:"convert",snippet:"TOSTRING(值)",select:/值/},{key:"tonumber",label:"TONUMBER",title:"TONUMBER(值) — 转数字",group:"convert",snippet:"TONUMBER(值)",select:/值/},{key:"toboolean",label:"TOBOOLEAN",title:"TOBOOLEAN(值) — 转布尔",group:"convert",snippet:"TOBOOLEAN(值)",select:/值/},{key:"now",label:"NOW",title:"NOW() — 当前时间戳(毫秒)",group:"date",snippet:"NOW()"},{key:"today",label:"TODAY",title:"TODAY() — 今天零点时间戳",group:"date",snippet:"TODAY()"},{key:"dateadd",label:"DATEADD",title:"DATEADD(时间戳, 天数) — 日期加 N 天",group:"date",snippet:"DATEADD(TODAY(), 7)",select:/TODAY\(\)/},{key:"datedif",label:"DATEDIF",title:"DATEDIF(起始, 结束) — 相隔整天数",group:"date",snippet:"DATEDIF(TODAY(), DATEADD(TODAY(), 7))",select:/TODAY\(\)/},{key:"count",label:"COUNT",title:"COUNT(数组) — 数组长度",group:"agg",snippet:"COUNT(数组)",select:/数组/},{key:"countif",label:"COUNTIF",title:"COUNTIF(数组, 目标值) — 统计相等元素个数",group:"agg",snippet:"COUNTIF(数组, 目标值)",select:/数组/},{key:"sumif",label:"SUMIF",title:"SUMIF(数组, 目标值) — 相等元素求和",group:"agg",snippet:"SUMIF(数组, 目标值)",select:/数组/},{key:"keys",label:"KEYS",title:"KEYS(对象) — 对象的键名数组",group:"agg",snippet:"KEYS(对象)",select:/对象/},{key:"values",label:"VALUES",title:"VALUES(对象) — 对象的键值数组",group:"agg",snippet:"VALUES(对象)",select:/对象/}],ce=[{key:"logic",label:"逻辑"},{key:"math",label:"运算"},{key:"compare",label:"比较"},{key:"text",label:"文本"},{key:"convert",label:"转换"},{key:"date",label:"日期"},{key:"agg",label:"聚合"}];function We(t){var l;return((l=ce.find(n=>n.key===t))==null?void 0:l.label)??t}const $=Object.values(m.BUILTIN_FUNCTIONS).map(t=>{var l;return{name:t.name,signature:t.signature,description:t.description,params:t.params??[],example:t.example,group:((l=q.find(n=>n.label.toUpperCase()===t.name))==null?void 0:l.group)??"logic"}}),ue=e.computed(()=>{const t=H.value.trim().toLowerCase();return t?$.filter(l=>(l.name+" "+l.signature+" "+l.description).toLowerCase().includes(t)):$}),K=e.ref(new Set);function Ye(t){const l=new Set(K.value);l.has(t)?l.delete(t):l.add(t),K.value=l}function G(t){return K.value.has(t)}function de(t){if(t.fields&&t.fields.length>0)return t.fields.map(n=>n.key);const l=u.context[t.name];return l&&typeof l=="object"&&!Array.isArray(l)?Object.keys(l):[]}const pe={number:"数字",string:"文本",boolean:"布尔",enum:"枚举",object:"键值对",any:"任意"};function me(t){return t&&pe[t]?pe[t]:"任意"}function je(t,l){N(`{${t.name}} == '${l}'`),y("insert",{type:"text",value:`{${t.name}} == '${l}'`})}function qe(t,l){N(`{${t.name}}.${l}`),y("insert",{type:"text",value:`{${t.name}}.${l}`})}function Ke(t){const l=q.find(n=>n.label.toUpperCase()===t.name);l?ie(l):N(t.signature),y("insert",{type:"function",value:t.name})}return U({validate:()=>F.value,evaluate:t=>m.evaluateFormula(d.value,t??u.context,u.functions,u.variables),insertText:t=>N(t),focus:j}),(t,l)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["fe",{"theme-light":h.value==="light"}]),style:e.normalizeStyle({"--fe-height":p.height})},[e.createElementVNode("header",Ge,[e.createElementVNode("div",Xe,[l[7]||(l[7]=e.createElementVNode("span",{class:"fe-mark"},"Σ",-1)),e.createElementVNode("div",Qe,[e.createElementVNode("div",Ze,e.toDisplayString(p.title),1),e.createElementVNode("div",Je,e.toDisplayString(p.subtitle),1)])]),e.createElementVNode("div",et,[p.showVariablesPanel?(e.openBlock(),e.createElementBlock("button",{key:0,class:e.normalizeClass(["fe-btn",{active:L.value}]),type:"button",disabled:p.disabled,title:L.value?"收起变量面板":"展开变量面板",onClick:l[0]||(l[0]=n=>L.value=!L.value)},[...l[8]||(l[8]=[e.createElementVNode("span",{class:"fe-btn-ico"},"☰",-1),e.createTextVNode("变量 ",-1)])],10,tt)):e.createCommentVNode("",!0),e.createElementVNode("button",{class:e.normalizeClass(["fe-btn",{active:O.value}]),type:"button",disabled:p.disabled,title:O.value?"收起函数面板":"展开函数面板(点选插入函数)",onClick:l[1]||(l[1]=n=>O.value=!O.value)},[...l[9]||(l[9]=[e.createElementVNode("span",{class:"fe-btn-ico"},"ƒ",-1),e.createTextVNode("函数 ",-1)])],10,lt),e.createElementVNode("button",{class:"fe-btn",type:"button",disabled:p.disabled,title:"一键格式化(按块状 IF 层级对齐缩进)",onClick:He},[...l[10]||(l[10]=[e.createElementVNode("span",{class:"fe-btn-ico"},"⇄",-1),e.createTextVNode("格式化 ",-1)])],8,nt),e.createElementVNode("button",{class:"fe-btn",type:"button",disabled:p.disabled,title:"复制公式",onClick:ze},[...l[11]||(l[11]=[e.createElementVNode("span",{class:"fe-btn-ico"},"⧉",-1),e.createTextVNode("复制 ",-1)])],8,ot),e.createElementVNode("button",{class:"fe-btn",type:"button",disabled:p.disabled,title:"清空公式",onClick:Pe},[...l[12]||(l[12]=[e.createElementVNode("span",{class:"fe-btn-ico"},"✕",-1),e.createTextVNode("清空 ",-1)])],8,at),e.createElementVNode("button",{class:e.normalizeClass(["fe-btn",{active:w.value}]),type:"button",disabled:p.disabled,title:"语法帮助",onClick:l[2]||(l[2]=n=>w.value=!w.value)},[...l[13]||(l[13]=[e.createElementVNode("span",{class:"fe-btn-ico"},"?",-1),e.createTextVNode("帮助 ",-1)])],10,st),e.createElementVNode("button",{class:"fe-btn",type:"button",disabled:p.disabled,title:h.value==="dark"?"切换到浅色主题":"切换到深色主题",onClick:$e},[e.createElementVNode("span",it,e.toDisplayString(h.value==="dark"?"☀":"🌙"),1),e.createTextVNode(" "+e.toDisplayString(h.value==="dark"?"浅色":"深色"),1)],8,rt)])]),e.createVNode(e.Transition,{name:"fe-drop"},{default:e.withCtx(()=>[w.value?(e.openBlock(),e.createElementBlock("div",ct,[l[15]||(l[15]=e.createElementVNode("div",{class:"fe-help-col"},[e.createElementVNode("div",{class:"fe-help-title"},"语法速查"),e.createElementVNode("table",{class:"fe-help-table"},[e.createElementVNode("tbody",null,[e.createElementVNode("tr",null,[e.createElementVNode("td",{class:"fe-help-k"},"变量引用"),e.createElementVNode("td",null,[e.createElementVNode("code",null,"{变量名}")])]),e.createElementVNode("tr",null,[e.createElementVNode("td",{class:"fe-help-k"},"成员访问"),e.createElementVNode("td",null,[e.createElementVNode("code",null,"{订单}.金额"),e.createTextVNode(" / "),e.createElementVNode("code",null,"{订单}['金额']"),e.createTextVNode(" / "),e.createElementVNode("code",null,"{订单}.商品[0]")])]),e.createElementVNode("tr",null,[e.createElementVNode("td",{class:"fe-help-k"},"字符串"),e.createElementVNode("td",null,[e.createElementVNode("code",null,"'文本'"),e.createTextVNode(" 或 "),e.createElementVNode("code",null,'"文本"')])]),e.createElementVNode("tr",null,[e.createElementVNode("td",{class:"fe-help-k"},"布尔/空"),e.createElementVNode("td",null,[e.createElementVNode("code",null,"true"),e.createTextVNode(),e.createElementVNode("code",null,"false"),e.createTextVNode(),e.createElementVNode("code",null,"null"),e.createTextVNode(),e.createElementVNode("code",null,"undefined")])]),e.createElementVNode("tr",null,[e.createElementVNode("td",{class:"fe-help-k"},"块状条件"),e.createElementVNode("td",null,[e.createElementVNode("code",null,"IF 条件 THEN 真值 ELSE 假值"),e.createTextVNode("(可多级 "),e.createElementVNode("code",null,"ELSE IF"),e.createTextVNode(")")])]),e.createElementVNode("tr",null,[e.createElementVNode("td",{class:"fe-help-k"},"中文关键字"),e.createElementVNode("td",null,[e.createElementVNode("code",null,"如果 条件 那么 真值 否则 假值"),e.createTextVNode(" / "),e.createElementVNode("code",null,"否则如果")])]),e.createElementVNode("tr",null,[e.createElementVNode("td",{class:"fe-help-k"},"函数条件"),e.createElementVNode("td",null,[e.createElementVNode("code",null,"IF(条件, 真值, 假值)"),e.createTextVNode("、"),e.createElementVNode("code",null,"IFS(…)"),e.createTextVNode("(兼容写法)")])]),e.createElementVNode("tr",null,[e.createElementVNode("td",{class:"fe-help-k"},"枚举比较"),e.createElementVNode("td",null,[e.createTextVNode("公式写标签 "),e.createElementVNode("code",null,"{会员等级} == '金卡'"),e.createTextVNode(",求值用代码值,标签/值双向可匹配")])]),e.createElementVNode("tr",null,[e.createElementVNode("td",{class:"fe-help-k"},"比较"),e.createElementVNode("td",null,[e.createElementVNode("code",null,"=="),e.createTextVNode(),e.createElementVNode("code",null,"!="),e.createTextVNode(),e.createElementVNode("code",null,"<"),e.createTextVNode(),e.createElementVNode("code",null,">"),e.createTextVNode(),e.createElementVNode("code",null,"<="),e.createTextVNode(),e.createElementVNode("code",null,">=")])]),e.createElementVNode("tr",null,[e.createElementVNode("td",{class:"fe-help-k"},"逻辑"),e.createElementVNode("td",null,[e.createElementVNode("code",null,"&&"),e.createTextVNode(),e.createElementVNode("code",null,"||"),e.createTextVNode(),e.createElementVNode("code",null,"!"),e.createTextVNode(),e.createElementVNode("code",null,"AND"),e.createTextVNode(),e.createElementVNode("code",null,"OR"),e.createTextVNode(),e.createElementVNode("code",null,"NOT()")])]),e.createElementVNode("tr",null,[e.createElementVNode("td",{class:"fe-help-k"},"三元"),e.createElementVNode("td",null,[e.createElementVNode("code",null,"条件 ? 真值 : 假值")])])])]),e.createElementVNode("p",{class:"fe-help-tip"},[e.createTextVNode(" 提示:插入的模板会选中占位文字(如“条件”),直接输入即可替换;光标在变量 "),e.createElementVNode("code",null,"{…}"),e.createTextVNode(" 内按退格/删除键可整体删除该变量。 ")])],-1)),e.createElementVNode("div",ut,[l[14]||(l[14]=e.createElementVNode("div",{class:"fe-help-title"},"内置函数",-1)),e.createElementVNode("ul",dt,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref($),n=>(e.openBlock(),e.createElementBlock("li",{key:n.name},[e.createElementVNode("code",null,e.toDisplayString(n.signature),1),e.createElementVNode("span",null,e.toDisplayString(n.description),1)]))),128))])])])):e.createCommentVNode("",!0)]),_:1}),p.showToolbar?(e.openBlock(),e.createElementBlock("div",pt,[(e.openBlock(),e.createElementBlock(e.Fragment,null,e.renderList(ce,n=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:n.key},[e.createElementVNode("span",mt,e.toDisplayString(n.label),1),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(q.filter(a=>a.group===n.key),a=>(e.openBlock(),e.createElementBlock("button",{key:a.key,class:"fe-chip",type:"button",disabled:p.disabled,title:a.title,onClick:o=>ie(a)},e.toDisplayString(a.label),9,ft))),128))],64))),64)),l[16]||(l[16]=e.createElementVNode("span",{class:"fe-toolbar-hint"},"插入的占位文字可直接输入替换",-1))])):e.createCommentVNode("",!0),e.createElementVNode("div",Et,[L.value?(e.openBlock(),e.createElementBlock("aside",kt,[e.createElementVNode("div",Nt,[l[17]||(l[17]=e.createElementVNode("span",null,"可选变量",-1)),e.createElementVNode("span",vt,e.toDisplayString(p.variables.length),1)]),e.createElementVNode("div",yt,[e.withDirectives(e.createElementVNode("input",{"onUpdate:modelValue":l[3]||(l[3]=n=>P.value=n),type:"text",placeholder:"搜索变量…",disabled:p.disabled},null,8,bt),[[e.vModelText,P.value]])]),e.createElementVNode("div",gt,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(le.value,n=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:n.name},[e.createElementVNode("div",{class:e.normalizeClass(["fe-var-row",{disabled:p.disabled}]),title:`点击插入 {${n.name}}`,onClick:a=>Ue(n.name)},[e.createElementVNode("div",Tt,[n.type==="enum"||n.type==="object"?(e.openBlock(),e.createElementBlock("span",{key:0,class:e.normalizeClass(["fe-var-expand",{open:G(n.name)}]),title:n.type==="enum"?"展开标签":"展开字段",onClick:e.withModifiers(a=>Ye(n.name),["stop"])},"▸",10,ht)):e.createCommentVNode("",!0),e.createElementVNode("span",St,"{"+e.toDisplayString(n.name)+"}",1),e.createElementVNode("span",{class:e.normalizeClass(["fe-var-badge",`vt-${n.type??"any"}`])},e.toDisplayString(me(n.type)),3),n.required?(e.openBlock(),e.createElementBlock("span",xt,"*")):e.createCommentVNode("",!0)]),e.createElementVNode("div",Ct,[e.createTextVNode(e.toDisplayString(n.label??n.name),1),n.description?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createTextVNode(" — "+e.toDisplayString(n.description),1)],64)):e.createCommentVNode("",!0)])],10,Vt),G(n.name)&&n.type==="enum"?(e.openBlock(),e.createElementBlock("div",Bt,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(n.options??[],a=>(e.openBlock(),e.createElementBlock("div",{key:typeof a=="string"?a:a.label,class:"fe-var-chip",title:`插入 {${n.name}} == '${typeof a=="string"?a:a.label}'`,onClick:e.withModifiers(o=>je(n,typeof a=="string"?a:a.label),["stop"])},e.toDisplayString(typeof a=="string"?a:a.label),9,It))),128))])):e.createCommentVNode("",!0),G(n.name)&&n.type==="object"?(e.openBlock(),e.createElementBlock("div",Lt,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(de(n),a=>(e.openBlock(),e.createElementBlock("div",{key:a,class:"fe-var-chip",title:`插入 {${n.name}}.${a}`,onClick:e.withModifiers(o=>qe(n,a),["stop"])},e.toDisplayString(a),9,Ot))),128)),de(n).length===0?(e.openBlock(),e.createElementBlock("div",Ft," 无字段(context 中对象为空) ")):e.createCommentVNode("",!0)])):e.createCommentVNode("",!0)],64))),128)),le.value.length===0?(e.openBlock(),e.createElementBlock("div",Dt,[l[18]||(l[18]=e.createElementVNode("div",{class:"fe-vars-empty-ico"},"∅",-1)),p.variables.length===0?(e.openBlock(),e.createElementBlock("div",Rt,"暂无可用变量")):(e.openBlock(),e.createElementBlock("div",wt,"没有匹配的变量"))])):e.createCommentVNode("",!0)]),l[19]||(l[19]=e.createElementVNode("div",{class:"fe-vars-foot"},"点击变量插入;枚举/键值对可点 ▸ 展开选标签/字段",-1))])):e.createCommentVNode("",!0),O.value?(e.openBlock(),e.createElementBlock("aside",_t,[e.createElementVNode("div",At,[l[20]||(l[20]=e.createElementVNode("span",null,"内置函数",-1)),e.createElementVNode("span",$t,e.toDisplayString(e.unref($).length),1)]),e.createElementVNode("div",Ut,[e.withDirectives(e.createElementVNode("input",{"onUpdate:modelValue":l[4]||(l[4]=n=>H.value=n),type:"text",placeholder:"搜索函数…",disabled:p.disabled},null,8,Mt),[[e.vModelText,H.value]])]),e.createElementVNode("div",Pt,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(ue.value,n=>(e.openBlock(),e.createElementBlock("div",{key:n.name,class:e.normalizeClass(["fe-fn-row",{disabled:p.disabled}]),title:n.example?`${n.signature}
39
+ 示例: ${n.example}`:n.signature,onClick:a=>Ke(n)},[e.createElementVNode("div",zt,[e.createElementVNode("span",Wt,e.toDisplayString(n.name),1),e.createElementVNode("span",Yt,e.toDisplayString(We(n.group)),1)]),e.createElementVNode("div",jt,e.toDisplayString(n.signature),1),e.createElementVNode("div",qt,e.toDisplayString(n.description),1)],10,Ht))),128)),ue.value.length===0?(e.openBlock(),e.createElementBlock("div",Kt,[...l[21]||(l[21]=[e.createElementVNode("div",{class:"fe-vars-empty-ico"},"∅",-1),e.createElementVNode("div",null,"没有匹配的函数",-1)])])):e.createCommentVNode("",!0)]),l[22]||(l[22]=e.createElementVNode("div",{class:"fe-vars-foot"},"点击函数插入到光标处",-1))])):e.createCommentVNode("",!0),e.createElementVNode("div",Gt,[e.createElementVNode("div",Xt,[e.createElementVNode("div",Qt,[e.createElementVNode("div",{class:"fe-gutter-inner",style:e.normalizeStyle({transform:`translateY(${-_.value}px)`})},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(ye.value,n=>(e.openBlock(),e.createElementBlock("span",{key:n,class:e.normalizeClass(["fe-gutter-line",{err:ge.value.has(n)}])},e.toDisplayString(n),3))),128))],4)]),e.createElementVNode("div",{ref_key:"wrapRef",ref:Q,class:"fe-code-wrap"},[e.createElementVNode("div",{class:"fe-line-marker",style:e.normalizeStyle({top:be.value+"px"})},null,4),e.createElementVNode("pre",{ref_key:"preRef",ref:Ne,class:"fe-highlight","aria-hidden":"true",style:e.normalizeStyle({transform:`translate(${-X.value}px, ${-_.value}px)`})},[e.createElementVNode("code",{innerHTML:Ce.value},null,8,Zt)],4),e.withDirectives(e.createElementVNode("textarea",{ref_key:"taRef",ref:E,"onUpdate:modelValue":l[5]||(l[5]=n=>d.value=n),class:"fe-textarea",placeholder:p.placeholder,disabled:p.disabled,readonly:p.readonly,spellcheck:!1,autocapitalize:"off",autocomplete:"off",wrap:"off",onInput:Be,onScroll:Ie,onKeydown:Ae,onClick:v,onDblclick:De,onKeyup:v,onSelect:v,onMousemove:Oe,onMouseleave:Fe},null,40,Jt),[[e.vModelText,d.value]]),e.createVNode(e.Transition,{name:"fe-fade"},{default:e.withCtx(()=>[b.value?(e.openBlock(),e.createElementBlock("div",{key:0,class:"fe-tooltip",style:e.normalizeStyle({left:b.value.x+"px",top:b.value.y+"px"})},e.toDisplayString(b.value.text),5)):e.createCommentVNode("",!0)]),_:1})],512)]),e.createVNode(e.Transition,{name:"fe-drop"},{default:e.withCtx(()=>[M.value&&I.value.length>0?(e.openBlock(),e.createElementBlock("div",el,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(I.value,(n,a)=>(e.openBlock(),e.createElementBlock("div",{key:a,class:"fe-error-row",onClick:o=>Me(n.start,n.end)},[e.createElementVNode("span",{class:e.normalizeClass(["fe-error-dot",n.severity])},null,2),e.createElementVNode("span",ll,e.toDisplayString(n.message),1),e.createElementVNode("span",nl,e.toDisplayString(n.kind),1)],8,tl))),128))])):e.createCommentVNode("",!0)]),_:1})])]),e.createElementVNode("footer",ol,[e.createElementVNode("div",al,[e.createElementVNode("span",{class:e.normalizeClass(["fe-status",F.value.ok&&d.value.trim()?"ok":"bad"]),onClick:l[6]||(l[6]=n=>M.value=!M.value)},[l[23]||(l[23]=e.createElementVNode("span",{class:"fe-status-dot"},null,-1)),e.createTextVNode(" "+e.toDisplayString(d.value.trim()?Ve.value.label:"等待输入"),1)],2),I.value.length>0?(e.openBlock(),e.createElementBlock("span",sl,e.toDisplayString(I.value.length)+" 处 ",1)):e.createCommentVNode("",!0)]),p.showPreview?(e.openBlock(),e.createElementBlock("div",{key:0,class:e.normalizeClass(["fe-foot-result",he.value])},[l[24]||(l[24]=e.createElementVNode("span",{class:"fe-result-label"},"实时求值",-1)),S.value?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createElementVNode("span",{class:"fe-result-value",title:S.value.ok?String(S.value.value):""},e.toDisplayString(Te.value),9,rl),ne.value?(e.openBlock(),e.createElementBlock("span",il,e.toDisplayString(ne.value),1)):e.createCommentVNode("",!0)],64)):(e.openBlock(),e.createElementBlock("span",cl,"—"))],2)):e.createCommentVNode("",!0),e.createElementVNode("div",ul,"Ln "+e.toDisplayString(A.value.line)+", Col "+e.toDisplayString(A.value.col),1)])],6))}}),pl=(p,U)=>{const R=p.__vccOpts||p;for(const[u,y]of U)R[u]=y;return R},ml=pl(dl,[["__scopeId","data-v-10da7669"]]);exports.BUILTIN_FUNCTIONS=m.BUILTIN_FUNCTIONS;exports.ParseError=m.ParseError;exports.RuntimeError=m.RuntimeError;exports.evaluate=m.evaluate;exports.evaluateFormula=m.evaluateFormula;exports.formatFormula=m.formatFormula;exports.formatValue=m.formatValue;exports.lexErrors=m.lexErrors;exports.parse=m.parse;exports.parseFormula=m.parseFormula;exports.tokenize=m.tokenize;exports.validateFormula=m.validateFormula;exports.valueTypeName=m.valueTypeName;exports.FormulaEditor=ml;
@@ -0,0 +1,35 @@
1
+ export { FormulaEditor } from './FormulaEditor'
2
+ export type { FormulaEditorProps, FormulaEditorExpose, FormulaEditorEmits, CustomFunctionSpec } from './FormulaEditor'
3
+
4
+ /* 引擎(./engine 子路径同源,主入口一并导出便于直接使用) */
5
+ export { tokenize } from './engine/lexer'
6
+ export { parse, ParseError } from './engine/parser'
7
+ export { evaluate, RuntimeError, BUILTIN_FUNCTIONS } from './engine/evaluator'
8
+ export { formatFormula } from './engine/format'
9
+ export type { FnMap } from './engine/evaluator'
10
+ export type {
11
+ Token,
12
+ TokenType,
13
+ ExprNode,
14
+ FunctionSpec,
15
+ FormulaError,
16
+ ValidationResult,
17
+ EvalResult,
18
+ VariableDef,
19
+ ValueType,
20
+ EnumOption,
21
+ FieldDef,
22
+ NumberNode,
23
+ StringNode,
24
+ BooleanNode,
25
+ NullNode,
26
+ VariableNode,
27
+ MemberNode,
28
+ CallNode,
29
+ BinaryNode,
30
+ UnaryNode,
31
+ ConditionalNode,
32
+ IfNode,
33
+ } from './engine/types'
34
+
35
+ export { parseFormula, evaluateFormula, validateFormula, formatFormula, formatValue, valueTypeName, lexErrors } from './engine'