Commit 570e82110c9916b21555a32094eaed769b84354f

Authored by 邱嘉伟
2 parents 377df153 2a6613bb

Merge branch 'feature/dataflow' of http://gitlab.qgutech.com/tianqiang/qx-common…

… into feature/dataflow

Too many changes to show.

To preserve performance only 27 of 35 files are displayed.

1 { 1 {
2 "name": "@qx/common", 2 "name": "@qx/common",
3 - "version": "3.0.0-alpha.45", 3 + "version": "3.0.0-alpha.47",
4 "description": "A react library developed with dumi", 4 "description": "A react library developed with dumi",
5 "license": "MIT", 5 "license": "MIT",
6 "module": "dist/index.js", 6 "module": "dist/index.js",
@@ -51,6 +51,9 @@ @@ -51,6 +51,9 @@
51 "classnames": "^2.3.2", 51 "classnames": "^2.3.2",
52 "codemirror": "5.65.8", 52 "codemirror": "5.65.8",
53 "dayjs": "^1.11.9", 53 "dayjs": "^1.11.9",
  54 + "hot-formula-parser": "^4.0.0",
  55 + "js-beautify": "^1.14.9",
  56 + "jshint": "^2.13.6",
54 "lodash-es": "^4.17.21", 57 "lodash-es": "^4.17.21",
55 "rc-virtual-list": "^3.4.13", 58 "rc-virtual-list": "^3.4.13",
56 "react-codemirror2": "^7.2.1" 59 "react-codemirror2": "^7.2.1"
@@ -59,6 +62,10 @@ @@ -59,6 +62,10 @@
59 "@commitlint/cli": "^17.1.2", 62 "@commitlint/cli": "^17.1.2",
60 "@commitlint/config-conventional": "^17.1.0", 63 "@commitlint/config-conventional": "^17.1.0",
61 "@qx/utils": "0.0.58", 64 "@qx/utils": "0.0.58",
  65 + "@types/codemirror": "^5.60.10",
  66 + "@types/hot-formula-parser": "^4.0.1",
  67 + "@types/js-beautify": "^1.14.1",
  68 + "@types/jshint": "^2.12.2",
62 "@types/lodash-es": "^4.17.8", 69 "@types/lodash-es": "^4.17.8",
63 "@types/react": "^18.0.0", 70 "@types/react": "^18.0.0",
64 "@types/react-dom": "^18.0.0", 71 "@types/react-dom": "^18.0.0",
  1 +export enum FIELD_TYPE_PROPS {
  2 + EMPTY = '',
  3 + // 文本
  4 + TEXT = 'TEXT',
  5 + STRING = 'STRING',
  6 +
  7 + // 日期
  8 + DATE = 'DATE',
  9 + DATE_TIME = 'DATE_TIME',
  10 + // 人员
  11 + USER = 'USER',
  12 + USER_MULTI = 'USER_MULTI',
  13 + // 部门
  14 + ORG = 'ORG',
  15 + ORG_MULTI = 'ORG_MULTI',
  16 + // 数字
  17 + NUM = 'NUM',
  18 + // 布尔
  19 + BOOL = 'BOOL',
  20 + // 枚举
  21 + ENUM = 'ENUM',
  22 + ENUM_MULTI = 'ENUM_MULTI',
  23 + // 文件
  24 + FILE = 'FILE',
  25 +
  26 + // 公式:数值类
  27 + FORMULA = 'FORMULA',
  28 +
  29 + DOUBLE = 'DOUBLE',
  30 + INTEGER = 'INTEGER',
  31 + DECIMAL = 'DECIMAL',
  32 + PERCENT = 'PERCENT',
  33 +
  34 + YEAR = 'YEAR',
  35 + YEAR_MONTH = 'YEAR_MONTH',
  36 + YEAR_HOUR = 'YEAR_HOUR',
  37 + YEAR_DATE = 'YEAR_DATE',
  38 + YEAR_MIN = 'YEAR_MIN',
  39 + YEAR_SEC = 'YEAR_SEC',
  40 + HOUR = 'HOUR',
  41 + HOUR_MIN = 'HOUR_MIN',
  42 + HOUR_SEC = 'HOUR_SEC',
  43 +
  44 + TREE = 'TREE',
  45 + REL = 'REL',
  46 + REL_MULTI = 'REL_MULTI',
  47 + REL_FIELD = 'REL_FIELD',
  48 +
  49 + TABLE = 'TABLE',
  50 +
  51 + // 流程专用
  52 + FLOW_WF_APRV_USR = 'FLOW_WF_APRV_USR',
  53 + FLOW_WF_DQ_MODEL = 'FLOW_WF_DQ_MODEL',
  54 + FLOW_WF_RECORD = 'FLOW_WF_RECORD',
  55 +
  56 + //参数专用
  57 + OBJECT = 'OBJECT',
  58 + ARRAY = 'ARRAY',
  59 +
  60 + // 组合文本,用于文本和变量组合
  61 + COMBINED_TEXT = 'COMBINED_TEXT',
  62 + // 时间
  63 + TIME = 'TIME',
  64 +}
  65 +
  66 +export const formatEnum: Record<string, string> = {
  67 + YEAR: 'YYYY',
  68 + YEAR_MONTH: 'YYYY-MM',
  69 + YEAR_DATE: 'YYYY-MM-DD',
  70 + YEAR_HOUR: 'YYYY-MM-DD HH:00',
  71 + YEAR_MIN: 'YYYY-MM-DD HH:mm',
  72 + YEAR_SEC: 'YYYY-MM-DD HH:mm:ss',
  73 + HOUR_MIN: 'HH:mm',
  74 + HOUR_SEC: 'HH:mm:ss',
  75 +};
1 -export * from './qx-filter-condition';  
2 -export * from './qx-parameter-setting';  
3 -export * from './qx-sort-condition';  
4 -export * from './qx-base-icon';  
5 -export * from './qx-tags-input';  
6 -export * from './qx-user-selector';  
7 -export * from './qx-org-selector';  
8 -export * from './qx-form-select';  
9 -export * from './qx-pos-selector';  
10 -export * from './qx-role-selector';  
11 export * from './qx-app-selector'; 1 export * from './qx-app-selector';
12 -export * from './utils';  
13 -export * from './qx-field';  
14 -export * from './qx-field-setter';  
15 export * from './qx-base-condition'; 2 export * from './qx-base-condition';
  3 +export * from './qx-base-icon';
16 export * from './qx-btn'; 4 export * from './qx-btn';
17 -export * from './qx-progress';  
18 -export * from './qx-search-input'; 5 +export * from './qx-condition';
19 export * from './qx-dynamic-component'; 6 export * from './qx-dynamic-component';
20 -export * from './qx-widget-icon'; 7 +export * from './qx-field';
  8 +export * from './qx-field-setter';
  9 +export * from './qx-filter-condition';
21 export * from './qx-flow-node-selector'; 10 export * from './qx-flow-node-selector';
  11 +export * from './qx-form-select';
  12 +export * from './qx-function-operation';
22 export * from './qx-icon-selector'; 13 export * from './qx-icon-selector';
23 -export * from './qx-condition';  
24 -  
25 - 14 +export * from './qx-org-selector';
  15 +export * from './qx-parameter-setting';
  16 +export * from './qx-pos-selector';
  17 +export * from './qx-progress';
  18 +export * from './qx-role-selector';
  19 +export * from './qx-search-input';
  20 +export * from './qx-sort-condition';
  21 +export * from './qx-tags-input';
  22 +export * from './qx-user-selector';
  23 +export * from './qx-widget-icon';
  24 +export * from './utils';
src/qx-code-editor-old/functions.ts renamed from src/qx-code-editor/functions.ts
src/qx-code-editor-old/index.less renamed from src/qx-code-editor/index.less
  1 +import React, { useEffect, useState } from 'react';
  2 +import { UnControlled as CodeMirror } from 'react-codemirror2';
  3 +import { cloneDeep, flatten, sortBy, size, isEqual, forEach, debounce } from 'lodash-es';
  4 +import type { VariableMappingProps } from '../qx-field-setter';
  5 +import funObjects from './functions';
  6 +import 'codemirror/lib/codemirror.css';
  7 +import 'codemirror/addon/hint/show-hint';
  8 +import 'codemirror/addon/hint/show-hint.css';
  9 +import { CloseCircleFilled } from '@ant-design/icons';
  10 +import './index.less';
  11 +
  12 +/**
  13 + * 填充到指定位数
  14 + * eg:000000000
  15 + *
  16 + * @param val 原始值
  17 + * @param length 填充后长度
  18 + * @param fill 填充值
  19 + */
  20 +const strFill = (val: string | number, length: number, fill: string | number) => {
  21 + return val.toString().padStart(length, fill.toString());
  22 +};
  23 +
  24 +type PositionProps = {
  25 + s: number;
  26 + e: number;
  27 +};
  28 +
  29 +export type VariableProps = {
  30 + variable: string;
  31 + pos: PositionProps;
  32 +};
  33 +
  34 +/**
  35 + * 字符串中提取变量(${xxx})
  36 + * @param code
  37 + */
  38 +export const getAllVariable = (code: string) => {
  39 + let codeLocal: string = cloneDeep(code);
  40 + if (!codeLocal) {
  41 + return [];
  42 + }
  43 + const variables: VariableProps[] = [];
  44 +
  45 + function loopGet() {
  46 + const pos: PositionProps = {
  47 + s: codeLocal.indexOf('${'),
  48 + e: codeLocal.indexOf('}'),
  49 + };
  50 + if (pos.s === -1 && pos.e === -1) {
  51 + return;
  52 + }
  53 + const variable = codeLocal.slice(pos.s, pos.e + 1);
  54 + variables.push({ variable, pos });
  55 + codeLocal = codeLocal.replace(variable, strFill(0, variable.length, 0));
  56 +
  57 + loopGet();
  58 + }
  59 +
  60 + loopGet();
  61 + return variables;
  62 +};
  63 +
  64 +const domTagGen = (variable: string, text: string, color?: string) => {
  65 + // tag html
  66 + const dom = document.createElement('span');
  67 + // dom.className = 'ant-tag tag';
  68 + dom.style.background = '#c9dffc';
  69 + dom.style.borderRadius = '2px';
  70 + dom.style.margin = '0 1px';
  71 + dom.style.padding = '1px 3px';
  72 + dom.style.fontSize = '96%';
  73 + if (color) {
  74 + dom.style.color = color;
  75 + }
  76 + dom.setAttribute('data-widget', variable);
  77 + dom.innerHTML = text;
  78 + return dom;
  79 +};
  80 +
  81 +type CodeHighLightProps = {
  82 + // eg: 'hello ${v1} ${v2}!'
  83 + value: string;
  84 + // eg: {'${v1}': 'hehe', '${v2}': 'enen'}
  85 + variableObj: Record<string, string> | undefined;
  86 + onChange: (val: any) => void;
  87 + className?: any;
  88 + newVariable?: VariableMappingProps;
  89 + style?: any;
  90 + autofocus?: boolean;
  91 + focusFunHandler?: (str: string) => void;
  92 + readOnly?: boolean;
  93 + // 是否使用函数(使用,则执行针对函数关键词的高亮匹配处理)
  94 + isUseFun?: boolean;
  95 + from?: string;
  96 + resetValue?: string;
  97 + allowClear?: boolean | undefined;
  98 +};
  99 +
  100 +/**
  101 + * 变量高亮
  102 + * const inputRef = React.useRef<any>(null);
  103 + * `const cm = inputRef.current.editor;`
  104 + *
  105 + * @param props
  106 + * @constructor
  107 + */
  108 +const CodeEditor: React.FC<CodeHighLightProps> = ({
  109 + value,
  110 + variableObj,
  111 + newVariable,
  112 + className,
  113 + onChange,
  114 + autofocus,
  115 + focusFunHandler,
  116 + readOnly,
  117 + isUseFun,
  118 + from,
  119 + resetValue,
  120 + allowClear,
  121 +}) => {
  122 + const [valueLocal, setValueLocal] = useState<string>();
  123 + // 变量值、名称映射关系
  124 + const [variableObjLocal, setVariableObjLocal] = useState<Record<string, string>>({});
  125 + const [codeEditor, setCodeEditor] = useState<any>();
  126 + const [isInitDone, setIsInitDone] = useState<boolean>(false);
  127 + const [focusFun, setFocusFun] = useState<string>();
  128 +
  129 + const funObjs = flatten(funObjects.map((item: any) => item.children));
  130 + const funObjSimple: any = {};
  131 + funObjs.map((item) => Object.assign(funObjSimple, { [item.title]: item.title }));
  132 + // 以变量字符长度排序,确保`DATAIF`比`IF`优先匹配
  133 + const funObjsSort = sortBy(funObjs, function (o) {
  134 + return 0 - o.title.length;
  135 + });
  136 +
  137 + useEffect(() => {
  138 + if (value && variableObj) {
  139 + // 限制初始值只设置一次
  140 + if (!isInitDone) {
  141 + setValueLocal(value);
  142 + }
  143 +
  144 + if (size(variableObj) > 0) {
  145 + setVariableObjLocal(variableObj);
  146 + }
  147 + }
  148 + }, [value, variableObj]);
  149 +
  150 + useEffect(() => {
  151 + if (typeof focusFunHandler === 'function') {
  152 + focusFunHandler(focusFun || '');
  153 + }
  154 + }, [focusFun]);
  155 +
  156 + const cmUtils = {
  157 + // 插入变量
  158 + insert: (editor: any, variable: string, isFormula: boolean = false) => {
  159 + if (!editor) {
  160 + return;
  161 + }
  162 + // 光标位置插入新值(变量)
  163 + editor.replaceSelection(variable);
  164 + cmUtils.variableRender(editor);
  165 +
  166 + // "公式"类型变量时
  167 + if (isFormula) {
  168 + // 光标位置固定插入内容
  169 + editor.replaceSelection('()');
  170 + // 光标左移
  171 + codeEditor.execCommand('goCharLeft');
  172 + }
  173 + // 让编辑器聚集
  174 + editor.focus();
  175 + },
  176 +
  177 + // 替换变量(变量示例:${xxx})
  178 + variableRender: (editor: any) => {
  179 + if (!editor) {
  180 + return;
  181 + }
  182 +
  183 + const code = cloneDeep(editor.getValue());
  184 + // 换行分隔
  185 + const codeArr = code.split('\n');
  186 + const variReplace = (cm: any, line: number) => {
  187 + const sIndex = codeArr[line].indexOf('${');
  188 + const eIndex = codeArr[line].indexOf('}');
  189 + if (sIndex === -1 && eIndex === -1) {
  190 + return;
  191 + }
  192 +
  193 + const variable = codeArr[line].slice(sIndex, eIndex + 1);
  194 + const text: string = variableObjLocal[variable] || '(已缺失)';
  195 + const hasError: boolean = variableObjLocal[variable] === undefined;
  196 +
  197 + cm?.markText(
  198 + {
  199 + line: line,
  200 + ch: sIndex,
  201 + },
  202 + {
  203 + line: line,
  204 + ch: eIndex + 1,
  205 + },
  206 + {
  207 + replacedWith: domTagGen(variable, text, hasError ? 'red' : '#026be1'),
  208 + },
  209 + );
  210 + codeArr[line] = codeArr[line].replace(variable, strFill(0, variable.length, 0));
  211 +
  212 + variReplace(cm, line);
  213 + };
  214 +
  215 + const funReplace = (cm: any, line: number, funStr: string) => {
  216 + const sIndex = codeArr[line].indexOf(funStr);
  217 + const eIndex = sIndex + funStr.length;
  218 + if (sIndex === -1) {
  219 + return;
  220 + }
  221 + const variable = codeArr[line].slice(sIndex, eIndex);
  222 + const text: string = funObjSimple[variable] || '(已缺失)';
  223 + const hasError: boolean = funObjSimple[variable] === undefined;
  224 +
  225 + cm?.markText(
  226 + {
  227 + line: line,
  228 + ch: sIndex,
  229 + },
  230 + {
  231 + line: line,
  232 + ch: eIndex,
  233 + },
  234 + {
  235 + replacedWith: domTagGen(variable, text, hasError ? 'red' : '#026be1'),
  236 + },
  237 + );
  238 + codeArr[line] = codeArr[line].replace(variable, strFill(0, variable.length, 0));
  239 +
  240 + funReplace(cm, line, funStr);
  241 + };
  242 +
  243 + for (let i = 0; i < editor.doc.size; i++) {
  244 + variReplace(editor, i);
  245 + if (Boolean(isUseFun)) {
  246 + funObjsSort.map((v) => funReplace(editor, i, v.title));
  247 + }
  248 + }
  249 + },
  250 + };
  251 +
  252 + useEffect(() => {
  253 + if (!codeEditor || !newVariable) {
  254 + return;
  255 + }
  256 +
  257 + setVariableObjLocal(() => {
  258 + return Object.assign(variableObjLocal, {
  259 + [newVariable.key]: newVariable.name,
  260 + });
  261 + });
  262 +
  263 + cmUtils.insert(codeEditor, newVariable.key, newVariable.type === 'fun');
  264 + }, [newVariable, codeEditor]);
  265 +
  266 + useEffect(() => {
  267 + // 消息提醒 新增模板时 触发方式变为"定时触发"时 需要将消息内容中的表单字段值清除
  268 + if (resetValue) {
  269 + setValueLocal(resetValue);
  270 + }
  271 + // console.log('--resetValue--',resetValue)
  272 + }, [resetValue]);
  273 +
  274 + const onValueChange = (editor: any, data: any, val: string) => {
  275 + setCodeEditor(editor);
  276 + cmUtils.variableRender(editor);
  277 +
  278 + // TODO 默认值bug修复 暂时注释
  279 + // const isSame = isEqual(valueLocal, val);
  280 + // if (isSame) {
  281 + // return;
  282 + // }
  283 +
  284 + setIsInitDone(true);
  285 + onChange(val);
  286 + };
  287 +
  288 + useEffect(() => {
  289 + if (variableObjLocal) {
  290 + cmUtils.variableRender(codeEditor);
  291 + }
  292 + }, [variableObjLocal]);
  293 +
  294 + // 字符串反转
  295 + const strReverse = (str: string) => {
  296 + return str.split('').reverse().join('');
  297 + };
  298 +
  299 + /**
  300 + * 查找编辑器光标所对应函数,设置其函数说明信息
  301 + * todo 待优化,规则不够完善
  302 + * @param editor
  303 + */
  304 + const findFocusFun = (editor: any) => {
  305 + const cursor = editor.getCursor();
  306 + const lineStr = editor.getLine(cursor.line);
  307 + const lineStrPart = lineStr.substring(0, cursor.ch);
  308 + const lineStrPartRev = strReverse(lineStrPart);
  309 +
  310 + let flag: boolean = false;
  311 + let funName: string = '';
  312 +
  313 + forEach(funObjsSort, (v) => {
  314 + if (flag) {
  315 + return;
  316 + }
  317 + const i = (lineStrPartRev || '').indexOf(strReverse(v.title));
  318 + if (i > -1) {
  319 + flag = true;
  320 + funName = v.title;
  321 + return;
  322 + }
  323 + });
  324 + return funName;
  325 + };
  326 +
  327 + const handleClear = () => {
  328 + // 清除组件内容
  329 + codeEditor.doc.setValue('');
  330 + };
  331 +
  332 + return (
  333 + //这里className中的qx-copy-send-cm 是抄送节点--消息内容专用的样式,如果有必要,可将className回滚设置为'qx-formula-cm ' + className
  334 + <div
  335 + className={
  336 + from === 'copySend' ? 'qx-copy-send-cm ' + className : 'qx-formula-cm ' + className
  337 + }
  338 + style={{ height: '100%', padding: 0 }}
  339 + >
  340 + <CodeMirror
  341 + value={(valueLocal || '').toString()}
  342 + editorDidMount={(editor) => setCodeEditor(editor)}
  343 + // onCursorActivity={(e) => console.log('e', e)}
  344 + // onCursorActivity={(e) => e?.showHint()} //没有会报错
  345 + options={{
  346 + // mode: 'text/html',
  347 + lineNumbers: false,
  348 + autofocus,
  349 + readOnly: Boolean(readOnly),
  350 + cursorHeight: Boolean(readOnly) ? 0 : 'auto',
  351 + // 滚动(false,默认)或自动换行
  352 + lineWrapping: true,
  353 + }}
  354 + onChange={debounce(onValueChange, 300)}
  355 + onCursor={(editor) => {
  356 + const funName = findFocusFun(editor);
  357 + setFocusFun(funName);
  358 + }}
  359 + />
  360 + {allowClear && value && (
  361 + <CloseCircleFilled className={'qx-field-setter__clear'} onClick={handleClear} />
  362 + )}
  363 + </div>
  364 + );
  365 +};
  366 +
  367 +export default CodeEditor;
1 -import React, { useEffect, useState } from 'react';  
2 -import { UnControlled as CodeMirror } from 'react-codemirror2';  
3 -import { cloneDeep, flatten, sortBy, size, isEqual, forEach, debounce } from 'lodash-es';  
4 -import type { VariableMappingProps } from '../qx-field-setter';  
5 -import funObjects from './functions'; 1 +import { CloseCircleFilled } from '@ant-design/icons';
  2 +import _ from 'lodash-es';
  3 +import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
  4 +import ReactDOM from 'react-dom';
  5 +import { handleHighlight } from '../qx-function-operation/components/var-picker';
  6 +import {
  7 + domTagGen,
  8 + flatten,
  9 + reverseStr,
  10 + strFill,
  11 + widgetMapping,
  12 +} from '../qx-function-operation/util';
  13 +import './style.less';
  14 +
  15 +import Cm from 'codemirror';
6 import 'codemirror/lib/codemirror.css'; 16 import 'codemirror/lib/codemirror.css';
7 -import 'codemirror/addon/hint/show-hint'; 17 +import 'codemirror/lib/codemirror.js';
  18 +import { UnControlled as CodeMirror } from 'react-codemirror2';
  19 +import './plugins/cm-extensions';
  20 +
  21 +import 'codemirror/mode/javascript/javascript.js';
  22 +import 'codemirror/theme/idea.css';
  23 +
8 import 'codemirror/addon/hint/show-hint.css'; 24 import 'codemirror/addon/hint/show-hint.css';
9 -import { CloseCircleFilled } from '@ant-design/icons';  
10 -import './index.less'; 25 +import 'codemirror/addon/hint/show-hint.js'; // ctrl代码提示补全
11 26
12 -/**  
13 - * 填充到指定位数  
14 - * eg:000000000  
15 - *  
16 - * @param val 原始值  
17 - * @param length 填充后长度  
18 - * @param fill 填充值  
19 - */  
20 -const strFill = (val: string | number, length: number, fill: string | number) => {  
21 - return val.toString().padStart(length, fill.toString());  
22 -}; 27 +import 'codemirror/addon/selection/active-line.js'; // 光标代码高亮
  28 +
  29 +import 'codemirror/addon/edit/matchbrackets.js';
  30 +import 'codemirror/addon/lint/javascript-lint.js';
  31 +import 'codemirror/addon/lint/lint.css';
  32 +import 'codemirror/addon/lint/lint.js'; // 错误提示
  33 +import beautify from 'js-beautify';
  34 +import { JSHINT } from 'jshint';
  35 +window.JSHINT = JSHINT;
23 36
24 type PositionProps = { 37 type PositionProps = {
25 s: number; 38 s: number;
@@ -36,7 +49,7 @@ export type VariableProps = { @@ -36,7 +49,7 @@ export type VariableProps = {
36 * @param code 49 * @param code
37 */ 50 */
38 export const getAllVariable = (code: string) => { 51 export const getAllVariable = (code: string) => {
39 - let codeLocal: string = cloneDeep(code); 52 + let codeLocal: string = _.clone(code);
40 if (!codeLocal) { 53 if (!codeLocal) {
41 return []; 54 return [];
42 } 55 }
@@ -61,307 +74,655 @@ export const getAllVariable = (code: string) => { @@ -61,307 +74,655 @@ export const getAllVariable = (code: string) => {
61 return variables; 74 return variables;
62 }; 75 };
63 76
64 -const domTagGen = (variable: string, text: string, color?: string) => {  
65 - // tag html  
66 - const dom = document.createElement('span');  
67 - // dom.className = 'ant-tag tag';  
68 - dom.style.background = '#c9dffc';  
69 - dom.style.borderRadius = '2px';  
70 - dom.style.margin = '0 1px';  
71 - dom.style.padding = '1px 3px';  
72 - dom.style.fontSize = '96%';  
73 - if (color) {  
74 - dom.style.color = color; 77 +const getWidgetByType = (type?: string) => {
  78 + let res: string;
  79 + switch (type) {
  80 + case 'TEXT':
  81 + res = 'qxInput';
  82 + break;
  83 + case 'NUM':
  84 + res = 'qxNumber';
  85 + break;
  86 + default:
  87 + res = '';
75 } 88 }
76 - dom.setAttribute('data-widget', variable);  
77 - dom.innerHTML = text;  
78 - return dom; 89 + // @ts-ignore
  90 + return res;
  91 +};
  92 +
  93 +type VarProps = {
  94 + key: string;
  95 + name?: string;
  96 + type?: string;
79 }; 97 };
80 98
81 -type CodeHighLightProps = { 99 +type EditorProps = {
  100 + cRef: any;
82 // eg: 'hello ${v1} ${v2}!' 101 // eg: 'hello ${v1} ${v2}!'
83 value: string; 102 value: string;
84 // eg: {'${v1}': 'hehe', '${v2}': 'enen'} 103 // eg: {'${v1}': 'hehe', '${v2}': 'enen'}
85 - variableObj: Record<string, string> | undefined;  
86 - onChange: (val: any) => void; 104 + // variableObj: Record<string, string> | undefined;
  105 + isUseFun: boolean;
  106 + autofocus?: boolean;
  107 + newVariable: VarProps;
  108 + funcDataList: any[];
  109 + varDataList: any[];
87 className?: any; 110 className?: any;
88 - newVariable?: VariableMappingProps;  
89 style?: any; 111 style?: any;
90 - autofocus?: boolean;  
91 - focusFunHandler?: (str: string) => void;  
92 readOnly?: boolean; 112 readOnly?: boolean;
93 - // 是否使用函数(使用,则执行针对函数关键词的高亮匹配处理)  
94 - isUseFun?: boolean;  
95 from?: string; 113 from?: string;
96 resetValue?: string; 114 resetValue?: string;
97 allowClear?: boolean | undefined; 115 allowClear?: boolean | undefined;
  116 + onChange: (code: string) => void;
  117 + onFocusFunc: (funcName: string | null) => void;
98 }; 118 };
99 119
100 -/**  
101 - * 变量高亮  
102 - * const inputRef = React.useRef<any>(null);  
103 - * `const cm = inputRef.current.editor;`  
104 - *  
105 - * @param props  
106 - * @constructor  
107 - */  
108 -const CodeEditor: React.FC<CodeHighLightProps> = ({ 120 +const CodeEditor: React.FC<EditorProps> = ({
  121 + cRef,
  122 + funcDataList,
  123 + varDataList,
  124 + isUseFun,
  125 + autofocus,
  126 + readOnly,
  127 + allowClear,
  128 + resetValue,
109 value, 129 value,
110 - variableObj,  
111 newVariable, 130 newVariable,
  131 + from,
112 className, 132 className,
113 onChange, 133 onChange,
114 - autofocus,  
115 - focusFunHandler,  
116 - readOnly,  
117 - isUseFun,  
118 - from,  
119 - resetValue,  
120 - allowClear, 134 + onFocusFunc,
121 }) => { 135 }) => {
122 - const [valueLocal, setValueLocal] = useState<string>();  
123 - // 变量值、名称映射关系  
124 - const [variableObjLocal, setVariableObjLocal] = useState<Record<string, string>>({});  
125 - const [codeEditor, setCodeEditor] = useState<any>();  
126 const [isInitDone, setIsInitDone] = useState<boolean>(false); 136 const [isInitDone, setIsInitDone] = useState<boolean>(false);
127 - const [focusFun, setFocusFun] = useState<string>();  
128 -  
129 - const funObjs = flatten(funObjects.map((item: any) => item.children));  
130 - const funObjSimple: any = {};  
131 - funObjs.map((item) => Object.assign(funObjSimple, { [item.title]: item.title }));  
132 - // 以变量字符长度排序,确保`DATAIF`比`IF`优先匹配  
133 - const funObjsSort = sortBy(funObjs, function (o) {  
134 - return 0 - o.title.length;  
135 - }); 137 + const [valueLocal, setValueLocal] = useState<string>();
  138 + const [editor, setEditor] = useState<any>(null);
  139 + const formatRef = useRef(false); // 代码格式化引起的变化
  140 + // 定义匹配括号的正则表达式
  141 + const bracketRegex = /[(){}\[\]]/g;
136 142
137 useEffect(() => { 143 useEffect(() => {
138 - if (value && variableObj) {  
139 - // 限制初始值只设置一次  
140 - if (!isInitDone) {  
141 - setValueLocal(value); 144 + if (!editor) return;
  145 + editor.on('mousedown', (_cm: any, event: any) => {
  146 + // 1 = 鼠标左键,2 = 鼠标右键,4 = 鼠标中键
  147 + if (event.buttons === 1) {
  148 + const element = event.target as Element;
  149 + if (element.classList.contains('funcName')) {
  150 + const funcName = element.getAttribute('data-widget');
  151 + const funcData = funcDataListFlatten.current.find(
  152 + (data: any) => data.funcNameEg === funcName,
  153 + );
  154 + if (funcData && typeof onFocusFunc === 'function') {
  155 + onFocusFunc(funcData);
  156 + }
  157 + }
  158 + }
  159 + });
  160 + // 添加占位符功能
  161 + const placeholderText = editor.getOption('placeholder');
  162 + const placeholderElement = document.createElement('div');
  163 + placeholderElement.className = 'CodeMirror-placeholder';
  164 + placeholderElement.textContent = placeholderText;
  165 + editor.getWrapperElement().appendChild(placeholderElement);
  166 +
  167 + const handleChange = () => {
  168 + const value = editor.getValue();
  169 + if (value?.length) {
  170 + placeholderElement.style.display = 'none';
  171 + } else {
  172 + placeholderElement.style.display = 'block';
142 } 173 }
  174 + const lastLine = editor.lastLine();
  175 + editor.scrollIntoView(
  176 + { line: lastLine, ch: 0, margin: { bottom: 50 } },
  177 + 100,
  178 + );
  179 +
  180 + setTimeout(() => {
  181 + // 获取编辑器的包装元素
  182 + const wrapperElements =
  183 + document.getElementsByClassName('CodeMirror-widget');
  184 + // 将 cm-ignore-events 的值设为 false
  185 + for (let i = 0; i < (wrapperElements || []).length; i++) {
  186 + wrapperElements[i].setAttribute('cm-ignore-events', 'false');
  187 + }
  188 + }, 100);
  189 +
  190 + // 在内容中查找匹配的括号
  191 + let match: any;
  192 + while ((match = bracketRegex.exec(value))) {
  193 + const from = { line: 0, ch: match.index };
  194 + const to = { line: 0, ch: match.index + 1 };
143 195
144 - if (size(variableObj) > 0) {  
145 - setVariableObjLocal(variableObj); 196 + // 标记匹配的括号并应用 CSS 类名
  197 + editor.markText(from, to, { className: 'bracket-mark' });
146 } 198 }
147 - }  
148 - }, [value, variableObj]); 199 +
  200 + // const processedContent = value.replace(/(\(|\)|\[|\]|\{|\})/g, '<span class="custom-paren">$1</span>');
  201 + // editor.setValue(processedContent);
  202 + };
  203 + handleChange();
  204 +
  205 + // 监听编辑器的输入事件
  206 + editor.on('change', handleChange);
  207 + }, [editor]);
  208 +
  209 + // 扁平化dataSource
  210 + const funcDataListFlatten: any = useRef([]);
  211 + if (!funcDataListFlatten.current.length && funcDataList.length) {
  212 + flatten(funcDataList, funcDataListFlatten.current);
  213 + }
  214 + const funcDataListSimple: any = {};
  215 + funcDataListFlatten.current.forEach((item: any) => {
  216 + Object.assign(funcDataListSimple, { [item.title]: item.title });
  217 + });
  218 + // 以变量字符长度排序,确保`DATAIF`比`IF`优先匹配
  219 + const funcDataListSort = funcDataListFlatten.current;
149 220
150 useEffect(() => { 221 useEffect(() => {
151 - if (typeof focusFunHandler === 'function') {  
152 - focusFunHandler(focusFun || ''); 222 + if (funcDataListSort.length) {
  223 + setTimeout(() => {
  224 + cmUtils.variableRender();
  225 + }, 100);
153 } 226 }
154 - }, [focusFun]); 227 + }, [JSON.stringify(funcDataListSort)]);
155 228
156 - const cmUtils = {  
157 - // 插入变量  
158 - insert: (editor: any, variable: string, isFormula: boolean = false) => {  
159 - if (!editor) {  
160 - return; 229 + // const varDataListFlatten: any[] = [];
  230 + const varDataListFlatten = useRef<any[]>([]);
  231 + if (!varDataListFlatten.current.length && varDataList.length) {
  232 + flatten(varDataList, varDataListFlatten.current);
  233 + }
  234 + // flatten(varDataList, varDataListFlatten);
  235 + const varDataListSimple: any = {};
  236 + varDataListFlatten.current.forEach((item) => {
  237 + Object.assign(varDataListSimple, { [item.key]: item.titleStr });
  238 + if (item.attrs) {
  239 + item.attrs.forEach((attrItem: any) =>
  240 + Object.assign(varDataListSimple, {
  241 + [attrItem.key]: `${item.titleStr}.${attrItem.titleStr}`,
  242 + }),
  243 + );
  244 + }
  245 + });
  246 +
  247 + // 插入的变量在函数内,并且前面也是变量
  248 + const isInFunc = (id: number, code: string) => {
  249 + let left1: number = 0;
  250 + let left2: number = 0;
  251 + let right1: number = 0;
  252 + let right2: number = 0;
  253 + // 前面是否是变量
  254 + const beforeVar = code.split('')[id - 1] === '}';
  255 + code.split('').forEach((item, index) => {
  256 + if (index < id) {
  257 + if (item === '(') {
  258 + left1++;
  259 + } else if (item === ')') {
  260 + left2++;
  261 + }
  262 + } else {
  263 + if (item === '(') {
  264 + right1++;
  265 + } else if (item === ')') {
  266 + right2++;
  267 + }
161 } 268 }
  269 + });
  270 + return left1 > left2 && right1 < right2 && beforeVar;
  271 + };
  272 +
  273 + const cmUtils: {
  274 + insert: (variable: string, isFormula: boolean) => void;
  275 + variableRender: () => void;
  276 + } = {
  277 + // 插入变量
  278 + insert: (variable: string, isFormula: boolean = false) => {
162 // 光标位置插入新值(变量) 279 // 光标位置插入新值(变量)
163 - editor.replaceSelection(variable);  
164 - cmUtils.variableRender(editor); 280 + // editor.replaceSelection(variable);
165 281
166 // "公式"类型变量时 282 // "公式"类型变量时
167 if (isFormula) { 283 if (isFormula) {
168 // 光标位置固定插入内容 284 // 光标位置固定插入内容
169 - editor.replaceSelection('()'); 285 + editor.replaceSelection(variable + '()');
170 // 光标左移 286 // 光标左移
171 - codeEditor.execCommand('goCharLeft'); 287 + editor.execCommand('goCharLeft');
  288 + } else {
  289 + const pos = editor.getCursor()?.ch;
  290 + const inFunc = isInFunc(pos, editor.getValue());
  291 + if (inFunc) {
  292 + editor.replaceSelection(',' + variable);
  293 + } else {
  294 + editor.replaceSelection(variable);
  295 + }
172 } 296 }
  297 + cmUtils.variableRender();
173 // 让编辑器聚集 298 // 让编辑器聚集
174 editor.focus(); 299 editor.focus();
175 }, 300 },
176 301
177 // 替换变量(变量示例:${xxx}) 302 // 替换变量(变量示例:${xxx})
178 - variableRender: (editor: any) => {  
179 - if (!editor) {  
180 - return;  
181 - }  
182 -  
183 - const code = cloneDeep(editor.getValue()); 303 + variableRender: () => {
  304 + const code = _.cloneDeep(editor.getValue());
184 // 换行分隔 305 // 换行分隔
185 const codeArr = code.split('\n'); 306 const codeArr = code.split('\n');
186 - const variReplace = (cm: any, line: number) => {  
187 - const sIndex = codeArr[line].indexOf('${');  
188 - const eIndex = codeArr[line].indexOf('}');  
189 - if (sIndex === -1 && eIndex === -1) {  
190 - return;  
191 - }  
192 307
193 - const variable = codeArr[line].slice(sIndex, eIndex + 1);  
194 - const text: string = variableObjLocal[variable] || '(已缺失)';  
195 - const hasError: boolean = variableObjLocal[variable] === undefined;  
196 -  
197 - cm?.markText( 308 + const makeText = (
  309 + line: number,
  310 + sIndex: number,
  311 + eIndex: number,
  312 + variable: string,
  313 + text: string,
  314 + hasError: boolean,
  315 + type?: string,
  316 + ) => {
  317 + editor.markText(
198 { 318 {
199 line: line, 319 line: line,
200 ch: sIndex, 320 ch: sIndex,
201 }, 321 },
202 { 322 {
203 line: line, 323 line: line,
204 - ch: eIndex + 1, 324 + ch: eIndex,
205 }, 325 },
206 { 326 {
207 - replacedWith: domTagGen(variable, text, hasError ? 'red' : '#026be1'), 327 + replacedWith: domTagGen(
  328 + variable,
  329 + text,
  330 + hasError ? 'red' : '#026be1',
  331 + type,
  332 + ),
  333 + inclusiveRight: false,
  334 + readOnly: false,
208 }, 335 },
209 ); 336 );
210 - codeArr[line] = codeArr[line].replace(variable, strFill(0, variable.length, 0));  
211 -  
212 - variReplace(cm, line);  
213 }; 337 };
214 338
215 - const funReplace = (cm: any, line: number, funStr: string) => {  
216 - const sIndex = codeArr[line].indexOf(funStr);  
217 - const eIndex = sIndex + funStr.length;  
218 - if (sIndex === -1) {  
219 - return; 339 + const replacement = (
  340 + line: number,
  341 + regexp: any,
  342 + refObj: any,
  343 + type?: string,
  344 + ) => {
  345 + const lineStr = codeArr[line];
  346 + let match: any;
  347 + while ((match = regexp.exec(lineStr)) !== null) {
  348 + const variable = match[0];
  349 + const sIndex = match.index;
  350 + const eIndex = sIndex + variable.length;
  351 + const text: string = refObj[variable] || '(已缺失)';
  352 + if (!type || (type === 'function' && lineStr[eIndex] === '(')) {
  353 + const hasError: boolean = refObj[variable] === undefined;
  354 + makeText(line, sIndex, eIndex, variable, text, hasError, type);
  355 + codeArr[line] = codeArr[line].replace(
  356 + variable,
  357 + strFill(0, variable.length, 0),
  358 + );
  359 + }
220 } 360 }
221 - const variable = codeArr[line].slice(sIndex, eIndex);  
222 - const text: string = funObjSimple[variable] || '(已缺失)';  
223 - const hasError: boolean = funObjSimple[variable] === undefined;  
224 -  
225 - cm?.markText(  
226 - {  
227 - line: line,  
228 - ch: sIndex,  
229 - },  
230 - {  
231 - line: line,  
232 - ch: eIndex,  
233 - },  
234 - {  
235 - replacedWith: domTagGen(variable, text, hasError ? 'red' : '#026be1'),  
236 - },  
237 - );  
238 - codeArr[line] = codeArr[line].replace(variable, strFill(0, variable.length, 0));  
239 -  
240 - funReplace(cm, line, funStr);  
241 }; 361 };
242 362
  363 + const funcRegexp = new RegExp(
  364 + funcDataListSort
  365 + .map((data: any) => {
  366 + return '\\b' + data.title + '\\b';
  367 + })
  368 + .join('|'),
  369 + 'g',
  370 + );
  371 +
243 for (let i = 0; i < editor.doc.size; i++) { 372 for (let i = 0; i < editor.doc.size; i++) {
244 - variReplace(editor, i);  
245 - if (Boolean(isUseFun)) {  
246 - funObjsSort.map((v) => funReplace(editor, i, v.title)); 373 + // 替换变量名称
  374 + const regexp = /\$\{.*?\}/g;
  375 + replacement(i, regexp, varDataListSimple);
  376 + if (Boolean(isUseFun) && funcDataListSort.length > 0) {
  377 + // 替换函数公式名称
  378 + replacement(i, funcRegexp, funcDataListSimple, 'function');
247 } 379 }
248 } 380 }
249 }, 381 },
250 }; 382 };
251 383
  384 + const getFuncId = (name: string) => {
  385 + return funcDataListFlatten.current.find(
  386 + (item: any) => item.funcNameEg === name,
  387 + )?.id;
  388 + };
  389 +
252 useEffect(() => { 390 useEffect(() => {
253 - if (!codeEditor || !newVariable) {  
254 - return; 391 + if (value && !isInitDone) {
  392 + // 限制初始值只设置一次
  393 + setValueLocal(value);
255 } 394 }
256 -  
257 - setVariableObjLocal(() => {  
258 - return Object.assign(variableObjLocal, {  
259 - [newVariable.key]: newVariable.name,  
260 - });  
261 - });  
262 -  
263 - cmUtils.insert(codeEditor, newVariable.key, newVariable.type === 'fun');  
264 - }, [newVariable, codeEditor]); 395 + }, [value]);
265 396
266 useEffect(() => { 397 useEffect(() => {
267 // 消息提醒 新增模板时 触发方式变为"定时触发"时 需要将消息内容中的表单字段值清除 398 // 消息提醒 新增模板时 触发方式变为"定时触发"时 需要将消息内容中的表单字段值清除
268 if (resetValue) { 399 if (resetValue) {
269 setValueLocal(resetValue); 400 setValueLocal(resetValue);
270 } 401 }
271 - // console.log('--resetValue--',resetValue)  
272 }, [resetValue]); 402 }, [resetValue]);
273 403
274 - const onValueChange = (editor: any, data: any, val: string) => {  
275 - setCodeEditor(editor);  
276 - cmUtils.variableRender(editor);  
277 -  
278 - // TODO 默认值bug修复 暂时注释  
279 - // const isSame = isEqual(valueLocal, val);  
280 - // if (isSame) {  
281 - // return;  
282 - // } 404 + useEffect(() => {
  405 + if (newVariable) {
  406 + cmUtils.insert(newVariable.key, newVariable.type === 'fun');
  407 + }
  408 + }, [newVariable]);
283 409
  410 + // 编辑器内容变化响应
  411 + const onHandleChange = (editor: any, data: any, value: string) => {
  412 + cmUtils.variableRender();
  413 + onChange(value);
284 setIsInitDone(true); 414 setIsInitDone(true);
285 - onChange(val);  
286 - };  
287 -  
288 - useEffect(() => {  
289 - if (variableObjLocal) {  
290 - cmUtils.variableRender(codeEditor); 415 + if (!formatRef.current) {
  416 + editor.showHint();
  417 + }
  418 + formatRef.current = false;
  419 + // 函数补全时,光标左移
  420 + if (data.origin === 'complete' && data.text?.[0]?.endsWith('()')) {
  421 + editor.execCommand('goCharLeft');
  422 + const funcName = data.text?.[0].slice(0, -2);
  423 + const funcData = funcDataListFlatten.current.find(
  424 + (data: any) => data.funcNameEg === funcName,
  425 + );
  426 + if (funcData && typeof onFocusFunc === 'function') {
  427 + onFocusFunc(funcData);
  428 + }
291 } 429 }
292 - }, [variableObjLocal]); 430 + if (value === '') {
  431 + onFocusFunc(null);
  432 + }
  433 + };
293 434
294 - // 字符串反转  
295 - const strReverse = (str: string) => {  
296 - return str.split('').reverse().join(''); 435 + // 光标移动响应
  436 + // const onHandleCursor = (editor: any, cursor: any) => {
  437 + // const lineStr = editor.getLine(cursor.line);
  438 + // const funcName = getLegalVaribleNameFromIndex(lineStr, cursor.ch);
  439 + // const funcData = funcDataListFlatten.find(
  440 + // (data) => data.title === funcName,
  441 + // );
  442 + // if (funcData && typeof onFocusFunc === 'function') {
  443 + // onFocusFunc(funcData);
  444 + // }
  445 + // };
  446 +
  447 + // 格式化code文本
  448 + const autoFormatSelection = () => {
  449 + const code = editor.getValue(); // 获取编辑器中的代码
  450 + let beautifiedCode = beautify(code); // 使用代码美化工具(如 js-beautify)美化代码
  451 + beautifiedCode = beautifiedCode.replace(
  452 + /\$\s*\{\s*(.*?)\s*\}/g,
  453 + function (a, b) {
  454 + return '${' + b + '}';
  455 + },
  456 + );
  457 + formatRef.current = true;
  458 + editor.setValue(beautifiedCode); // 将美化后的代码设置回编辑器
297 }; 459 };
298 460
299 - /**  
300 - * 查找编辑器光标所对应函数,设置其函数说明信息  
301 - * todo 待优化,规则不够完善  
302 - * @param editor  
303 - */  
304 - const findFocusFun = (editor: any) => {  
305 - const cursor = editor.getCursor();  
306 - const lineStr = editor.getLine(cursor.line);  
307 - const lineStrPart = lineStr.substring(0, cursor.ch);  
308 - const lineStrPartRev = strReverse(lineStrPart);  
309 -  
310 - let flag: boolean = false;  
311 - let funName: string = '';  
312 -  
313 - forEach(funObjsSort, (v) => {  
314 - if (flag) {  
315 - return;  
316 - }  
317 - const i = (lineStrPartRev || '').indexOf(strReverse(v.title));  
318 - if (i > -1) {  
319 - flag = true;  
320 - funName = v.title;  
321 - return;  
322 - } 461 + // 代码提示
  462 + const hintCompletion = (cm: any, options: any) => {
  463 + const funRegex = /[a-zA-Z0-9_\u4e00-\u9fff]/;
  464 + const dotRegex = /\./;
  465 + const varRegex = /^\.(\}.*?\{\$)/;
  466 + const comp = funcDataListSort.map((data: any) => data.title);
  467 + return new Promise((accept) => {
  468 + setTimeout(() => {
  469 + const cursor = cm.getCursor();
  470 + const line = cm.getLine(cursor.line);
  471 + let start = cursor.ch;
  472 + let end = cursor.ch;
  473 + let completions = [];
  474 + while (start && funRegex.test(line.charAt(start - 1))) --start;
  475 + while (end < line.length && funRegex.test(line.charAt(end))) ++end;
  476 + const word = line.slice(start, end);
  477 + if (start && dotRegex.test(line.charAt(start - 1))) {
  478 + // 提示对象.属性
  479 + if (word === '') {
  480 + const lineReverse = reverseStr(line.slice(0, start));
  481 + const matched = varRegex.exec(lineReverse);
  482 + const word = matched ? reverseStr(matched[1]) : '';
  483 + const findVar = varDataListFlatten.current.find(
  484 + (item: any) => item.key === word,
  485 + );
  486 + if (findVar && findVar.attrs) {
  487 + completions.push(
  488 + ...findVar.attrs.map((attrItem: any) => ({
  489 + text: attrItem.key,
  490 + displayText: attrItem.titleStr,
  491 + from: Cm.Pos(cursor.line, start - word.length - 1),
  492 + to: Cm.Pos(cursor.line, end),
  493 + customInfo: {
  494 + widget: getWidgetByType(attrItem.fieldGroupType),
  495 + },
  496 + })),
  497 + );
  498 + }
  499 + }
  500 + } else {
  501 + // 提示公式函数名、变量
  502 + if (word) {
  503 + for (let i = 0; i < comp.length; i++) {
  504 + if (comp[i].toLowerCase().indexOf(word.toLowerCase()) > -1) {
  505 + completions.push(comp[i]);
  506 + }
  507 + }
  508 + for (let i = 0; i < varDataListFlatten.current.length; i++) {
  509 + const it = varDataListFlatten.current[i];
  510 + if (it.titleStr.indexOf(word) > -1) {
  511 + completions.push({
  512 + text: it.key,
  513 + displayText: it.titleStr,
  514 + });
  515 + }
  516 + }
  517 + }
  518 + }
  519 +
  520 + // const firstFieldIndex = completions.findIndex(item => typeof item !== 'string');
  521 + completions = completions.map((item: any, index) => {
  522 + if (typeof item === 'string') {
  523 + return {
  524 + text: item + '()',
  525 + displayText: item,
  526 + render: (element: any, self: any, data: any) => {
  527 + const container = document.createElement('div');
  528 + container.className = 'hint-line';
  529 + const top = document.createElement('div');
  530 + // @ts-ignore
  531 + const jsxEl: React.ReactElement = handleHighlight(
  532 + item,
  533 + word,
  534 + 'fx',
  535 + );
  536 + ReactDOM?.render(jsxEl, top);
  537 + // top.innerHTML = <span>122</span>;
  538 + top.className = 'hint-line_left';
  539 + const bottom = document.createElement('div');
  540 + bottom.className = 'hint-line_right';
  541 + // @ts-ignore
  542 + const desc =
  543 + funcDataListFlatten.current.find(
  544 + (it: any) => it.funcNameEg === item,
  545 + )?.funcName || '';
  546 + bottom.innerHTML = desc;
  547 + container.appendChild(top);
  548 + container.appendChild(bottom);
  549 + element.appendChild(container);
  550 + element.addEventListener('click', function () {
  551 + self.complete(); // 选择当前提示项并插入到编辑器中
  552 + self.close(); // 关闭代码提示列表
  553 + });
  554 + },
  555 + };
  556 + } else {
  557 + return {
  558 + text: item.text,
  559 + displayText: item.displayText,
  560 + ...(item.from && {
  561 + from: item.from,
  562 + to: item.to,
  563 + }),
  564 + render: (element: any, self: any) => {
  565 + element.style.padding = '5px';
  566 + const DIV = document.createElement('div');
  567 + DIV.style.display = 'flex';
  568 + DIV.style.justifyContent = 'space-between';
  569 + const LEFT = document.createElement('span');
  570 + // @ts-ignore
  571 + const jsxEl: React.ReactElement = handleHighlight(
  572 + item.displayText,
  573 + word,
  574 + );
  575 + ReactDOM?.render(jsxEl, LEFT);
  576 + // LEFT.innerHTML = item.displayText;
  577 + const variable = varDataListFlatten.current.find(
  578 + (it) => it.key === item.text,
  579 + );
  580 + let RIGHT;
  581 + if (variable?.widget) {
  582 + const { name, color, bgColor } =
  583 + widgetMapping[variable.widget] || {};
  584 + RIGHT = document.createElement('span');
  585 + RIGHT.className = 'tag';
  586 + RIGHT.innerHTML = name;
  587 + RIGHT.style.color = color;
  588 + RIGHT.style.backgroundColor = bgColor;
  589 + } else if (item.customInfo?.widget) {
  590 + const { name, color, bgColor } =
  591 + widgetMapping[item.customInfo?.widget] || {};
  592 + RIGHT = document.createElement('span');
  593 + RIGHT.className = 'tag';
  594 + RIGHT.innerHTML = name;
  595 + RIGHT.style.color = color;
  596 + RIGHT.style.backgroundColor = bgColor;
  597 + }
  598 + DIV.appendChild(LEFT);
  599 + if (RIGHT) DIV.appendChild(RIGHT);
  600 + // if (firstFieldIndex === index) {
  601 + // const TITLE = document.createElement('div');
  602 + // TITLE.innerHTML = '当前表单字段';
  603 + // TITLE.style.padding = '5px';
  604 + // element.appendChild(TITLE)
  605 + // }
  606 + element.appendChild(DIV);
  607 + element.addEventListener('click', function () {
  608 + self.complete(); // 选择当前提示项并插入到编辑器中
  609 + self.close(); // 关闭代码提示列表
  610 + });
  611 + },
  612 + };
  613 + }
  614 + });
  615 +
  616 + if (completions.length) {
  617 + return accept({
  618 + list: completions,
  619 + from: Cm.Pos(cursor.line, start),
  620 + to: Cm.Pos(cursor.line, end),
  621 + });
  622 + } else {
  623 + return accept(null);
  624 + }
  625 + }, 50);
323 }); 626 });
324 - return funName;  
325 }; 627 };
326 628
327 - const handleClear = () => {  
328 - // 清除组件内容  
329 - codeEditor.doc.setValue(''); 629 + // 清除组件内容
  630 + const onHandleClear = () => {
  631 + editor.doc.setValue('');
330 }; 632 };
331 633
  634 + useImperativeHandle(cRef, () => ({
  635 + autoFormatSelection,
  636 + // 获取编辑器使用到的函数公式名称, eg: [IF, SUM, CONCAT]
  637 + getUsedFuncList() {
  638 + const usedFuncList = [];
  639 + const codeArr = _.cloneDeep(editor.getValue()).split('\n');
  640 + if (Boolean(isUseFun) && funcDataListSort.length > 0) {
  641 + const funcRegexp = new RegExp(
  642 + funcDataListSort
  643 + .map((data: any) => {
  644 + return '\\b' + data.title + '\\b';
  645 + })
  646 + .join('|'),
  647 + 'g',
  648 + );
  649 + for (let i = 0; i < editor.doc.size; i++) {
  650 + const lineStr = codeArr[i];
  651 + let match: any;
  652 + while ((match = funcRegexp.exec(lineStr)) !== null) {
  653 + const variable = match[0];
  654 + if (lineStr[match['index'] + variable.length] === '(') {
  655 + const funcId = getFuncId(variable);
  656 + if (funcDataListSimple[variable] && funcId) {
  657 + usedFuncList.push(funcId);
  658 + }
  659 + }
  660 + }
  661 + }
  662 + }
  663 + return usedFuncList;
  664 + },
  665 + getEditor() {
  666 + return editor;
  667 + },
  668 + }));
  669 +
  670 + function customLint() {
  671 + return [];
  672 + }
  673 +
332 return ( 674 return (
333 - //这里className中的qx-copy-send-cm 是抄送节点--消息内容专用的样式,如果有必要,可将className回滚设置为'qx-formula-cm ' + className  
334 <div 675 <div
335 className={ 676 className={
336 - from === 'copySend' ? 'qx-copy-send-cm ' + className : 'qx-formula-cm ' + className 677 + from === 'copySend'
  678 + ? 'qx-copy-send-cm ' + className
  679 + : 'qx-formula-cm ' + className
337 } 680 }
338 - style={{ height: '100%', padding: 0 }}  
339 > 681 >
  682 + {/* @ts-ignore */}
340 <CodeMirror 683 <CodeMirror
  684 + editorDidMount={(editor) => {
  685 + setEditor(editor);
  686 + editor.addKeyMap({
  687 + 'Ctrl-f': autoFormatSelection,
  688 + });
  689 + }}
341 value={(valueLocal || '').toString()} 690 value={(valueLocal || '').toString()}
342 - editorDidMount={(editor) => setCodeEditor(editor)}  
343 - // onCursorActivity={(e) => console.log('e', e)}  
344 - // onCursorActivity={(e) => e?.showHint()} //没有会报错  
345 options={{ 691 options={{
346 - // mode: 'text/html',  
347 - lineNumbers: false,  
348 - autofocus, 692 + mode: 'javascript', // 语言
  693 + theme: 'idea',
  694 + placeholder: '请输入函数',
  695 + extraKeys: { Ctrl: 'autocomplete' }, //ctrl+空格自动提示配置
  696 + hintOptions: {
  697 + completeSingle: false,
  698 + hint: hintCompletion,
  699 + className: 'myCodeMirrorHint',
  700 + },
  701 + autofocus, //自动获取焦点
  702 + matchBrackets: true, // 匹配括号
  703 + autoCloseBrackets: true,
  704 + lint: {
  705 + getAnnotations: customLint,
  706 + },
349 readOnly: Boolean(readOnly), 707 readOnly: Boolean(readOnly),
350 cursorHeight: Boolean(readOnly) ? 0 : 'auto', 708 cursorHeight: Boolean(readOnly) ? 0 : 'auto',
351 // 滚动(false,默认)或自动换行 709 // 滚动(false,默认)或自动换行
352 lineWrapping: true, 710 lineWrapping: true,
  711 + styleActiveLine: true, // 光标行代码高亮
353 }} 712 }}
354 - onChange={debounce(onValueChange, 300)}  
355 - onCursor={(editor) => {  
356 - const funName = findFocusFun(editor);  
357 - setFocusFun(funName);  
358 - }} 713 + // onKeyEvent={handleKeyEvent}
  714 + // onBeforeChange={handleBeforeChange}
  715 + // onCursorActivity={handleCursorActivity}
  716 + // onCursor={onHandleCursor}
  717 + onChange={onHandleChange}
359 /> 718 />
360 {allowClear && value && ( 719 {allowClear && value && (
361 - <CloseCircleFilled className={'qx-field-setter__clear'} onClick={handleClear} /> 720 + <CloseCircleFilled
  721 + className={'qx-field-setter__clear'}
  722 + onClick={onHandleClear}
  723 + />
362 )} 724 )}
363 </div> 725 </div>
364 ); 726 );
365 }; 727 };
366 -  
367 export default CodeEditor; 728 export default CodeEditor;
  1 +import CodeMirror from 'codemirror';
  2 +
  3 +// Comment/uncomment the specified range
  4 +CodeMirror.defineExtension('commentRange', function (isComment, from, to) {
  5 + let cm = this,
  6 + curMode = CodeMirror.innerMode(
  7 + cm.getMode(),
  8 + cm.getTokenAt(from).state,
  9 + ).mode;
  10 + cm.operation(function () {
  11 + if (isComment) {
  12 + // Comment range
  13 + cm.replaceRange(curMode.commentEnd, to);
  14 + cm.replaceRange(curMode.commentStart, from);
  15 + if (from.line == to.line && from.ch == to.ch)
  16 + // An empty comment inserted - put cursor inside
  17 + cm.setCursor(from.line, from.ch + curMode.commentStart.length);
  18 + } else {
  19 + // Uncomment range
  20 + let selText = cm.getRange(from, to);
  21 + let startIndex = selText.indexOf(curMode.commentStart);
  22 + let endIndex = selText.lastIndexOf(curMode.commentEnd);
  23 + if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
  24 + // Take string till comment start
  25 + selText =
  26 + selText.substr(0, startIndex) +
  27 + // From comment start till comment end
  28 + selText.substring(
  29 + startIndex + curMode.commentStart.length,
  30 + endIndex,
  31 + ) +
  32 + // From comment end till string end
  33 + selText.substr(endIndex + curMode.commentEnd.length);
  34 + }
  35 + cm.replaceRange(selText, from, to);
  36 + }
  37 + });
  38 +});
  39 +
  40 +// Applies automatic formatting to the specified range
  41 +CodeMirror.defineExtension('autoFormatRange', function (from, to) {
  42 + let cm = this;
  43 + let outer = cm.getMode(),
  44 + text = cm.getRange(from, to).split('\n');
  45 + let state = CodeMirror.copyState(outer, cm.getTokenAt(from).state);
  46 + let tabSize = cm.getOption('tabSize');
  47 +
  48 + let out = '',
  49 + lines = 0,
  50 + atSol = from.ch == 0;
  51 + function newline() {
  52 + out += '\n';
  53 + atSol = true;
  54 + ++lines;
  55 + }
  56 +
  57 + for (let i = 0; i < text.length; ++i) {
  58 + let stream = new CodeMirror.StringStream(text[i], tabSize);
  59 + while (!stream.eol()) {
  60 + let inner = CodeMirror.innerMode(outer, state);
  61 + let style = outer.token(stream, state),
  62 + cur = stream.current();
  63 + stream.start = stream.pos;
  64 + if (!atSol || /\S/.test(cur)) {
  65 + out += cur;
  66 + atSol = false;
  67 + }
  68 + if (
  69 + !atSol &&
  70 + inner.mode.newlineAfterToken &&
  71 + inner.mode.newlineAfterToken(
  72 + style,
  73 + cur,
  74 + stream.string.slice(stream.pos) || text[i + 1] || '',
  75 + inner.state,
  76 + )
  77 + )
  78 + newline();
  79 + }
  80 + if (!stream.pos && outer.blankLine) outer.blankLine(state);
  81 + if (!atSol) newline();
  82 + }
  83 +
  84 + cm.operation(function () {
  85 + cm.replaceRange(out, from, to);
  86 + for (let cur = from.line + 1, end = from.line + lines; cur <= end; ++cur)
  87 + cm.indentLine(cur, 'smart');
  88 + cm.setSelection(from, cm.getCursor(false));
  89 + });
  90 +});
  91 +
  92 +// Applies automatic mode-aware indentation to the specified range
  93 +CodeMirror.defineExtension('autoIndentRange', function (from, to) {
  94 + let cmInstance = this;
  95 + this.operation(function () {
  96 + for (let i = from.line; i <= to.line; i++) {
  97 + cmInstance.indentLine(i, 'smart');
  98 + }
  99 + });
  100 +});
  1 +@import '~@qx/ui/src/style/variable.less';
  2 +
  3 +.qx-formula-cm {
  4 + .CodeMirror {
  5 + height: auto;
  6 + line-height: 24px;
  7 + }
  8 +}
  9 +
  10 +.cm-bg .CodeMirror {
  11 + border: 1px solid #e7e6e6;
  12 + background-color: #f9f9f9 !important;
  13 +}
  14 +
  15 +.qx-copy-send-cm {
  16 + height: 100%;
  17 +
  18 + .react-codemirror2 {
  19 + height: 100%;
  20 +
  21 + .CodeMirror {
  22 + height: auto;
  23 + min-height: 80px;
  24 + line-height: 24px;
  25 + }
  26 + }
  27 +}
  28 +
  29 +.qx-field-setter__clear {
  30 + position: absolute;
  31 + bottom: 8px;
  32 + right: 8px;
  33 + color: #bcb9b9;
  34 + font-size: 14px;
  35 +}
  36 +
  37 +.CodeMirror-line {
  38 + .CodeMirror-matchingbracket {
  39 + outline: none !important;
  40 + background: #ccc;
  41 + border-radius: 2px;
  42 + //padding: 0 1px 2px;
  43 + }
  44 +}
  45 +
  46 +body {
  47 + .CodeMirror-hints.idea {
  48 + background: #fff !important;
  49 + border: 1px solid @N4;
  50 + border-radius: 5px;
  51 + width: 240px;
  52 + .tree-node-high {
  53 + color: @B8;
  54 + }
  55 + .tag {
  56 + padding: 0 8px;
  57 + border-radius: 4px;
  58 + background-color: #e7efff;
  59 + font-size: 14px;
  60 + color: #1764ff;
  61 + white-space: nowrap;
  62 + margin-left: 10px;
  63 + }
  64 + .hint-line {
  65 + padding: 5px 0;
  66 + display: flex;
  67 + justify-content: space-between;
  68 + font-size: 13px;
  69 + &_left {
  70 + //overflow: hidden;
  71 + //white-space: nowrap;
  72 + //text-overflow: ellipsis;
  73 + width: 50%;
  74 + margin-right: 10px;
  75 + }
  76 + &_right {
  77 + color: #7c7e86;
  78 + width: 50%;
  79 + overflow: hidden;
  80 + text-align: right;
  81 + white-space: nowrap;
  82 + text-overflow: ellipsis;
  83 + }
  84 + }
  85 + }
  86 + .CodeMirror-hints.idea .CodeMirror-hint-active {
  87 + background: #f5f5f5 !important;
  88 + }
  89 + .CodeMirror-hints::-webkit-scrollbar {
  90 + width: 10px; /* 设置滚动条宽度 */
  91 + background-color: #fff;
  92 + border-radius: 5px;
  93 + }
  94 +
  95 + .CodeMirror-hints::-webkit-scrollbar-track {
  96 + background-color: #f1f1f1; /* 设置滚动条轨道背景色 */
  97 + border-radius: 10px; /* 设置滚动条轨道的圆角 */
  98 + }
  99 +
  100 + .CodeMirror-hints::-webkit-scrollbar-thumb {
  101 + background-color: #ccc; /* 设置滚动条滑块颜色 */
  102 + border-radius: 10px; /* 设置滚动条滑块的圆角 */
  103 + }
  104 +
  105 + .CodeMirror-hints::-webkit-scrollbar-thumb:hover {
  106 + background-color: #555; /* 设置滚动条滑块的悬停颜色 */
  107 + }
  108 + .CodeMirror-placeholder {
  109 + position: absolute;
  110 + top: 5px;
  111 + left: 5px;
  112 + color: #bfbfbf;
  113 + }
  114 +}
  115 +
  116 +.CodeMirror-hint:hover {
  117 + background: #f5f5f5 !important;
  118 +}
  119 +
  120 +.CodeMirror-line {
  121 + .bracket-mark {
  122 + padding: 0 1px 2px;
  123 + margin: 0 1px 0 0;
  124 + }
  125 +}
  1 +import {
  2 + CloseOutlined,
  3 + ControlOutlined,
  4 + RightOutlined,
  5 +} from '@ant-design/icons';
  6 +import { QxFieldPopover } from '@qx/common';
  7 +import {
  8 + DatePicker,
  9 + Input,
  10 + InputNumber,
  11 + Menu,
  12 + Select,
  13 + Tag,
  14 + TimePicker,
  15 + message,
  16 +} from 'antd';
  17 +import type { DataNode } from 'antd/lib/tree';
  18 +import moment from 'dayjs';
  19 +import { cloneDeep, findIndex, isEmpty, isEqual, size } from 'lodash-es';
1 import type { ReactElement } from 'react'; 20 import type { ReactElement } from 'react';
2 import React, { 21 import React, {
  22 + useCallback,
3 useEffect, 23 useEffect,
4 useImperativeHandle, 24 useImperativeHandle,
  25 + useMemo,
5 useRef, 26 useRef,
6 useState, 27 useState,
7 - useMemo,  
8 - useCallback,  
9 } from 'react'; 28 } from 'react';
10 -import { DatePicker, Input, InputNumber, Menu, Select, Tag, message, TimePicker } from 'antd';  
11 -import { NumFormulaEnum, NumFormulaOptions } from './constant';  
12 -import type { DataNode } from 'antd/lib/tree';  
13 -import moment from 'dayjs'; 29 +import { formatEnum } from '../constant';
  30 +import CodeEditor, {
  31 + VariableProps,
  32 + getAllVariable,
  33 +} from '../qx-code-editor-old';
14 import type { MappingValueProps } from '../qx-filter-condition/filter'; 34 import type { MappingValueProps } from '../qx-filter-condition/filter';
15 -import CodeEditor, { getAllVariable, VariableProps } from '../qx-code-editor';  
16 -import { CloseOutlined, ControlOutlined, RightOutlined } from '@ant-design/icons';  
17 -import { cloneDeep, isEqual, isEmpty, findIndex, size } from 'lodash-es';  
18 -import { RelTreeSetter } from './components/rel-tree-setter'; 35 +import {
  36 + ConditionCol,
  37 + FIELD_TYPE,
  38 + FIELD_TYPE_PROPS,
  39 + ParamValueType,
  40 +} from '../qx-filter-condition/filter';
19 import { AddressSetter } from './components/address-setter'; 41 import { AddressSetter } from './components/address-setter';
20 -import {QxFieldPopover} from '@qx/common';  
21 import { InputSetter } from './components/input-setter'; 42 import { InputSetter } from './components/input-setter';
22 import { OrgSetter } from './components/org-setter'; 43 import { OrgSetter } from './components/org-setter';
23 import { RelSetter } from './components/rel-setter'; 44 import { RelSetter } from './components/rel-setter';
  45 +import { RelTreeSetter } from './components/rel-tree-setter';
24 import { UserSetter } from './components/user-setter'; 46 import { UserSetter } from './components/user-setter';
25 -import { getSelect, getPreviewSelect } from './service';  
26 -import { QxBaseIcon } from '@qx/common';  
27 -import { ConditionCol, FIELD_TYPE_PROPS, FIELD_TYPE, ParamValueType } from '../qx-filter-condition/filter'; 47 +import { NumFormulaEnum, NumFormulaOptions } from './constant';
28 import './index.less'; 48 import './index.less';
  49 +import { getPreviewSelect, getSelect } from './service';
29 50
30 const { Option } = Select; 51 const { Option } = Select;
31 const dateFormat = 'YYYY-MM-DD'; 52 const dateFormat = 'YYYY-MM-DD';
32 const dateTimeFormat = 'YYYY-MM-DD HH:mm:ss'; 53 const dateTimeFormat = 'YYYY-MM-DD HH:mm:ss';
33 54
34 -export const formatEnum: Record<string, string> = {  
35 - YEAR: 'YYYY',  
36 - YEAR_MONTH: 'YYYY-MM',  
37 - YEAR_DATE: 'YYYY-MM-DD',  
38 - YEAR_HOUR: 'YYYY-MM-DD HH:00',  
39 - YEAR_MIN: 'YYYY-MM-DD HH:mm',  
40 - YEAR_SEC: 'YYYY-MM-DD HH:mm:ss',  
41 - HOUR_MIN: 'HH:mm',  
42 - HOUR_SEC: 'HH:mm:ss',  
43 -};  
44 -  
45 // 时间可选字段过滤 55 // 时间可选字段过滤
46 const timeSelectedFormatEnum: Record<string, string> = { 56 const timeSelectedFormatEnum: Record<string, string> = {
47 YEAR_HOUR: 'YYYY-MM-DD HH:00', 57 YEAR_HOUR: 'YYYY-MM-DD HH:00',
@@ -156,7 +166,7 @@ export interface paramColSelectProps extends ColSelectProps { @@ -156,7 +166,7 @@ export interface paramColSelectProps extends ColSelectProps {
156 iconText?: string; // Popover-icon 自定义 后面跟随文本 166 iconText?: string; // Popover-icon 自定义 后面跟随文本
157 allowClear?: boolean; 167 allowClear?: boolean;
158 popupOnBody?: boolean; // 下拉 跟随 body 还是自身 168 popupOnBody?: boolean; // 下拉 跟随 body 还是自身
159 - getName?: (val: any) => void 169 + getName?: (val: any) => void;
160 } 170 }
161 171
162 export const QxFieldSetter: React.FC<paramColSelectProps> = ({ 172 export const QxFieldSetter: React.FC<paramColSelectProps> = ({
@@ -201,7 +211,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -201,7 +211,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
201 211
202 // @ts-ignore 212 // @ts-ignore
203 const isEnum = 213 const isEnum =
204 - (!!fieldGroupType && [FIELD_TYPE_PROPS.ENUM_MULTI, FIELD_TYPE_PROPS.ENUM].includes(fieldGroupType)) || 214 + (!!fieldGroupType &&
  215 + [FIELD_TYPE_PROPS.ENUM_MULTI, FIELD_TYPE_PROPS.ENUM].includes(
  216 + fieldGroupType,
  217 + )) ||
205 ['qxSelect', 'qxMultiSelect'].includes(props?.widget || ''); 218 ['qxSelect', 'qxMultiSelect'].includes(props?.widget || '');
206 //单选多选 选项数据 219 //单选多选 选项数据
207 const [options, setOptions] = useState<any[]>([]); 220 const [options, setOptions] = useState<any[]>([]);
@@ -232,7 +245,11 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -232,7 +245,11 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
232 request = getSelect; 245 request = getSelect;
233 } 246 }
234 247
235 - request(params.appCode ? params.appCode : 'id', params.funCode, _data).then((res: any) => { 248 + request(
  249 + params.appCode ? params.appCode : 'id',
  250 + params.funCode,
  251 + _data,
  252 + ).then((res: any) => {
236 if (!!res?.length) { 253 if (!!res?.length) {
237 setOptions( 254 setOptions(
238 res.filter((item: any) => { 255 res.filter((item: any) => {
@@ -305,7 +322,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -305,7 +322,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
305 valueFin = valCp; 322 valueFin = valCp;
306 } else if (Boolean(isMixValue) && valueType === ParamValueType.FIELD) { 323 } else if (Boolean(isMixValue) && valueType === ParamValueType.FIELD) {
307 // 多选时,如果是`TreeSelect`传入值,val为当前选择项。这里处理追加上已选择的项 324 // 多选时,如果是`TreeSelect`传入值,val为当前选择项。这里处理追加上已选择的项
308 - valueFin = [...(getMappingValues(valueLocal || [], ParamValueType.FIELD) || []), ...valueFin]; 325 + valueFin = [
  326 + ...(getMappingValues(valueLocal || [], ParamValueType.FIELD) || []),
  327 + ...valueFin,
  328 + ];
309 } else if (Boolean(isMultiple)) { 329 } else if (Boolean(isMultiple)) {
310 valueFin = valCp; 330 valueFin = valCp;
311 } else if (valueFin && valueFin.length > 0) { 331 } else if (valueFin && valueFin.length > 0) {
@@ -356,92 +376,97 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -356,92 +376,97 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
356 * @param str 字符串形式 376 * @param str 字符串形式
357 * @param joinParent 拼接父节点 377 * @param joinParent 拼接父节点
358 */ 378 */
359 - const getName = props?.getName || ((val: string[], str?: boolean, joinParent?: boolean) => {  
360 - const name: any[] = [];  
361 - let flag: boolean = false;  
362 - (colsTree || []).map((tree) => {  
363 - (val || []).map((valItem: any) => {  
364 - if (tree.key === valItem) {  
365 - if (str) {  
366 - name.push(tree?.titleStr);  
367 - } else {  
368 - name.push(tree?.title || tree?.titleStr);  
369 - }  
370 - flag = true;  
371 - return;  
372 - }  
373 - });  
374 - (tree?.children || []).map((treeChild: any) => { 379 + const getName =
  380 + props?.getName ||
  381 + ((val: string[], str?: boolean, joinParent?: boolean) => {
  382 + const name: any[] = [];
  383 + let flag: boolean = false;
  384 + (colsTree || []).map((tree) => {
375 (val || []).map((valItem: any) => { 385 (val || []).map((valItem: any) => {
376 - if (treeChild.key === valItem) { 386 + if (tree.key === valItem) {
377 if (str) { 387 if (str) {
378 - if (typeof joinParent === 'boolean' && !joinParent) {  
379 - name.push(`${treeChild?.titleStr}`); 388 + name.push(tree?.titleStr);
  389 + } else {
  390 + name.push(tree?.title || tree?.titleStr);
  391 + }
  392 + flag = true;
  393 + return;
  394 + }
  395 + });
  396 + (tree?.children || []).map((treeChild: any) => {
  397 + (val || []).map((valItem: any) => {
  398 + if (treeChild.key === valItem) {
  399 + if (str) {
  400 + if (typeof joinParent === 'boolean' && !joinParent) {
  401 + name.push(`${treeChild?.titleStr}`);
  402 + } else {
  403 + name.push(
  404 + <>
  405 + ${tree?.titleStr}
  406 + <RightOutlined className={'qx-field-setter__icon'} />$
  407 + {treeChild?.titleStr}
  408 + </>,
  409 + );
  410 + }
380 } else { 411 } else {
381 name.push( 412 name.push(
382 <> 413 <>
383 - ${tree?.titleStr}  
384 - <RightOutlined className={'qx-field-setter__icon'} />${treeChild?.titleStr} 414 + {tree?.title || tree?.titleStr}
  415 + <RightOutlined className={'qx-field-setter__icon'} />
  416 + {treeChild?.title || treeChild?.titleStr}
385 </>, 417 </>,
386 ); 418 );
387 } 419 }
388 - } else {  
389 - name.push(  
390 - <>  
391 - {tree?.title || tree?.titleStr}  
392 - <RightOutlined className={'qx-field-setter__icon'} />  
393 - {treeChild?.title || treeChild?.titleStr}  
394 - </>,  
395 - ); 420 + flag = true;
  421 + return;
396 } 422 }
397 - flag = true;  
398 - return;  
399 - }  
400 - });  
401 - if (treeChild.children) {  
402 - treeChild.children.map((_it: any) => {  
403 - (val || []).map((valItem: any) => {  
404 - if (_it.key === valItem) {  
405 - if (str) {  
406 - if (typeof joinParent === 'boolean' && !joinParent) {  
407 - name.push(`${_it?.titleStr}`); 423 + });
  424 + if (treeChild.children) {
  425 + treeChild.children.map((_it: any) => {
  426 + (val || []).map((valItem: any) => {
  427 + if (_it.key === valItem) {
  428 + if (str) {
  429 + if (typeof joinParent === 'boolean' && !joinParent) {
  430 + name.push(`${_it?.titleStr}`);
  431 + } else {
  432 + name.push(
  433 + <>
  434 + ${tree?.titleStr}
  435 + <RightOutlined className={'qx-field-setter__icon'} />$
  436 + {treeChild?.titleStr}
  437 + <RightOutlined className={'qx-field-setter__icon'} />$
  438 + {_it?.titleStr}
  439 + </>,
  440 + );
  441 + }
408 } else { 442 } else {
409 name.push( 443 name.push(
410 <> 444 <>
411 - ${tree?.titleStr}  
412 - <RightOutlined className={'qx-field-setter__icon'} />${treeChild?.titleStr}  
413 - <RightOutlined className={'qx-field-setter__icon'} />${_it?.titleStr} 445 + {tree?.title || tree?.titleStr}
  446 + <RightOutlined className={'qx-field-setter__icon'} />
  447 + {treeChild?.title || treeChild?.titleStr}
  448 + <RightOutlined className={'qx-field-setter__icon'} />
  449 + {_it?.title || _it?.titleStr}
414 </>, 450 </>,
415 ); 451 );
416 } 452 }
417 - } else {  
418 - name.push(  
419 - <>  
420 - {tree?.title || tree?.titleStr}  
421 - <RightOutlined className={'qx-field-setter__icon'} />  
422 - {treeChild?.title || treeChild?.titleStr}  
423 - <RightOutlined className={'qx-field-setter__icon'} />  
424 - {_it?.title || _it?.titleStr}  
425 - </>,  
426 - ); 453 + flag = true;
  454 + return;
427 } 455 }
428 - flag = true;  
429 - return;  
430 - } 456 + });
431 }); 457 });
432 - });  
433 - } 458 + }
  459 + });
434 }); 460 });
  461 + if (colsTree === undefined && val?.[0]?.startsWith('${')) {
  462 + return;
  463 + }
  464 + if (str) {
  465 + return flag ? name : undefined;
  466 + } else {
  467 + return flag ? name : val;
  468 + }
435 }); 469 });
436 - if (colsTree === undefined && val?.[0]?.startsWith('${')) {  
437 - return;  
438 - }  
439 - if (str) {  
440 - return flag ? name : undefined;  
441 - } else {  
442 - return flag ? name : val;  
443 - }  
444 - });  
445 470
446 /** 471 /**
447 * 名称转换 472 * 名称转换
@@ -449,7 +474,11 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -449,7 +474,11 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
449 * @param str 字符串形式 474 * @param str 字符串形式
450 * @param joinParent 拼接父节点 475 * @param joinParent 拼接父节点
451 */ 476 */
452 - const getCompleteName = (val: string[], str?: boolean, joinParent?: boolean) => { 477 + const getCompleteName = (
  478 + val: string[],
  479 + str?: boolean,
  480 + joinParent?: boolean,
  481 + ) => {
453 const name: any[] = []; 482 const name: any[] = [];
454 let flag: boolean = false; 483 let flag: boolean = false;
455 484
@@ -564,7 +593,11 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -564,7 +593,11 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
564 type = FIELD_TYPE_PROPS.ORG; 593 type = FIELD_TYPE_PROPS.ORG;
565 } else { 594 } else {
566 // 无值时,或手动设置值时,组件类型使用传入类型判断 595 // 无值时,或手动设置值时,组件类型使用传入类型判断
567 - if (!valueLocal || valueLocal.length === 0 || valueLocal[0]?.type === ParamValueType.MANUAL) { 596 + if (
  597 + !valueLocal ||
  598 + valueLocal.length === 0 ||
  599 + valueLocal[0]?.type === ParamValueType.MANUAL
  600 + ) {
568 if (fieldGroupType === FIELD_TYPE_PROPS.NUM) { 601 if (fieldGroupType === FIELD_TYPE_PROPS.NUM) {
569 type = COMP_TYPES.INPUT_NUMBER; 602 type = COMP_TYPES.INPUT_NUMBER;
570 } 603 }
@@ -579,7 +612,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -579,7 +612,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
579 * @param mValues 612 * @param mValues
580 * @param type 613 * @param type
581 */ 614 */
582 - function getMappingValues(mValues: MappingValueProps[], type?: ParamValueType) { 615 + function getMappingValues(
  616 + mValues: MappingValueProps[],
  617 + type?: ParamValueType,
  618 + ) {
583 if (!mValues || size(mValues) === 0) { 619 if (!mValues || size(mValues) === 0) {
584 return []; 620 return [];
585 } 621 }
@@ -591,7 +627,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -591,7 +627,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
591 } 627 }
592 } 628 }
593 629
594 - function getMappingExtValue(mValues: MappingValueProps[], type?: ParamValueType) { 630 + function getMappingExtValue(
  631 + mValues: MappingValueProps[],
  632 + type?: ParamValueType,
  633 + ) {
595 if (!mValues || size(mValues) === 0) { 634 if (!mValues || size(mValues) === 0) {
596 return []; 635 return [];
597 } 636 }
@@ -661,15 +700,21 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -661,15 +700,21 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
661 //手机/邮箱/地址 只能选自己类型的 700 //手机/邮箱/地址 只能选自己类型的
662 if ( 701 if (
663 co.extract && 702 co.extract &&
664 - ['qxMobile', 'qxEmail', 'qxSwitch', 'qxBizNo', 'qxAddress', 'qxUploadImage'].indexOf(  
665 - props.widget || '',  
666 - ) > -1 703 + [
  704 + 'qxMobile',
  705 + 'qxEmail',
  706 + 'qxSwitch',
  707 + 'qxBizNo',
  708 + 'qxAddress',
  709 + 'qxUploadImage',
  710 + ].indexOf(props.widget || '') > -1
667 ) { 711 ) {
668 return co.extract.widget === props.widget; 712 return co.extract.widget === props.widget;
669 } 713 }
670 714
671 if ( 715 if (
672 - (fieldGroupType === FIELD_TYPE_PROPS.REL || fieldGroupType === FIELD_TYPE_PROPS.REL_MULTI) && 716 + (fieldGroupType === FIELD_TYPE_PROPS.REL ||
  717 + fieldGroupType === FIELD_TYPE_PROPS.REL_MULTI) &&
673 co.fieldGroupType === FIELD_TYPE_PROPS.FLOW_WF_RECORD 718 co.fieldGroupType === FIELD_TYPE_PROPS.FLOW_WF_RECORD
674 ) { 719 ) {
675 return true; 720 return true;
@@ -690,7 +735,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -690,7 +735,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
690 return true; 735 return true;
691 } 736 }
692 if (ex && co.extract && ex.relId) { 737 if (ex && co.extract && ex.relId) {
693 - return ex.relId === co.extract.relId || ex.relId === co.extract.qxProps?.relId; 738 + return (
  739 + ex.relId === co.extract.relId ||
  740 + ex.relId === co.extract.qxProps?.relId
  741 + );
694 } else { 742 } else {
695 return false; 743 return false;
696 } 744 }
@@ -740,7 +788,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -740,7 +788,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
740 ((co.extract && co.extract.fieldKey === 'id') || 788 ((co.extract && co.extract.fieldKey === 'id') ||
741 co.key.endsWith('.id') || 789 co.key.endsWith('.id') ||
742 co.key.endsWith('|id'))) || 790 co.key.endsWith('|id'))) ||
743 - (co.fieldGroupType === 'TIME' && fieldGroupType === 'DATE' && !!props?.timeSelected) || 791 + (co.fieldGroupType === 'TIME' &&
  792 + fieldGroupType === 'DATE' &&
  793 + !!props?.timeSelected) ||
744 (co.fieldGroupType === 'DATE' && 794 (co.fieldGroupType === 'DATE' &&
745 fieldGroupType === 'TIME' && 795 fieldGroupType === 'TIME' &&
746 !!props?.dateSelected && 796 !!props?.dateSelected &&
@@ -752,7 +802,8 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -752,7 +802,8 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
752 }; 802 };
753 803
754 // @ts-ignore 804 // @ts-ignore
755 - const loopCheckCol = useCallback((nodeChildren: ColsTreeProps[]) => { 805 + const loopCheckCol = useCallback(
  806 + (nodeChildren: ColsTreeProps[]) => {
756 return nodeChildren.filter((co: ColsTreeProps) => { 807 return nodeChildren.filter((co: ColsTreeProps) => {
757 if (co.children && co.children.length > 0) { 808 if (co.children && co.children.length > 0) {
758 co.children = loopCheckCol(co.children); 809 co.children = loopCheckCol(co.children);
@@ -777,9 +828,13 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -777,9 +828,13 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
777 828
778 // 禁用选项 829 // 禁用选项
779 if (excludeKeys && size(excludeKeys) > 0) { 830 if (excludeKeys && size(excludeKeys) > 0) {
780 - colsTreeProps = colsTreeProps?.filter((co: ColsTreeProps) => excludeKeys.includes(co.key)); 831 + colsTreeProps = colsTreeProps?.filter((co: ColsTreeProps) =>
  832 + excludeKeys.includes(co.key),
  833 + );
781 colsTreeProps.map((col) => { 834 colsTreeProps.map((col) => {
782 - col.children = col?.children?.filter((co: ColsTreeProps) => excludeKeys.includes(co.key)); 835 + col.children = col?.children?.filter((co: ColsTreeProps) =>
  836 + excludeKeys.includes(co.key),
  837 + );
783 }); 838 });
784 } 839 }
785 // 公式相关 840 // 公式相关
@@ -791,7 +846,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -791,7 +846,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
791 const formula = valueLocal?.[0]?.value; 846 const formula = valueLocal?.[0]?.value;
792 const variableObjNew: Record<string, string> = {}; 847 const variableObjNew: Record<string, string> = {};
793 // 追加公式(简版)变量预定义 848 // 追加公式(简版)变量预定义
794 - const formulaOptions = NumFormulaOptions.filter((item) => item.key !== NumFormulaEnum.CUSTOM); 849 + const formulaOptions = NumFormulaOptions.filter(
  850 + (item) => item.key !== NumFormulaEnum.CUSTOM,
  851 + );
795 formulaOptions.map((item) => 852 formulaOptions.map((item) =>
796 Object.assign(variableObjNew, { 853 Object.assign(variableObjNew, {
797 [`\${${item.key}\}`]: item.key, 854 [`\${${item.key}\}`]: item.key,
@@ -863,7 +920,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -863,7 +920,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
863 fieldGroupType === FIELD_TYPE_PROPS.COMBINED_TEXT ? ( 920 fieldGroupType === FIELD_TYPE_PROPS.COMBINED_TEXT ? (
864 <CodeEditor 921 <CodeEditor
865 className={'select ant-input btn-text ' + (colsTree ? '' : 'full')} 922 className={'select ant-input btn-text ' + (colsTree ? '' : 'full')}
866 - value={getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[0] || ''} 923 + value={
  924 + getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[0] ||
  925 + ''
  926 + }
867 variableObj={variableObj} 927 variableObj={variableObj}
868 resetValue={props?.resetValue || ''} 928 resetValue={props?.resetValue || ''}
869 allowClear={!!props?.allowClear} 929 allowClear={!!props?.allowClear}
@@ -877,25 +937,39 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -877,25 +937,39 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
877 value={valueLocal} 937 value={valueLocal}
878 disabled={disabled} 938 disabled={disabled}
879 getName={getName} 939 getName={getName}
880 - onChange={(val) => onFilterValueChange(val || [], ParamValueType.MANUAL)} 940 + onChange={(val) =>
  941 + onFilterValueChange(val || [], ParamValueType.MANUAL)
  942 + }
881 /> 943 />
882 - ) : getFieldSimpleComType() === COMP_TYPES.INPUT_NUMBER && !isMultiple ? ( 944 + ) : getFieldSimpleComType() === COMP_TYPES.INPUT_NUMBER &&
  945 + !isMultiple ? (
883 <InputNumber 946 <InputNumber
884 className={'select ' + (colsTree ? '' : 'full')} 947 className={'select ' + (colsTree ? '' : 'full')}
885 placeholder="请填写数字" 948 placeholder="请填写数字"
886 defaultValue={ 949 defaultValue={
887 - valueLocal == undefined || valueLocal?.length == 0 || valueLocal[0]?.value == '' 950 + valueLocal == undefined ||
  951 + valueLocal?.length == 0 ||
  952 + valueLocal[0]?.value == ''
888 ? '' 953 ? ''
889 - : Number(getMappingValues(valueLocal || [], ParamValueType.MANUAL)) 954 + : Number(
  955 + getMappingValues(valueLocal || [], ParamValueType.MANUAL),
  956 + )
890 } 957 }
891 value={ 958 value={
892 - valueLocal == undefined || valueLocal?.length == 0 || valueLocal[0]?.value == '' 959 + valueLocal == undefined ||
  960 + valueLocal?.length == 0 ||
  961 + valueLocal[0]?.value == ''
893 ? '' 962 ? ''
894 - : Number(getMappingValues(valueLocal || [], ParamValueType.MANUAL)) 963 + : Number(
  964 + getMappingValues(valueLocal || [], ParamValueType.MANUAL),
  965 + )
895 } 966 }
896 disabled={disabled} 967 disabled={disabled}
897 onChange={(val) => 968 onChange={(val) =>
898 - onFilterValueChange([val == 0 || val ? val.toString() : ''], ParamValueType.MANUAL) 969 + onFilterValueChange(
  970 + [val == 0 || val ? val.toString() : ''],
  971 + ParamValueType.MANUAL,
  972 + )
899 } 973 }
900 /> 974 />
901 ) : getFieldSimpleComType() === COMP_TYPES.USER ? ( 975 ) : getFieldSimpleComType() === COMP_TYPES.USER ? (
@@ -907,7 +981,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -907,7 +981,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
907 getName={getName} 981 getName={getName}
908 // @ts-ignore 982 // @ts-ignore
909 params={{ ...props.params, field: props.field }} 983 params={{ ...props.params, field: props.field }}
910 - onChange={(val, type, isDelete) => onFilterValueChange(val || [], type, 0, isDelete)} 984 + onChange={(val, type, isDelete) =>
  985 + onFilterValueChange(val || [], type, 0, isDelete)
  986 + }
911 /> 987 />
912 ) : getFieldSimpleComType() === COMP_TYPES.ORG ? ( 988 ) : getFieldSimpleComType() === COMP_TYPES.ORG ? (
913 <OrgSetter 989 <OrgSetter
@@ -918,7 +994,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -918,7 +994,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
918 // @ts-ignore 994 // @ts-ignore
919 getName={getName} 995 getName={getName}
920 params={{ ...props.params, field: props.field }} 996 params={{ ...props.params, field: props.field }}
921 - onChange={(val, type, isDelete) => onFilterValueChange(val || [], type, 0, isDelete)} 997 + onChange={(val, type, isDelete) =>
  998 + onFilterValueChange(val || [], type, 0, isDelete)
  999 + }
922 /> 1000 />
923 ) : getFieldSimpleComType() === COMP_TYPES.REL ? ( 1001 ) : getFieldSimpleComType() === COMP_TYPES.REL ? (
924 <RelSetter 1002 <RelSetter
@@ -954,30 +1032,36 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -954,30 +1032,36 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
954 ) : getFieldSimpleComType() === COMP_TYPES.INPUT ? ( 1032 ) : getFieldSimpleComType() === COMP_TYPES.INPUT ? (
955 <Input 1033 <Input
956 className={'select ' + (colsTree ? '' : 'full')} 1034 className={'select ' + (colsTree ? '' : 'full')}
957 - defaultValue={getMappingValues(valueLocal || [], ParamValueType.MANUAL).toString()} 1035 + defaultValue={getMappingValues(
  1036 + valueLocal || [],
  1037 + ParamValueType.MANUAL,
  1038 + ).toString()}
958 // value={getMappingValues(valueLocal || [], ParamValueType.MANUAL).toString()} 1039 // value={getMappingValues(valueLocal || [], ParamValueType.MANUAL).toString()}
959 placeholder="请输入" 1040 placeholder="请输入"
960 disabled={disabled} 1041 disabled={disabled}
961 - onChange={(e) => onFilterValueChange([e.target.value], ParamValueType.MANUAL)} 1042 + onChange={(e) =>
  1043 + onFilterValueChange([e.target.value], ParamValueType.MANUAL)
  1044 + }
962 /> 1045 />
963 ) : getFieldSimpleComType() === COMP_TYPES.DATE_PICKER ? ( 1046 ) : getFieldSimpleComType() === COMP_TYPES.DATE_PICKER ? (
964 - <DatePicker  
965 - className={'select ' + (colsTree ? '' : 'full')}  
966 - placeholder="请输入日期(时间)"  
967 - defaultValue={  
968 - getMappingValues(valueLocal || [], ParamValueType.FIELD) ?  
969 - undefined : moment(  
970 - getMappingValues(valueLocal || [], ParamValueType.FIELD),  
971 - formatEnum[  
972 - !!extract?.type  
973 - ? extract.type === 'REL_FIELD'  
974 - ? extract.renderData.type  
975 - : extract.type  
976 - : fieldType || 'YEAR_SEC'  
977 - ] || dateFormat,  
978 - )  
979 - }  
980 - value={ 1047 + <DatePicker
  1048 + className={'select ' + (colsTree ? '' : 'full')}
  1049 + placeholder="请输入日期(时间)"
  1050 + defaultValue={
  1051 + getMappingValues(valueLocal || [], ParamValueType.FIELD)
  1052 + ? undefined
  1053 + : moment(
  1054 + getMappingValues(valueLocal || [], ParamValueType.FIELD),
  1055 + formatEnum[
  1056 + !!extract?.type
  1057 + ? extract.type === 'REL_FIELD'
  1058 + ? extract.renderData.type
  1059 + : extract.type
  1060 + : fieldType || 'YEAR_SEC'
  1061 + ] || dateFormat,
  1062 + )
  1063 + }
  1064 + value={
981 getMappingValues(valueLocal || [], ParamValueType.FIELD) 1065 getMappingValues(valueLocal || [], ParamValueType.FIELD)
982 ? undefined 1066 ? undefined
983 : moment( 1067 : moment(
@@ -1008,7 +1092,12 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1008,7 +1092,12 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1008 ) : getFieldSimpleComType() === COMP_TYPE_FORMULA ? ( 1092 ) : getFieldSimpleComType() === COMP_TYPE_FORMULA ? (
1009 <CodeEditor 1093 <CodeEditor
1010 className={'select ant-input btn-text ' + (colsTree ? '' : 'full')} 1094 className={'select ant-input btn-text ' + (colsTree ? '' : 'full')}
1011 - value={getMappingValues(valueLocal || [], ParamValueType.OPERATOR)?.[0] || ''} 1095 + value={
  1096 + getMappingValues(
  1097 + valueLocal || [],
  1098 + ParamValueType.OPERATOR,
  1099 + )?.[0] || ''
  1100 + }
1012 variableObj={variableObj} 1101 variableObj={variableObj}
1013 newVariable={newVariable} 1102 newVariable={newVariable}
1014 onChange={(val: string) => { 1103 onChange={(val: string) => {
@@ -1028,14 +1117,24 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1028,14 +1117,24 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1028 style={{ flex: 1 }} 1117 style={{ flex: 1 }}
1029 placeholder="请填写数字" 1118 placeholder="请填写数字"
1030 defaultValue={ 1119 defaultValue={
1031 - valueLocal == undefined || valueLocal?.length == 0 || valueLocal[0]?.value == '' 1120 + valueLocal == undefined ||
  1121 + valueLocal?.length == 0 ||
  1122 + valueLocal[0]?.value == ''
1032 ? '' 1123 ? ''
1033 - : getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[0] 1124 + : getMappingValues(
  1125 + valueLocal || [],
  1126 + ParamValueType.MANUAL,
  1127 + )?.[0]
1034 } 1128 }
1035 value={ 1129 value={
1036 - valueLocal == undefined || valueLocal?.length == 0 || valueLocal[0]?.value == '' 1130 + valueLocal == undefined ||
  1131 + valueLocal?.length == 0 ||
  1132 + valueLocal[0]?.value == ''
1037 ? '' 1133 ? ''
1038 - : getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[0] 1134 + : getMappingValues(
  1135 + valueLocal || [],
  1136 + ParamValueType.MANUAL,
  1137 + )?.[0]
1039 } 1138 }
1040 disabled={disabled} 1139 disabled={disabled}
1041 onChange={(val) => 1140 onChange={(val) =>
@@ -1060,14 +1159,24 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1060,14 +1159,24 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1060 style={{ flex: 1 }} 1159 style={{ flex: 1 }}
1061 placeholder="请填写数字" 1160 placeholder="请填写数字"
1062 defaultValue={ 1161 defaultValue={
1063 - valueLocal == undefined || valueLocal?.length == 0 || valueLocal[1]?.value == '' 1162 + valueLocal == undefined ||
  1163 + valueLocal?.length == 0 ||
  1164 + valueLocal[1]?.value == ''
1064 ? '' 1165 ? ''
1065 - : getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[1] 1166 + : getMappingValues(
  1167 + valueLocal || [],
  1168 + ParamValueType.MANUAL,
  1169 + )?.[1]
1066 } 1170 }
1067 value={ 1171 value={
1068 - valueLocal == undefined || valueLocal?.length == 0 || valueLocal[1]?.value == '' 1172 + valueLocal == undefined ||
  1173 + valueLocal?.length == 0 ||
  1174 + valueLocal[1]?.value == ''
1069 ? '' 1175 ? ''
1070 - : getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[1] 1176 + : getMappingValues(
  1177 + valueLocal || [],
  1178 + ParamValueType.MANUAL,
  1179 + )?.[1]
1071 } 1180 }
1072 disabled={disabled} 1181 disabled={disabled}
1073 onChange={(val) => 1182 onChange={(val) =>
@@ -1088,7 +1197,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1088,7 +1197,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1088 value={ 1197 value={
1089 getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[0] 1198 getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[0]
1090 ? moment( 1199 ? moment(
1091 - getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[0], 1200 + getMappingValues(
  1201 + valueLocal || [],
  1202 + ParamValueType.MANUAL,
  1203 + )?.[0],
1092 formatEnum[ 1204 formatEnum[
1093 !!extract?.type 1205 !!extract?.type
1094 ? extract.type === 'REL_FIELD' 1206 ? extract.type === 'REL_FIELD'
@@ -1139,7 +1251,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1139,7 +1251,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1139 value={ 1251 value={
1140 getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[1] 1252 getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[1]
1141 ? moment( 1253 ? moment(
1142 - getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[1], 1254 + getMappingValues(
  1255 + valueLocal || [],
  1256 + ParamValueType.MANUAL,
  1257 + )?.[1],
1143 formatEnum[ 1258 formatEnum[
1144 !!extract?.type 1259 !!extract?.type
1145 ? extract.type === 'REL_FIELD' 1260 ? extract.type === 'REL_FIELD'
@@ -1185,7 +1300,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1185,7 +1300,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1185 getCompleteName={getCompleteName} 1300 getCompleteName={getCompleteName}
1186 disabled={disabled} 1301 disabled={disabled}
1187 params={{ ...props.params, field: props.field }} 1302 params={{ ...props.params, field: props.field }}
1188 - onChange={(val) => onFilterValueChange(val || [], ParamValueType.MANUAL)} 1303 + onChange={(val) =>
  1304 + onFilterValueChange(val || [], ParamValueType.MANUAL)
  1305 + }
1189 /> 1306 />
1190 ) : getFieldSimpleComType() === COMP_TYPES.RANGE_TIME ? ( 1307 ) : getFieldSimpleComType() === COMP_TYPES.RANGE_TIME ? (
1191 <div className={'select full'} style={{ width: '100%' }}> 1308 <div className={'select full'} style={{ width: '100%' }}>
@@ -1195,7 +1312,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1195,7 +1312,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1195 value={ 1312 value={
1196 getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[0] 1313 getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[0]
1197 ? moment( 1314 ? moment(
1198 - getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[0], 1315 + getMappingValues(
  1316 + valueLocal || [],
  1317 + ParamValueType.MANUAL,
  1318 + )?.[0],
1199 formatEnum[ 1319 formatEnum[
1200 !!extract?.type 1320 !!extract?.type
1201 ? extract.type === 'REL_FIELD' 1321 ? extract.type === 'REL_FIELD'
@@ -1235,7 +1355,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1235,7 +1355,10 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1235 value={ 1355 value={
1236 getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[1] 1356 getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[1]
1237 ? moment( 1357 ? moment(
1238 - getMappingValues(valueLocal || [], ParamValueType.MANUAL)?.[1], 1358 + getMappingValues(
  1359 + valueLocal || [],
  1360 + ParamValueType.MANUAL,
  1361 + )?.[1],
1239 formatEnum[ 1362 formatEnum[
1240 !!extract?.type 1363 !!extract?.type
1241 ? extract.type === 'REL_FIELD' 1364 ? extract.type === 'REL_FIELD'
@@ -1279,7 +1402,8 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1279,7 +1402,8 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1279 className={'select qx-filter-select' + (colsTree ? '' : 'full')} 1402 className={'select qx-filter-select' + (colsTree ? '' : 'full')}
1280 mode={ 1403 mode={
1281 !Boolean(isMultiple) || 1404 !Boolean(isMultiple) ||
1282 - (Boolean(isMultiple) && fieldGroupType === FIELD_TYPE_PROPS.TEXT) 1405 + (Boolean(isMultiple) &&
  1406 + fieldGroupType === FIELD_TYPE_PROPS.TEXT)
1283 ? 'tags' 1407 ? 'tags'
1284 : 'multiple' 1408 : 'multiple'
1285 } 1409 }
@@ -1302,7 +1426,8 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1302,7 +1426,8 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1302 closable={closable} 1426 closable={closable}
1303 onClose={onClose} 1427 onClose={onClose}
1304 > 1428 >
1305 - {_val === undefined ? null : String(_val) === String([String(label)]) && 1429 + {_val === undefined ? null : String(_val) ===
  1430 + String([String(label)]) &&
1306 String(label).startsWith('${') ? ( 1431 String(label).startsWith('${') ? (
1307 <span style={{ color: 'red' }}>已缺失</span> 1432 <span style={{ color: 'red' }}>已缺失</span>
1308 ) : ( 1433 ) : (
@@ -1325,7 +1450,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1325,7 +1450,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1325 ); 1450 );
1326 }} 1451 }}
1327 onChange={(val) => { 1452 onChange={(val) => {
1328 - const valOptionsArr = getValueOptions().map((valItem: any) => valItem?.key); 1453 + const valOptionsArr = getValueOptions().map(
  1454 + (valItem: any) => valItem?.key,
  1455 + );
1329 // 原值类型 1456 // 原值类型
1330 let valueType: any; 1457 let valueType: any;
1331 // 查询原值(如果存在于`valueLocal`下)类型 1458 // 查询原值(如果存在于`valueLocal`下)类型
@@ -1343,9 +1470,17 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1343,9 +1470,17 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1343 valueType = ParamValueType.MANUAL; 1470 valueType = ParamValueType.MANUAL;
1344 } 1471 }
1345 } 1472 }
1346 - if (fieldGroupType === FIELD_TYPE_PROPS.BOOL && !Boolean(isMultiple)) { 1473 + if (
  1474 + fieldGroupType === FIELD_TYPE_PROPS.BOOL &&
  1475 + !Boolean(isMultiple)
  1476 + ) {
1347 if (val.length > 0) { 1477 if (val.length > 0) {
1348 - onFilterValueChange([val[val.length - 1]], valueType, -1, true); 1478 + onFilterValueChange(
  1479 + [val[val.length - 1]],
  1480 + valueType,
  1481 + -1,
  1482 + true,
  1483 + );
1349 } else { 1484 } else {
1350 onFilterValueChange(val, valueType, -1, true); 1485 onFilterValueChange(val, valueType, -1, true);
1351 } 1486 }
@@ -1355,17 +1490,26 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1355,17 +1490,26 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1355 }} 1490 }}
1356 > 1491 >
1357 {getValueOptions().map((valItem: any) => ( 1492 {getValueOptions().map((valItem: any) => (
1358 - <Option key={valItem?.key || valItem?.code} value={valItem?.key || valItem?.code}> 1493 + <Option
  1494 + key={valItem?.key || valItem?.code}
  1495 + value={valItem?.key || valItem?.code}
  1496 + >
1359 {valItem?.value || valItem?.name} 1497 {valItem?.value || valItem?.name}
1360 </Option> 1498 </Option>
1361 ))} 1499 ))}
1362 {fieldGroupType && fieldGroupType === FIELD_TYPE_PROPS.DATE && ( 1500 {fieldGroupType && fieldGroupType === FIELD_TYPE_PROPS.DATE && (
1363 - <Option value={'__action_date'} style={{ borderTop: '1px solid #f0f0f0' }}> 1501 + <Option
  1502 + value={'__action_date'}
  1503 + style={{ borderTop: '1px solid #f0f0f0' }}
  1504 + >
1364 指定日期 1505 指定日期
1365 </Option> 1506 </Option>
1366 )} 1507 )}
1367 {fieldGroupType && fieldGroupType === FIELD_TYPE_PROPS.TIME && ( 1508 {fieldGroupType && fieldGroupType === FIELD_TYPE_PROPS.TIME && (
1368 - <Option value={'__action_time'} style={{ borderTop: '1px solid #f0f0f0' }}> 1509 + <Option
  1510 + value={'__action_time'}
  1511 + style={{ borderTop: '1px solid #f0f0f0' }}
  1512 + >
1369 指定时间 1513 指定时间
1370 </Option> 1514 </Option>
1371 )} 1515 )}
@@ -1391,10 +1535,14 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1391,10 +1535,14 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1391 : disabled 1535 : disabled
1392 : disabled 1536 : disabled
1393 } 1537 }
1394 - allowClear={!isMixValue || valueLocal?.[0]?.type === ParamValueType.FIELD} 1538 + allowClear={
  1539 + !isMixValue || valueLocal?.[0]?.type === ParamValueType.FIELD
  1540 + }
1395 onChange={(val) => { 1541 onChange={(val) => {
1396 const valLocal = typeof val === 'string' ? [val] : val; 1542 const valLocal = typeof val === 'string' ? [val] : val;
1397 - const valOptionsArr = getValueOptions().map((valItem: any) => valItem?.key); 1543 + const valOptionsArr = getValueOptions().map(
  1544 + (valItem: any) => valItem?.key,
  1545 + );
1398 let valueType = (valOptionsArr || []).includes(valLocal?.[0]) 1546 let valueType = (valOptionsArr || []).includes(valLocal?.[0])
1399 ? ParamValueType.OPERATOR 1547 ? ParamValueType.OPERATOR
1400 : ParamValueType.MANUAL; 1548 : ParamValueType.MANUAL;
@@ -1405,27 +1553,42 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1405,27 +1553,42 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1405 }} 1553 }}
1406 > 1554 >
1407 {fieldGroupType && fieldGroupType === FIELD_TYPE_PROPS.DATE && ( 1555 {fieldGroupType && fieldGroupType === FIELD_TYPE_PROPS.DATE && (
1408 - <Option value={'__action_date'} style={{ borderBottom: '1px solid #f0f0f0' }}> 1556 + <Option
  1557 + value={'__action_date'}
  1558 + style={{ borderBottom: '1px solid #f0f0f0' }}
  1559 + >
1409 指定日期 1560 指定日期
1410 </Option> 1561 </Option>
1411 )} 1562 )}
1412 {fieldGroupType && fieldGroupType === FIELD_TYPE_PROPS.USER && ( 1563 {fieldGroupType && fieldGroupType === FIELD_TYPE_PROPS.USER && (
1413 - <Option value={'__action_user'} style={{ borderBottom: '1px solid #f0f0f0' }}> 1564 + <Option
  1565 + value={'__action_user'}
  1566 + style={{ borderBottom: '1px solid #f0f0f0' }}
  1567 + >
1414 选择人员 1568 选择人员
1415 </Option> 1569 </Option>
1416 )} 1570 )}
1417 {fieldGroupType && fieldGroupType === FIELD_TYPE_PROPS.ORG && ( 1571 {fieldGroupType && fieldGroupType === FIELD_TYPE_PROPS.ORG && (
1418 - <Option value={'__action_org'} style={{ borderBottom: '1px solid #f0f0f0' }}> 1572 + <Option
  1573 + value={'__action_org'}
  1574 + style={{ borderBottom: '1px solid #f0f0f0' }}
  1575 + >
1419 选择部门 1576 选择部门
1420 </Option> 1577 </Option>
1421 )} 1578 )}
1422 {fieldGroupType && fieldGroupType === FIELD_TYPE_PROPS.TIME && ( 1579 {fieldGroupType && fieldGroupType === FIELD_TYPE_PROPS.TIME && (
1423 - <Option value={'__action_time'} style={{ borderBottom: '1px solid #f0f0f0' }}> 1580 + <Option
  1581 + value={'__action_time'}
  1582 + style={{ borderBottom: '1px solid #f0f0f0' }}
  1583 + >
1424 指定时间 1584 指定时间
1425 </Option> 1585 </Option>
1426 )} 1586 )}
1427 {getValueOptions().map((valItem: any) => ( 1587 {getValueOptions().map((valItem: any) => (
1428 - <Option key={valItem?.key || valItem?.code} value={valItem?.key || valItem?.code}> 1588 + <Option
  1589 + key={valItem?.key || valItem?.code}
  1590 + value={valItem?.key || valItem?.code}
  1591 + >
1429 {valItem?.value || valItem?.name} 1592 {valItem?.value || valItem?.name}
1430 </Option> 1593 </Option>
1431 ))} 1594 ))}
@@ -1438,7 +1601,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1438,7 +1601,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1438 // @ts-ignore 1601 // @ts-ignore
1439 width={ 1602 width={
1440 props.popWidth || 1603 props.popWidth ||
1441 - (currentElem && currentElem.current && currentElem.current.clientWidth + 'px') 1604 + (currentElem &&
  1605 + currentElem.current &&
  1606 + currentElem.current.clientWidth + 'px')
1442 } 1607 }
1443 data={colsTreeProps} 1608 data={colsTreeProps}
1444 popFooter={props.extraFooter} 1609 popFooter={props.extraFooter}
@@ -1471,7 +1636,8 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1471,7 +1636,8 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1471 //TODO 临时方案,可多选字段 1636 //TODO 临时方案,可多选字段
1472 if (FIELD_TYPE.FORMULA === fieldType) { 1637 if (FIELD_TYPE.FORMULA === fieldType) {
1473 const _historyValue = 1638 const _historyValue =
1474 - getMappingValues(valueLocal || [], ParamValueType.FIELD) || []; 1639 + getMappingValues(valueLocal || [], ParamValueType.FIELD) ||
  1640 + [];
1475 if (!_historyValue.includes(val.toString())) { 1641 if (!_historyValue.includes(val.toString())) {
1476 const newVal = [..._historyValue, ...[val.toString()]]; 1642 const newVal = [..._historyValue, ...[val.toString()]];
1477 onFilterValueChange(newVal, ParamValueType.FIELD, 0, true); 1643 onFilterValueChange(newVal, ParamValueType.FIELD, 0, true);
@@ -1488,7 +1654,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1488,7 +1654,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1488 > 1654 >
1489 <ControlOutlined /> 1655 <ControlOutlined />
1490 {props?.iconText ? ( 1656 {props?.iconText ? (
1491 - <span className={'qx-field-setter__icon-text'}>{props.iconText}</span> 1657 + <span className={'qx-field-setter__icon-text'}>
  1658 + {props.iconText}
  1659 + </span>
1492 ) : null} 1660 ) : null}
1493 </span> 1661 </span>
1494 </QxFieldPopover> 1662 </QxFieldPopover>
@@ -1520,7 +1688,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1520,7 +1688,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1520 } 1688 }
1521 placeholder="请输入日期(时间)" 1689 placeholder="请输入日期(时间)"
1522 defaultValue={ 1690 defaultValue={
1523 - !isEmpty(getMappingExtValue(valueLocal || [], ParamValueType.MANUAL)) 1691 + !isEmpty(
  1692 + getMappingExtValue(valueLocal || [], ParamValueType.MANUAL),
  1693 + )
1524 ? moment( 1694 ? moment(
1525 getMappingExtValue(valueLocal || [], ParamValueType.MANUAL), 1695 getMappingExtValue(valueLocal || [], ParamValueType.MANUAL),
1526 formatEnum[ 1696 formatEnum[
@@ -1534,7 +1704,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1534,7 +1704,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1534 : undefined 1704 : undefined
1535 } 1705 }
1536 value={ 1706 value={
1537 - !isEmpty(getMappingExtValue(valueLocal || [], ParamValueType.MANUAL)) 1707 + !isEmpty(
  1708 + getMappingExtValue(valueLocal || [], ParamValueType.MANUAL),
  1709 + )
1538 ? moment( 1710 ? moment(
1539 getMappingExtValue(valueLocal || [], ParamValueType.MANUAL), 1711 getMappingExtValue(valueLocal || [], ParamValueType.MANUAL),
1540 formatEnum[ 1712 formatEnum[
@@ -1556,7 +1728,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1556,7 +1728,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1556 style={{ width: '100%', marginTop: '6px' }} 1728 style={{ width: '100%', marginTop: '6px' }}
1557 placeholder="请输入时间" 1729 placeholder="请输入时间"
1558 defaultValue={ 1730 defaultValue={
1559 - !isEmpty(getMappingExtValue(valueLocal || [], ParamValueType.MANUAL)) 1731 + !isEmpty(
  1732 + getMappingExtValue(valueLocal || [], ParamValueType.MANUAL),
  1733 + )
1560 ? moment( 1734 ? moment(
1561 getMappingExtValue(valueLocal || [], ParamValueType.MANUAL), 1735 getMappingExtValue(valueLocal || [], ParamValueType.MANUAL),
1562 formatEnum[ 1736 formatEnum[
@@ -1570,7 +1744,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1570,7 +1744,9 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1570 : undefined 1744 : undefined
1571 } 1745 }
1572 value={ 1746 value={
1573 - !isEmpty(getMappingExtValue(valueLocal || [], ParamValueType.MANUAL)) 1747 + !isEmpty(
  1748 + getMappingExtValue(valueLocal || [], ParamValueType.MANUAL),
  1749 + )
1574 ? moment( 1750 ? moment(
1575 getMappingExtValue(valueLocal || [], ParamValueType.MANUAL), 1751 getMappingExtValue(valueLocal || [], ParamValueType.MANUAL),
1576 formatEnum[ 1752 formatEnum[
@@ -1616,4 +1792,4 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({ @@ -1616,4 +1792,4 @@ export const QxFieldSetter: React.FC<paramColSelectProps> = ({
1616 )} 1792 )}
1617 </div> 1793 </div>
1618 ); 1794 );
1619 -} 1795 +};
1 import type { DataNode } from 'antd/lib/tree'; 1 import type { DataNode } from 'antd/lib/tree';
  2 +import { FIELD_TYPE_PROPS } from '../constant';
  3 +
  4 +export { FIELD_TYPE_PROPS };
2 5
3 export type OperatorProps = { 6 export type OperatorProps = {
4 text: string; 7 text: string;
@@ -691,71 +694,6 @@ export interface ColsTreeProps extends DataNode { @@ -691,71 +694,6 @@ export interface ColsTreeProps extends DataNode {
691 type?: any; 694 type?: any;
692 } 695 }
693 696
694 -export enum FIELD_TYPE_PROPS {  
695 - EMPTY = '',  
696 - // 文本  
697 - TEXT = 'TEXT',  
698 - STRING = 'STRING',  
699 -  
700 - // 日期  
701 - DATE = 'DATE',  
702 - DATE_TIME = 'DATE_TIME',  
703 - // 人员  
704 - USER = 'USER',  
705 - USER_MULTI = 'USER_MULTI',  
706 - // 部门  
707 - ORG = 'ORG',  
708 - ORG_MULTI = 'ORG_MULTI',  
709 - // 数字  
710 - NUM = 'NUM',  
711 - // 布尔  
712 - BOOL = 'BOOL',  
713 - // 枚举  
714 - ENUM = 'ENUM',  
715 - ENUM_MULTI = 'ENUM_MULTI',  
716 - // 文件  
717 - FILE = 'FILE',  
718 -  
719 - // 公式:数值类  
720 - FORMULA = 'FORMULA',  
721 -  
722 - DOUBLE = 'DOUBLE',  
723 - INTEGER = 'INTEGER',  
724 - DECIMAL = 'DECIMAL',  
725 - PERCENT = 'PERCENT',  
726 -  
727 - YEAR = 'YEAR',  
728 - YEAR_MONTH = 'YEAR_MONTH',  
729 - YEAR_HOUR = 'YEAR_HOUR',  
730 - YEAR_DATE = 'YEAR_DATE',  
731 - YEAR_MIN = 'YEAR_MIN',  
732 - YEAR_SEC = 'YEAR_SEC',  
733 - HOUR = 'HOUR',  
734 - HOUR_MIN = 'HOUR_MIN',  
735 - HOUR_SEC = 'HOUR_SEC',  
736 -  
737 - TREE = 'TREE',  
738 - REL = 'REL',  
739 - REL_MULTI = 'REL_MULTI',  
740 - REL_FIELD = 'REL_FIELD',  
741 -  
742 - TABLE = 'TABLE',  
743 -  
744 - // 流程专用  
745 - FLOW_WF_APRV_USR = 'FLOW_WF_APRV_USR',  
746 - FLOW_WF_DQ_MODEL = 'FLOW_WF_DQ_MODEL',  
747 - FLOW_WF_RECORD = 'FLOW_WF_RECORD',  
748 -  
749 - //参数专用  
750 - OBJECT = 'OBJECT',  
751 - ARRAY = 'ARRAY',  
752 -  
753 - // 组合文本,用于文本和变量组合  
754 - COMBINED_TEXT = 'COMBINED_TEXT',  
755 - // 时间  
756 - TIME = 'TIME',  
757 -}  
758 -  
759 export interface QxQueryProps { 697 export interface QxQueryProps {
760 paramMappings?: ParamMappingProps[]; 698 paramMappings?: ParamMappingProps[];
761 sqlType?: SqlTypeProps; 699 sqlType?: SqlTypeProps;
@@ -855,7 +793,11 @@ export const getOperationsType = (data: any) => { @@ -855,7 +793,11 @@ export const getOperationsType = (data: any) => {
855 fileTypeTem = fieldType; 793 fileTypeTem = fieldType;
856 } else if (fieldType === FIELD_TYPE_PROPS.REL_FIELD) { 794 } else if (fieldType === FIELD_TYPE_PROPS.REL_FIELD) {
857 fileTypeTem = data.refType; 795 fileTypeTem = data.refType;
858 - } else if (fieldType === 'HOUR_MIN' || fieldType === 'HOUR_SEC' || fieldType === 'TIME') { 796 + } else if (
  797 + fieldType === 'HOUR_MIN' ||
  798 + fieldType === 'HOUR_SEC' ||
  799 + fieldType === 'TIME'
  800 + ) {
859 fileTypeTem = FIELD_TYPE_PROPS.TIME; 801 fileTypeTem = FIELD_TYPE_PROPS.TIME;
860 } else { 802 } else {
861 fileTypeTem = FIELD_TYPE_PROPS.TEXT; 803 fileTypeTem = FIELD_TYPE_PROPS.TEXT;
  1 +import React, { useEffect, useState } from 'react';
  2 +
  3 +import { FunctionProps } from '../index';
  4 +
  5 +type DescProps = {
  6 + scriptEdit: boolean;
  7 + funcData: FunctionProps | undefined | null;
  8 +};
  9 +
  10 +const highLightFunc = (str: string | undefined, func: string) => {
  11 + if (!str) return '';
  12 + const startIndex = str.indexOf(func);
  13 + const endIndex = startIndex + func.length;
  14 + if (startIndex > -1) {
  15 + return (
  16 + <>
  17 + <span>{str.slice(0, startIndex)}</span>
  18 + <span className="highlight">{func}</span>
  19 + <span>{str.slice(endIndex)}</span>
  20 + </>
  21 + );
  22 + } else {
  23 + return str;
  24 + }
  25 +};
  26 +
  27 +const DescBox: React.FC<DescProps> = ({ scriptEdit, funcData }) => {
  28 + const defaulTips = () => {
  29 + return (
  30 + <ul className={'fx-ul'}>
  31 + <li className="line">从左侧面板选择函数和变量,或输入函数</li>
  32 + <li className="line">公式编辑示例:PRODUCT(单价, 数量)</li>
  33 + <li
  34 + className="line"
  35 + dangerouslySetInnerHTML={{
  36 + __html:
  37 + '支持运算符:加(+)、减(-)、乘(*)、除(/)、大于(>)、小于(<)、等于(==)、不等于(!=)、大于等于(>=)、小于等于(<=)',
  38 + }}
  39 + ></li>
  40 + <li className="line">支持用"."来获取对象/对象数组的属性</li>
  41 + </ul>
  42 + );
  43 + };
  44 +
  45 + const scriptTips = () => {
  46 + return (
  47 + <ul className={'fx-ul'}>
  48 + <li className="line">
  49 + 支持JavaScript ES5标准语法,请在函数头部定义变量接受字段动态值
  50 + </li>
  51 + <li className="line">脚本需要有‘return’ 关键字显式的返回数据类型</li>
  52 + <li className="line">每行最大支持1000字符,最多支持2000行</li>
  53 + </ul>
  54 + );
  55 + };
  56 +
  57 + const fxTips = () => {
  58 + const { title, desc, formula, returnType, demon, funcNameEg } =
  59 + funcData || {};
  60 + return (
  61 + <>
  62 + <div className="line">
  63 + <span style={{ fontWeight: 600 }}>描述</span>:
  64 + <span className="highlight">{title}</span>
  65 + {desc}
  66 + </div>
  67 + <div className="line">
  68 + <span style={{ fontWeight: 600 }}>表达式</span>:
  69 + {highLightFunc(formula, funcNameEg)}
  70 + </div>
  71 + <div className="line">
  72 + <span style={{ fontWeight: 600 }}>返回值类型</span>:{returnType}
  73 + </div>
  74 + <div className="line">
  75 + <span style={{ fontWeight: 600 }}>示例</span>:
  76 + {highLightFunc(demon, funcNameEg)}
  77 + </div>
  78 + </>
  79 + );
  80 + };
  81 +
  82 + const [tips, setTips] = useState<any>(null);
  83 + useEffect(() => {
  84 + let tips = !!funcData ? fxTips() : scriptEdit ? scriptTips() : defaulTips();
  85 + setTips(tips);
  86 + }, [scriptEdit, funcData]);
  87 +
  88 + return (
  89 + <div className="describle-box">
  90 + <div className="title-bar">{funcData?.title}</div>
  91 + <div className="desc-content">{tips}</div>
  92 + </div>
  93 + );
  94 +};
  95 +
  96 +export default DescBox;
  1 +import { DownOutlined, SearchOutlined } from '@ant-design/icons';
  2 +import { Empty, Input, Tree } from 'antd';
  3 +import _ from 'lodash-es';
  4 +import React, {
  5 + memo,
  6 + useEffect,
  7 + useLayoutEffect,
  8 + useMemo,
  9 + useRef,
  10 + useState,
  11 +} from 'react';
  12 +import { FunctionProps } from '../index';
  13 +import emptyImg from '../svg/custom_chart_null.png';
  14 +import { handleHighlight } from './var-picker';
  15 +type PickProps = {
  16 + dataSource: FunctionProps[];
  17 + onHover: (nodeData: FunctionProps | null) => void;
  18 + onPick: (funcData: FunctionProps) => void;
  19 + pickFunc: (funcData: FunctionProps) => void;
  20 +};
  21 +const FxPicker: React.FC<PickProps> = ({
  22 + onHover,
  23 + onPick,
  24 + pickFunc,
  25 + ...props
  26 +}): JSX.Element => {
  27 + const [searchVal, setSearchVal] = useState('');
  28 + const [selectIndex, setSelectIndex] = useState(0);
  29 + const searchInputRef = useRef(null);
  30 + const [filterData, setFilterData] = useState<FunctionProps[]>([]);
  31 + // const selectFunc = useRef<string>('');
  32 + // const change
  33 +
  34 + const dataSource: FunctionProps[] = useMemo(() => {
  35 + let cloneData = _.cloneDeep(props.dataSource || []);
  36 + cloneData.forEach((item) => {
  37 + (item.children || []).forEach((it: any) => {
  38 + // @ts-ignore
  39 + if (!it.titleCopy && typeof it.title === 'string') {
  40 + it.titleCopy = it.title;
  41 + }
  42 + // @ts-ignore
  43 + it.title = <span style={{ display: 'inline-block' }}>{it.title}</span>;
  44 + });
  45 + });
  46 + return cloneData;
  47 + }, [props.dataSource]);
  48 +
  49 + useEffect(() => {
  50 + let cloneData = _.cloneDeep(dataSource);
  51 + cloneData.forEach((item: any) => {
  52 + if (item.title?.includes(searchVal)) {
  53 + item.title = handleHighlight(item.title, searchVal);
  54 + item._hidden = false;
  55 + } else {
  56 + if ((item.children || []).length === 0) {
  57 + item._hidden = true;
  58 + }
  59 + (item.children || []).forEach((it: any) => {
  60 + if (!it.funcNameEg.toLowerCase()?.includes(searchVal.toLowerCase())) {
  61 + it._hidden = true;
  62 + item.hiddenLen = (item.hiddenLen || 0) + 1;
  63 + if (item.hiddenLen === item.children?.length) {
  64 + item._hidden = true;
  65 + }
  66 + } else {
  67 + const title = handleHighlight(it.titleCopy, searchVal, 'fx');
  68 + it.title = <span style={{ display: 'inline-block' }}>{title}</span>;
  69 + }
  70 + });
  71 + }
  72 + });
  73 + cloneData = cloneData.filter((item: any) => item._hidden !== true);
  74 + cloneData.forEach((item) => {
  75 + item.children = (item.children || []).filter((it: any) => !it._hidden);
  76 + });
  77 + setFilterData(cloneData);
  78 + }, [searchVal]);
  79 +
  80 + // 根据查询value过滤公式列表数据
  81 + // const dataSourceFilter: FunctionProps[] = useMemo(() => {
  82 + // // 扁平化dataSource
  83 + // const dataSourceFlatten: FunctionProps[] = [];
  84 + // flatten(dataSource, dataSourceFlatten);
  85 + // return dataSourceFlatten.filter((ds) => ds.title.includes(searchVal));
  86 + // }, [searchVal, dataSource]);
  87 +
  88 + const handleSearch = _.debounce((val: string) => {
  89 + setSelectIndex(0);
  90 + setSearchVal(val);
  91 + }, 200);
  92 +
  93 + // 搜索框值变化时触发
  94 + const onHandleSearchChange = (e: any) => {
  95 + const val = e.target.value;
  96 + handleSearch(val);
  97 + };
  98 +
  99 + // 设置当前选中的函数公式
  100 + // const onHandlePickFunc = (funcData: FunctionProps) => {
  101 + // _.isFunction(onPick) && onPick(funcData);
  102 + // searchInputRef?.current?.blur();
  103 + // };
  104 + /**
  105 + * 设置键盘事件逻辑
  106 + * keyCode 38:ArrowUp 40:ArrowDown 13: Enter
  107 + */
  108 + // const onHandleKeydown = (e: any) => {
  109 + // if (e.keyCode == 38 && selectIndex > 0) {
  110 + // setSelectIndex(selectIndex - 1);
  111 + // } else if (e.keyCode == 40 && selectIndex < dataSourceFilter.length - 1) {
  112 + // setSelectIndex(selectIndex + 1);
  113 + // } else if (e.keyCode == 13) {
  114 + // onHandlePickFunc(dataSourceFilter[selectIndex]);
  115 + // }
  116 + // _.isFunction(onHover) && onHover(dataSourceFilter[selectIndex]);
  117 + // };
  118 +
  119 + // 设置popover-content内滚动条位置。DOM更新之后立即执行
  120 + useLayoutEffect(() => {
  121 + const dom = document.getElementsByClassName('fun-selected')[0];
  122 + if (dom) {
  123 + dom.scrollIntoView(Object.assign({ block: 'center', inline: 'nearest' }));
  124 + }
  125 + }, [selectIndex]);
  126 +
  127 + // const onHandleUpload = () => {
  128 + // message.info('功能暂未开发');
  129 + // };
  130 +
  131 + // const onHandleCustomFunc = () => {
  132 + // message.info('功能暂未开发');
  133 + // };
  134 +
  135 + const onHandleMouseEnter = (event: any, nodeData: FunctionProps) => {
  136 + if (nodeData.children && nodeData.children.length > 0) return;
  137 + if (_.isFunction(onHover)) {
  138 + onHover(nodeData);
  139 + }
  140 + };
  141 +
  142 + const onHandleSelectTNode = (key: any, event: any) => {
  143 + if (event.node.children && event.node.children.length > 0) return;
  144 + if (_.isFunction(onPick)) {
  145 + onPick(event.node);
  146 + }
  147 + if (_.isFunction(onHover)) {
  148 + onHover(event.node);
  149 + }
  150 + pickFunc(event.node);
  151 + // selectFunc.current = event.node.funcNameEg;
  152 + };
  153 +
  154 + // 设置popover-content
  155 + // const searchContent = useMemo(() => {
  156 + // return (
  157 + // <div className="search-content">
  158 + // {dataSourceFilter.map((ds, index) => {
  159 + // return (
  160 + // <div
  161 + // className={index === selectIndex ? 'fun-selected' : ''}
  162 + // key={ds.key}
  163 + // onClick={() => onHandlePickFunc(ds)}
  164 + // >
  165 + // {ds.title}
  166 + // </div>
  167 + // );
  168 + // })}
  169 + // </div>
  170 + // );
  171 + // }, [dataSourceFilter, selectIndex]);
  172 +
  173 + const treeRender = useMemo(() => {
  174 + return searchVal.length && !filterData?.length ? (
  175 + <Empty
  176 + image={emptyImg}
  177 + style={{
  178 + height: '100%',
  179 + width: '100%',
  180 + display: 'flex',
  181 + flexDirection: 'column',
  182 + justifyContent: 'center',
  183 + color: '#999',
  184 + }}
  185 + />
  186 + ) : (
  187 + <Tree
  188 + key={Math.random()}
  189 + rootClassName={'custom-tree'}
  190 + blockNode={true}
  191 + selectable={true}
  192 + defaultExpandAll={!!searchVal.length}
  193 + switcherIcon={<DownOutlined />}
  194 + titleRender={(nodeData) => {
  195 + const { title, titleDesc } = nodeData;
  196 + return (
  197 + <div
  198 + className="custom-title"
  199 + onMouseEnter={(event) => {
  200 + onHandleMouseEnter(event, nodeData);
  201 + }}
  202 + onMouseLeave={() => {
  203 + // if (nodeData.funcNameEg === selectFunc.current) {
  204 + // selectFunc.current = '';
  205 + // return;
  206 + // }
  207 + if (_.isFunction(onHover)) {
  208 + onHover(null);
  209 + }
  210 + }}
  211 + >
  212 + <span>{title}</span>
  213 + <span>{titleDesc}</span>
  214 + </div>
  215 + );
  216 + }}
  217 + treeData={searchVal.length ? filterData : dataSource}
  218 + defaultExpandedKeys={['COMMON']}
  219 + onSelect={onHandleSelectTNode}
  220 + />
  221 + );
  222 + }, [dataSource, filterData]);
  223 +
  224 + return (
  225 + <div className="function-box">
  226 + <div className="search-bar">
  227 + {/*<Popover*/}
  228 + {/* overlayClassName={'custom-search-popover'}*/}
  229 + {/* placement="bottom"*/}
  230 + {/* content={searchContent}*/}
  231 + {/* trigger={'focus'}*/}
  232 + {/* autoAdjustOverflow={false}*/}
  233 + {/* getPopupContainer={(HTMLElement) => HTMLElement.parentNode}*/}
  234 + {/*>*/}
  235 + <Input
  236 + bordered={false}
  237 + ref={searchInputRef}
  238 + onChange={onHandleSearchChange}
  239 + prefix={<SearchOutlined className="search" />}
  240 + // addonAfter={
  241 + // <>
  242 + // <UploadOutlined className="upload" onClick={onHandleUpload} />
  243 + // <PlusOutlined className="plus" onClick={onHandleCustomFunc} />
  244 + // </>
  245 + // }
  246 + placeholder="搜索函数"
  247 + />
  248 + {/*</Popover>*/}
  249 + </div>
  250 + {dataSource?.length ? (
  251 + treeRender
  252 + ) : (
  253 + <div className={'qx-empty-center'}>(暂无数据)</div>
  254 + )}
  255 + {/*{_.size(dataSource) === 0 && (*/}
  256 + {/* <div className={'qx-empty-center'}>(暂无数据)</div>*/}
  257 + {/*)}*/}
  258 + </div>
  259 + );
  260 +};
  261 +
  262 +export default memo(FxPicker);
  1 +import { DownOutlined, SearchOutlined } from '@ant-design/icons';
  2 +import { getWidgetsIcon } from '@qx/utils';
  3 +import { Empty, Input, Tooltip, Tree } from 'antd';
  4 +import _ from 'lodash-es';
  5 +import React, {
  6 + memo,
  7 + useEffect,
  8 + useLayoutEffect,
  9 + useMemo,
  10 + useRef,
  11 + useState,
  12 +} from 'react';
  13 +import { ColsTreeSelectProps } from '../index';
  14 +import emptyImg from '../svg/custom_chart_null.png';
  15 +import { widgetMapping } from '../util';
  16 +
  17 +type VarProps = {
  18 + dataSource: ColsTreeSelectProps[];
  19 + onPick: (varData: ColsTreeSelectProps) => void;
  20 +};
  21 +
  22 +const IconMap: any = {
  23 + CUR_USER: 'qx-f-icon-user',
  24 + CUR_ORG: 'qx-f-icon-org',
  25 + CUR_CORP: 'qx-f-icon-corp',
  26 + LOGIN_INFO: 'qx-f-icon-loginInfo',
  27 + CUR_SERVER: 'qx-f-icon-server',
  28 +};
  29 +
  30 +export const handleHighlight = (title: string, val: string, type?: string) => {
  31 + if (!title) return null;
  32 + let index: number;
  33 + if (type === 'fx') {
  34 + index = title.toLowerCase()?.indexOf(val.toLowerCase());
  35 + } else {
  36 + index = title.indexOf(val);
  37 + }
  38 + return (
  39 + <span>
  40 + {title.slice(0, index)}
  41 + <span className={'tree-node-high'}>
  42 + {title.slice(index, index + val.length)}
  43 + </span>
  44 + {title.slice(index + (val?.length || 0))}
  45 + </span>
  46 + );
  47 +};
  48 +
  49 +const VarPicker: React.FC<VarProps> = ({ onPick, ...props }) => {
  50 + const [searchVal, setSearchVal] = useState('');
  51 + const [selectIndex, setSelectIndex] = useState(0);
  52 + const searchInputRef = useRef(null);
  53 + const [filterData, setFilterData] = useState<ColsTreeSelectProps[]>([]);
  54 +
  55 + const dataSource: ColsTreeSelectProps[] = useMemo(() => {
  56 + let cloneData = _.cloneDeep(props.dataSource || []);
  57 + cloneData.forEach((item) => {
  58 + (item.children || []).forEach((it) => {
  59 + // @ts-ignore
  60 + it.title = <span style={{ display: 'inline-block' }}>{it.title}</span>;
  61 + });
  62 + });
  63 + return cloneData;
  64 + }, [props.dataSource]);
  65 +
  66 + useEffect(() => {
  67 + let cloneData = _.cloneDeep(dataSource);
  68 + cloneData.forEach((item: any) => {
  69 + if (item.titleStr?.includes(searchVal)) {
  70 + item.titleStr = handleHighlight(item.titleStr, searchVal);
  71 + item._hidden = false;
  72 + } else {
  73 + (item.children || []).forEach((it: any) => {
  74 + if (!it.titleStr?.includes(searchVal)) {
  75 + it._hidden = true;
  76 + item.hiddenLen = (item.hiddenLen || 0) + 1;
  77 + if (item.hiddenLen === item.children?.length) {
  78 + item._hidden = true;
  79 + }
  80 + } else {
  81 + it.titleStr = handleHighlight(it.titleStr, searchVal);
  82 + }
  83 + });
  84 + }
  85 + });
  86 + cloneData = cloneData.filter((item: any) => item._hidden !== true);
  87 + cloneData.forEach((item) => {
  88 + item.children = (item.children || []).filter((it: any) => !it._hidden);
  89 + });
  90 + setFilterData(cloneData);
  91 + }, [searchVal]);
  92 +
  93 + // 根据查询value过滤公式列表数据
  94 + // const dataSourceFilter: ColsTreeSelectProps[] = useMemo(() => {
  95 + // // 扁平化dataSource
  96 + // const dataSourceFlatten: ColsTreeSelectProps[] = [];
  97 + // flatten(dataSource, dataSourceFlatten);
  98 + // return dataSourceFlatten.filter((ds) => ds.titleStr?.includes(searchVal));
  99 + // }, [searchVal, dataSource]);
  100 +
  101 + const handleSearch = _.debounce((val: string) => {
  102 + setSelectIndex(0);
  103 + setSearchVal(val.trim());
  104 + }, 200);
  105 +
  106 + // 搜索框值变化时触发
  107 + const onHandleSearchChange = (e: any) => {
  108 + const val = e.target.value;
  109 + handleSearch(val);
  110 + };
  111 +
  112 + // 设置当前选中的变量
  113 + const onHandlePickVar = (varData: ColsTreeSelectProps) => {
  114 + _.isFunction(onPick) && onPick(varData);
  115 + searchInputRef?.current?.blur();
  116 + };
  117 + /**
  118 + * 设置键盘事件逻辑
  119 + * keyCode 38:ArrowUp 40:ArrowDown 13: Enter
  120 + */
  121 + // const onHandleKeydown = (e: any) => {
  122 + // if (e.keyCode == 38 && selectIndex > 0) {
  123 + // setSelectIndex(selectIndex - 1);
  124 + // } else if (e.keyCode == 40 && selectIndex < dataSourceFilter.length - 1) {
  125 + // setSelectIndex(selectIndex + 1);
  126 + // } else if (e.keyCode == 13) {
  127 + // onHandlePickVar(dataSourceFilter[selectIndex]);
  128 + // }
  129 + // };
  130 +
  131 + // 设置popover-content内滚动条位置。DOM更新之后立即执行
  132 + useLayoutEffect(() => {
  133 + const dom = document.getElementsByClassName('var-selected')[0];
  134 + dom &&
  135 + dom.scrollIntoView(Object.assign({ block: 'center', inline: 'nearest' }));
  136 + }, [selectIndex]);
  137 +
  138 + const onHandleSelectTNode: any = (key: string, event: any) => {
  139 + if (event.node.children && event.node.children.length > 0) return;
  140 + _.isFunction(onPick) && onPick(event.node);
  141 + };
  142 +
  143 + // 设置popover-content
  144 + // const searchContent = useMemo(() => {
  145 + // return (
  146 + // <div className="search-content">
  147 + // {dataSourceFilter.map((ds, index) => {
  148 + // return (
  149 + // <div
  150 + // className={index === selectIndex ? 'var-selected' : ''}
  151 + // key={ds.key}
  152 + // onClick={() => onHandlePickVar(ds)}
  153 + // >
  154 + // {ds.titleStr}
  155 + // </div>
  156 + // );
  157 + // })}
  158 + // </div>
  159 + // );
  160 + // }, [dataSourceFilter, selectIndex]);
  161 +
  162 + const treeRender = useMemo(() => {
  163 + return searchVal.length && !filterData?.length ? (
  164 + <Empty
  165 + image={emptyImg}
  166 + style={{
  167 + height: '100%',
  168 + width: '100%',
  169 + display: 'flex',
  170 + flexDirection: 'column',
  171 + justifyContent: 'center',
  172 + color: '#999',
  173 + }}
  174 + />
  175 + ) : (
  176 + <Tree
  177 + key={Math.random()}
  178 + rootClassName={'custom-tree'}
  179 + blockNode={true}
  180 + selectable={true}
  181 + switcherIcon={<DownOutlined />}
  182 + defaultExpandAll={!!searchVal.length}
  183 + titleRender={(nodeData: any) => {
  184 + const { titleStr, widget } = nodeData;
  185 + const key = nodeData.key.slice(6, -1);
  186 + const { name, color, bgColor } = widgetMapping[widget] || {};
  187 + const icon = getWidgetsIcon(widget);
  188 + return (
  189 + <div className="custom-title">
  190 + <Tooltip title={titleStr} placement={'topRight'}>
  191 + <div
  192 + style={
  193 + IconMap[key]
  194 + ? { display: 'flex', alignItems: 'center' }
  195 + : {}
  196 + }
  197 + >
  198 + {IconMap[key] ? (
  199 + <span className={`qx-f-icon-common ${IconMap[key]}`} />
  200 + ) : widget ? (
  201 + <span className="icon">{icon}</span>
  202 + ) : null}
  203 + <span>{titleStr}</span>
  204 + </div>
  205 + </Tooltip>
  206 + {widget && (
  207 + <span
  208 + className="tag"
  209 + style={{ backgroundColor: bgColor, color: color }}
  210 + >
  211 + {name}
  212 + </span>
  213 + )}
  214 + </div>
  215 + );
  216 + }}
  217 + treeData={searchVal.length ? filterData : dataSource}
  218 + defaultExpandedKeys={['FORM']}
  219 + onSelect={onHandleSelectTNode}
  220 + />
  221 + );
  222 + }, [dataSource, filterData]);
  223 +
  224 + return (
  225 + <div className="variable-box">
  226 + <div className="search-bar">
  227 + {/*<Popover*/}
  228 + {/* overlayClassName={'custom-search-popover'}*/}
  229 + {/* placement="bottom"*/}
  230 + {/* content={searchContent}*/}
  231 + {/* trigger={'focus'}*/}
  232 + {/* autoAdjustOverflow={false}*/}
  233 + {/* getPopupContainer={(HTMLElement) => HTMLElement.parentNode}*/}
  234 + {/*>*/}
  235 + <Input
  236 + bordered={false}
  237 + ref={searchInputRef}
  238 + onChange={onHandleSearchChange}
  239 + prefix={<SearchOutlined className="search" />}
  240 + placeholder="搜索变量"
  241 + />
  242 + {/*</Popover>*/}
  243 + </div>
  244 + {dataSource?.length ? (
  245 + treeRender
  246 + ) : (
  247 + <div className={'qx-empty-center'}>(暂无数据)</div>
  248 + )}
  249 + {/*{_.size(dataSource) === 0 && (*/}
  250 + {/* <div className={'qx-empty-center'}>(暂无数据)</div>*/}
  251 + {/*)}*/}
  252 + </div>
  253 + );
  254 +};
  255 +
  256 +export default memo(VarPicker);
  1 +@import '~@qx/ui/src/style/variable.less';
  2 +
  3 +.fx-wrapper {
  4 + .ant-modal-content {
  5 + border-radius: 8px;
  6 + background-color: #ffffff;
  7 + padding: 0 24px;
  8 +
  9 + .ant-modal-close,
  10 + .ant-modal-header,
  11 + .ant-modal-footer {
  12 + display: none;
  13 + }
  14 + .ant-modal-body {
  15 + position: relative;
  16 + height: 600px;
  17 + padding: 0 0 48px;
  18 +
  19 + .header {
  20 + padding: 24px 0 12px;
  21 + height: 60px;
  22 + line-height: 24px;
  23 + display: flex;
  24 + align-items: center;
  25 + justify-content: space-between;
  26 +
  27 + > .header-item {
  28 + height: 100%;
  29 + display: flex;
  30 + align-items: center;
  31 + }
  32 + // 左侧
  33 + .title {
  34 + font-size: 16px;
  35 + font-weight: 700;
  36 + color: #242835;
  37 + padding-right: 8px;
  38 + }
  39 + .manual {
  40 + display: flex;
  41 + align-items: center;
  42 + color: @N8;
  43 + cursor: pointer;
  44 + font-size: 12px;
  45 + .qx-f-icon-help {
  46 + margin-right: 4px;
  47 + cursor: pointer;
  48 + height: 16px;
  49 + width: 16px;
  50 + path {
  51 + fill: @N6;
  52 + }
  53 + }
  54 +
  55 + &:hover {
  56 + color: @B8;
  57 + .qx-f-icon-help {
  58 + path {
  59 + fill: @B8;
  60 + }
  61 + }
  62 + }
  63 + }
  64 + // 右侧
  65 + .icon-right {
  66 + cursor: pointer;
  67 + width: 16px;
  68 + height: 16px;
  69 + color: @N7;
  70 + &.expand {
  71 + margin-right: 12px;
  72 + }
  73 + }
  74 + }
  75 + .qx-operation {
  76 + height: calc(100% - 60px);
  77 + overflow: auto;
  78 + border: 1px solid #e9e9ea;
  79 + border-radius: 4px;
  80 + //display: flex;
  81 + //flex-direction: column;
  82 +
  83 + .toolbar {
  84 + display: flex;
  85 + height: 40px;
  86 + border-radius: 4px 4px 0px 0px;
  87 + background-color: #f4f4f5;
  88 + justify-content: space-between;
  89 + padding: 8px 12px;
  90 + line-height: 24px;
  91 +
  92 + .field-name {
  93 + font-size: 14px;
  94 + font-weight: 700;
  95 + color: #242835;
  96 + }
  97 + .tools {
  98 + display: flex;
  99 + font-size: 14px;
  100 + font-weight: 400;
  101 + color: #242835;
  102 +
  103 + .tool-item {
  104 + position: relative;
  105 + cursor: pointer;
  106 + display: flex;
  107 + height: 24px;
  108 + align-items: center;
  109 + margin-left: 16px;
  110 + &:not(:last-child) {
  111 + width: 56px;
  112 + display: flex;
  113 + align-items: center;
  114 + justify-content: center;
  115 + &:hover {
  116 + border-radius: 2px;
  117 + background-color: @N4;
  118 + }
  119 + .qx-f-icon-beautify,
  120 + .qx-f-icon-copy {
  121 + width: 16px;
  122 + height: 16px;
  123 + margin-right: 4px;
  124 + }
  125 + .qx-f-icon-beautify {
  126 + background: url('./svg/beautify.svg') no-repeat;
  127 + }
  128 + .qx-f-icon-copy {
  129 + background: url('./svg/copy.svg') no-repeat;
  130 + }
  131 + }
  132 + &:last-child {
  133 + button.ant-switch {
  134 + bottom: 0;
  135 + position: relative;
  136 + line-height: 24px;
  137 + margin-right: 4px;
  138 + &:after {
  139 + position: absolute;
  140 + width: 60px;
  141 + height: 16px;
  142 + left: 28px;
  143 + top: 0;
  144 + content: '';
  145 + z-index: 99;
  146 + }
  147 + }
  148 + }
  149 + }
  150 + }
  151 + }
  152 + .editor-box {
  153 + position: relative;
  154 + height: 200px;
  155 + min-height: 200px;
  156 + max-height: 452px;
  157 + padding-top: 8px;
  158 + overflow: hidden;
  159 +
  160 + .drag-bar {
  161 + position: absolute;
  162 + right: 0;
  163 + bottom: 0;
  164 + height: 12px;
  165 + width: 12px;
  166 + cursor: ns-resize;
  167 +
  168 + &::before {
  169 + content: '';
  170 + position: absolute;
  171 + left: 50%;
  172 + top: 0;
  173 + bottom: 0;
  174 + width: 1px;
  175 + background-color: #86909c;
  176 + transform: rotateZ(45deg);
  177 + }
  178 + &::after {
  179 + content: '';
  180 + position: absolute;
  181 + left: 75%;
  182 + top: 50%;
  183 + bottom: 0;
  184 + width: 1px;
  185 + background-color: #86909c;
  186 + transform: rotateZ(45deg);
  187 + }
  188 + }
  189 + .qx-formula-cm {
  190 + max-height: 100%;
  191 + padding: 0 8px 36px 8px;
  192 + overflow: auto;
  193 + }
  194 + .error-zone {
  195 + position: absolute;
  196 + bottom: -36px;
  197 + left: 0;
  198 + right: 0;
  199 + height: 36px;
  200 + line-height: 36px;
  201 + text-align: center;
  202 + font-size: 14px;
  203 + font-weight: 400;
  204 + color: #e34d59;
  205 + background-color: #fdf2f3;
  206 + transition: bottom 0.2s linear;
  207 +
  208 + &.show {
  209 + bottom: 0;
  210 + }
  211 + }
  212 + .cm-s-idea .CodeMirror-activeline-background {
  213 + background-color: #fff;
  214 + }
  215 + }
  216 + .custom {
  217 + display: flex;
  218 + height: calc(100% - 240px);
  219 +
  220 + .function-box,
  221 + .variable-box,
  222 + .describle-box {
  223 + position: relative;
  224 + width: 30%;
  225 + padding-top: 32px;
  226 + border-top: 1px solid #e9e9ea;
  227 + border-right: 1px solid #e9e9ea;
  228 +
  229 + &::before {
  230 + content: '';
  231 + position: absolute;
  232 + top: 32px;
  233 + left: 0;
  234 + right: 0;
  235 + height: 1px;
  236 + background-color: #e9e9ea;
  237 + }
  238 +
  239 + .search-bar {
  240 + position: absolute;
  241 + top: 0;
  242 + left: 0;
  243 + height: 32px;
  244 + width: 100%;
  245 + padding: 0 12px;
  246 + .ant-input-prefix {
  247 + margin-right: 8px;
  248 + }
  249 +
  250 + .search {
  251 + color: #7c7e86;
  252 + width: 14px;
  253 + height: 14px;
  254 + }
  255 +
  256 + .custom-search-popover {
  257 + width: 100%;
  258 + padding-top: 0;
  259 +
  260 + .ant-popover-arrow {
  261 + display: none;
  262 + }
  263 + .ant-popover-inner-content {
  264 + padding: 6px 0;
  265 + height: 215px;
  266 + overflow: auto;
  267 + }
  268 + .search-content {
  269 + height: 100%;
  270 + div {
  271 + height: 32px;
  272 + padding: 0 12px;
  273 + line-height: 32px;
  274 + cursor: pointer;
  275 +
  276 + &:hover,
  277 + &.fun-selected,
  278 + &.var-selected {
  279 + background-color: #f4f4f5;
  280 + }
  281 + }
  282 + }
  283 + }
  284 + }
  285 + .custom-tree {
  286 + height: 100%;
  287 + overflow: auto;
  288 +
  289 + .tree-node-high {
  290 + color: @B8;
  291 + }
  292 +
  293 + .ant-tree-treenode {
  294 + padding: 0 4px;
  295 +
  296 + &:hover {
  297 + background-color: #f4f4f5;
  298 + }
  299 +
  300 + .ant-tree-node-selected {
  301 + background-color: transparent;
  302 + }
  303 + }
  304 + .ant-tree-switcher {
  305 + line-height: 36px;
  306 + position: relative;
  307 + .anticon {
  308 + color: @N9 !important;
  309 + }
  310 + &:after {
  311 + content: '';
  312 + position: absolute;
  313 + max-width: 200px;
  314 + min-width: 170px;
  315 + height: 36px;
  316 + background: transparent;
  317 + top: 0;
  318 + left: 24px;
  319 + z-index: 999;
  320 + }
  321 + }
  322 + .ant-tree-switcher-noop {
  323 + display: none;
  324 + }
  325 + .custom-title {
  326 + display: flex;
  327 + height: 36px;
  328 + padding: 7px 0;
  329 + line-height: 22px;
  330 + justify-content: space-between;
  331 + }
  332 + }
  333 + }
  334 + .function-box {
  335 + .search-bar {
  336 + padding: 0;
  337 + .upload {
  338 + height: 16px;
  339 + width: 16px;
  340 + margin-right: 12px;
  341 + cursor: pointer;
  342 + }
  343 + .plus {
  344 + height: 16px;
  345 + width: 16px;
  346 + color: #1764ff;
  347 + cursor: pointer;
  348 + }
  349 + .ant-input-group-addon {
  350 + background-color: transparent;
  351 + border: none;
  352 + }
  353 + }
  354 + .custom-tree {
  355 + .ant-tree-node-content-wrapper {
  356 + &:hover {
  357 + background-color: unset;
  358 + }
  359 + }
  360 + }
  361 + .custom-title {
  362 + span {
  363 + &:not(:first-child) {
  364 + color: @N7;
  365 + }
  366 + }
  367 + }
  368 + }
  369 + .variable-box {
  370 + .search-bar {
  371 + padding: 0;
  372 + }
  373 + .custom-tree {
  374 + .ant-tree-node-content-wrapper {
  375 + flex: 1;
  376 + overflow: hidden;
  377 + &:hover {
  378 + background-color: unset;
  379 + }
  380 + }
  381 + .custom-title {
  382 + div {
  383 + flex: 1;
  384 + max-width: 129px;
  385 + overflow: hidden;
  386 + white-space: nowrap;
  387 + text-overflow: ellipsis;
  388 + //.custom-title_left {
  389 + // width: 100%;
  390 + // overflow: hidden;
  391 + // white-space: nowrap;
  392 + // text-overflow: ellipsis;
  393 + //}
  394 + }
  395 + .icon {
  396 + padding-right: 4px;
  397 + }
  398 + .tag {
  399 + padding: 0 8px;
  400 + border-radius: 4px;
  401 + background-color: #e7efff;
  402 + font-size: 14px;
  403 + color: #1764ff;
  404 + white-space: nowrap;
  405 + margin-left: 10px;
  406 + }
  407 + }
  408 + }
  409 + }
  410 + .describle-box {
  411 + width: 40%;
  412 + border-right: none;
  413 +
  414 + .title-bar {
  415 + position: absolute;
  416 + top: 0;
  417 + left: 0;
  418 + height: 32px;
  419 + width: 100%;
  420 + padding: 0 12px;
  421 + font-size: 14px;
  422 + font-weight: 400;
  423 + line-height: 32px;
  424 + color: #242835;
  425 + }
  426 + .desc-content {
  427 + height: 100%;
  428 + overflow: auto;
  429 + padding: 8px 12px;
  430 + .fx-ul {
  431 + list-style: disc !important;
  432 + padding-inline-start: 20px;
  433 + }
  434 +
  435 + .line {
  436 + font-size: 14px;
  437 + font-weight: 400;
  438 + line-height: 22px;
  439 + color: #50535d;
  440 +
  441 + .highlight {
  442 + color: #00a870;
  443 + }
  444 + }
  445 + }
  446 + }
  447 + }
  448 + }
  449 + .footer {
  450 + position: absolute;
  451 + bottom: 0;
  452 + left: 0;
  453 + right: 0;
  454 + height: 48px;
  455 + padding: 8px 0;
  456 + text-align: right;
  457 +
  458 + .ant-btn {
  459 + border-radius: 4px;
  460 +
  461 + &:not(:last-child) {
  462 + margin-right: 8px;
  463 + }
  464 + }
  465 + }
  466 + }
  467 + }
  468 +
  469 + &.fx-fullscreen {
  470 + .ant-modal-content {
  471 + position: fixed;
  472 + top: 0;
  473 + left: 0;
  474 + right: 0;
  475 + bottom: 0;
  476 + border-radius: 0 !important;
  477 +
  478 + .ant-modal-body {
  479 + position: relative;
  480 + height: 100%;
  481 + width: 100%;
  482 + padding: 60px 0 48px;
  483 +
  484 + .header {
  485 + position: absolute;
  486 + top: 0;
  487 + left: 0;
  488 + right: 0;
  489 + height: 60px;
  490 + }
  491 + .qx-operation {
  492 + position: relative;
  493 + height: 100%;
  494 + width: 100%;
  495 + padding: 40px 0 250px;
  496 +
  497 + .toolbar {
  498 + position: absolute;
  499 + top: 0;
  500 + left: 0;
  501 + right: 0;
  502 + height: 40px;
  503 + }
  504 + // 全屏
  505 + .editor-box {
  506 + height: 100% !important;
  507 +
  508 + .drag-bar {
  509 + display: none !important;
  510 + }
  511 + .qx-formula-cm {
  512 + height: 100%;
  513 + }
  514 + }
  515 + .custom {
  516 + position: absolute;
  517 + bottom: 0;
  518 + left: 0;
  519 + right: 0;
  520 + height: 250px !important;
  521 + }
  522 + }
  523 + .footer {
  524 + position: absolute;
  525 + bottom: 0;
  526 + left: 0;
  527 + right: 0;
  528 + height: 48px;
  529 + }
  530 + }
  531 + }
  532 + }
  533 +}
  534 +
  535 +.CodeMirror-lint-tooltip {
  536 + z-index: 9999 !important;
  537 +}
  538 +.CodeMirror-hints {
  539 + display: block !important;
  540 + z-index: 9999 !important;
  541 +
  542 + .CodeMirror-hint {
  543 + display: block !important;
  544 +
  545 + &.CodeMirror-hint-active {
  546 + }
  547 + }
  548 +}
  549 +.rel-more_modal_full.ant-modal {
  550 + .ant-modal-body {
  551 + height: 98% !important;
  552 + padding: 0 0 48px !important;
  553 + .qx-operation .editor-box {
  554 + height: 55% !important;
  555 + max-height: unset !important;
  556 + }
  557 + .qx-operation .custom {
  558 + height: calc(45% - 40px) !important;
  559 + }
  560 + }
  561 +}
  562 +
  563 +.qx-empty-center {
  564 + color: #999;
  565 + display: flex;
  566 + justify-content: center;
  567 + align-items: center;
  568 +}
  569 +
  570 +.qx-f-icon-common {
  571 + width: 24px;
  572 + height: 24px;
  573 + padding: 4px;
  574 + display: inline-block;
  575 + color: #7c7e86;
  576 +}
  577 +
  578 +.qx-f-icon-user {
  579 + background: url('./svg/people.svg') no-repeat;
  580 + background-size: 16px 16px;
  581 + background-position: 3px 3px;
  582 +}
  583 +
  584 +.qx-f-icon-corp {
  585 + background: url('./svg/company.svg') no-repeat;
  586 + background-size: 16px 16px;
  587 + background-position: 3px 3px;
  588 +}
  589 +
  590 +.qx-f-icon-loginInfo {
  591 + background: url('./svg/idcard.svg') no-repeat;
  592 + background-size: 16px 16px;
  593 + background-position: 3px 3px;
  594 +}
  595 +
  596 +.qx-f-icon-server {
  597 + background: url('./svg/storage.svg') no-repeat;
  598 + background-size: 16px 16px;
  599 + background-position: 3px 3px;
  600 +}
  601 +
  602 +.qx-f-icon-org {
  603 + background: url('./svg/department.svg') no-repeat;
  604 + background-size: 16px 16px;
  605 + background-position: 3px 3px;
  606 +}
  607 +
  608 +.hint-className {
  609 + padding: 5px 0;
  610 + font-size: 14px;
  611 +}
  1 +### 函数编辑器
  2 +
  3 +###
  4 +
  5 +```tsx
  6 +import { request, QxFunctionOperationModal } from '@qx/common';
  7 +import React, { useState, useMemo } from 'react';
  8 +import { FIELD_TYPE_PROPS } from '../constant';
  9 +
  10 +const numWidgets = ['qxNumber', 'qxMoney', 'qxPercent', 'qxFormula'];
  11 +const dateWidgets = ['qxDatetime'];
  12 +const boolWidgets = ['qxSwitch'];
  13 +
  14 +
  15 +/**
  16 + * 归类字段类型
  17 + */
  18 +const getOperationsType = (data: any) => {
  19 + if (!data) {
  20 + return undefined;
  21 + }
  22 + const fieldType = data.fieldType;
  23 + if (!fieldType) {
  24 + return FIELD_TYPE_PROPS.EMPTY;
  25 + }
  26 + let fileTypeTem: FIELD_TYPE_PROPS;
  27 + if (fieldType.startsWith(FIELD_TYPE_PROPS.USER)) {
  28 + fileTypeTem = FIELD_TYPE_PROPS.USER;
  29 + } else if (fieldType.indexOf(FIELD_TYPE_PROPS.ORG) !== -1) {
  30 + fileTypeTem = FIELD_TYPE_PROPS.ORG;
  31 + } else if (
  32 + fieldType === 'DATETIME' ||
  33 + fieldType === FIELD_TYPE_PROPS.DATE ||
  34 + fieldType === 'TIME' ||
  35 + // fieldType.startsWith(FIELD_TYPE_PROPS.HOUR) ||
  36 + fieldType.startsWith(FIELD_TYPE_PROPS.YEAR)
  37 + ) {
  38 + fileTypeTem = FIELD_TYPE_PROPS.DATE;
  39 + } else if (
  40 + fieldType === FIELD_TYPE_PROPS.DOUBLE ||
  41 + fieldType === FIELD_TYPE_PROPS.DECIMAL ||
  42 + fieldType === FIELD_TYPE_PROPS.INTEGER ||
  43 + fieldType === FIELD_TYPE_PROPS.FORMULA ||
  44 + fieldType === FIELD_TYPE_PROPS.PERCENT
  45 + ) {
  46 + fileTypeTem = FIELD_TYPE_PROPS.NUM;
  47 + } else if (
  48 + fieldType === FIELD_TYPE_PROPS.ENUM ||
  49 + fieldType === FIELD_TYPE_PROPS.ENUM_MULTI ||
  50 + fieldType === FIELD_TYPE_PROPS.TREE ||
  51 + fieldType === FIELD_TYPE_PROPS.REL ||
  52 + fieldType === FIELD_TYPE_PROPS.REL_MULTI ||
  53 + fieldType === FIELD_TYPE_PROPS.TABLE ||
  54 + fieldType === FIELD_TYPE_PROPS.FILE ||
  55 + fieldType === FIELD_TYPE_PROPS.BOOL
  56 + ) {
  57 + fileTypeTem = fieldType;
  58 + } else if (fieldType === FIELD_TYPE_PROPS.REL_FIELD) {
  59 + fileTypeTem = data.refType;
  60 + } else if (fieldType === 'HOUR_MIN' || fieldType === 'HOUR_SEC') {
  61 + fileTypeTem = FIELD_TYPE_PROPS.TIME;
  62 + } else if (fieldType === 'LOCATION') {
  63 + fileTypeTem = FIELD_TYPE_PROPS.LOCATION;
  64 + } else {
  65 + fileTypeTem = FIELD_TYPE_PROPS.TEXT;
  66 + }
  67 + return fileTypeTem;
  68 +};
  69 +
  70 +const getOperationsTypeByWidget = (widget: string, qxProps?: any, max?: number) => {
  71 + if (!widget) {
  72 + return FIELD_TYPE_PROPS.EMPTY;
  73 + }
  74 + let fileTypeTem: FIELD_TYPE_PROPS | undefined;
  75 + if (widget === 'userSelector') {
  76 + fileTypeTem = FIELD_TYPE_PROPS.USER;
  77 + } else if (widget === 'orgSelector') {
  78 + fileTypeTem = FIELD_TYPE_PROPS.ORG;
  79 + } else if (
  80 + dateWidgets.includes(widget) ||
  81 + (widget === 'qxFormula' &&
  82 + qxProps?.calculateMode === 'DATE' &&
  83 + qxProps?.calculate?.formula === 'INC_DEC')
  84 + ) {
  85 + fileTypeTem = FIELD_TYPE_PROPS.DATE;
  86 + } else if (numWidgets.includes(widget)) {
  87 + fileTypeTem = FIELD_TYPE_PROPS.NUM;
  88 + } else if (boolWidgets.includes(widget)) {
  89 + fileTypeTem = FIELD_TYPE_PROPS.BOOL;
  90 + } else if (widget === 'relField') {
  91 + fileTypeTem = getOperationsType({ fieldType: qxProps?.refFieldType || '' });
  92 + } else if (widget === 'qxTime') {
  93 + fileTypeTem = FIELD_TYPE_PROPS.TIME;
  94 + } else if (widget === 'qxLocation') {
  95 + fileTypeTem = FIELD_TYPE_PROPS.LOCATION;
  96 + } else if (widget === 'qxSelect') {
  97 + fileTypeTem = FIELD_TYPE_PROPS.ENUM;
  98 + } else if (widget === 'qxMultiSelect') {
  99 + fileTypeTem = FIELD_TYPE_PROPS.ENUM_MULTI;
  100 + } else if (widget === 'relSelector') {
  101 + if (max === 1) {
  102 + fileTypeTem = FIELD_TYPE_PROPS.REL;
  103 + } else {
  104 + fileTypeTem = FIELD_TYPE_PROPS.REL_MULTI;
  105 + }
  106 + } else if (widget === 'qxTree') {
  107 + fileTypeTem = FIELD_TYPE_PROPS.TREE;
  108 + } else if (widget === 'qxUpload' || widget === 'qxUploadImage') {
  109 + fileTypeTem = FIELD_TYPE_PROPS.FILE;
  110 + } else {
  111 + fileTypeTem = FIELD_TYPE_PROPS.TEXT;
  112 + }
  113 + // if (textWidgets.includes(widget)) {
  114 + // fileTypeTem =
  115 + // })
  116 + return fileTypeTem;
  117 +};
  118 +
  119 +
  120 +export default () => {
  121 + const [modalVisible, setModalVisible] = useState(true);
  122 +
  123 + const [props, setProps] = useState({
  124 + value: {
  125 + type: 'FORMULA',
  126 + expression: undefined,
  127 + values: undefined,
  128 + customScript: false,
  129 + },
  130 + schema: {
  131 + qxProps: {
  132 + widget: 'qxInput',
  133 + fieldName: {
  134 + title: '文本11',
  135 + },
  136 + calculateMode: '',
  137 + calculate: {
  138 + formula: '',
  139 + },
  140 + refFieldType: '', // 关联属性需要
  141 + relId: ''
  142 + }
  143 + }
  144 + });
  145 +
  146 + const formulaColsTree = useMemo(() => {
  147 + return [];
  148 + }, []);
  149 +
  150 +
  151 + const uniKey = useMemo(() => {
  152 + return '';
  153 + }, []);
  154 +
  155 + const handleFxChange = (val: string, usedFuncList?: string, scriptEdit?: boolean) => {
  156 + setProps((prev) => ({
  157 + ...prev,
  158 + value: {
  159 + type: 'FORMULA',
  160 + expression: val,
  161 + functionList: usedFuncList,
  162 + customScript: scriptEdit,
  163 + }
  164 + }));
  165 + };
  166 +
  167 + return (
  168 + <QxFunctionOperationModal
  169 + modalParams={{
  170 + visible: modalVisible,
  171 + onCancel:() => setModalVisible(false),
  172 + onOk:() => setModalVisible(false),
  173 + }}
  174 + autofocus={true}
  175 + colsTree={formulaColsTree}
  176 + value={props?.value?.type === 'FORMULA' ? (props?.value?.values || props?.value?.expression || '') : ''}
  177 + onChange={handleFxChange}
  178 + isScriptEditMode={props.value?.customScript}
  179 + fieldName={props.schema?.fieldName?.title || props?.addons?.formData?.title || ''}
  180 + uniKey={uniKey}
  181 + isInSubForm={false}
  182 + defaultSetting={{
  183 + widget: props?.schema?.qxProps?.widget,
  184 + fieldName: props.schema?.fieldName?.title || props?.addons?.formData?.title || '',
  185 + fieldGroupType: getOperationsTypeByWidget(props?.schema?.qxProps?.widget, props?.schema?.qxProps),
  186 + currentRelId: props?.schema?.qxProps?.relId,
  187 + }}
  188 + />
  189 + );
  190 +};
  191 +```
  192 +
  193 +<API id="QxFunctionOperationModal"></API>
  1 +import React, {
  2 + useCallback,
  3 + useEffect,
  4 + useImperativeHandle,
  5 + useMemo,
  6 + useRef,
  7 + useState,
  8 +} from 'react';
  9 +import './index.less';
  10 +
  11 +import {
  12 + CloseOutlined,
  13 + CompressOutlined,
  14 + ExpandOutlined,
  15 +} from '@ant-design/icons';
  16 +import { handleWindowOpen } from '@qx/utils';
  17 +import { Button, Modal, Switch } from 'antd';
  18 +import { ModalProps, TreeDataNode } from 'antd/lib';
  19 +import _ from 'lodash-es';
  20 +import { FIELD_TYPE_PROPS } from '../constant';
  21 +import CodeEditor from '../qx-code-editor';
  22 +import DescBox from './components/desc-box';
  23 +import FxPicker from './components/fx-picker';
  24 +import VarPicker from './components/var-picker';
  25 +import { getFunctionList } from './service';
  26 +import {
  27 + SYSVariable,
  28 + checkFormulaExpress,
  29 + copyText,
  30 + handleFlattenList,
  31 +} from './util';
  32 +
  33 +export interface FunctionProps {
  34 + title: string;
  35 + titleDesc?: string;
  36 + key: string;
  37 + selectable?: boolean;
  38 + icon?: any;
  39 + children?: FunctionProps[];
  40 + desc?: string;
  41 + formula?: string;
  42 + params?: any;
  43 + returnType?: string;
  44 + demon?: string;
  45 + funcNameEg: string;
  46 +}
  47 +export interface ColsTreeSelectProps extends TreeDataNode {
  48 + attrs?: { titleStr: string; fieldGroupType: FIELD_TYPE_PROPS; key: string }[];
  49 + key: string;
  50 + titleStr?: string;
  51 + iconName?: string;
  52 + widget?: string;
  53 + fieldGroupType?: FIELD_TYPE_PROPS;
  54 + children?: ColsTreeSelectProps[];
  55 +}
  56 +
  57 +interface QxFunOperationProps {
  58 + appCode: string;
  59 + funCode: string;
  60 + cRef?: any;
  61 + value: any;
  62 + onChange: (val: string, val2?: string[], val3?: boolean) => void;
  63 + colsTree: ColsTreeSelectProps[];
  64 + style?: any;
  65 + autofocus?: boolean;
  66 + fieldName: string;
  67 + fieldType: FIELD_TYPE_PROPS;
  68 + uniKey: string;
  69 + isScriptEditMode?: boolean;
  70 + isInSubForm?: boolean;
  71 + isFullScreen: boolean;
  72 +}
  73 +
  74 +/**
  75 + * 启效函数运算器
  76 + *
  77 + * @param props
  78 + * @constructor
  79 + */
  80 +export const QxFunctionOperation: React.FC<QxFunOperationProps> = ({
  81 + cRef,
  82 + value,
  83 + appCode,
  84 + funCode,
  85 + onChange,
  86 + colsTree,
  87 + autofocus,
  88 + fieldName = '',
  89 + fieldType,
  90 + isScriptEditMode = false,
  91 + uniKey,
  92 + isInSubForm,
  93 +}) => {
  94 + const editorBoxRef = useRef<any>();
  95 + const codeEditorRef = useRef<any>();
  96 +
  97 + // 变量列表
  98 + const [varDataList, setVarDataList] = useState<ColsTreeSelectProps[]>([]);
  99 + // 公式函数列表
  100 + const [funcDataList, setFuncDataList] = useState<FunctionProps[]>([]);
  101 + // 动态设置函数释义下的"当前函数"
  102 + const [funDescription, setFunDescription] = useState<FunctionProps | null>();
  103 + // 新变量(插入)
  104 + const [newVariable, setNewVariable] = useState<any>();
  105 + // 标识脚本编辑
  106 + const [scriptEdit, setScriptEdit] = useState(false);
  107 + // 错误悬浮提示
  108 + const [errorMap, setErrorMap] = useState({
  109 + show: false,
  110 + msg: '',
  111 + });
  112 + const pickedFunc = useRef<FunctionProps>(null);
  113 + const pickFunc = (data: FunctionProps) => {
  114 + // @ts-ignore
  115 + pickedFunc.current = data;
  116 + setFunDescription(data);
  117 + };
  118 +
  119 + // const onMouseDown = (event: any) => {
  120 + // let startPy = event.clientY;
  121 + // let offsetHeight = editorBoxRef?.current.offsetHeight;
  122 + // document.body.onmousemove = function (e: any) {
  123 + // const offsetY = e.clientY - startPy;
  124 + // const height = offsetHeight + offsetY;
  125 + // if (!startPy || height > 400 || height < 200) return;
  126 + // editorBoxRef?.current?.setAttribute('style', `height: ${height}px`);
  127 + // };
  128 + // document.body.onmouseup = function () {
  129 + // document.body.onmousemove = null;
  130 + // };
  131 + // };
  132 +
  133 + useEffect(() => {
  134 + if (isScriptEditMode) setScriptEdit(isScriptEditMode);
  135 + }, [isScriptEditMode]);
  136 +
  137 + const dealFunctionList = (originData: any) => {
  138 + // const titleEnum: any = {
  139 + // TEXT: '文本函数',
  140 + // MATH: '数学函数',
  141 + // DATE: '日期函数',
  142 + // LOGICAL: '逻辑函数',
  143 + // REGULAR: '正则函数',
  144 + // ADVANCED: '高级函数',
  145 + // CUSTOM: '自定义',
  146 + // COMMON: '常用函数',
  147 + // };
  148 + if (!_.isArray(originData)) {
  149 + return;
  150 + }
  151 + const funcList = originData?.map(
  152 + (item: { name: string; code: string; details?: any[] }) => {
  153 + return {
  154 + key: item.code,
  155 + title: item.name,
  156 + selectable: false,
  157 + children: (item.details || []).map((it: any) => ({
  158 + ...it,
  159 + title: it.funcNameEg,
  160 + key: it.id,
  161 + titleDesc: it.funcName,
  162 + formula: it.methodView,
  163 + desc: it.description,
  164 + returnType: it.dataType,
  165 + demon: it.executeDemo,
  166 + })),
  167 + };
  168 + },
  169 + );
  170 + setFuncDataList(funcList);
  171 + };
  172 +
  173 + useEffect(() => {
  174 + let _colsTree;
  175 + let curLevel = colsTree.filter((o) => !o.titleStr?.startsWith('【主表】'));
  176 + const mainLevel = colsTree
  177 + .filter((o) => o.titleStr?.startsWith('【主表】'))
  178 + .map((o) => ({
  179 + ...o,
  180 + titleStr: o.titleStr?.substring(4),
  181 + }));
  182 + if (isInSubForm) {
  183 + if (_.isEmpty(curLevel)) {
  184 + _colsTree = mainLevel;
  185 + } else {
  186 + _colsTree = mainLevel.concat({
  187 + key: 'SUB',
  188 + titleStr: '子表字段',
  189 + children: curLevel,
  190 + });
  191 + }
  192 + } else {
  193 + _colsTree = curLevel;
  194 + }
  195 + setVarDataList([
  196 + {
  197 + key: 'FORM',
  198 + titleStr: '表单变量',
  199 + children: _colsTree,
  200 + },
  201 + {
  202 + key: 'SYS',
  203 + titleStr: '系统变量',
  204 + children: SYSVariable,
  205 + },
  206 + ]);
  207 + }, [JSON.stringify(colsTree), SYSVariable, isInSubForm]);
  208 + useEffect(() => {
  209 + getFunctionList(appCode, funCode).then((res: any) => {
  210 + if (res) {
  211 + dealFunctionList(res);
  212 + }
  213 + });
  214 + }, []);
  215 +
  216 + const flattenVarList = useMemo(() => {
  217 + const _list: any[] = [];
  218 + handleFlattenList(varDataList || [], _list);
  219 + return _list;
  220 + }, [varDataList]);
  221 +
  222 + // 编辑器光标移动
  223 + const onHandleHoverFxChange = useCallback((data: any) => {
  224 + if (data) {
  225 + setFunDescription(data);
  226 + } else if (pickedFunc.current) {
  227 + setFunDescription(pickedFunc.current);
  228 + } else {
  229 + setFunDescription(null);
  230 + }
  231 + }, []);
  232 +
  233 + // 选中函数公式
  234 + const onHandlerPickFunc = useCallback((data: any) => {
  235 + setNewVariable({
  236 + key: data.funcNameEg,
  237 + name: data.title,
  238 + type: 'fun',
  239 + });
  240 + }, []);
  241 +
  242 + // 选中变量
  243 + const onHandlerPickVar = useCallback((data: any) => {
  244 + setNewVariable({
  245 + key: data.key,
  246 + name: data.titleStr,
  247 + });
  248 + }, []);
  249 +
  250 + useEffect(() => {
  251 + if (!funcDataList.length) return;
  252 + const { errMsg, errCode } = checkFormulaExpress(
  253 + value,
  254 + funcDataList,
  255 + varDataList,
  256 + codeEditorRef?.current?.getUsedFuncList(),
  257 + fieldType,
  258 + flattenVarList,
  259 + );
  260 + setErrorMap({
  261 + show: errCode ? true : false,
  262 + msg: errMsg,
  263 + });
  264 + }, [funcDataList]);
  265 +
  266 + // 捕获编辑器内容改变
  267 + const onHandlerEditorChange = (code: any) => {
  268 + console.log('code: ', code);
  269 + onChange(code);
  270 + if (!funcDataList.length) return;
  271 + const { errMsg, errCode } = checkFormulaExpress(
  272 + code,
  273 + funcDataList,
  274 + varDataList,
  275 + codeEditorRef?.current?.getUsedFuncList(),
  276 + fieldType,
  277 + flattenVarList,
  278 + );
  279 + setErrorMap({
  280 + show: errCode ? true : false,
  281 + msg: errMsg,
  282 + });
  283 + };
  284 +
  285 + useImperativeHandle(cRef, () => ({
  286 + scriptEdit,
  287 + getUsedFuncList() {
  288 + return codeEditorRef?.current?.getUsedFuncList();
  289 + },
  290 + }));
  291 +
  292 + return (
  293 + <div className="qx-operation">
  294 + <div className="toolbar">
  295 + <div className="field-name">{fieldName} =</div>
  296 + <div className="tools">
  297 + <div
  298 + className="tool-item"
  299 + onClick={() => {
  300 + codeEditorRef?.current?.autoFormatSelection();
  301 + }}
  302 + >
  303 + <span className="qx-f-icon-beautify" />
  304 + {/* <UngroupOutlined /> */}
  305 + <span className="text ">美化</span>
  306 + </div>
  307 + <div
  308 + className="tool-item"
  309 + onClick={() => {
  310 + const editor = codeEditorRef?.current?.getEditor();
  311 + const s_len = editor.getValue().length;
  312 + const startPos = { line: 0, ch: 0, sticky: null };
  313 + const endPos = editor.doc.posFromIndex(s_len);
  314 + editor.setSelection(startPos, endPos);
  315 + copyText(editor.getValue());
  316 + }}
  317 + >
  318 + <span className="qx-f-icon-copy" />
  319 + <span className="text ">复制</span>
  320 + </div>
  321 + <div className="tool-item">
  322 + <Switch
  323 + checked={scriptEdit}
  324 + size="small"
  325 + onChange={(checked) => {
  326 + setScriptEdit(checked);
  327 + }}
  328 + />
  329 + <span className="text">脚本编辑</span>
  330 + </div>
  331 + </div>
  332 + </div>
  333 + <div
  334 + ref={editorBoxRef}
  335 + className="editor-box"
  336 + onClick={() => {
  337 + const editor = codeEditorRef?.current?.getEditor();
  338 + editor.focus();
  339 + }}
  340 + >
  341 + <CodeEditor
  342 + cRef={codeEditorRef}
  343 + key={uniKey}
  344 + value={value}
  345 + isUseFun={true}
  346 + autofocus={autofocus}
  347 + funcDataList={funcDataList}
  348 + varDataList={varDataList}
  349 + newVariable={newVariable}
  350 + onChange={onHandlerEditorChange}
  351 + onFocusFunc={(data: any) => {
  352 + // @ts-ignore
  353 + pickedFunc.current = data;
  354 + setFunDescription(data);
  355 + }}
  356 + />
  357 + <div className={`error-zone ${errorMap.show ? 'show' : ''}`}>
  358 + {errorMap.msg}
  359 + </div>
  360 + </div>
  361 + <div className="custom">
  362 + <FxPicker
  363 + dataSource={funcDataList}
  364 + onHover={onHandleHoverFxChange}
  365 + onPick={onHandlerPickFunc}
  366 + pickFunc={pickFunc}
  367 + />
  368 + <VarPicker dataSource={varDataList} onPick={onHandlerPickVar} />
  369 + <DescBox scriptEdit={scriptEdit} funcData={funDescription} />
  370 + </div>
  371 + </div>
  372 + );
  373 +};
  374 +
  375 +interface QxFunOperationModalProps extends QxFunOperationProps {
  376 + modalParams: ModalProps;
  377 + defaultSetting?: any; // 函数公式 一个字段校验 所需参数 放置处
  378 + isInSubForm?: boolean;
  379 + appCode: string;
  380 + funCode: string;
  381 +}
  382 +
  383 +/**
  384 + * "函数运算器"Modal弹窗版
  385 + *
  386 + * @param value
  387 + * @param onChange
  388 + * @param style
  389 + * @param colsTree
  390 + * @param autofocus
  391 + * @param modalParams
  392 + * @param fieldName
  393 + * @param fieldType
  394 + * @param uniKey
  395 + * @constructor
  396 + */
  397 +export const QxFunctionOperationModal: React.FC<QxFunOperationModalProps> = ({
  398 + value,
  399 + onChange,
  400 + style,
  401 + colsTree,
  402 + autofocus,
  403 + modalParams,
  404 + fieldName,
  405 + fieldType,
  406 + uniKey,
  407 + isScriptEditMode,
  408 + isInSubForm,
  409 + appCode,
  410 + funCode,
  411 +}) => {
  412 + const qxFuncOperaionRef = useRef<any>();
  413 + const [funValues, setFunValues] = useState<any>();
  414 +
  415 + useEffect(() => {
  416 + setFunValues(value);
  417 + }, [value]);
  418 +
  419 + const [isFullScreen, setIsFullScreen] = useState(false);
  420 + /**实现F11全屏效果*/
  421 + const fullScreen = () => {
  422 + // var docElm = document.documentElement;
  423 + // /*W3C*/
  424 + // if (docElm.requestFullscreen) {
  425 + // docElm.requestFullscreen();
  426 + // } /*FireFox */ else if (docElm.mozRequestFullScreen) {
  427 + // docElm.mozRequestFullScreen();
  428 + // } /*Chrome等 */ else if (docElm.webkitRequestFullScreen) {
  429 + // docElm.webkitRequestFullScreen();
  430 + // } /*IE11*/ else if (docElm.msRequestFullscreen) {
  431 + // docElm.msRequestFullscreen();
  432 + // }
  433 + setIsFullScreen(true);
  434 + };
  435 + /**退出F11全屏*/
  436 + const exitFullScreen = () => {
  437 + // if (document.exitFullscreen) {
  438 + // document.exitFullscreen();
  439 + // } else if (document.mozCancelFullScreen) {
  440 + // document.mozCancelFullScreen();
  441 + // } else if (document.webkitCancelFullScreen) {
  442 + // document.webkitCancelFullScreen();
  443 + // } else if (document.msExitFullscreen) {
  444 + // document.msExitFullscreen();
  445 + // }
  446 + setIsFullScreen(false);
  447 + };
  448 +
  449 + // 点击确定 调用方法
  450 + const onHandleCancel = () => {
  451 + setFunValues(value || '');
  452 +
  453 + const e: any = new MouseEvent('click', {
  454 + bubbles: true,
  455 + cancelable: true,
  456 + });
  457 + setTimeout(() => modalParams?.onCancel && modalParams?.onCancel(e), 200);
  458 + };
  459 +
  460 + // 点击确定 调用方法
  461 + const onHandleOk = () => {
  462 + const { getUsedFuncList, scriptEdit } = qxFuncOperaionRef.current;
  463 + if (!_.isEqual(funValues, value) || scriptEdit !== isScriptEditMode) {
  464 + const usedFuncList = getUsedFuncList();
  465 + console.log(
  466 + 'code: ',
  467 + funValues,
  468 + ' useFuncList: ',
  469 + usedFuncList,
  470 + ' scriptEdit: ',
  471 + scriptEdit,
  472 + );
  473 + onChange(funValues, usedFuncList, scriptEdit);
  474 + }
  475 +
  476 + const e: any = new MouseEvent('click', {
  477 + bubbles: true,
  478 + cancelable: true,
  479 + });
  480 + setTimeout(() => modalParams?.onOk && modalParams?.onOk(e), 200);
  481 + };
  482 +
  483 + const newColsTree = useMemo(() => {
  484 + (colsTree || []).forEach((item) => {
  485 + if (item.widget === 'qxSelect') {
  486 + const code = item.key.slice(2, -1);
  487 + item.attrs = [
  488 + {
  489 + key: '${' + code + '.code' + '}',
  490 + titleStr: '编号',
  491 + fieldGroupType: FIELD_TYPE_PROPS.TEXT,
  492 + },
  493 + {
  494 + key: '${' + code + '.name' + '}',
  495 + titleStr: '文本',
  496 + fieldGroupType: FIELD_TYPE_PROPS.TEXT,
  497 + },
  498 + {
  499 + key: '${' + code + '.score' + '}',
  500 + titleStr: '赋值',
  501 + fieldGroupType: FIELD_TYPE_PROPS.NUM,
  502 + },
  503 + ];
  504 + }
  505 + });
  506 + return colsTree;
  507 + }, [colsTree]);
  508 +
  509 + // const handleTree = (colsTree: ColsTreeSelectProps) => {
  510 + // console.log(colsTree);
  511 + // return colsTree;
  512 + // };
  513 +
  514 + return (
  515 + <Modal
  516 + key={uniKey}
  517 + visible={modalParams.visible}
  518 + wrapClassName={`fx-wrapper`}
  519 + width={880}
  520 + centered={true}
  521 + maskClosable={false}
  522 + destroyOnClose={true}
  523 + footer={null}
  524 + {...modalParams}
  525 + className={`rel-more_modal ${isFullScreen ? 'rel-more_modal_full' : ''}`}
  526 + >
  527 + <div className="header">
  528 + <div className="header-item">
  529 + <span className="title">函数编辑</span>
  530 + <span
  531 + className="manual"
  532 + onClick={() => {
  533 + handleWindowOpen(
  534 + 'https://qgutech.yuque.com/g/team-qx/helper/mlh9yzyewec7hb81/collaborator' +
  535 + '/join?token=mjqmsZwJo9iTTkTb&source=doc_collaborator#',
  536 + );
  537 + }}
  538 + >
  539 + <svg
  540 + width="16"
  541 + height="16"
  542 + viewBox="0 0 16 16"
  543 + fill="none"
  544 + className="qx-f-icon-help"
  545 + >
  546 + <g>
  547 + <path
  548 + fillRule="evenodd"
  549 + clipRule="evenodd"
  550 + d="M1.33331 7.99992C1.33331 4.31802 4.31808 1.33325 7.99998 1.33325C11.6819 1.33325 14.6666 4.31802 14.6666 7.99992C14.6666 11.6818 11.6819 14.6666 7.99998 14.6666C4.31808 14.6666 1.33331 11.6818 1.33331 7.99992ZM6.86652 6.65945V6.50573C6.86732 6.32282 6.93273 6.11993 7.07868 5.96739C7.21347 5.82651 7.4762 5.66659 7.99602 5.66659C8.48416 5.66659 8.8236 5.89964 8.99671 6.16424C9.18158 6.4468 9.14515 6.68389 9.05741 6.80234C8.94799 6.95007 8.81212 7.08651 8.64793 7.23433C8.59797 7.2793 8.5364 7.33288 8.47038 7.39032L8.47037 7.39034C8.35231 7.49307 8.22004 7.60817 8.11457 7.70852C7.74008 8.06486 7.3333 8.5622 7.3333 9.33325L7.33547 9.67087L8.66877 9.6623L8.66665 9.33143C8.66714 9.10341 8.76061 8.93428 9.03368 8.67446C9.12546 8.58712 9.20797 8.51574 9.30238 8.43406L9.30242 8.43402C9.37224 8.37362 9.44858 8.30758 9.54 8.22528C9.73012 8.05413 9.94168 7.84862 10.1288 7.59597C10.6342 6.91365 10.516 6.05112 10.1125 5.43426C9.69713 4.79943 8.94146 4.33325 7.99602 4.33325C7.17786 4.33325 6.54355 4.59802 6.11528 5.04563C5.69841 5.48133 5.5347 6.0303 5.53318 6.50235V6.65945H6.86652ZM7.33545 10.3333V11.6692H8.66879V10.3333H7.33545Z"
  551 + />
  552 + </g>
  553 + </svg>
  554 + 公式使用帮助手册
  555 + </span>
  556 + </div>
  557 + {/* <Tooltip placement="right" title={'公式使用帮助手册'}>
  558 + </Tooltip> */}
  559 + <div className="header-item">
  560 + {isFullScreen ? (
  561 + <CompressOutlined
  562 + className="icon-right expand"
  563 + onClick={exitFullScreen}
  564 + />
  565 + ) : (
  566 + <ExpandOutlined
  567 + className="icon-right expand"
  568 + onClick={fullScreen}
  569 + />
  570 + )}
  571 + <CloseOutlined
  572 + className="icon-right close"
  573 + onClick={onHandleCancel}
  574 + />
  575 + </div>
  576 + </div>
  577 + <QxFunctionOperation
  578 + cRef={qxFuncOperaionRef}
  579 + value={funValues}
  580 + onChange={(code: string) => {
  581 + setFunValues(code);
  582 + }}
  583 + appCode={appCode}
  584 + funCode={funCode}
  585 + isInSubForm={isInSubForm}
  586 + style={style}
  587 + colsTree={newColsTree}
  588 + isScriptEditMode={isScriptEditMode}
  589 + autofocus={autofocus}
  590 + fieldName={fieldName}
  591 + fieldType={fieldType}
  592 + uniKey={uniKey}
  593 + isFullScreen={isFullScreen}
  594 + />
  595 + <div className="footer">
  596 + <Button onClick={onHandleCancel}>取消</Button>
  597 + <Button type="primary" onClick={onHandleOk}>
  598 + 确定
  599 + </Button>
  600 + </div>
  601 + </Modal>
  602 + );
  603 +};
  1 +import { request } from '@qx/common';
  2 +
  3 +/**
  4 + *
  5 + * @param appCode
  6 + * @param funCode
  7 + */
  8 +export function getFunctionList(appCode: string, funCode: string) {
  9 + return request.get(
  10 + `/qx-apaas-lowcode/function/${appCode}/${funCode}/listFunctions`,
  11 + );
  12 +}
  1 +<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
  2 +<g id="general/beautify">
  3 +<path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M8.08163 1.33325C7.62049 1.33325 7.24695 1.70679 7.24695 2.16793V4.37048C7.24695 4.59185 7.33489 4.80416 7.49142 4.96069C7.64795 5.11722 7.86026 5.20516 8.08163 5.20516C8.303 5.20516 8.5153 5.11722 8.67183 4.96069C8.82836 4.80416 8.9163 4.59185 8.9163 4.37048V2.16793C8.9163 1.70658 8.54206 1.33325 8.08163 1.33325ZM4.01636 3.17374C3.90543 3.1727 3.79541 3.19377 3.69272 3.23574C3.59003 3.27771 3.49674 3.33973 3.4183 3.41818C3.33985 3.49662 3.27784 3.58991 3.23587 3.6926C3.1939 3.79529 3.17282 3.90531 3.17386 4.01624C3.17491 4.12717 3.19806 4.23678 3.24195 4.33866C3.28539 4.43947 3.34828 4.53071 3.42701 4.60717L4.98284 6.16251C5.13936 6.31898 5.35162 6.40688 5.57294 6.40688C5.79426 6.40688 6.00656 6.31894 6.16308 6.16247C6.48896 5.83659 6.48896 5.30856 6.16308 4.98268L4.60727 3.42686C4.53082 3.34814 4.43958 3.28526 4.33878 3.24183C4.2369 3.19793 4.12729 3.17479 4.01636 3.17374ZM12.1493 3.1871C11.9306 3.18504 11.7199 3.26885 11.5624 3.42052L11.5602 3.4227L9.99996 4.98288C9.84375 5.13937 9.75601 5.35146 9.75601 5.57257C9.75601 5.74239 9.80776 5.90688 9.90254 6.04496V6.06536L10.0002 6.163C10.1567 6.31947 10.369 6.40737 10.5903 6.40737C10.8116 6.40737 11.0239 6.31943 11.1805 6.16296L12.7404 4.60298L12.7426 4.60072C12.8943 4.44323 12.9781 4.23251 12.976 4.01387C12.974 3.79524 12.8862 3.58614 12.7316 3.43153C12.577 3.27692 12.3679 3.18916 12.1493 3.1871ZM8.08281 7.24019C7.97188 7.23914 7.86186 7.26022 7.75917 7.30219C7.65648 7.34416 7.56318 7.40618 7.48474 7.48462C7.4063 7.56306 7.34428 7.65635 7.30231 7.75904C7.26034 7.86173 7.23926 7.97176 7.24031 8.08269C7.24135 8.19362 7.2645 8.30322 7.3084 8.40511C7.35182 8.50589 7.41469 8.59711 7.49339 8.67355L13.2457 14.4259L13.2497 14.4296C13.4079 14.5778 13.6175 14.6586 13.8341 14.655C14.0508 14.6514 14.2575 14.5637 14.4107 14.4104C14.5639 14.2572 14.6514 14.0503 14.6549 13.8337C14.6583 13.617 14.5773 13.4075 14.4291 13.2494L14.4255 13.2456L8.67368 7.49328C8.59724 7.41457 8.50601 7.3517 8.40523 7.30828C8.30335 7.26438 8.19374 7.24123 8.08281 7.24019ZM2.16805 7.24683C1.70762 7.24683 1.33337 7.62015 1.33337 8.0815C1.33337 8.30287 1.42131 8.51518 1.57785 8.67171C1.73438 8.82824 1.94668 8.91618 2.16805 8.91618H4.37061C4.59198 8.91618 4.80428 8.82824 4.96081 8.67171C5.11734 8.51518 5.20528 8.30287 5.20528 8.0815C5.20528 7.86013 5.11734 7.64783 4.96081 7.4913C4.80428 7.33477 4.59198 7.24683 4.37061 7.24683H2.16805ZM11.6295 7.24683C11.4081 7.24683 11.1958 7.33477 11.0393 7.4913C10.8827 7.64783 10.7948 7.86013 10.7948 8.0815C10.7948 8.30287 10.8827 8.51518 11.0393 8.67171C11.1958 8.82824 11.4081 8.91618 11.6295 8.91618H13.832C14.0534 8.91618 14.2657 8.82824 14.4222 8.67171C14.5788 8.51518 14.6667 8.30287 14.6667 8.0815C14.6667 7.86013 14.5788 7.64783 14.4222 7.4913C14.2657 7.33477 14.0534 7.24683 13.832 7.24683H11.6295ZM5.45959 9.87733C5.24095 9.87512 5.03018 9.95879 4.87258 10.1104L4.87024 10.1126L3.31014 11.6727C3.15367 11.8292 3.06577 12.0415 3.06577 12.2628C3.06577 12.4328 3.11761 12.5974 3.21255 12.7356V12.756L3.31038 12.8537C3.46687 13.0099 3.67896 13.0976 3.90007 13.0976C4.12119 13.0976 4.33348 13.0097 4.48997 12.8534L6.04987 11.2935L6.05196 11.2914C6.20374 11.134 6.28771 10.9233 6.2858 10.7047C6.28389 10.486 6.19627 10.2769 6.04177 10.1222C5.88727 9.96746 5.67823 9.87954 5.45959 9.87733ZM8.08163 10.7952C7.6207 10.7952 7.24695 11.1685 7.24695 11.6294V13.8319C7.24695 14.0533 7.33489 14.2656 7.49142 14.4221C7.64795 14.5786 7.86026 14.6666 8.08163 14.6666C8.303 14.6666 8.5153 14.5786 8.67183 14.4221C8.82836 14.2656 8.9163 14.0533 8.9163 13.8319V11.6294C8.9163 11.1683 8.54185 10.7952 8.08163 10.7952Z" fill="#50535D"/>
  4 +</g>
  5 +</svg>
  1 +<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" ><path fill-rule="evenodd" fill="#7C7E86" d="M16 3a2 2 0 0 1 2 2v1h12V5a2 2 0 1 1 4 0v1h6a4 4 0 0 1 4 4v30a4 4 0 0 1-4 4H8a4 4 0 0 1-4-4V10a4 4 0 0 1 4-4h6V5a2 2 0 0 1 2-2Zm14 7v1a2 2 0 1 0 4 0v-1h6v7H8v-7h6v1a2 2 0 1 0 4 0v-1h12ZM8 40V21h32v19H8Zm5-11h8v-4h-8v4Zm8 7h-8v-4h8v4Zm6-7h8v-4h-8v4Z" clip-rule="evenodd"/></svg>
  1 +<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" ><path fill="#7C7E86" fill-rule="evenodd" d="M8 8h14v32H8V8Zm14 36H4V8a4 4 0 0 1 4-4h14a4 4 0 0 1 4 4v4h14a4 4 0 0 1 4 4v28H22Zm4-28v24h14V16H26Zm-12-2h-4v-3h4v3Zm-4 5h4v-3h-4v3Zm22 3h-4v-3h4v3Zm-16-8h4v-3h-4v3Zm4 5h-4v-3h4v3Zm14 3h4v-3h-4v3Z" clip-rule="evenodd"/></svg>
  1 +<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
  2 +<g id="edit/copy">
  3 +<path id="Vector" fill-rule="evenodd" clip-rule="evenodd" d="M12.6667 2.66659H6.66667V1.33325H12.6667C13.403 1.33325 14 1.93021 14 2.66659V9.99992H12.6667V2.66659ZM2 5.33325C2 4.59687 2.59695 3.99992 3.33333 3.99992H10C10.735 3.99992 11.3333 4.5944 11.3333 5.33218V13.3361C11.3333 14.075 10.7347 14.6666 10.0009 14.6666H3.33184C2.59351 14.6666 2 14.0677 2 13.3333V5.33325ZM10 5.33325H3.33333V13.3333H10V5.33325Z" fill="#50535D"/>
  4 +</g>
  5 +</svg>
  1 +<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"><path fill="#7C7E86" fill-rule="evenodd" d="M15 7a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v12a3 3 0 0 1-3 3h-4v1h9a3 3 0 0 1 3 3v2h3a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H31a3 3 0 0 1-3-3V31a3 3 0 0 1 3-3h3v-1H14v1h3a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H7a3 3 0 0 1-3-3V31a3 3 0 0 1 3-3h3v-2a3 3 0 0 1 3-3h9v-1h-4a3 3 0 0 1-3-3V7Zm4 1v10h10V8H19ZM8 32v8h8v-8H8Zm24 0v8h8v-8h-8Z" clip-rule="evenodd"/></svg>
  1 +<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" ><path fill="#7C7E86" fill-rule="evenodd" d="M4 10a4 4 0 0 1 4-4h32a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H8a4 4 0 0 1-4-4V10Zm36 0H8v28h32V10Zm-20 9h-9v-4h9v4Zm0 7h-9v-4h9v4Zm-4 7h-5v-4h5v4Zm13-5a5 5 0 0 0-5 5h-4a9.002 9.002 0 0 1 5.357-8.232 6 6 0 1 1 7.286 0A9.002 9.002 0 0 1 38 33h-4a5 5 0 0 0-5-5Zm-2-8a2 2 0 1 1 4 0 2 2 0 0 1-4 0Z" clip-rule="evenodd"/></svg>
  1 +<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" ><path fill="#7C7E86" fill-rule="evenodd" d="M21 13a3 3 0 1 1 6 0 3 3 0 0 1-6 0Zm3-7a7 7 0 1 0 0 14C12.954 20 4 28.954 4 40a2 2 0 1 0 4 0c0-8.837 7.163-16 16-16s16 7.163 16 16a2 2 0 1 0 4 0c0-11.046-8.954-20-20-20a7 7 0 1 0 0-14Z" clip-rule="evenodd"/></svg>
  1 +<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"><path fill="#7C7E86" fill-rule="evenodd" d="M8 4a3 3 0 0 0-3 3v34a3 3 0 0 0 3 3h32a3 3 0 0 0 3-3V7a3 3 0 0 0-3-3H8Zm31 24H9v-8h30v8ZM9 40v-8h30v8H9Zm30-24V8H9v8h30ZM15.02 34H11v4.02h4.02V34ZM11 10h4.02v4.02H11V10Zm4.02 12H11v4.02h4.02V22Z" clip-rule="evenodd"/></svg>