|
|
@@ -1,6 +1,8 @@
|
|
|
import { DataObjectUtils } from "@imengyu/js-request-transform";
|
|
|
import { doSerializeInternalVar } from "./CommonCategoryDynamicData";
|
|
|
import { DateUtils } from "@imengyu/imengyu-utils";
|
|
|
+import { back, navTo, redirectTo } from "@/components/utils/PageAction";
|
|
|
+import { navigateToAutoContent } from "../common/CommonContent";
|
|
|
|
|
|
export interface IDynamicCompareExpressionContext {
|
|
|
sourceData: {
|
|
|
@@ -43,15 +45,15 @@ function parseMapExpression(mapExpression: string) {
|
|
|
* @returns
|
|
|
*/
|
|
|
export function doEvaluateDynamicDataExpression(expressions: string, context: IDynamicCompareExpressionContext) {
|
|
|
- const expressionArr = expressions.split('/');
|
|
|
+ if (!expressions)
|
|
|
+ return undefined;
|
|
|
+ const expressionArr = expressions.replace(/\n/g, '/').split('/');
|
|
|
const tempData = new Map<string, any>();
|
|
|
|
|
|
function evaluateExpression(expression: string, prevResult: any) : any {
|
|
|
const arr = expression.split(',');
|
|
|
- if (arr.length < 2)
|
|
|
- return undefined;
|
|
|
const type = arr[0];
|
|
|
- const key = arr[1];
|
|
|
+ const key = arr[1] || '';
|
|
|
|
|
|
function accessDynamicKeyOrPrevResult(key: string, write: boolean, setValue?: any) {
|
|
|
//基础取值
|
|
|
@@ -245,6 +247,8 @@ export function doEvaluateDynamicDataExpression(expressions: string, context: ID
|
|
|
* 评估单条动态比较表达式(格式: 取值:符号:值)
|
|
|
*/
|
|
|
function evaluateSingleCompareExpression(expression: string, context: IDynamicCompareExpressionContext): boolean {
|
|
|
+ if (!expression)
|
|
|
+ return false;
|
|
|
const arr = expression.split(':');
|
|
|
if (arr.length !== 3)
|
|
|
return false;
|
|
|
@@ -282,7 +286,6 @@ function evaluateSingleCompareExpression(expression: string, context: IDynamicCo
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
/**
|
|
|
* 评估动态比较表达式
|
|
|
*
|
|
|
@@ -296,11 +299,200 @@ function evaluateSingleCompareExpression(expression: string, context: IDynamicCo
|
|
|
*/
|
|
|
export function doEvaluateDynamicCompareExpression(expression: string, context: IDynamicCompareExpressionContext): boolean {
|
|
|
const trimmed = expression.trim();
|
|
|
- if (!trimmed) return false;
|
|
|
-
|
|
|
+ if (!trimmed)
|
|
|
+ return false;
|
|
|
const orParts = trimmed.split(/\s*\|\|\s*/).map((s) => s.trim()).filter(Boolean);
|
|
|
return orParts.some((orPart) => {
|
|
|
const andParts = orPart.split(/\s*&&\s*/).map((s) => s.trim()).filter(Boolean);
|
|
|
return andParts.every((andPart) => evaluateSingleCompareExpression(andPart, context));
|
|
|
});
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 调用动态函数
|
|
|
+ *
|
|
|
+ * 格式:
|
|
|
+ * 函数名:参数1:参数2:参数3
|
|
|
+ *
|
|
|
+ * 前缀# 表示行号,用于跳转
|
|
|
+ * 例如:
|
|
|
+ * if:A,K#main.externalLink:jumpMp:default/
|
|
|
+ * #jumpMp#openOfficialAccountArticle:A,K#main.externalLink/
|
|
|
+ * #default#navigateToAutoContent:K#main
|
|
|
+ *
|
|
|
+ * 多个函数用 / 分隔,会按顺序依次调用,并返回最后一个函数的返回值
|
|
|
+ *
|
|
|
+ * @param functions
|
|
|
+ * @param context
|
|
|
+ */
|
|
|
+export async function doCallDynamicFunction(functions: string, context: IDynamicCompareExpressionContext) {
|
|
|
+ const functionArr = functions.replace(/\n/g, '/').split('/');
|
|
|
+ let result = null;
|
|
|
+ let currentPointer = 0;
|
|
|
+ let breakFlag = false;
|
|
|
+
|
|
|
+ //记录行号映射
|
|
|
+ //格式#name#expression
|
|
|
+ let lineMap = new Map<string, number>();
|
|
|
+ for (let i = 0; i < functionArr.length; i++) {
|
|
|
+ if (functionArr[i].startsWith('#')) {
|
|
|
+ const endIndex = functionArr[i].substring(1).indexOf('#');
|
|
|
+ lineMap.set(functionArr[i].substring(1, endIndex + 1), i);
|
|
|
+ functionArr[i] = functionArr[i].substring(endIndex + 2);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //执行函数
|
|
|
+ for (; currentPointer < functionArr.length; currentPointer++) {
|
|
|
+ result = await evaluateExpression(functionArr[currentPointer], result);
|
|
|
+ if (breakFlag)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ function functionJump(line: string) {
|
|
|
+ const lineNumber = !Number.isNaN(Number(line)) ? Number(line) : lineMap.get(line);
|
|
|
+ if (!lineNumber || lineNumber < currentPointer)
|
|
|
+ throw new Error('doCallDynamicFunction: bad line number: ' + line);
|
|
|
+ currentPointer = lineNumber - 1;
|
|
|
+ }
|
|
|
+ async function evaluateExpression(expression: string, prevResult: any) : Promise<any> {
|
|
|
+ const arr = expression.split(':');
|
|
|
+ const functionName = arr[0];
|
|
|
+ const params = arr.slice(1);
|
|
|
+ function assertParamCount(count: number) {
|
|
|
+ if (params.length !== count)
|
|
|
+ throw new Error('doCallDynamicFunction: invalid param count: ' + functionName);
|
|
|
+ }
|
|
|
+ switch (functionName) {
|
|
|
+ /**
|
|
|
+ * 条件判断
|
|
|
+ * 格式: if:判断:true的跳转行号:false的跳转行号
|
|
|
+ */
|
|
|
+ case 'if': {
|
|
|
+ assertParamCount(3);
|
|
|
+ let returnNewLineName = '';
|
|
|
+ const result = params[0].includes(':') ?
|
|
|
+ doEvaluateDynamicCompareExpression(params[0], context) :
|
|
|
+ doEvaluateDynamicDataExpression(params[0], context);
|
|
|
+ if (result)
|
|
|
+ returnNewLineName = doEvaluateDynamicDataExpression(params[1], context);
|
|
|
+ else
|
|
|
+ returnNewLineName = doEvaluateDynamicDataExpression(params[2], context);
|
|
|
+ functionJump(returnNewLineName);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 分支
|
|
|
+ * 格式: switch:判断:值:跳转:值:跳转...:默认跳转
|
|
|
+ */
|
|
|
+ case 'switch': {
|
|
|
+ assertParamCount(2);
|
|
|
+ const values = [] as any[];
|
|
|
+ for (let i = 1; i < params.length; i += 2)
|
|
|
+ values.push(doEvaluateDynamicDataExpression(params[i], context));
|
|
|
+ const valueMatch = doEvaluateDynamicDataExpression(params[1], context);
|
|
|
+ const index = values.indexOf(valueMatch);
|
|
|
+ if (index !== -1)
|
|
|
+ functionJump(params[index * 2 + 2]);
|
|
|
+ else if (params.length > 2 && params.length % 2 === 0)
|
|
|
+ functionJump(params[params.length - 1]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 跳转
|
|
|
+ * 格式: jump:跳转行号
|
|
|
+ */
|
|
|
+ case 'jump': {
|
|
|
+ assertParamCount(1);
|
|
|
+ functionJump(doEvaluateDynamicDataExpression(params[0], context));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 终止当前脚本
|
|
|
+ * 格式: break
|
|
|
+ */
|
|
|
+ case 'break':
|
|
|
+ breakFlag = true;
|
|
|
+ break;
|
|
|
+ /**
|
|
|
+ * 返回值
|
|
|
+ * 格式: return:值
|
|
|
+ */
|
|
|
+ case 'return':
|
|
|
+ return doEvaluateDynamicDataExpression(params[0], context);
|
|
|
+ case 'get':
|
|
|
+ assertParamCount(1);
|
|
|
+ return context.sourceData.main[doEvaluateDynamicDataExpression(params[0], context)];
|
|
|
+ case 'set':
|
|
|
+ assertParamCount(2);
|
|
|
+ context.sourceData.main[doEvaluateDynamicDataExpression(params[0], context)] =
|
|
|
+ doEvaluateDynamicDataExpression(params[1], context);
|
|
|
+ break;
|
|
|
+ case 'navTo':
|
|
|
+ assertParamCount(2);
|
|
|
+ navTo(doEvaluateDynamicDataExpression(params[0], context), doEvaluateDynamicDataExpression(params[1], context));
|
|
|
+ break;
|
|
|
+ case 'redirectTo':
|
|
|
+ assertParamCount(2);
|
|
|
+ redirectTo(doEvaluateDynamicDataExpression(params[0], context), doEvaluateDynamicDataExpression(params[1], context));
|
|
|
+ break;
|
|
|
+ case 'back':
|
|
|
+ back();
|
|
|
+ break;
|
|
|
+ case 'openOfficialAccountChat':
|
|
|
+ assertParamCount(1);
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ (uni as any).openOfficialAccountChat({
|
|
|
+ username: doEvaluateDynamicDataExpression(params[0], context),
|
|
|
+ success: (res: any) => {
|
|
|
+ console.log('doCallDynamicFunction: openOfficialAccountChat success: ' + JSON.stringify(res));
|
|
|
+ resolve(res);
|
|
|
+ },
|
|
|
+ fail: (err: any) => {
|
|
|
+ console.error('doCallDynamicFunction: openOfficialAccountArticle failed: ' + err);
|
|
|
+ reject(err);
|
|
|
+ },
|
|
|
+ });
|
|
|
+ });
|
|
|
+ case 'openOfficialAccountArticle':
|
|
|
+ assertParamCount(1);
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ (uni as any).openOfficialAccountArticle({
|
|
|
+ url: doEvaluateDynamicDataExpression(params[0], context),
|
|
|
+ success: (res: any) => {
|
|
|
+ console.log('doCallDynamicFunction: openOfficialAccountArticle success: ' + JSON.stringify(res));
|
|
|
+ resolve(res);
|
|
|
+ },
|
|
|
+ fail: (err: any) => {
|
|
|
+ console.error('doCallDynamicFunction: openOfficialAccountArticle failed: ' + err);
|
|
|
+ reject(err);
|
|
|
+ },
|
|
|
+ });
|
|
|
+ });
|
|
|
+ case 'navigateToMiniProgram':
|
|
|
+ assertParamCount(2);
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ uni.navigateToMiniProgram({
|
|
|
+ appId: doEvaluateDynamicDataExpression(params[0], context),
|
|
|
+ path: doEvaluateDynamicDataExpression(params[1], context),
|
|
|
+ envVersion: doEvaluateDynamicDataExpression(params[2], context),
|
|
|
+ success: (res: any) => resolve(res),
|
|
|
+ fail: (err: any) => {
|
|
|
+ console.error('doCallDynamicFunction: navigateToMiniProgram failed: ' + err);
|
|
|
+ reject(err);
|
|
|
+ },
|
|
|
+ });
|
|
|
+ });
|
|
|
+ case 'navigateToAutoContent':
|
|
|
+ assertParamCount(1);
|
|
|
+ navigateToAutoContent(
|
|
|
+ doEvaluateDynamicDataExpression(params[0], context),
|
|
|
+ doEvaluateDynamicDataExpression(params[1], context)
|
|
|
+ );
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new Error('doCallDynamicFunction: unknown function: ' + functionName);
|
|
|
+ }
|
|
|
+ return prevResult;
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
}
|