fix/show-count-traffic (#1070)
Reviewed-on: daoyoucloud/tachybase#1070 Co-authored-by: bai.zixv <bai.zixv@foxmail.com> Co-committed-by: bai.zixv <bai.zixv@foxmail.com>
This commit is contained in:
parent
1273e55e9b
commit
da87d7251b
@ -1,12 +1,16 @@
|
|||||||
import { Plugin } from '@tachybase/client';
|
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 {
|
export class PluginFieldAppends extends Plugin {
|
||||||
async afterAdd() {
|
|
||||||
this.app.pm.add(SCFormula);
|
|
||||||
}
|
|
||||||
async load() {
|
async load() {
|
||||||
this.app.dataSourceManager.addFieldInterfaces([ShowFieldFormulaInterface]);
|
this.app.addComponents({
|
||||||
|
Viewformula: ViewFormula,
|
||||||
|
ViewCode: ViewCode,
|
||||||
|
});
|
||||||
|
this.app.dataSourceManager.addFieldInterfaces([ShowFieldFormulaInterface, ShowFieldCodeInterface]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -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 <ShowValue value={resultShowValue} />;
|
||||||
|
} else {
|
||||||
|
return <>{resultShowValue}</>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ShowValue = React.memo((props: { value: string }) => {
|
||||||
|
const { value } = props;
|
||||||
|
return <Input.ReadPretty value={value} />;
|
||||||
|
});
|
||||||
|
|
||||||
|
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<any>({
|
||||||
|
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: <p>{item.children}</p>,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.childrenType === 'normal') {
|
||||||
|
return <Descriptions items={showItems} />;
|
||||||
|
} 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,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,4 @@
|
|||||||
import { CollectionFieldInterface, interfacesProperties, Plugin } from '@tachybase/client';
|
import { CollectionFieldInterface, interfacesProperties } from '@tachybase/client';
|
||||||
|
|
||||||
import { ViewFormula } from './Formula.view';
|
|
||||||
|
|
||||||
const { defaultProps } = interfacesProperties;
|
const { defaultProps } = interfacesProperties;
|
||||||
|
|
||||||
@ -15,14 +13,6 @@ export interface FormulaProps {
|
|||||||
decimal: string;
|
decimal: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SCFormula extends Plugin {
|
|
||||||
async load() {
|
|
||||||
this.app.addComponents({
|
|
||||||
Viewformula: ViewFormula,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ShowFieldFormulaInterface extends CollectionFieldInterface {
|
export class ShowFieldFormulaInterface extends CollectionFieldInterface {
|
||||||
name = 'formulaShow';
|
name = 'formulaShow';
|
||||||
type = 'object';
|
type = 'object';
|
||||||
|
@ -3,100 +3,122 @@ import { Input } from '@tachybase/client';
|
|||||||
import { evaluators } from '@tachybase/evaluators/client';
|
import { evaluators } from '@tachybase/evaluators/client';
|
||||||
import { useField, useFieldSchema, useForm } from '@tachybase/schema';
|
import { useField, useFieldSchema, useForm } from '@tachybase/schema';
|
||||||
|
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { FormulaProps } from './Formula.interface';
|
import { FormulaProps } from './Formula.interface';
|
||||||
|
|
||||||
export const ViewFormula = (props: FormulaProps) => {
|
export const ViewFormula = (props: FormulaProps) => {
|
||||||
const { resultShowValue } = useAction(props);
|
const { resultShowValue } = useAction(props);
|
||||||
return <Input.ReadPretty value={resultShowValue} />;
|
return <ShowValue value={resultShowValue} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ShowValue = React.memo((props: { value: string }) => {
|
||||||
|
const { value } = props;
|
||||||
|
return <Input.ReadPretty value={value} />;
|
||||||
|
});
|
||||||
|
|
||||||
function useAction(props: FormulaProps) {
|
function useAction(props: FormulaProps) {
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const field = useField();
|
const field = useField();
|
||||||
|
|
||||||
const { formulaString, prefix, suffix, decimal } = props;
|
const { formulaString, prefix, suffix, decimal } = props;
|
||||||
|
|
||||||
const path: any = field.path.entire;
|
const path: any = field.path.entire;
|
||||||
const fieldPath = path?.replace(`.${fieldSchema.name}`, '');
|
const fieldPath = path?.replace(`.${fieldSchema.name}`, '');
|
||||||
const engine = evaluators.get('math.js');
|
const recordData = _.chain(form.values).get(fieldPath).value();
|
||||||
const evaluate = engine.evaluate.bind(engine);
|
|
||||||
const transformFormulaArray = transformFormula(formulaString);
|
|
||||||
|
|
||||||
let calculateData = [];
|
const formulaArry = transformFormula(formulaString);
|
||||||
|
|
||||||
const newFormulaArray = (data): [string, object] => {
|
const evaluateArray = getEvaluateArray(formulaArry, recordData);
|
||||||
calculateData = [];
|
if (!evaluateArray) {
|
||||||
let count = 0;
|
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 = {};
|
const scopes = {};
|
||||||
if (transformFormulaArray.length === 0) return;
|
|
||||||
for (let i = 0; i < transformFormulaArray.length; i++) {
|
for (let i = 0; i < formulaArry.length; i++) {
|
||||||
const item = transformFormulaArray[i];
|
const item = formulaArry[i];
|
||||||
|
|
||||||
if (!item) continue;
|
if (!item) continue;
|
||||||
const isNumber = !isNaN(Number(item));
|
|
||||||
const symbol = ['+', '-', '*', '/', '?', ':', '(', ')', '%'].includes(item);
|
const isNumber = isNumberFunc(item);
|
||||||
if (!isNumber && !symbol) {
|
const isSymbol = isSymbolFunc(item);
|
||||||
|
|
||||||
|
if (!isNumber && !isSymbol) {
|
||||||
|
// NOTE: item 是字段的情况
|
||||||
let value;
|
let value;
|
||||||
// 举例: ${fieldObj}.fieldName
|
// 举例: ${fieldObj}.fieldName
|
||||||
const pattern = /\${(.*?)}/g;
|
const pattern = /\${(.*?)}/g;
|
||||||
if (item.match(pattern)?.length) {
|
if (item.match(pattern)?.length) {
|
||||||
|
// NOTE: 关系字段
|
||||||
const target = item.match(pattern)[0].replace(/\${|}/g, '');
|
const target = item.match(pattern)[0].replace(/\${|}/g, '');
|
||||||
// 以.分割字符串
|
// 以.分割字符串
|
||||||
const targetField = item.split('.')[1];
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
if (path === fieldPath) {
|
const varName = `var${varIndex++}`;
|
||||||
// @ts-ignore
|
const varValue = value ?? 0;
|
||||||
value = _.chain(data).get(target).get(targetField, 0).value();
|
|
||||||
} else {
|
calculateData.push(`{{${varName}}}`);
|
||||||
// @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;
|
scopes[varName] = varValue;
|
||||||
calculateData.push('{{' + varName + '}}');
|
|
||||||
} else {
|
} else {
|
||||||
|
// NOTE: item 是数字或者符号的情况
|
||||||
calculateData.push(item);
|
calculateData.push(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [calculateData.join(''), scopes];
|
return [calculateData.join(''), scopes];
|
||||||
};
|
|
||||||
|
|
||||||
const formulaArray = newFormulaArray(form.values);
|
|
||||||
|
|
||||||
if (!formulaArray) {
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getResultShowValue(formulaArray, { prefix, suffix, decimal }): string {
|
||||||
|
const engine = evaluators.get('math.js');
|
||||||
|
const evaluate = engine.evaluate.bind(engine);
|
||||||
const [code, scopes] = formulaArray;
|
const [code, scopes] = formulaArray;
|
||||||
|
|
||||||
let resultShowValue;
|
let resultShowValue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const pre = prefix || '';
|
|
||||||
const suf = suffix || '';
|
|
||||||
const res = evaluate(code, scopes);
|
const res = evaluate(code, scopes);
|
||||||
const main = isNaN(res) ? res : Number(res).toFixed(+decimal || 0);
|
const mainRes = isNaN(res) ? res : Number(res).toFixed(+decimal || 0);
|
||||||
resultShowValue = pre + main + suf;
|
resultShowValue = `${prefix}${mainRes}${suffix}`;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
resultShowValue = `${code}`;
|
resultShowValue = `${code}`;
|
||||||
console.warn('code: ', code, ' scopes: ', scopes, 'error: ', resultShowValue, ' error message ', error.message);
|
console.warn('code: ', code, ' scopes: ', scopes, 'error: ', resultShowValue, ' error message ', error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { resultShowValue };
|
return resultShowValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function transformFormula(formula: string) {
|
// utils
|
||||||
if (!formula) return [];
|
function isNumberFunc(value) {
|
||||||
const formulaArray = formula.split(/([+\-*/?:()%])/).filter((item) => item);
|
return typeof value === 'number' && !isNaN(value);
|
||||||
return formulaArray;
|
}
|
||||||
|
|
||||||
|
function isSymbolFunc(value) {
|
||||||
|
return ['+', '-', '*', '/', '?', ':', '(', ')', '%'].includes(value);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user