UNPKG

zj-element

Version:

基于ElementUI的VUE组件——Жидзин(Zidjin)系列组件库。

220 lines (189 loc) 9.26 kB
/** x-format-string V1.0 自动格式化字符串,支持双花括号 20230213 by Sieyoo, 赵向明 */ import XTakeAt from "./x-take-at.js" const XTemplateString = function() {}; /** * function XTemplateString::format 对模板字符串进行编译,格式化为结果字符串 * @param {object} item 给定需要格式化的Key-Value对象。 * @param {string} text 要格式化的字符串文本 * @desc * @return {string} 格式化完成后字符串 * @example * 模板语法糖: * 1、基础版——直接引用值。语法:{{key}} * 例子: * param={category_id: 35, category_value: "杭州西湖区"}; * str="类别ID:{{category_id}},类别值:{{category_value}}。"; * XTemplateString.format(param, str); * 结果 ==> "类别ID:35,类别值:杭州西湖区。" * 2、高级版——管道选项值。即指定列表对象、标签键、值键,找到引用值。语法:{{key:key|options[labelKey]}} * 例子: * param={category_id: 35, category_list: [{value: 27, label: "杭州上城区"}, {value: 35, label: "杭州西湖区"}]}; * str="类别:{{ value: category_id | category_list[label] }}" * XTemplateString.format(param, str); * 结果 ==> "类别:杭州西湖区。" * 3、高级版——强制转换值。即指定来源键、目标键,生成数组对象。语法:{{(Array<targetKey:sourceKey>)key<splitFlag>}} * 例子: * param={photo_list: [{link: '/images/a.jpg', label: "杭州上城区"}, {link: '/images/b.jpg', label: "杭州西湖区"}]}; * str="{{ (Array< url: link, text: label >)photo_list }}" * str2="{{ (Array< :link >)photo_list }}" * XTemplateString.format(param, str); * 结果 ==> "[{url: '/images/a.jpg', text: "杭州上城区"}, {url: '/images/b.jpg', text: "杭州西湖区"}]" * XTemplateString.format(param, str2); * 结果 ==> "['/images/a.jpg', '/images/b.jpg']" */ XTemplateString.format = function(item, text){ // console.log("XTemplateString.format:", item, text) if(!text){ return ''; } // typeof(formData[it.field]) === 'object' ? JSON.stringify(formData[it.field]) : formData[it.field] // 暂时弃用,小程序不支持eval语法 // let expression = text.replace(/(\{\{(.*?)\}\})/g, "$${item.$2}"); // let result = eval('`'+expression+'`').replaceAll('"null"', '""'); let result = text; if(typeof text === 'object'){ result = JSON.stringify(text) } const re = new RegExp("(\{\{(.*?)\}\})"); while(re.test(result)){ const key = re.exec(result)[2]; let value = XTakeAt.getValue(item, key, ''); // 注意:如果key是高级模版语法,是匹配不出value值的。 // const logic = XTemplateString.matchLogic(item, key); // 逻辑运算处理 const label = XTemplateString.matchOptions(item, key); // 管道选项处理 const force = XTemplateString.matchForceType(item, key); // 强制转换处理 if(typeof(value) === 'string'){ value = value.replace(/"/g, '\\"') } // console.log("XTemplateString.format k-v:", key, '=', {value: value}, '==>', label); // 强制转换时要改变类型,所以字符串需要替换掉两头的""双引号 result = force ? result.replace(/("\{\{(.*?)\}\})"/, force) : result.replace(re, label || value ); } // 因为JSON反序列化不支持\r\n,所以只能强制替换掉 if( typeof result === 'string'){ result = result // .replace(/\\/g, '\\\\') //这个千万不能写,会导致\"都失效的 .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') .replace(/\t/g, '\\t') } if(typeof text === 'object'){ // 注:如果json中混入html语言或\n换行符等异物,会导致反解析失败 try { result = JSON.parse(result); } catch (err) { console.warn("【模块字符串】转换JSON时失败!", err, "result =>", result, {result}); result = text; } } // console.log("XTemplateString.format end:", result) return result; } // 逻辑运算值。 XTemplateString.matchLogic = function(item, expression_string){ // console.log("XTemplateString.matchLogic:", expression_string, item) if (!expression_string){ return null; } const re = (/([_\w]*?)\s*:?\s*([_\w]*?)\s*\|\s*([_\w]*?)\[\s*([_\w]*?)\s*\]/); // 检查是否 if(!re.test(expression_string)) return null; } // 管道选项值。作用:"value:category_id|category_list[label]" ==> "杭州西湖区" XTemplateString.matchOptions = function(item, expression_string){ // console.log("XTemplateString.matchOptions:", expression_string, item) if (!expression_string){ return null; } const re = (/([_\w]*?)\s*:?\s*([_\w]*?)\s*\|\s*([_\w]*?)\[\s*([_\w]*?)\s*\]/); // 检查是否 if(!re.test(expression_string)) return null; // console.log("XTemplateString.matchOptions re:", re.exec(expression_string)); const match = re.exec(expression_string); const itemKey = match[2]; const valueKey = match[1] || itemKey; const optionsKey = match[3]; const labelKey = match[4]; const options = XTakeAt.getValue(item, optionsKey, ''); // item[optionsKey]; const value = XTakeAt.getValue(item, itemKey, ''); // item[itemKey]; if(!options || !labelKey || !valueKey){ return value; } // 查找到当前匹配的对象, let current = options.find(it => it[valueKey] == value); let label = current && current[labelKey]; // console.log("XTemplateString.matchOptions label 2:", {key: label}); if(!label){ return value; } return label; } // 强制转换值。作用:(Array<url: url, text: uid>)files ==> [{"url": "/images/xx.jpg", "text": "标题"}] XTemplateString.matchForceType = function(item, expression_string){ // console.log("XTemplateString.matchForceType:", expression_string, item) if (!expression_string){ return null; } // const re = (/\(\s*([\w]*?)\s*<\s*([_:,\s\w]*?)\s*>\s*\)\s*([_\w]*?)\s*$/); // 不支持<,>分隔符 const re = (/\(\s*([\w]*?)\s*<\s*([_:,\s\w]*?)\s*>\s*\)\s*([_\w]*)\s*(?:\<(.*?)\>)?\s*/); // 检查是否 if(!re.test(expression_string)) return null; // console.log("XTemplateString.matchForceType re:", re.exec(expression_string)); const match = re.exec(expression_string); const type = match[1]; const pairs = match[2]; const key = match[3]; const splitFlag = match[4]; // console.log("matchForceType type:(", type, ") pairs:(", pairs, ") key:(", key, ")"); const value = XTakeAt.getValue(item, key, ''); // console.log("matchForceType value:", value) // 边界检查:如果列表值为空,返回空列表! if(!value || !value.length || !value[0]){ return "[]"; } // 边界检查:如果匹配不完整,退出! if(!type || type!=='Array' || !key){ return typeof value === 'object' && JSON.stringify(value) || value; } // 生成转换用的键值对。 let pairList = pairs.split(',').map(it => { // console.log("matchForceType it:", it) let kv = it.split(':'); return { targetKey: kv.length>=1 ? kv[0].trim() : '', sourceKey: kv.length>=2 ? kv[1].trim() : kv[0] || '', // 功能细节:如果没有传sourceKey,则代表来源键和目标键同名! } }); // console.log("matchForceType pairList:", pairList) let sourceValue = value; // 边界检查:如为字符串,逗号分隔为数组 if(typeof sourceValue === "string"){ sourceValue = sourceValue.split(splitFlag || ';'); // 问:能不能指定分隔符?by Sieyoo } // 边界检查:如果不是数组,退出! if(!Array.isArray(sourceValue) ){ return typeof value === 'object' && JSON.stringify(value) || value; } // console.log("matchForceType sourceValue:", sourceValue) let result = sourceValue.map(item => { // 如果没有目标键,则返回一维数组,即数组内是字符串 if(pairList.length && !pairList[0].targetKey){ return item[pairList[0].sourceKey] || item || ''; } // 如果来源值是一维(字符串)数组,则仅返回第一组键值对(可解释为非贪婪模式?) if(pairList.length && typeof item === "string"){ return {[pairList[0].sourceKey]: item}; } // 生成新的键值对 let newObj = {} pairList.forEach(it => { newObj[it.targetKey] = item[it.sourceKey] || ''; }); return newObj; }) // console.log("matchForceType result:", result) return JSON.stringify(result); } export default XTemplateString;