From da87d7251b4a836ae18518b24740eda2c9222876 Mon Sep 17 00:00:00 2001 From: "bai.zixv" Date: Wed, 29 May 2024 20:45:58 +0800 Subject: [PATCH] fix/show-count-traffic (#1070) Reviewed-on: https://git.daoyoucloud.com/daoyoucloud/tachybase/pulls/1070 Co-authored-by: bai.zixv Co-committed-by: bai.zixv --- .../client/features/field-appends/index.ts | 14 +- .../field-appends/show-code/Code.interface.ts | 75 ++++++++ .../field-appends/show-code/Code.view.tsx | 109 ++++++++++++ .../show-formula/Formula.interface.ts | 12 +- .../show-formula/Formula.view.tsx | 160 ++++++++++-------- 5 files changed, 285 insertions(+), 85 deletions(-) create mode 100644 packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-code/Code.view.tsx diff --git a/packages/plugins/@hera/plugin-core/src/client/features/field-appends/index.ts b/packages/plugins/@hera/plugin-core/src/client/features/field-appends/index.ts index bcd689bad..e3fb946a7 100644 --- a/packages/plugins/@hera/plugin-core/src/client/features/field-appends/index.ts +++ b/packages/plugins/@hera/plugin-core/src/client/features/field-appends/index.ts @@ -1,12 +1,16 @@ import { Plugin } from '@tachybase/client'; -import { SCFormula, ShowFieldFormulaInterface } from './show-formula/Formula.interface'; +import { ShowFieldCodeInterface } from './show-code/Code.interface'; +import { ViewCode } from './show-code/Code.view'; +import { ShowFieldFormulaInterface } from './show-formula/Formula.interface'; +import { ViewFormula } from './show-formula/Formula.view'; export class PluginFieldAppends extends Plugin { - async afterAdd() { - this.app.pm.add(SCFormula); - } async load() { - this.app.dataSourceManager.addFieldInterfaces([ShowFieldFormulaInterface]); + this.app.addComponents({ + Viewformula: ViewFormula, + ViewCode: ViewCode, + }); + this.app.dataSourceManager.addFieldInterfaces([ShowFieldFormulaInterface, ShowFieldCodeInterface]); } } diff --git a/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-code/Code.interface.ts b/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-code/Code.interface.ts index e69de29bb..c956fc751 100644 --- a/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-code/Code.interface.ts +++ b/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-code/Code.interface.ts @@ -0,0 +1,75 @@ +import { CollectionFieldInterface, interfacesProperties, Plugin } from '@tachybase/client'; + +const { defaultProps } = interfacesProperties; + +export interface CodeFieldProps { + // 代码字符串 + jsCode: string; + // 前缀 + prefix: string; + // 后缀 + suffix: string; + // 精度 + decimal: string; +} + +export class ShowFieldCodeInterface extends CollectionFieldInterface { + name = 'codeShow'; + type = 'object'; + group = 'advanced'; + title = 'jsCode(显示)'; + description = '通过jsCode, 用于定制化显示用户界面内容'; + sortable = true; + default = { + type: 'virtual', + uiSchema: { + type: 'string', + 'x-component': 'ViewCode', + 'x-component-props': { + jsCode: '', + prefix: '', + suffix: '', + decimal: '', + } as CodeFieldProps, + 'x-read-pretty': true, + }, + }; + properties = { + ...defaultProps, + 'uiSchema.x-component-props.prefix': { + type: 'string', + title: '前缀', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + 'uiSchema.x-component-props.suffix': { + type: 'string', + title: '后缀', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + 'uiSchema.x-component-props.decimal': { + type: 'string', + title: '{{t("Precision")}}', + 'x-component': 'Select', + 'x-decorator': 'FormItem', + default: '0', + enum: [ + { value: '0', label: '1' }, + { value: '1', label: '1.0' }, + { value: '2', label: '1.00' }, + { value: '3', label: '1.000' }, + { value: '4', label: '1.0000' }, + { value: '5', label: '1.00000' }, + ], + }, + 'uiSchema.x-component-props.jsCode': { + type: 'string', + title: 'JSCode', + 'x-decorator': 'FormItem', + 'x-component': 'Input.TextArea', + default: '', + required: true, + }, + }; +} diff --git a/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-code/Code.view.tsx b/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-code/Code.view.tsx new file mode 100644 index 000000000..20185b3cd --- /dev/null +++ b/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-code/Code.view.tsx @@ -0,0 +1,109 @@ +import React, { useEffect, useState } from 'react'; +import { Input } from '@tachybase/client'; +import { useField, useFieldSchema, useForm } from '@tachybase/schema'; + +import { Descriptions } from 'antd'; +import _ from 'lodash'; + +import { CodeFieldProps } from './Code.interface'; + +export const ViewCode = (props: CodeFieldProps) => { + const resultShowValue = useAction(props); + if (typeof resultShowValue === 'string') { + return ; + } else { + return <>{resultShowValue}; + } +}; + +const ShowValue = React.memo((props: { value: string }) => { + const { value } = props; + return ; +}); + +function useAction(props: CodeFieldProps): string | React.ReactNode { + const form = useForm(); + const fieldSchema = useFieldSchema(); + const field = useField(); + const { jsCode, prefix, suffix, decimal } = props; + + const path: any = field.path.entire; + const fieldPath = path?.replace(`.${fieldSchema.name}`, ''); + const recordData = _.chain(form.values).get(fieldPath).value(); + + const [result, setResult] = useState({ + items: [], + childrenType: '', + }); + + const formatFunc = (value: string | number): string => { + let main = value; + if (typeof value === 'number') { + main = isNaN(value) ? value : Number(value).toFixed(+decimal || 0); + } + return `${prefix}${main}${suffix}`; + }; + useEffect(() => { + dynamicCode({ jsCode, form, path, recordData, result }, { setResult, formatFunc }); + }, []); + + const showItems = result.items.map((item) => { + return { + label: item.label, + children:

{item.children}

, + }; + }); + + if (result.childrenType === 'normal') { + return ; + } else if (result.childrenType === 'jsx') { + return <>{result?.items?.map((item) => item.children)}; + } else { + return result.items?.[0]?.children; + } +} + +async function dynamicCode({ jsCode, form, path, recordData, result }, { setResult, formatFunc }) { + try { + eval(jsCode); + // NOTE: 示例代码, 仿照此例配置即可 + // { + // import('dayjs') + // .then((module) => { + // // 使用加载的模块 + // const dayjs = module.default; // 假设模块默认导出了一个函数 + // const localeSetting = { invalidDate: '-' }; + // dayjs.updateLocale('en', localeSetting); + // const date_pay = form.getValuesIn(path.replace('.date_fix', '.date_pay')); + + // const date_receive = form.getValuesIn(path.replace('.date_fix', '.date_receive')); + // const date_show = date_pay || date_receive; + + // const formartedDate = dayjs(date_show ?? '-').format('YYYY-MM-DD'); + // setResult({ + // childrenType: 'jsx', + // items: [ + // { + // children: formartedDate, + // }, + // ], + // }); + // }) + // .catch((error) => { + // // 处理加载模块时的错误 + // console.error('Failed to load module:', error); + // }); + // } + } catch (error) { + setResult({ + childrenType: '', + items: [ + { + key: '1', + label: '数据异常', + children: '请检查字段配置内容,error:' + error, + }, + ], + }); + } +} diff --git a/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-formula/Formula.interface.ts b/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-formula/Formula.interface.ts index 34ad534e3..9b9aa4d0c 100644 --- a/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-formula/Formula.interface.ts +++ b/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-formula/Formula.interface.ts @@ -1,6 +1,4 @@ -import { CollectionFieldInterface, interfacesProperties, Plugin } from '@tachybase/client'; - -import { ViewFormula } from './Formula.view'; +import { CollectionFieldInterface, interfacesProperties } from '@tachybase/client'; const { defaultProps } = interfacesProperties; @@ -15,14 +13,6 @@ export interface FormulaProps { decimal: string; } -export class SCFormula extends Plugin { - async load() { - this.app.addComponents({ - Viewformula: ViewFormula, - }); - } -} - export class ShowFieldFormulaInterface extends CollectionFieldInterface { name = 'formulaShow'; type = 'object'; diff --git a/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-formula/Formula.view.tsx b/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-formula/Formula.view.tsx index 293bde3cf..448a1a090 100644 --- a/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-formula/Formula.view.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/features/field-appends/show-formula/Formula.view.tsx @@ -3,100 +3,122 @@ import { Input } from '@tachybase/client'; import { evaluators } from '@tachybase/evaluators/client'; import { useField, useFieldSchema, useForm } from '@tachybase/schema'; +import _ from 'lodash'; + import { FormulaProps } from './Formula.interface'; export const ViewFormula = (props: FormulaProps) => { const { resultShowValue } = useAction(props); - return ; + return ; }; +const ShowValue = React.memo((props: { value: string }) => { + const { value } = props; + return ; +}); + function useAction(props: FormulaProps) { const form = useForm(); const fieldSchema = useFieldSchema(); const field = useField(); + const { formulaString, prefix, suffix, decimal } = props; const path: any = field.path.entire; const fieldPath = path?.replace(`.${fieldSchema.name}`, ''); + const recordData = _.chain(form.values).get(fieldPath).value(); + + const formulaArry = transformFormula(formulaString); + + const evaluateArray = getEvaluateArray(formulaArry, recordData); + if (!evaluateArray) { + return { resultShowValue: '' }; + } else { + const resultShowValue = getResultShowValue(evaluateArray, { prefix, suffix, decimal }); + return { resultShowValue }; + } +} + +function transformFormula(formula: string) { + if (!formula) { + return []; + } + const formulaArray = formula.split(/([+\-*/?:()%])/).filter((item) => item); + return formulaArray; +} + +function getEvaluateArray(formulaArry, recordData): [string, object] { + if (formulaArry.length < 1) { + return; + } + + let varIndex = 0; + const calculateData = []; + const scopes = {}; + + for (let i = 0; i < formulaArry.length; i++) { + const item = formulaArry[i]; + + if (!item) continue; + + const isNumber = isNumberFunc(item); + const isSymbol = isSymbolFunc(item); + + if (!isNumber && !isSymbol) { + // NOTE: item 是字段的情况 + let value; + // 举例: ${fieldObj}.fieldName + const pattern = /\${(.*?)}/g; + if (item.match(pattern)?.length) { + // NOTE: 关系字段 + const target = item.match(pattern)[0].replace(/\${|}/g, ''); + // 以.分割字符串 + const targetField = item.split('.')[1]; + const targetObj = _.chain(recordData).get(target).value(); + value = _.chain(targetObj).get(targetField, 0).value(); + } else { + // NOTE: 普通字段 + value = _.chain(recordData).get(item, 0).value(); + } + + const varName = `var${varIndex++}`; + const varValue = value ?? 0; + + calculateData.push(`{{${varName}}}`); + scopes[varName] = varValue; + } else { + // NOTE: item 是数字或者符号的情况 + calculateData.push(item); + } + } + + return [calculateData.join(''), scopes]; +} + +function getResultShowValue(formulaArray, { prefix, suffix, decimal }): string { const engine = evaluators.get('math.js'); const evaluate = engine.evaluate.bind(engine); - const transformFormulaArray = transformFormula(formulaString); - - let calculateData = []; - - const newFormulaArray = (data): [string, object] => { - calculateData = []; - let count = 0; - const scopes = {}; - if (transformFormulaArray.length === 0) return; - for (let i = 0; i < transformFormulaArray.length; i++) { - const item = transformFormulaArray[i]; - if (!item) continue; - const isNumber = !isNaN(Number(item)); - const symbol = ['+', '-', '*', '/', '?', ':', '(', ')', '%'].includes(item); - if (!isNumber && !symbol) { - let value; - // 举例: ${fieldObj}.fieldName - const pattern = /\${(.*?)}/g; - if (item.match(pattern)?.length) { - const target = item.match(pattern)[0].replace(/\${|}/g, ''); - // 以.分割字符串 - const targetField = item.split('.')[1]; - - if (path === fieldPath) { - // @ts-ignore - value = _.chain(data).get(target).get(targetField, 0).value(); - } else { - // @ts-ignore - value = _.chain(data).get(fieldPath).get(target).get(targetField, 0).value(); - } - } else { - if (path === fieldPath) { - // @ts-ignore - value = _.chain(data).get(item, 0).value(); - } else { - // @ts-ignore - value = _.chain(data).get(fieldPath).get(item, 0).value(); - } - } - if (!value) { - value = 0; - } - count += 1; - const varName = 'var' + count; - const varValue = value; - scopes[varName] = varValue; - calculateData.push('{{' + varName + '}}'); - } else { - calculateData.push(item); - } - } - return [calculateData.join(''), scopes]; - }; - - const formulaArray = newFormulaArray(form.values); - - if (!formulaArray) { - return {}; - } const [code, scopes] = formulaArray; + let resultShowValue; + try { - const pre = prefix || ''; - const suf = suffix || ''; const res = evaluate(code, scopes); - const main = isNaN(res) ? res : Number(res).toFixed(+decimal || 0); - resultShowValue = pre + main + suf; + const mainRes = isNaN(res) ? res : Number(res).toFixed(+decimal || 0); + resultShowValue = `${prefix}${mainRes}${suffix}`; } catch (error) { resultShowValue = `${code}`; console.warn('code: ', code, ' scopes: ', scopes, 'error: ', resultShowValue, ' error message ', error.message); } - return { resultShowValue }; + return resultShowValue; } -function transformFormula(formula: string) { - if (!formula) return []; - const formulaArray = formula.split(/([+\-*/?:()%])/).filter((item) => item); - return formulaArray; +// utils +function isNumberFunc(value) { + return typeof value === 'number' && !isNaN(value); +} + +function isSymbolFunc(value) { + return ['+', '-', '*', '/', '?', ':', '(', ')', '%'].includes(value); }