UNPKG

enterprise-loyalty-components

Version:

企业版忠诚度组件库

1,503 lines (1,459 loc) 86.3 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var React = require('react'); var CodeMirror = _interopDefault(require('codemirror')); require('codemirror/lib/codemirror.css'); require('codemirror/addon/dialog/dialog.css'); require('codemirror/addon/scroll/simplescrollbars.css'); require('codemirror/addon/hint/show-hint.css'); var moment = _interopDefault(require('moment')); var injectSheet = _interopDefault(require('react-jss')); var antd = require('antd'); var ReactDOM = require('react-dom'); var EventListener = _interopDefault(require('react-event-listener')); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) t[p[i]] = s[p[i]]; return t; } function styleInject(css, ref) { if ( ref === void 0 ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } var css = ".cm-property,\n.cm-custom-property-id {\n color: #05a;\n}\n\n.cm-function,\n.cm-declaration {\n color: #30a;\n}\n\n.cm-operator {\n color: #ec8a3c;\n}\n\n.cm-time,\n.cm-date,\n.cm-datetime {\n color: #9e6cef;\n}\n\n.CodeMirror {\n line-height: 20px;\n border: 1px solid #d9d9d9;\n transition: all 0.3s;\n border-radius: 2px;\n height: 150px !important;\n}\n\n.CodeMirror.CodeMirror-focused {\n border-color: #3dad91;\n box-shadow: 0 0 0 2px rgba(30, 160, 132, 0.2);\n}\n\n.CodeMirror .CodeMirror-placeholder {\n color: #999;\n}\n\n.CodeMirror-hints {\n z-index: 10000 !important;\n}\n\n.WrongExpression .CodeMirror {\n border-color: #f5222d;\n box-shadow: 0 0 0 2px rgba(241, 7, 7, 0.2);\n}\n\n.EditorDisabled .CodeMirror {\n border-color: #d9d9d9;\n box-shadow: none;\n background-color: #f5f5f5;\n cursor: not-allowed;\n}\n\n.EditorDisabled .CodeMirror-lines {\n cursor: not-allowed;\n}\n\n.EditorDisabled .CodeMirror-cursors {\n display: none;\n}\n"; styleInject(css); function fullMatchTypes(type, targets) { if (type === null) { return false; } var types = type.split(' '); var matched = true; while (matched && targets.length) { var target = targets.shift(); matched = types.indexOf(target) > -1; } return matched; } // 判断 obj 对象是否有 key 属性,obj 可使用全大小写 function includesIgnoreCase(arr, key) { return !!arr.find(function (item) { return item === key || item.toLowerCase() === key || item.toUpperCase() === key; }); } // 创建用以替换 token 的 Element function createSpanReplacementNode(context, className) { var $span = document.createElement('span'); $span.innerText = context; if (className) { $span.classList.add(className); } return $span; } // 创建日期类型的Maker,点击可弹出日期选择控件 function createDateReplacementNode(context, className, onMarkerClick) { var $span = document.createElement('span'); $span.innerText = context; if (className) { $span.classList.add(className); } $span.addEventListener('click', onMarkerClick, false); // 设置清除事件监听的函数,marker在清除的时候需要调用该函数 $span.removeMarkerListener = function () { $span.removeEventListener('click', onMarkerClick, false); }; return $span; } function defineMode(config) { CodeMirror.defineMode('expression', function () { var atoms = ['false', 'true', 'null']; var fqn = config.fqn; var operators = config.operators; var functions = config.functions; var declarations = config.declarations; var operatorChars = /^[\/*+\-%<>!=&|~^]/; var brackets = /^[\{}\(\)]/; // const brackets = config.brackets || /^[\{}\(\)\[\]]/; var specialOperatorWords = operators.reduce(function (o, n) { return o.concat(n.split(' ')); }, []); var paths = []; function tokenBase(stream, state) { var ch = stream.next(); if (ch === null) { return null; } if (ch === ',') { paths.length = 0; return 'comma'; } // {-1000}和{1000}表示为属性ID if (ch === '{' && stream.match(/-?\d+\}/)) { return 'custom-property-id'; } if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) { // numbers stream.match(/^[0-9]*(\.[0-9]+)?([eE][-+]?[0-9]+)?/); stream.match(/^\.(?!\.)/); paths.length = 0; return 'number'; } else if (ch === "'" || ch === '"') { // strings state.tokenize = tokenLiteral(ch); return state.tokenize(stream, state); } else if (brackets.test(ch)) { // brackets paths.length = 0; return 'bracket'; } else if (ch === '.') { // .1 for 0.1 if (stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) { paths.length = 0; return 'number'; } if (stream.match(/^\.+/)) { return null; } return 'dot'; } else if (operatorChars.test(ch)) { // operators stream.eatWhile(operatorChars); paths.length = 0; return 'operator'; } else { // 此处只匹配了中文,其他文字暂未处理 stream.eatWhile(/^[_\w\d\[\]\u4e00-\u9fa5]/); var word = stream.current(); if (word === 'this') { paths.length = 0; paths.push('this'); return 'fqn root property'; } if (paths[0] === 'this') { paths.push(word); } if (paths.length > 0 && isInRightFqnPath(fqn, paths)) { return 'fqn property'; } paths.length = 0; if (includesIgnoreCase(specialOperatorWords, word)) { return 'operator'; } if (includesIgnoreCase(atoms, word)) { return 'atom'; } if (includesIgnoreCase(functions, word)) { return 'function'; } if (declarations.includes(word)) { return 'declaration'; } return null; } } // 'string', with char specified in quote escaped by '\' function tokenLiteral(quote) { return function (stream, state) { var escaped = false; var ch = stream.next(); while (ch) { if (ch === quote && !escaped) { state.tokenize = tokenBase; break; } escaped = !escaped && ch === '\\'; ch = stream.next(); } return 'string'; }; } function pushContext(stream, state, type) { state.context = { prev: state.context, indent: stream.indentation(), col: stream.column(), type: type }; } function popContext(state) { state.indent = state.context.indent; state.context = state.context.prev; } return { startState: function () { return { tokenize: tokenBase, context: null }; }, token: function (stream, state) { if (stream.sol()) { if (state.context && state.context.align === null) { state.context.align = false; } } if (state.tokenize === tokenBase && stream.eatSpace()) { return null; } var style = state.tokenize(stream, state); if (state.context && state.context.align === null) { state.context.align = true; } var tok = stream.current(); if (tok === '(') { pushContext(stream, state, ')'); } else if (tok === '[') { pushContext(stream, state, ']'); } else if (state.context && state.context.type === tok) { popContext(state); } return style; }, // indent(state, textAfter) { // const cx = state.context; // if (!cx) { // return CodeMirror.Pass; // } // const closing = textAfter.charAt(0) === cx.type; // if (cx.align) { // return cx.col + (closing ? 0 : 1); // } else { // return cx.indent + (closing ? 0 : config.indentUnit); // } // }, // closeBrackets: "()[]{}''\"\"``" closeBrackets: "(){}''\"\"``" }; }); } // 判断 leefPath 是否在 fqn 的路径上 function isInRightFqnPath(fqn, paths) { var cPaths = paths.slice(); var matchedObj = fqn; while (cPaths.length > 0) { var path = cPaths.shift(); if (path === 'this') { matchedObj = fqn; } else if (matchedObj.hasOwnProperty(path)) { if (matchedObj[path].$type) { matchedObj = matchedObj[path].$type; } else { matchedObj = matchedObj[path]; } } else { return false; } } return matchedObj; } function isObject(data) { return Object.prototype.toString.call(data) === '[object Object]'; } var Pos = CodeMirror.Pos; function scriptHint(editor, getToken, options) { // Find the token at the cursor var cur = editor.getCursor(); var token = getToken(editor, cur); if (/\b(?:string|comment)\b/.test(token.type)) { return; } var innerMode = CodeMirror.innerMode(editor.getMode(), token.state); token.state = innerMode.state; // If it's not a 'word-style' token, ignore the token. var isWordStyle = /^[\S]*$/.test(token.string) && token.string !== '.'; if (!isWordStyle) { token = { start: cur.ch, end: cur.ch, string: '', state: token.state, type: token.string === '.' ? 'property' : null }; } else if (token.end > cur.ch) { token.end = cur.ch; token.string = token.string.slice(0, cur.ch - token.start); } else if (isWordStyle) { var start = cur.ch - token.string.length; var preToken = getToken(editor, { line: cur.line, ch: start }); if (preToken.string === '.') { token = { start: start, end: start, string: token.string, state: token.state, type: 'property' }; } } var tprop = token; var context; // If it is a property, find out what it is a property of. while (fullMatchTypes(tprop.type, ['property'])) { tprop = getToken(editor, Pos(cur.line, tprop.start)); if (tprop.string === '.') { tprop = getToken(editor, Pos(cur.line, tprop.start)); if (!context) { context = []; } context.push(tprop); } } return { list: getCompletions(token, context, options), from: Pos(cur.line, token.start), to: Pos(cur.line, token.start + token.string.length) }; } function forAllProps(obj, callback) { if (isObject(obj)) { Object.keys(obj).forEach(function (item) { callback(item); }); } } function getCompletions(token, context, options) { var found = []; var start = token.string.toLowerCase(); var global = options && options.globalScope || {}; function maybeAdd(str) { if (str.toLowerCase().lastIndexOf(start, 0) === 0 && found.indexOf(str) === -1) { found.push(str); } } function maybeAddCustomProperty(property) { var text = "{" + property.id + "}"; if (property.name.startsWith(start) && !found.find(function (item) { return item.text === text; })) { found.push({ text: text, displayText: property.name }); } } function maybeAddDeclaration(declaration) { if (declaration.toLowerCase().lastIndexOf(start, 0) === 0 && found.indexOf(declaration) === -1) { found.push({ text: generateDefaultDeclarationValue(declaration), displayText: declaration }); } } if (context && context.length) { // If this is a property, see if it belongs to some object we can // find in the current environment. var obj = context.pop(); var base = void 0; if (fullMatchTypes(obj.type, ['fqn', 'property'])) { base = base || getFqnObjWithPath(global, obj.string); } while (base !== null && context.length) { var children = base[context.pop().string]; // 存在$type属性,表示路径到终点了 if (children.$type) { base = null; } else { base = children; } } if (base !== null) { forAllProps(base, maybeAdd); } } else { options.operators.forEach(maybeAdd); options.declarations.forEach(maybeAddDeclaration); options.functions.forEach(maybeAdd); options.customProperties.forEach(maybeAddCustomProperty); } return found; } function getFqnObjWithPath(global, path) { var pathArr = path.split('.'); var obj = global; while (pathArr.length) { var p = pathArr.shift(); if (p === 'this') { continue; } obj = obj[p]; } return obj; } function generateHint(config) { return function (editor, options) { options.globalScope = config.fqn; options.operators = config.operators || []; options.functions = config.functions || []; options.declarations = config.declarations || []; options.customProperties = config.customProperties || []; return scriptHint(editor, function (e, cur) { return e.getTokenAt(cur); }, options); }; } function registerHint(config) { CodeMirror.registerHelper('hint', 'expression', generateHint(config)); } // 生成默认的类型声明值 function generateDefaultDeclarationValue(declaration) { var declarationValue = ''; if (declaration === 'DateTime') { declarationValue = "\"" + moment().toISOString() + "\""; } else if (declaration === 'Date') { declarationValue = "\"" + moment().format('YYYY-MM-DD') + "\""; } else if (declaration === 'Time') { declarationValue = "\"" + moment().format('HH:mm:ss') + "\""; } return declaration + "(" + declarationValue + ")"; } var TypeEnum; (function (TypeEnum) { TypeEnum["Number"] = "Number"; TypeEnum["Integer"] = "Integer"; TypeEnum["String"] = "String"; TypeEnum["Date"] = "Date"; TypeEnum["Time"] = "Time"; TypeEnum["DateTime"] = "DateTime"; TypeEnum["Arithmetic"] = "Arithmetic"; TypeEnum["Comparison"] = "Comparison"; TypeEnum["LogicOperator"] = "LogicOperator"; TypeEnum["IdentiferComparison"] = "IdentiferComparison"; TypeEnum["BracketStart"] = "BracketStart"; TypeEnum["BracketEnd"] = "BracketEnd"; TypeEnum["Comma"] = "Comma"; TypeEnum["Semicolon"] = "Semicolon"; TypeEnum["Function"] = "Function"; TypeEnum["FunctionCall"] = "FunctionCall"; TypeEnum["FqnProperty"] = "FqnProperty"; TypeEnum["CustomProperty"] = "CustomProperty"; TypeEnum["Identifier"] = "Identifier"; TypeEnum["Boolean"] = "Boolean"; TypeEnum["Null"] = "Null"; TypeEnum["LogicNot"] = "LogicNot"; })(TypeEnum || (TypeEnum = {})); function isScalaType(t) { return [ TypeEnum.CustomProperty, TypeEnum.FqnProperty, TypeEnum.Boolean, TypeEnum.Integer, TypeEnum.Number, TypeEnum.String, TypeEnum.Null ].includes(t); } function isOperatorType(t) { return [ TypeEnum.Comparison, TypeEnum.Arithmetic, TypeEnum.LogicOperator, TypeEnum.IdentiferComparison ].includes(t); } /** * 判断路径path是否在target中存在 * @param path 路径,必须以this开头 * @param fqn 目标对象 */ function matchFqnPath(path, fqn, pos) { var paths = path.split('.'); paths.shift(); var obj = fqn; while (paths.length > 0) { var subPath = paths.shift(); if (obj.hasOwnProperty && obj.hasOwnProperty(subPath)) { obj = obj[subPath]; } else { throw { code: 'InvalidFqnPath', pos: pos }; } } return obj; } /** * 获取括号结束位置,即左括号对应的右括号的位置 * start 为左括号的位置 */ function getBracketEndTokenPosition(start, tokens) { var i = start; var bracketCount = 0; while ((i < tokens.length && bracketCount > 0) || i === start) { var itoken = tokens[i++]; if (itoken.type === TypeEnum.BracketStart) { bracketCount++; } else if (itoken.type === TypeEnum.BracketEnd) { bracketCount--; } } if (bracketCount > 0) { throw FunctionError(tokens[start], 'Function Arguments must end with `)`.'); } return i - 1; } function FunctionError(token, message) { return { code: 'InvalidFunction', pos: token.pos, message: message }; } function getFuncArgs(tokens) { var args = []; var i = 0; var brackCount = 0; while (i < tokens.length) { var token = tokens[i++]; if (token.type === TypeEnum.Comma) { if (brackCount === 0) { args.push(tokens.splice(0, i - 1)); tokens.shift(); i = 0; } // 防止出现`SUM(1,2,)`参数最后为逗号的情况 if (!tokens.length) { throw { message: 'Function arguments error' }; } continue; } if (token.type === TypeEnum.BracketStart) { brackCount++; } else if (token.type === TypeEnum.BracketEnd) { brackCount--; } if (i === tokens.length) { if (brackCount !== 0) { throw { message: 'Function arguments error' }; } args.push(tokens); } } return args; } function macthOperatorType(valve) { if ([','].includes(valve)) { return TypeEnum.Comma; } if (['==', '!=', '>', '>=', '<', '<='].includes(valve)) { return TypeEnum.Comparison; } if (['&&', '||', '!'].includes(valve)) { return TypeEnum.LogicOperator; } if (['+', '-', '*', '/', '%'].includes(valve)) { return TypeEnum.Arithmetic; } throw { code: 'invalid-operator', message: '无效的比较符' }; } // 判断是否为正确的日期格式 function isAcceptableDate(v) { return /^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))$/.test(v); } // 判断是否为正确的时间格式 function isAcceptableTime(v) { // return /^(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9])$/.test(v); return /^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])$/.test(v); } // 判断是否为正确的ISO时间格式 function isAcceptableDateTime(v) { // tslint:disable-next-line return /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/.test(v); } var ArithmeticOperators = ['+', '-', '*', '/', '%']; var ComparisonOperators = ['>', '<', '=']; var SingleCharOperators = ['(', ')', ';']; var LogicOperators = ['&', '|', '!']; function Position(start, end, line) { return { start: start, end: end, line: line }; } function generateTokens(input, options) { var fqn = options.fqn; var functions = options.functions; var declarations = options.declarations; var customProperties = options.customProperties; var tokens = []; var len = input.length; var i = 0; var line = 0; while (i < len) { var ch = input[i++]; if (ch === ';') { break; } if (ch === ' ' || ch === '\t') { continue; } if (ch === '\n') { line++; continue; } // 特殊字符串 if (SingleCharOperators.includes(ch)) { tokens.push({ type: { '(': TypeEnum.BracketStart, ')': TypeEnum.BracketEnd }[ch], value: ch, pos: Position(i - 1, i - 1, line) }); continue; } var doubleOperators = eatSpecialDoubleOperators(input.slice(i - 1)); if (doubleOperators) { tokens.push({ type: TypeEnum.IdentiferComparison, value: doubleOperators.replace(/\s+/g, ' ').toUpperCase(), pos: Position(i - 1, i - 1 + doubleOperators.length, line) }); i = i + doubleOperators.length; continue; } var singleOperators = eatSpecialSingleOperators(input.slice(i - 1)); if (singleOperators) { tokens.push({ type: TypeEnum.IdentiferComparison, value: singleOperators.toUpperCase(), pos: Position(i - 1, i - 1 + singleOperators.length, line) }); i = i + singleOperators.length; continue; } var commaToken = eatContinuousOperators([','], i - 1); if (commaToken) { tokens.push(__assign({}, commaToken, { type: macthOperatorType(commaToken.value) })); i = commaToken.pos.end + 1; continue; } var continueToken = eatContinuousOperators(LogicOperators.concat(ArithmeticOperators, ComparisonOperators), i - 1); if (continueToken) { tokens.push(__assign({}, continueToken, { type: macthOperatorType(continueToken.value) })); i = continueToken.pos.end + 1; continue; } var customProperty = eatCustomProperties(i - 1); if (customProperty) { tokens.push(customProperty); i = customProperty.pos.end + 1; continue; } // fqn if (ch === 't') { var start = i - 1; var firstWord = input.slice(start, start + 4); var nextOfThis = input[start + 4]; if (firstWord === 'this' && ['.', ' ', '\t', undefined].includes(nextOfThis)) { i = start + 4; while (i < len && /[A-Za-z_0-9\.\[\]]/.test(input[i])) { i++; } var word = input.slice(start, i); var pos = Position(start, i - 1, line); var fqnPathType = matchFqnPath(word, fqn, pos); if (word) { tokens.push({ vType: fqnPathType, type: TypeEnum.FqnProperty, value: word, pos: pos }); } continue; } } // 内置函数 if (/[A-Za-z_]/.test(ch)) { var start = i - 1; while (i < len && /[A-Za-z_0-9]/.test(input[i])) { i++; } var word = input.slice(start, i); if ((functions.includes(word.toLowerCase()) || functions.includes(word.toUpperCase())) && input[i] === '(') { tokens.push({ type: TypeEnum.Function, value: word, pos: Position(start, i - 1, line) }); continue; } else if (declarations.includes(word) && input[i] === '(') { tokens.push({ type: TypeEnum.Function, value: word, pos: Position(start, i - 1, line) }); continue; } else if (['TRUE', 'true', 'FALSE', 'false'].includes(word)) { tokens.push({ type: TypeEnum.Boolean, value: word, pos: Position(start, i - 1, line) }); continue; } else if (['NULL', 'null'].includes(word)) { tokens.push({ type: TypeEnum.Null, value: word, pos: Position(start, i - 1, line) }); continue; } else { throw { code: 'InvalidIdentifier', pos: Position(start, i - 1, line) }; } } // 数字(整数&浮点数) if (/[0-9]/.test(ch)) { var start = i - 1; var isDot = false; while (i < len) { var char = input[i]; if (char === '.') { if (isDot) { throw { code: 'InvalidNumberFormatter', message: 'Too many dot in a number.', pos: Position(i, i, line) }; } isDot = true; i++; } else if (/[0-9]/.test(char)) { i++; } else { break; } } var word = input.slice(start, i); var type = word.includes('.') ? TypeEnum.Number : TypeEnum.Integer; tokens.push({ type: type, vType: { $type: type }, value: word, pos: Position(start, i - 1, line) }); continue; } // 字符串字面量 if (ch === '\'' || ch === '"') { var start = i - 1; var escaped = false; while (i < len) { var char = input[i]; if (char === ch && !escaped) { break; } escaped = !escaped && char === '\\'; i++; } if (input[i] !== ch) { throw { code: 'StringEndLost', message: '字符串缺少结束符', pos: Position(start, i - 1, line) }; } var word = input.slice(start + 1, i); i++; tokens.push({ type: TypeEnum.String, vType: { $type: TypeEnum.String }, value: word, pos: Position(start, i - 1, line) }); continue; } throw { code: 'InvalidIdentifier', pos: Position(i - 1, i - 1, line) }; } return tokens; // 连续的操作符 function eatContinuousOperators(opers, start) { var index = start; var word = input[index]; var leftSpace = input[index - 1] ? [' ', '\t'].includes(input[index - 1]) : false; var rightSpace = input[index + 1] ? [' ', '\t'].includes(input[index + 1]) : false; if (opers.includes(word)) { while (opers.includes(input[index + 1])) { word += input[index + 1]; index++; } return { value: word, pos: Position(start, index, line), leftSpace: leftSpace, rightSpace: rightSpace }; } return false; } // 自定义属性ID=> {12345} function eatCustomProperties(start) { var matched = input.slice(start).match(/^-?\{\d+\}/); if (matched) { var value = matched[0]; var pos = Position(start, start + value.length - 1, line); var propertyId_1 = Number(value.slice(1, value.length - 1)); var customProperty = customProperties.find(function (item) { return item.id === propertyId_1; }); if (!customProperty) { throw { code: 'invalid-custom-property', pos: pos }; } return { value: value, pos: pos, type: TypeEnum.CustomProperty, vType: { $type: customProperty.dataType } }; } return false; } } var specialSingleOperators = ['in', 'contain', 'between', 'and']; function eatSpecialSingleOperators(input) { var i = 0; var word = input[i]; while (i <= input.length && ![' ', '\t', undefined].includes(input[i + 1])) { word += input[++i]; } if (specialSingleOperators.find(function (item) { return item.toLowerCase() === word || item.toUpperCase() === word; })) { return word; } return false; } function eatSpecialDoubleOperators(input) { var matched = matchRegExpList(input, [ /^(begin\s+with)\s+/, /^(BEGIN\s+WITH)\s+/, /^(end\s+with)\s+/, /^(END\s+WITH)\s+/, /^(include\s+any)\s+/, /^(INCLUDE\s+ANY)\s+/, /^(include\s+all)\s+/, /^(INCLUDE\s+ALL)\s+/, /^(exclude\s+any)\s+/, /^(EXCLUDE\s+ANY)\s+/, /^(exclude\s+all)\s+/, /^(EXCLUDE\s+ALL)\s+/, /^(not\s+in)\s+/, /^(NOT\s+IN)\s+/, /^(not\s+contain)\s+/, /^(NOT\s+CONTAIN)\s+/ ]); if (matched) { return matched; } return false; } function matchRegExpList(input, regexpList) { var i = 0; while (i < regexpList.length) { var matched = input.match(regexpList[i]); if (matched) { return matched[1]; } i++; } return false; } function matchBrackets(tokens) { var start = tokens.findIndex(function (item) { return item.type === TypeEnum.BracketStart; }); if (start === -1) { return; } var end = getBracketEndTokenPosition(start, tokens); var eatTokens = tokens.slice(start, end + 1); if (eatTokens.length <= 2) { throw { code: 'BracketsContentEmpty', message: '括号内内容不能为空', pos: { start: tokens[start].pos.start, end: tokens[end].pos.end, line: tokens[start].pos.line } }; } tokens.splice(start, eatTokens.length, match(eatTokens.slice(1, eatTokens.length - 1))); matchBrackets(tokens); } function matchFunctions(tokens) { var index = tokens.findIndex(function (item) { return item.type === TypeEnum.Function; }); if (index === -1) { return; } var start = index + 1; var end = getBracketEndTokenPosition(start, tokens); var eatTokens = tokens.slice(index, end + 1); var argsTokens = tokens.slice(index + 2, end); var args = getFuncArgs(argsTokens); args.forEach(function (arg) { if (!arg.length) { throw { code: 'invalid-function-arg', message: '函数参数值不能为空' }; } }); tokens.splice(index, eatTokens.length, { type: TypeEnum.FunctionCall, value: tokens[index].value, tokens: eatTokens, args: args.map(function (arg) { return match(arg); }) }); matchFunctions(tokens); } function matchCommonOperator(tokens, type, operators) { var i = tokens.findIndex(function (item) { return item.type === type && operators.includes(item.value); }); if (i === -1) { return; } var token = tokens[i]; var left = tokens[i - 1]; var right = tokens[i + 1]; if (!left || !right) { throw { code: 'E1' }; } tokens.splice(i - 1, 3, { tokens: [left, token, right], operator: token, left: match([left]), right: match([right]) }); matchCommonOperator(tokens, type, operators); } function matchArithmetic(tokens, operators) { matchCommonOperator(tokens, TypeEnum.Arithmetic, operators); } function matchLogicOperator(tokens) { matchCommonOperator(tokens, TypeEnum.LogicOperator, ['&&', '||']); } function matchComparison(tokens) { matchCommonOperator(tokens, TypeEnum.Comparison, ['>', '>=', '<', '<=', '==', '!=']); matchCommonOperator(tokens, TypeEnum.IdentiferComparison, [ 'CONTAIN', 'NOT CONTAIN', 'IN', 'NOT IN', 'BEGIN WITH', 'END WITH', 'INCLUDE ANY', 'INCLUDE ALL', 'EXCLUDE ANY', 'EXCLUDE ALL', 'BETWEEN' ]); } function matchLogicNot(tokens) { var i = tokens.findIndex(function (vtoken) { return vtoken.type === TypeEnum.LogicOperator && vtoken.value.includes('!'); }); if (i === -1) { return; } var token = tokens[i]; var right = tokens[i + 1]; if (!right) { throw { code: 'E1' }; } tokens.splice(i, 2, { tokens: [token, right], operator: token, right: match([right]) }); matchLogicNot(tokens); } function matchIdentiferComparison(tokens) { var i = tokens.findIndex(function (vtoken) { return (vtoken.type === TypeEnum.IdentiferComparison); }); if (i === -1) { return ''; } var token = tokens[i]; if (['BEGIN WITH', 'END WITH', 'INCLUDE ANY', 'INCLUDE ALL', 'EXCLUDE ANY', 'EXCLUDE ALL'].includes(token.value)) { var left = tokens[i - 1]; var right = tokens[i + 1]; if (!left || !right) { throw { code: 'E1' }; } tokens.splice(i - 1, 3, { tokens: [left, token, right], operator: token, left: match([left]), right: match([right]) }); } else if (token.value === 'BETWEEN') { var left = tokens[i - 1]; if (!left) { throw { code: 'BetweenLeftComparsionTokenError' }; } var endIndex = matchBetweenAnd(tokens, i); var firstComparsionTokens = tokens.slice(i + 1, endIndex); if (firstComparsionTokens.length !== 1) { throw { code: 'AndLeftComparsionTokenError' }; } var secondComparsionToken = tokens[endIndex + 1]; if (!secondComparsionToken) { throw { code: 'AndRightComparsionTokenError' }; } tokens.splice(i - 1, 5, { tokens: [left, token, firstComparsionTokens[0], tokens[endIndex], secondComparsionToken], operator: token, left: match([left]), andLeft: match([firstComparsionTokens[0]]), andRight: match([secondComparsionToken]) }); } } // function matchNegativeDigit(tokens: IToken[]) { // const i = tokens.findIndex(token => { // return token.type === TypeEnum.Arithmetic && token.value === '-'; // }); // const leftToken = tokens[i-1]; // const rightToken = tokens[i+1]; // } function match(tokens) { if (tokens.length === 0) { return; } if (tokens.length === 1) { var token = tokens[0]; if (!token.type) { return token; } if (!isScalaType(token.type) && token.type !== TypeEnum.FunctionCall) { throw { code: 'invalid-token-start' }; } return tokens[0]; } if (tokens.length === 2) { var token = tokens[0]; if (token.type === TypeEnum.LogicOperator && token.value.includes('!')) { var right = tokens[1]; return { tokens: [token, right], operator: token, right: match([right]) }; } else if (token.type === TypeEnum.Arithmetic) { if (token.value === '-') { if (!token.rightSpace) { var right = tokens[1]; return { tokens: [token, right], operator: token, right: match([right]) }; } } } throw { code: 'invalid-token-size' }; } if (tokens.length === 3) { if (tokens[0].type === 'Function') { matchFunctions(tokens); return match(tokens); } if (!isOperatorType(tokens[1].type)) { throw { code: 'invalid-operator' }; } if (!isScalaType(tokens[0].type)) { if (!tokens[0].left) { throw { code: 'invalid-expression' }; } } if (!isScalaType(tokens[2].type)) { if (!tokens[2].left) { throw { code: 'invalid-expression' }; } } return { left: tokens[0], operator: tokens[1], right: tokens[2] }; } // 函数 matchFunctions(tokens); // 括号 matchBrackets(tokens); // 逻辑非 matchLogicNot(tokens); // 四则运算乘除取余 matchArithmetic(tokens, ['*', '/', '%']); // 四则运算加减 matchArithmetic(tokens, ['+', '-']); // 比较 BEGIN WITH/END WITH... matchIdentiferComparison(tokens); // 比较符运算 matchComparison(tokens); // 逻辑与、逻辑或 matchLogicOperator(tokens); return match(tokens); } function matchBetweenAnd(tokens, betweenIndex) { var i = betweenIndex + 1; var matched = false; while (i < tokens.length) { if (tokens[i].type === TypeEnum.IdentiferComparison && tokens[i].value === 'AND') { matched = true; return i; } i++; } if (!matched) { throw { code: 'BetweenNotMatchedAnd' }; } return i; } function generateType($type) { return { $type: $type }; } // 是否为数字类型 function isDigit(v) { return ['Number', 'Integer'].includes(v); } // 是否存在非数字类型 function hasNotDigitArgs(args) { return args.find(function (arg) { return !isDigit(arg.vType.$type); }); } // 是否存在Number类型 function hasNumberArgs(args) { return args.find(function (arg) { return 'Number' === arg.vType.$type; }); } // 是否存在非布尔类型 function hasNotBooleanArgs(args) { return args.find(function (arg) { return 'Boolean' !== arg.vType.$type; }); } // 是否为字符串类型 function isString(v) { return ['String'].includes(v); } // 是否存在非类字符串类型 function hasNotStringArgs(args) { return args.find(function (arg) { return !isString(arg.vType.$type); }); } function AND(later) { var args = later.args; if (hasNotBooleanArgs(args)) { throw { message: 'AND 函数的参数只能为布尔类型' }; } later.vType = generateType('Boolean'); return later.vType; } function AVG(later) { var args = later.args; var firstArgType = args[0].vType.$type; if (['IntegerArray', 'NumberArray'].includes(firstArgType)) { if (args.length > 1) { throw { message: 'AVG 函数的第一个参数如果为数组,则只接受一个参数' }; } later.vType = generateType('Number'); return later.vType; } if (hasNotDigitArgs(args)) { throw { message: 'AVG 函数的参数不能为非数字类型' }; } later.vType = generateType('Number'); return later.vType; } function COUNT(later) { var args = later.args; var firstArgType = args[0].vType.$type; if (['IntegerArray', 'NumberArray'].includes(firstArgType)) { if (args.length > 1) { throw { message: 'COUNT 函数的第一个参数如果为数组,则只接受一个参数' }; } later.vType = generateType(firstArgType === 'IntegerArray' ? 'Integer' : 'Number'); return later.vType; } if (args.length === 1) { later.vType = generateType('Integer'); return later.vType; } if (isObject$2(args[0].vType.$type) && args.length > 1) { throw { message: '当 COUNT 函数第一个参数为数组时,只接受一个参数' }; } later.vType = generateType(TypeEnum.Integer); return later.vType; } function isObject$2(v) { return Object.prototype.toString.call(v) === '[object Object]'; } // 在日期上添加指定 秒/分/小时/日/月/年 function DATE_ADD(later, name) { var args = later.args; if (args.length !== 2) { throw { message: name + " \u51FD\u6570\u7684\u53EA\u63A5\u53D7\u4E24\u4E2A\u53C2\u6570" }; } if ('DateTime' !== args[0].vType.$type) { throw { message: name + " \u51FD\u6570\u7684\u7B2C\u4E00\u4E2A\u53C2\u6570\u5FC5\u987B\u4E3A DateTime \u7C7B\u578B" }; } if ('Integer' !== args[1].vType.$type) { throw { message: name + " \u51FD\u6570\u7684\u7B2C\u4E8C\u4E2A\u53C2\u6570\u5FC5\u987B\u4E3A\u6574\u6570\u7C7B\u578B" }; } later.vType = generateType('DateTime'); return later.vType; } // 日期上指定的值 function DATE_FETCH(later, name) { var args = later.args; if (args.length !== 1) { throw { message: name + " \u51FD\u6570\u7684\u53EA\u63A5\u53D7\u4E00\u4E2A\u53C2\u6570" }; } if ('DateTime' !== args[0].vType.$type) { throw { message: name + " \u51FD\u6570\u7684\u53C2\u6570\u5FC5\u987B\u4E3A DateTime \u7C7B\u578B" }; } later.vType = generateType('Integer'); return later.vType; } // 生成日期对象 function DATE(later) { var args = later.args; if (args.length !== 1) { throw { message: "Date \u51FD\u6570\u7684\u53EA\u63A5\u53D7\u4E00\u4E2A\u53C2\u6570" }; } var arg = args[0]; if (arg.vType.$type !== 'String' || !arg.value) { throw { message: 'Date 函数的参数必须为字符串类型' }; } if (!isAcceptableDate(arg.value)) { throw { message: 'Date 函数参数必须为 yyyy-dd-mm' }; } later.vType = generateType('Date'); return later.vType; } // 生成日期时间对象 function DATETIME(later) { var args = later.args; if (args.length !== 1) { throw { message: "DateTime \u51FD\u6570\u7684\u53EA\u63A5\u53D7\u4E00\u4E2A\u53C2\u6570" }; } var arg = args[0]; if (arg.vType.$type !== 'String' || !arg.value) { throw { message: 'DateTime 函数的参数必须为字符串类型' }; } // tslint:disable-next-line if (!isAcceptableDateTime(arg.value)) { throw { message: 'DateTime 函数参数必须为ISO时间格式 2019-01-01T00:00:00.000Z' }; } later.vType = generateType('DateTime'); return later.vType; } // 比较两个日期在指定单位上的偏移量 function DURATION_DIFF(later) { var name = 'DURATION_DIFF'; var args = later.args; if (args.length !== 3) { throw { message: name + " \u51FD\u6570\u7684\u63A5\u53D7\u4E09\u4E2A\u53C2\u6570" }; } if ('DateTime' !== args[0].vType.$type) { throw { message: name + " \u51FD\u6570\u7684\u7B2C\u4E00\u4E2A\u53C2\u6570\u5FC5\u987B\u4E3A DateTime \u7C7B\u578B" }; } if ('DateTime' !== args[1].vType.$type) { throw { message: name + " \u51FD\u6570\u7684\u7B2C\u4E8C\u4E2A\u53C2\u6570\u5FC5\u987B\u4E3A DateTime \u7C7B\u578B" }; } if (!['DAY', 'HOUR', 'MINUTE', 'SECOND'].includes(args[2].value)) { throw { message: name + " \u51FD\u6570\u7684\u7B2C\u4E09\u4E2A\u53C2\u6570\u5FC5\u987B\u4E3A DAY\u3001HOUR\u3001MINUTE\u3001SECOND \u4E2D\u7684\u4E00\u4E2A" }; } later.vType = generateType('Integer'); return later.vType; } // import { generateType } from '@/ast/functions/utils'; // 条件格式固定为:目标对象,比较符和参考值,目标对象/变量可以为变量、常量或函数返回值,不支持混合加减乘除运算,返回值为满足条件的参考值集合。 function FILTER(later) { var args = later.args; if (args.length < 2) { throw { message: 'FILTER 函数至少需要两个参数' }; } var conditionArgs = args.slice(1); for (var _i = 0, conditionArgs_1 = conditionArgs; _i < conditionArgs_1.length; _i++) { var arg = conditionArgs_1[_i]; if (arg.vType.$type !== TypeEnum.Boolean) { throw { message: 'FILTER 函数第一个之后的参数返回类型都必须为布尔类型' }; } if (!arg.operator) { throw { message: 'FILTER 函数条件参数必须为条件表达式' }; } } later.vType = args[0].vType; return later.vType; } // 生成ID对象 function ID(later) { var args = later.args; if (args.length !== 1) { throw { message: "ID \u51FD\u6570\u7684\u53EA\u63A5\u53D7\u4E00\u4E2A\u53C2\u6570" }; } var arg = args[0]; if ((arg.vType.$type !== 'String' && arg.vType.$type !== 'Integer') || !arg.value) { throw { message: 'ID 函数的参数必须为字符串或整数类型' }; } later.vType = generateType('Id'); return later.vType; } // 合并字符串 function JOINER(later) { var args = later.args; if (hasNotStringArgs(args)) { throw { message: 'JOINER 函数的参数不能为非 字符串、邮件地址、URL、手机号 等类型' }; } later.vType = generateType('String'); return later.vType; } // 获取字符串函数的长度 // 函数需要为一个参数 // 参数1:目标字符串 function LENGTH(later) { var args = later.args; if (args.length !== 1) { throw { message: 'LENGTH 函数的只接受一个参数' }; } if (!isString(args[0].vType.$type)) { throw { message: 'LENGTH 函数的参数必须为 字符串、邮件地址、URL、手机号 等类型' }; } later.vType = generateType('Integer'); return later.vType; } function MAX(later) { var args = later.args; var firstArgType = args[0].vType.$type; if (['IntegerArray', 'NumberArray'].includes(firstArgType)) { if (args.length > 1) { throw { message: 'MAX 函数的第一个参数如果为数组,则只接受一个参数' }; } later.vType = generateType(firstArgType === 'IntegerArray' ? 'Integer' : 'Number'); return later.vType; } if (hasNotDigitArgs(args)) { throw { message: 'MAX 函数的参数不能为非数字类型' }; } later.vType = generateType(hasNumberArgs(args) ? 'Number' : 'Integer'); return later.vType; } function MIN(later) { var args = later.args; var firstArgType = args[0].vType.$type; if (['IntegerArray', 'NumberArray'].includes(firstArgType)) { if (args.length > 1) { throw { message: 'MIN 函数的第一个参数如果为数组,则只接受一个参数' }; } later.vType = generateType(firstArgType === 'IntegerArray' ? 'Integer' : 'Number'); return later.vType; } if (hasNotDigitArgs(args)) { throw { message: 'MIN 函数的参数不能为非数字类型' }; } later.vType = generateType(hasNumberArgs(args) ? 'Number' : 'Integer'); return later.vType; } // 取余,与 / 相同 function MOD(later) { var args = later.args; if (args.length !== 2) { throw { message: 'MOD 函数只接受两个参数' }; } if (hasNotDigitArgs(args)) { throw { message: 'MOD 函数的参数不能为非数字类型' }; } later.vType = generateType('Number'); return later.vType; } function NEGATIVE(later) { var args = later.args; if (args.length > 1) { throw { message: 'NEGATIVE 函数只接受一个参数,且参数类型为布尔类型' }; } if (args[0].vType.$type !== 'Boolean') { throw { message: 'NEGATIVE 函数只接受一个参数,且参数类型为布尔类型' }; } later.vType = generateType('Boolean'); return later.vType; } // 生成当前日期时间对象 function NOW(later) { var args = later.args; if (args.length !== 0) { throw { message: "NOW \u51FD\u6570\u7684\u4E0D\u63A5\u53D7\u4EFB\u4F55\u53C2\u6570" }; } later.vType = generateType('DateTime'); return later.vType; } // 判断字符串在目标字符串中出现的次数 // 函数需要为两个参数 // 参数1: 指定字符串 // 参数2: 目标字符串 function OCCURRENCE_TIMES(later) { var args = later.args; if (args.length !== 2) { throw { message: 'OCCURRENCE_TIMES 函数的参数必须为两个' }; } if (!isString(args[0].vType.$type)) { throw { message: 'OCCURRENCE_TIMES 函数的第一个参数必须为字符串类型' }; } if (!isString(args[1].vType.$type)) { throw { message: 'OCCURRENCE_TIMES 函数的第二个参数必须为字符串类型' }; } later.vType = generateType(TypeEnum.Integer); return later.vType; } function OR(later) { var args = later.args; if (hasNotBooleanArgs(args)) { throw { message: 'OR 函数的参数只能为布尔类型' }; } later.vType = generateType('Boolean'); return later.vType; } // 向后截取字符串函数 // 函数需要为两个参数 /