初始化
This commit is contained in:
553
src/utils/StrUtil.js
Normal file
553
src/utils/StrUtil.js
Normal file
@@ -0,0 +1,553 @@
|
||||
/**
|
||||
* 字符串工具类
|
||||
**/
|
||||
export const StrUtil = {
|
||||
/**
|
||||
* 字符串是否为空白 空白的定义如下: <br>
|
||||
* 1、为null <br>
|
||||
* 2、为不可见字符(如空格)<br>
|
||||
* 3、""<br>
|
||||
*
|
||||
* @param str 被检测的字符串
|
||||
* @return boolean 是否为空
|
||||
*/
|
||||
isBlank: function (str) {
|
||||
return str === undefined || str == null || this.trim(str) === "";
|
||||
|
||||
},
|
||||
/**
|
||||
* 字符串是否为非空白 空白的定义如下: <br>
|
||||
* 1、不为null <br>
|
||||
* 2、不为不可见字符(如空格)<br>
|
||||
* 3、不为""<br>
|
||||
*
|
||||
* @param str 被检测的字符串
|
||||
* @return boolean 是否为非空
|
||||
*/
|
||||
isNotBlank: function (str) {
|
||||
// == 代表相同,=== 代表严格相同
|
||||
return false === StrUtil.isBlank(str);
|
||||
},
|
||||
/**
|
||||
* 字符串是否为空,空的定义如下:<br>
|
||||
* 1、为null <br>
|
||||
* 2、为""<br>
|
||||
*
|
||||
* @param str 被检测的字符串
|
||||
* @return boolean 是否为空
|
||||
*/
|
||||
isEmpty: function (str) {
|
||||
return str == null || str === "";
|
||||
|
||||
},
|
||||
/**
|
||||
* 字符串是否为非空白 空白的定义如下: <br>
|
||||
* 1、不为null <br>
|
||||
* 2、不为""<br>
|
||||
*
|
||||
* @param str 被检测的字符串
|
||||
* @return boolean 是否为非空
|
||||
*/
|
||||
isNotEmpty: function (str) {
|
||||
return !StrUtil.isEmpty(str);
|
||||
},
|
||||
/**
|
||||
* 空对象转字符串
|
||||
*
|
||||
* @param str 被检查的字符串
|
||||
* @return string 原字符串或者空串
|
||||
*/
|
||||
nullToStr: function (str) {
|
||||
if (StrUtil.isEmpty(str)) {
|
||||
return "";
|
||||
}
|
||||
return str;
|
||||
},
|
||||
/**
|
||||
* 空格截取
|
||||
*
|
||||
* @param str 截取的字符串
|
||||
* @return string
|
||||
*/
|
||||
trim: function (str) {
|
||||
if (str == null) {
|
||||
return "";
|
||||
}
|
||||
return str.toString().replace(/(^\s*)|(\s*$)|\r|\n/g, "");
|
||||
},
|
||||
/**
|
||||
* 比较两个字符串(大小写敏感)
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param that 比较的字符串
|
||||
* @return boolean
|
||||
*/
|
||||
equals: function (str, that) {
|
||||
return str === that;
|
||||
},
|
||||
/**
|
||||
* 比较两个字符串(大小写不敏感)
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param that 比较的字符串
|
||||
* @return boolean
|
||||
*/
|
||||
equalsIgnoreCase: function (str, that) {
|
||||
return String(str).toUpperCase() === String(that).toUpperCase();
|
||||
},
|
||||
/**
|
||||
* 将字符串按指定字符分割
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param sep 比较的字符串
|
||||
* @param maxLen 最大长度
|
||||
* @return string[] 分割后的数组
|
||||
*/
|
||||
split: function (str, sep, maxLen) {
|
||||
if (StrUtil.isEmpty(str)) {
|
||||
return null;
|
||||
}
|
||||
const value = String(str).split(sep);
|
||||
return maxLen ? value.slice(0, maxLen - 1) : value;
|
||||
},
|
||||
/**
|
||||
* 字符串格式化(%s )
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return 格式化后的字符串
|
||||
*/
|
||||
sprintf: function (str) {
|
||||
let args = arguments, flag = true, i = 1;
|
||||
str = str.replace(/%s/g, function () {
|
||||
const arg = args[i++];
|
||||
if (typeof arg === 'undefined') {
|
||||
flag = false;
|
||||
return '';
|
||||
}
|
||||
return arg;
|
||||
});
|
||||
return flag ? str : '';
|
||||
},
|
||||
/**
|
||||
* 判断字符串是否是以start开头
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param start 开始的字符串
|
||||
* @return boolean
|
||||
*/
|
||||
startWith: function (str, start) {
|
||||
const reg = new RegExp("^" + start);
|
||||
return reg.test(str);
|
||||
},
|
||||
/**
|
||||
* 判断字符串是否是以end结尾
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param end 结尾的字符串
|
||||
* @return boolean
|
||||
*/
|
||||
endWith: function (str, end) {
|
||||
const reg = new RegExp(end + "$");
|
||||
return reg.test(str);
|
||||
},
|
||||
containsWhitespace: function (input) {
|
||||
return this.contains(input, ' ');
|
||||
},
|
||||
//生成指定个数的字符
|
||||
repeat: function (ch, repeatTimes) {
|
||||
let result = "";
|
||||
for (let i = 0; i < repeatTimes; i++) {
|
||||
result += ch;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
deleteWhitespace: function (input) {
|
||||
return input.replace(/\s+/g, '');
|
||||
},
|
||||
rightPad: function (input, size, padStr) {
|
||||
return input + this.repeat(padStr, size);
|
||||
},
|
||||
leftPad: function (input, size, padStr) {
|
||||
return this.repeat(padStr, size) + input;
|
||||
},
|
||||
//首小写字母转大写
|
||||
capitalize: function (input) {
|
||||
let strLen = 0;
|
||||
if (input == null || (strLen = input.length) === 0) {
|
||||
return input;
|
||||
}
|
||||
return input.replace(/^[a-z]/, function (matchStr) {
|
||||
return matchStr.toLocaleUpperCase();
|
||||
});
|
||||
},
|
||||
//首大写字母转小写
|
||||
uncapitalize: function (input) {
|
||||
let strLen = 0;
|
||||
if (input == null || (strLen = input.length) === 0) {
|
||||
return input;
|
||||
}
|
||||
return input.replace(/^[A-Z]/, function (matchStr) {
|
||||
return matchStr.toLocaleLowerCase();
|
||||
});
|
||||
},
|
||||
//大写转小写,小写转大写
|
||||
swapCase: function (input) {
|
||||
return input.replace(/[a-z]/ig, function (matchStr) {
|
||||
if (matchStr >= 'A' && matchStr <= 'Z') {
|
||||
return matchStr.toLocaleLowerCase();
|
||||
} else if (matchStr >= 'a' && matchStr <= 'z') {
|
||||
return matchStr.toLocaleUpperCase();
|
||||
}
|
||||
});
|
||||
},
|
||||
//统计含有的子字符串的个数
|
||||
countMatches: function (input, sub) {
|
||||
if (this.isEmpty(input) || this.isEmpty(sub)) {
|
||||
return 0;
|
||||
}
|
||||
let count = 0;
|
||||
let index = 0;
|
||||
while ((index = input.indexOf(sub, index)) !== -1) {
|
||||
index += sub.length;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
},
|
||||
//只包含字母
|
||||
isAlpha: function (input) {
|
||||
return /^[a-z]+$/i.test(input);
|
||||
},
|
||||
//只包含字母、空格
|
||||
isAlphaSpace: function (input) {
|
||||
return /^[a-z\s]*$/i.test(input);
|
||||
},
|
||||
//只包含字母、数字
|
||||
isAlphanumeric: function (input) {
|
||||
return /^[a-z0-9]+$/i.test(input);
|
||||
},
|
||||
//只包含字母、数字和空格
|
||||
isAlphanumericSpace: function (input) {
|
||||
return /^[a-z0-9\s]*$/i.test(input);
|
||||
},
|
||||
//数字
|
||||
isNumeric: function (input) {
|
||||
return /^(?:[1-9]\d*|0)(?:\.\d+)?$/.test(input);
|
||||
},
|
||||
//小数
|
||||
isDecimal: function (input) {
|
||||
return /^[-+]?(?:0|[1-9]\d*)\.\d+$/.test(input);
|
||||
},
|
||||
//负小数
|
||||
isNegativeDecimal: function (input) {
|
||||
return /^\-?(?:0|[1-9]\d*)\.\d+$/.test(input);
|
||||
},
|
||||
//正小数
|
||||
isPositiveDecimal: function (input) {
|
||||
return /^\+?(?:0|[1-9]\d*)\.\d+$/.test(input);
|
||||
},
|
||||
//整数
|
||||
isInteger: function (input) {
|
||||
return /^[-+]?(?:0|[1-9]\d*)$/.test(input);
|
||||
},
|
||||
//正整数
|
||||
isPositiveInteger: function (input) {
|
||||
return /^\+?(?:0|[1-9]\d*)$/.test(input);
|
||||
},
|
||||
//负整数
|
||||
isNegativeInteger: function (input) {
|
||||
return /^\-?(?:0|[1-9]\d*)$/.test(input);
|
||||
},
|
||||
//只包含数字和空格
|
||||
isNumericSpace: function (input) {
|
||||
return /^[\d\s]*$/.test(input);
|
||||
},
|
||||
isWhitespace: function (input) {
|
||||
return /^\s*$/.test(input);
|
||||
},
|
||||
isAllLowerCase: function (input) {
|
||||
return /^[a-z]+$/.test(input);
|
||||
},
|
||||
isAllUpperCase: function (input) {
|
||||
return /^[A-Z]+$/.test(input);
|
||||
},
|
||||
defaultString: function (input, defaultStr) {
|
||||
return input == null ? defaultStr : input;
|
||||
},
|
||||
defaultIfBlank: function (input, defaultStr) {
|
||||
return this.isBlank(input) ? defaultStr : input;
|
||||
},
|
||||
defaultIfEmpty: function (input, defaultStr) {
|
||||
return this.isEmpty(input) ? defaultStr : input;
|
||||
},
|
||||
//字符串反转
|
||||
reverse: function (input) {
|
||||
if (this.isBlank(input)) {
|
||||
input;
|
||||
}
|
||||
return input.split("").reverse().join("");
|
||||
},
|
||||
//删掉特殊字符(英文状态下)
|
||||
removeSpecialCharacter: function (input) {
|
||||
return input.replace(/[!-/:-@\[-`{-~]/g, "");
|
||||
},
|
||||
//只包含特殊字符、数字和字母(不包括空格,若想包括空格,改为[ -~])
|
||||
isSpecialCharacterAlphanumeric: function (input) {
|
||||
return /^[!-~]+$/.test(input);
|
||||
},
|
||||
/**
|
||||
* 校验时排除某些字符串,即不能包含某些字符串
|
||||
* @param {Object} conditions:里面有多个属性,如下:
|
||||
*
|
||||
* @param {String} matcherFlag 匹配标识
|
||||
* 0:数字;1:字母;2:小写字母;3:大写字母;4:特殊字符,指英文状态下的标点符号及括号等;5:中文;
|
||||
* 6:数字和字母;7:数字和小写字母;8:数字和大写字母;9:数字、字母和特殊字符;10:数字和中文;
|
||||
* 11:小写字母和特殊字符;12:大写字母和特殊字符;13:字母和特殊字符;14:小写字母和中文;15:大写字母和中文;
|
||||
* 16:字母和中文;17:特殊字符、和中文;18:特殊字符、字母和中文;19:特殊字符、小写字母和中文;20:特殊字符、大写字母和中文;
|
||||
* 100:所有字符;
|
||||
* @param {Array} excludeStrArr 排除的字符串,数组格式
|
||||
* @param {String} length 长度,可为空。1,2表示长度1到2之间;10,表示10个以上字符;5表示长度为5
|
||||
* @param {Boolean} ignoreCase 是否忽略大小写
|
||||
* conditions={matcherFlag:"0",excludeStrArr:[],length:"",ignoreCase:true}
|
||||
*/
|
||||
isPatternMustExcludeSomeStr: function (input, conditions) {
|
||||
//参数
|
||||
const matcherFlag = conditions.matcherFlag;
|
||||
const excludeStrArr = conditions.excludeStrArr;
|
||||
const length = conditions.length;
|
||||
const ignoreCase = conditions.ignoreCase;
|
||||
//拼正则
|
||||
const size = excludeStrArr.length;
|
||||
let regex = (size === 0) ? "^" : "^(?!.*(?:{0}))";
|
||||
let subPattern = "";
|
||||
for (let i = 0; i < size; i++) {
|
||||
excludeStrArr[i] = Bee.StringUtils.escapeMetacharacterOfStr(excludeStrArr[i]);
|
||||
subPattern += excludeStrArr[i];
|
||||
if (i !== size - 1) {
|
||||
subPattern += "|";
|
||||
}
|
||||
}
|
||||
regex = this.format(regex, [subPattern]);
|
||||
switch (matcherFlag) {
|
||||
case '0':
|
||||
regex += "\\d";
|
||||
break;
|
||||
case '1':
|
||||
regex += "[a-zA-Z]";
|
||||
break;
|
||||
case '2':
|
||||
regex += "[a-z]";
|
||||
break;
|
||||
case '3':
|
||||
regex += "[A-Z]";
|
||||
break;
|
||||
case '4':
|
||||
regex += "[!-/:-@\[-`{-~]";
|
||||
break;
|
||||
case '5':
|
||||
regex += "[\u4E00-\u9FA5]";
|
||||
break;
|
||||
case '6':
|
||||
regex += "[a-zA-Z0-9]";
|
||||
break;
|
||||
case '7':
|
||||
regex += "[a-z0-9]";
|
||||
break;
|
||||
case '8':
|
||||
regex += "[A-Z0-9]";
|
||||
break;
|
||||
case '9':
|
||||
regex += "[!-~]";
|
||||
break;
|
||||
case '10':
|
||||
regex += "[0-9\u4E00-\u9FA5]";
|
||||
break;
|
||||
case '11':
|
||||
regex += "[a-z!-/:-@\[-`{-~]";
|
||||
break;
|
||||
case '12':
|
||||
regex += "[A-Z!-/:-@\[-`{-~]";
|
||||
break;
|
||||
case '13':
|
||||
regex += "[a-zA-Z!-/:-@\[-`{-~]";
|
||||
break;
|
||||
case '14':
|
||||
regex += "[a-z\u4E00-\u9FA5]";
|
||||
break;
|
||||
case '15':
|
||||
regex += "[A-Z\u4E00-\u9FA5]";
|
||||
break;
|
||||
case '16':
|
||||
regex += "[a-zA-Z\u4E00-\u9FA5]";
|
||||
break;
|
||||
case '17':
|
||||
regex += "[\u4E00-\u9FA5!-/:-@\[-`{-~]";
|
||||
break;
|
||||
case '18':
|
||||
regex += "[\u4E00-\u9FA5!-~]";
|
||||
break;
|
||||
case '19':
|
||||
regex += "[a-z\u4E00-\u9FA5!-/:-@\[-`{-~]";
|
||||
break;
|
||||
case '20':
|
||||
regex += "[A-Z\u4E00-\u9FA5!-/:-@\[-`{-~]";
|
||||
break;
|
||||
case '100':
|
||||
regex += "[\s\S]";
|
||||
break;
|
||||
default:
|
||||
alert(matcherFlag + ":This type is not supported!");
|
||||
}
|
||||
regex += this.isNotBlank(length) ? "{" + length + "}" : "+";
|
||||
regex += "$";
|
||||
const pattern = new RegExp(regex, ignoreCase ? "i" : "");
|
||||
return pattern.test(input);
|
||||
},
|
||||
/**
|
||||
* @param {String} message
|
||||
* @param {Array} arr
|
||||
* 消息格式化
|
||||
*/
|
||||
format: function (message, arr) {
|
||||
return message.replace(/{(\d+)}/g, function (matchStr, group1) {
|
||||
return arr[group1];
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 把连续出现多次的字母字符串进行压缩。如输入:aaabbbbcccccd 输出:3a4b5cd
|
||||
* @param {String} input
|
||||
* @param {Boolean} ignoreCase : true or false
|
||||
*/
|
||||
compressRepeatedStr: function (input, ignoreCase) {
|
||||
const pattern = new RegExp("([a-z])\\1+", ignoreCase ? "ig" : "g");
|
||||
return input.replace(pattern, function (matchStr, group1) {
|
||||
return matchStr.length + group1;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 校验必须同时包含某些字符串
|
||||
* @param {String} input
|
||||
* @param {Object} conditions:里面有多个属性,如下:
|
||||
*
|
||||
* @param {String} matcherFlag 匹配标识
|
||||
* 0:数字;1:字母;2:小写字母;3:大写字母;4:特殊字符,指英文状态下的标点符号及括号等;5:中文;
|
||||
* 6:数字和字母;7:数字和小写字母;8:数字和大写字母;9:数字、字母和特殊字符;10:数字和中文;
|
||||
* 11:小写字母和特殊字符;12:大写字母和特殊字符;13:字母和特殊字符;14:小写字母和中文;15:大写字母和中文;
|
||||
* 16:字母和中文;17:特殊字符、和中文;18:特殊字符、字母和中文;19:特殊字符、小写字母和中文;20:特殊字符、大写字母和中文;
|
||||
* 100:所有字符;
|
||||
* @param {Array} excludeStrArr 排除的字符串,数组格式
|
||||
* @param {String} length 长度,可为空。1,2表示长度1到2之间;10,表示10个以上字符;5表示长度为5
|
||||
* @param {Boolean} ignoreCase 是否忽略大小写
|
||||
* conditions={matcherFlag:"0",containStrArr:[],length:"",ignoreCase:true}
|
||||
*
|
||||
*/
|
||||
isPatternMustContainSomeStr: function (input, conditions) {
|
||||
//参数
|
||||
const matcherFlag = conditions.matcherFlag;
|
||||
const containStrArr = conditions.containStrArr;
|
||||
const length = conditions.length;
|
||||
const ignoreCase = conditions.ignoreCase;
|
||||
//创建正则
|
||||
const size = containStrArr.length;
|
||||
let regex = "^";
|
||||
let subPattern = "";
|
||||
for (let i = 0; i < size; i++) {
|
||||
containStrArr[i] = Bee.StringUtils.escapeMetacharacterOfStr(containStrArr[i]);
|
||||
subPattern += "(?=.*" + containStrArr[i] + ")";
|
||||
}
|
||||
regex += subPattern;
|
||||
switch (matcherFlag) {
|
||||
case '0':
|
||||
regex += "\\d";
|
||||
break;
|
||||
case '1':
|
||||
regex += "[a-zA-Z]";
|
||||
break;
|
||||
case '2':
|
||||
regex += "[a-z]";
|
||||
break;
|
||||
case '3':
|
||||
regex += "[A-Z]";
|
||||
break;
|
||||
case '4':
|
||||
regex += "[!-/:-@\[-`{-~]";
|
||||
break;
|
||||
case '5':
|
||||
regex += "[\u4E00-\u9FA5]";
|
||||
break;
|
||||
case '6':
|
||||
regex += "[a-zA-Z0-9]";
|
||||
break;
|
||||
case '7':
|
||||
regex += "[a-z0-9]";
|
||||
break;
|
||||
case '8':
|
||||
regex += "[A-Z0-9]";
|
||||
break;
|
||||
case '9':
|
||||
regex += "[!-~]";
|
||||
break;
|
||||
case '10':
|
||||
regex += "[0-9\u4E00-\u9FA5]";
|
||||
break;
|
||||
case '11':
|
||||
regex += "[a-z!-/:-@\[-`{-~]";
|
||||
break;
|
||||
case '12':
|
||||
regex += "[A-Z!-/:-@\[-`{-~]";
|
||||
break;
|
||||
case '13':
|
||||
regex += "[a-zA-Z!-/:-@\[-`{-~]";
|
||||
break;
|
||||
case '14':
|
||||
regex += "[a-z\u4E00-\u9FA5]";
|
||||
break;
|
||||
case '15':
|
||||
regex += "[A-Z\u4E00-\u9FA5]";
|
||||
break;
|
||||
case '16':
|
||||
regex += "[a-zA-Z\u4E00-\u9FA5]";
|
||||
break;
|
||||
case '17':
|
||||
regex += "[\u4E00-\u9FA5!-/:-@\[-`{-~]";
|
||||
break;
|
||||
case '18':
|
||||
regex += "[\u4E00-\u9FA5!-~]";
|
||||
break;
|
||||
case '19':
|
||||
regex += "[a-z\u4E00-\u9FA5!-/:-@\[-`{-~]";
|
||||
break;
|
||||
case '20':
|
||||
regex += "[A-Z\u4E00-\u9FA5!-/:-@\[-`{-~]";
|
||||
break;
|
||||
case '100':
|
||||
regex += "[\s\S]";
|
||||
break;
|
||||
default:
|
||||
alert(matcherFlag + ":This type is not supported!");
|
||||
}
|
||||
regex += this.isNotBlank(length) ? "{" + length + "}" : "+";
|
||||
regex += "$";
|
||||
const pattern = new RegExp(regex, ignoreCase ? "i" : "");
|
||||
return pattern.test(input);
|
||||
},
|
||||
//中文校验
|
||||
isChinese: function (input) {
|
||||
return /^[\u4E00-\u9FA5]+$/.test(input);
|
||||
},
|
||||
//去掉中文字符
|
||||
removeChinese: function (input) {
|
||||
return input.replace(/[\u4E00-\u9FA5]+/gm, "");
|
||||
},
|
||||
//转义元字符
|
||||
escapeMetacharacter: function (input) {
|
||||
const metacharacter = "^$()*+.[]|\\-?{}|";
|
||||
if (metacharacter.indexOf(input) >= 0) {
|
||||
input = "\\" + input;
|
||||
}
|
||||
return input;
|
||||
},
|
||||
//转义字符串中的元字符
|
||||
escapeMetacharacterOfStr: function (input) {
|
||||
return input.replace(/[\^\$\*\+\.\|\\\-\?\{\}\|]/gm, "\\$&");
|
||||
}
|
||||
};
|
||||
57
src/utils/ai.js
Normal file
57
src/utils/ai.js
Normal file
@@ -0,0 +1,57 @@
|
||||
export const initCoze = async function (userInfo) {
|
||||
return new CozeWebSDK.WebChatClient({
|
||||
config: {
|
||||
bot_id: "7517200991494717475",
|
||||
botInfo: {
|
||||
parameters: {
|
||||
user_role: userInfo.roleGroup,
|
||||
user_name: userInfo.nickName,
|
||||
user_id: userInfo.username,
|
||||
user_token: userInfo.user_token,
|
||||
},
|
||||
},
|
||||
},
|
||||
auth: {
|
||||
type: "token",
|
||||
token: userInfo.accessToken,
|
||||
onRefreshToken: async () => {
|
||||
return userInfo.userInfo.accessToken;
|
||||
},
|
||||
},
|
||||
userInfo: {
|
||||
id: userInfo.username,
|
||||
nickname: userInfo.nickName,
|
||||
url: userInfo.avater,
|
||||
},
|
||||
ui: {
|
||||
base: {
|
||||
icon: "https://p3-flow-imagex-sign.byteimg.com/ocean-cloud-tos/FileBizType.BIZ_BOT_ICON/4185184193750220_1751011524142392616_RI5wYOximW.jpg~tplv-a9rns2rl98-image-qvalue.jpeg?rk3s=bbd3e7ed&x-expires=1753606533&x-signature=OxWu7ZE9AsolGXHOZGxYQf36bt4%3D",
|
||||
layout: "pc",
|
||||
lang: "zh-CN",
|
||||
zIndex: 1000,
|
||||
},
|
||||
chatBot: {
|
||||
title: "“智水”AI辅导员",
|
||||
uploadable: true,
|
||||
width: 500,
|
||||
},
|
||||
asstBtn: {
|
||||
isNeed: false,
|
||||
},
|
||||
header: {
|
||||
isShow: true,
|
||||
isNeedClose: true,
|
||||
},
|
||||
footer: {
|
||||
isShow: false,
|
||||
expressionText: "智能体由 {{name}}提供技术支持",
|
||||
linkvars: {
|
||||
name: {
|
||||
text: "广西厚溥",
|
||||
link: "http://www.gxxdit.com/",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
25
src/utils/auth.js
Normal file
25
src/utils/auth.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
const TokenKey = 'Admin-Token'
|
||||
|
||||
export function getToken() {
|
||||
console.log('getToken')
|
||||
return Cookies.get(TokenKey)
|
||||
}
|
||||
|
||||
export function setToken(token) {
|
||||
return Cookies.set(TokenKey, token)
|
||||
}
|
||||
|
||||
export function setTokenKeySessionStorage(token) {
|
||||
sessionStorage.setItem(TokenKey, token)
|
||||
// return Cookies.set(TokenKey, token)
|
||||
}
|
||||
|
||||
export function getTokenKeySessionStorage() {
|
||||
return sessionStorage.getItem(TokenKey)
|
||||
}
|
||||
|
||||
export function removeToken() {
|
||||
return Cookies.remove(TokenKey)
|
||||
}
|
||||
82
src/utils/dict/Dict.js
Normal file
82
src/utils/dict/Dict.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import Vue from 'vue'
|
||||
import { mergeRecursive } from "@/utils/ruoyi";
|
||||
import DictMeta from './DictMeta'
|
||||
import DictData from './DictData'
|
||||
|
||||
const DEFAULT_DICT_OPTIONS = {
|
||||
types: [],
|
||||
}
|
||||
|
||||
/**
|
||||
* @classdesc 字典
|
||||
* @property {Object} label 标签对象,内部属性名为字典类型名称
|
||||
* @property {Object} dict 字段数组,内部属性名为字典类型名称
|
||||
* @property {Array.<DictMeta>} _dictMetas 字典元数据数组
|
||||
*/
|
||||
export default class Dict {
|
||||
constructor() {
|
||||
this.owner = null
|
||||
this.label = {}
|
||||
this.type = {}
|
||||
}
|
||||
|
||||
init(options) {
|
||||
if (options instanceof Array) {
|
||||
options = { types: options }
|
||||
}
|
||||
const opts = mergeRecursive(DEFAULT_DICT_OPTIONS, options)
|
||||
if (opts.types === undefined) {
|
||||
throw new Error('need dict types')
|
||||
}
|
||||
const ps = []
|
||||
this._dictMetas = opts.types.map(t => DictMeta.parse(t))
|
||||
this._dictMetas.forEach(dictMeta => {
|
||||
const type = dictMeta.type
|
||||
Vue.set(this.label, type, {})
|
||||
Vue.set(this.type, type, [])
|
||||
if (dictMeta.lazy) {
|
||||
return
|
||||
}
|
||||
ps.push(loadDict(this, dictMeta))
|
||||
})
|
||||
return Promise.all(ps)
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新加载字典
|
||||
* @param {String} type 字典类型
|
||||
*/
|
||||
reloadDict(type) {
|
||||
const dictMeta = this._dictMetas.find(e => e.type === type)
|
||||
if (dictMeta === undefined) {
|
||||
return Promise.reject(`the dict meta of ${type} was not found`)
|
||||
}
|
||||
return loadDict(this, dictMeta)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典
|
||||
* @param {Dict} dict 字典
|
||||
* @param {DictMeta} dictMeta 字典元数据
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function loadDict(dict, dictMeta) {
|
||||
return dictMeta.request(dictMeta)
|
||||
.then(response => {
|
||||
const type = dictMeta.type
|
||||
let dicts = dictMeta.responseConverter(response, dictMeta)
|
||||
if (!(dicts instanceof Array)) {
|
||||
console.error('the return of responseConverter must be Array.<DictData>')
|
||||
dicts = []
|
||||
} else if (dicts.filter(d => d instanceof DictData).length !== dicts.length) {
|
||||
console.error('the type of elements in dicts must be DictData')
|
||||
dicts = []
|
||||
}
|
||||
dict.type[type].splice(0, Number.MAX_SAFE_INTEGER, ...dicts)
|
||||
dicts.forEach(d => {
|
||||
Vue.set(dict.label[type], d.value, d.label)
|
||||
})
|
||||
return dicts
|
||||
})
|
||||
}
|
||||
17
src/utils/dict/DictConverter.js
Normal file
17
src/utils/dict/DictConverter.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import DictOptions from './DictOptions'
|
||||
import DictData from './DictData'
|
||||
|
||||
export default function(dict, dictMeta) {
|
||||
const label = determineDictField(dict, dictMeta.labelField, ...DictOptions.DEFAULT_LABEL_FIELDS)
|
||||
const value = determineDictField(dict, dictMeta.valueField, ...DictOptions.DEFAULT_VALUE_FIELDS)
|
||||
return new DictData(dict[label], dict[value], dict)
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定字典字段
|
||||
* @param {DictData} dict
|
||||
* @param {...String} fields
|
||||
*/
|
||||
function determineDictField(dict, ...fields) {
|
||||
return fields.find(f => Object.prototype.hasOwnProperty.call(dict, f))
|
||||
}
|
||||
13
src/utils/dict/DictData.js
Normal file
13
src/utils/dict/DictData.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @classdesc 字典数据
|
||||
* @property {String} label 标签
|
||||
* @property {*} value 标签
|
||||
* @property {Object} raw 原始数据
|
||||
*/
|
||||
export default class DictData {
|
||||
constructor(label, value, raw) {
|
||||
this.label = label
|
||||
this.value = value
|
||||
this.raw = raw
|
||||
}
|
||||
}
|
||||
38
src/utils/dict/DictMeta.js
Normal file
38
src/utils/dict/DictMeta.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import { mergeRecursive } from "@/utils/ruoyi";
|
||||
import DictOptions from './DictOptions'
|
||||
|
||||
/**
|
||||
* @classdesc 字典元数据
|
||||
* @property {String} type 类型
|
||||
* @property {Function} request 请求
|
||||
* @property {String} label 标签字段
|
||||
* @property {String} value 值字段
|
||||
*/
|
||||
export default class DictMeta {
|
||||
constructor(options) {
|
||||
this.type = options.type
|
||||
this.request = options.request
|
||||
this.responseConverter = options.responseConverter
|
||||
this.labelField = options.labelField
|
||||
this.valueField = options.valueField
|
||||
this.lazy = options.lazy === true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析字典元数据
|
||||
* @param {Object} options
|
||||
* @returns {DictMeta}
|
||||
*/
|
||||
DictMeta.parse= function(options) {
|
||||
let opts = null
|
||||
if (typeof options === 'string') {
|
||||
opts = DictOptions.metas[options] || {}
|
||||
opts.type = options
|
||||
} else if (typeof options === 'object') {
|
||||
opts = options
|
||||
}
|
||||
opts = mergeRecursive(DictOptions.metas['*'], opts)
|
||||
return new DictMeta(opts)
|
||||
}
|
||||
51
src/utils/dict/DictOptions.js
Normal file
51
src/utils/dict/DictOptions.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import { mergeRecursive } from "@/utils/ruoyi";
|
||||
import dictConverter from './DictConverter'
|
||||
|
||||
export const options = {
|
||||
metas: {
|
||||
'*': {
|
||||
/**
|
||||
* 字典请求,方法签名为function(dictMeta: DictMeta): Promise
|
||||
*/
|
||||
request: (dictMeta) => {
|
||||
console.log(`load dict ${dictMeta.type}`)
|
||||
return Promise.resolve([])
|
||||
},
|
||||
/**
|
||||
* 字典响应数据转换器,方法签名为function(response: Object, dictMeta: DictMeta): DictData
|
||||
*/
|
||||
responseConverter,
|
||||
labelField: 'label',
|
||||
valueField: 'value',
|
||||
},
|
||||
},
|
||||
/**
|
||||
* 默认标签字段
|
||||
*/
|
||||
DEFAULT_LABEL_FIELDS: ['label', 'name', 'title'],
|
||||
/**
|
||||
* 默认值字段
|
||||
*/
|
||||
DEFAULT_VALUE_FIELDS: ['value', 'id', 'uid', 'key'],
|
||||
}
|
||||
|
||||
/**
|
||||
* 映射字典
|
||||
* @param {Object} response 字典数据
|
||||
* @param {DictMeta} dictMeta 字典元数据
|
||||
* @returns {DictData}
|
||||
*/
|
||||
function responseConverter(response, dictMeta) {
|
||||
const dicts = response.content instanceof Array ? response.content : response
|
||||
if (dicts === undefined) {
|
||||
console.warn(`no dict data of "${dictMeta.type}" found in the response`)
|
||||
return []
|
||||
}
|
||||
return dicts.map(d => dictConverter(d, dictMeta))
|
||||
}
|
||||
|
||||
export function mergeOptions(src) {
|
||||
mergeRecursive(options, src)
|
||||
}
|
||||
|
||||
export default options
|
||||
33
src/utils/dict/index.js
Normal file
33
src/utils/dict/index.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import Dict from './Dict'
|
||||
import { mergeOptions } from './DictOptions'
|
||||
|
||||
export default function(Vue, options) {
|
||||
mergeOptions(options)
|
||||
Vue.mixin({
|
||||
data() {
|
||||
if (this.$options === undefined || this.$options.dicts === undefined || this.$options.dicts === null) {
|
||||
return {}
|
||||
}
|
||||
const dict = new Dict()
|
||||
dict.owner = this
|
||||
return {
|
||||
dict
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (!(this.dict instanceof Dict)) {
|
||||
return
|
||||
}
|
||||
options.onCreated && options.onCreated(this.dict)
|
||||
this.dict.init(this.$options.dicts).then(() => {
|
||||
options.onReady && options.onReady(this.dict)
|
||||
this.$nextTick(() => {
|
||||
this.$emit('dictReady', this.dict)
|
||||
if (this.$options.methods && this.$options.methods.onDictReady instanceof Function) {
|
||||
this.$options.methods.onDictReady.call(this, this.dict)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
6
src/utils/errorCode.js
Normal file
6
src/utils/errorCode.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
'401': '认证失败,无法访问系统资源',
|
||||
'403': '当前操作没有权限',
|
||||
'404': '访问资源不存在',
|
||||
'default': '系统未知错误,请反馈给管理员'
|
||||
}
|
||||
438
src/utils/generator/config.js
Normal file
438
src/utils/generator/config.js
Normal file
@@ -0,0 +1,438 @@
|
||||
export const formConf = {
|
||||
formRef: 'elForm',
|
||||
formModel: 'formData',
|
||||
size: 'medium',
|
||||
labelPosition: 'right',
|
||||
labelWidth: 100,
|
||||
formRules: 'rules',
|
||||
gutter: 15,
|
||||
disabled: false,
|
||||
span: 24,
|
||||
formBtns: true
|
||||
}
|
||||
|
||||
export const inputComponents = [
|
||||
{
|
||||
label: '单行文本',
|
||||
tag: 'el-input',
|
||||
tagIcon: 'input',
|
||||
placeholder: '请输入',
|
||||
defaultValue: undefined,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
clearable: true,
|
||||
prepend: '',
|
||||
append: '',
|
||||
'prefix-icon': '',
|
||||
'suffix-icon': '',
|
||||
maxlength: null,
|
||||
'show-word-limit': false,
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
required: true,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/input'
|
||||
},
|
||||
{
|
||||
label: '多行文本',
|
||||
tag: 'el-input',
|
||||
tagIcon: 'textarea',
|
||||
type: 'textarea',
|
||||
placeholder: '请输入',
|
||||
defaultValue: undefined,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
autosize: {
|
||||
minRows: 4,
|
||||
maxRows: 4
|
||||
},
|
||||
style: { width: '100%' },
|
||||
maxlength: null,
|
||||
'show-word-limit': false,
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
required: true,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/input'
|
||||
},
|
||||
{
|
||||
label: '密码',
|
||||
tag: 'el-input',
|
||||
tagIcon: 'password',
|
||||
placeholder: '请输入',
|
||||
defaultValue: undefined,
|
||||
span: 24,
|
||||
'show-password': true,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
clearable: true,
|
||||
prepend: '',
|
||||
append: '',
|
||||
'prefix-icon': '',
|
||||
'suffix-icon': '',
|
||||
maxlength: null,
|
||||
'show-word-limit': false,
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
required: true,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/input'
|
||||
},
|
||||
{
|
||||
label: '计数器',
|
||||
tag: 'el-input-number',
|
||||
tagIcon: 'number',
|
||||
placeholder: '',
|
||||
defaultValue: undefined,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
step: undefined,
|
||||
'step-strictly': false,
|
||||
precision: undefined,
|
||||
'controls-position': '',
|
||||
disabled: false,
|
||||
required: true,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/input-number'
|
||||
}
|
||||
]
|
||||
|
||||
export const selectComponents = [
|
||||
{
|
||||
label: '下拉选择',
|
||||
tag: 'el-select',
|
||||
tagIcon: 'select',
|
||||
placeholder: '请选择',
|
||||
defaultValue: undefined,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
clearable: true,
|
||||
disabled: false,
|
||||
required: true,
|
||||
filterable: false,
|
||||
multiple: false,
|
||||
options: [{
|
||||
label: '选项一',
|
||||
value: 1
|
||||
}, {
|
||||
label: '选项二',
|
||||
value: 2
|
||||
}],
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/select'
|
||||
},
|
||||
{
|
||||
label: '级联选择',
|
||||
tag: 'el-cascader',
|
||||
tagIcon: 'cascader',
|
||||
placeholder: '请选择',
|
||||
defaultValue: [],
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
props: {
|
||||
props: {
|
||||
multiple: false
|
||||
}
|
||||
},
|
||||
'show-all-levels': true,
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
filterable: false,
|
||||
required: true,
|
||||
options: [{
|
||||
id: 1,
|
||||
value: 1,
|
||||
label: '选项1',
|
||||
children: [{
|
||||
id: 2,
|
||||
value: 2,
|
||||
label: '选项1-1'
|
||||
}]
|
||||
}],
|
||||
dataType: 'dynamic',
|
||||
labelKey: 'label',
|
||||
valueKey: 'value',
|
||||
childrenKey: 'children',
|
||||
separator: '/',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/cascader'
|
||||
},
|
||||
{
|
||||
label: '单选框组',
|
||||
tag: 'el-radio-group',
|
||||
tagIcon: 'radio',
|
||||
defaultValue: undefined,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: {},
|
||||
optionType: 'default',
|
||||
border: false,
|
||||
size: 'medium',
|
||||
disabled: false,
|
||||
required: true,
|
||||
options: [{
|
||||
label: '选项一',
|
||||
value: 1
|
||||
}, {
|
||||
label: '选项二',
|
||||
value: 2
|
||||
}],
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/radio'
|
||||
},
|
||||
{
|
||||
label: '多选框组',
|
||||
tag: 'el-checkbox-group',
|
||||
tagIcon: 'checkbox',
|
||||
defaultValue: [],
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: {},
|
||||
optionType: 'default',
|
||||
border: false,
|
||||
size: 'medium',
|
||||
disabled: false,
|
||||
required: true,
|
||||
options: [{
|
||||
label: '选项一',
|
||||
value: 1
|
||||
}, {
|
||||
label: '选项二',
|
||||
value: 2
|
||||
}],
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/checkbox'
|
||||
},
|
||||
{
|
||||
label: '开关',
|
||||
tag: 'el-switch',
|
||||
tagIcon: 'switch',
|
||||
defaultValue: false,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: {},
|
||||
disabled: false,
|
||||
required: true,
|
||||
'active-text': '',
|
||||
'inactive-text': '',
|
||||
'active-color': null,
|
||||
'inactive-color': null,
|
||||
'active-value': true,
|
||||
'inactive-value': false,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/switch'
|
||||
},
|
||||
{
|
||||
label: '滑块',
|
||||
tag: 'el-slider',
|
||||
tagIcon: 'slider',
|
||||
defaultValue: null,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
disabled: false,
|
||||
required: true,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
'show-stops': false,
|
||||
range: false,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/slider'
|
||||
},
|
||||
{
|
||||
label: '时间选择',
|
||||
tag: 'el-time-picker',
|
||||
tagIcon: 'time',
|
||||
placeholder: '请选择',
|
||||
defaultValue: null,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
required: true,
|
||||
'picker-options': {
|
||||
selectableRange: '00:00:00-23:59:59'
|
||||
},
|
||||
format: 'HH:mm:ss',
|
||||
'value-format': 'HH:mm:ss',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/time-picker'
|
||||
},
|
||||
{
|
||||
label: '时间范围',
|
||||
tag: 'el-time-picker',
|
||||
tagIcon: 'time-range',
|
||||
defaultValue: null,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
required: true,
|
||||
'is-range': true,
|
||||
'range-separator': '至',
|
||||
'start-placeholder': '开始时间',
|
||||
'end-placeholder': '结束时间',
|
||||
format: 'HH:mm:ss',
|
||||
'value-format': 'HH:mm:ss',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/time-picker'
|
||||
},
|
||||
{
|
||||
label: '日期选择',
|
||||
tag: 'el-date-picker',
|
||||
tagIcon: 'date',
|
||||
placeholder: '请选择',
|
||||
defaultValue: null,
|
||||
type: 'date',
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
required: true,
|
||||
format: 'yyyy-MM-dd',
|
||||
'value-format': 'yyyy-MM-dd',
|
||||
readonly: false,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/date-picker'
|
||||
},
|
||||
{
|
||||
label: '日期范围',
|
||||
tag: 'el-date-picker',
|
||||
tagIcon: 'date-range',
|
||||
defaultValue: null,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: { width: '100%' },
|
||||
type: 'daterange',
|
||||
'range-separator': '至',
|
||||
'start-placeholder': '开始日期',
|
||||
'end-placeholder': '结束日期',
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
required: true,
|
||||
format: 'yyyy-MM-dd',
|
||||
'value-format': 'yyyy-MM-dd',
|
||||
readonly: false,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/date-picker'
|
||||
},
|
||||
{
|
||||
label: '评分',
|
||||
tag: 'el-rate',
|
||||
tagIcon: 'rate',
|
||||
defaultValue: 0,
|
||||
span: 24,
|
||||
labelWidth: null,
|
||||
style: {},
|
||||
max: 5,
|
||||
'allow-half': false,
|
||||
'show-text': false,
|
||||
'show-score': false,
|
||||
disabled: false,
|
||||
required: true,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/rate'
|
||||
},
|
||||
{
|
||||
label: '颜色选择',
|
||||
tag: 'el-color-picker',
|
||||
tagIcon: 'color',
|
||||
defaultValue: null,
|
||||
labelWidth: null,
|
||||
'show-alpha': false,
|
||||
'color-format': '',
|
||||
disabled: false,
|
||||
required: true,
|
||||
size: 'medium',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/color-picker'
|
||||
},
|
||||
{
|
||||
label: '上传',
|
||||
tag: 'el-upload',
|
||||
tagIcon: 'upload',
|
||||
action: 'https://jsonplaceholder.typicode.com/posts/',
|
||||
defaultValue: null,
|
||||
labelWidth: null,
|
||||
disabled: false,
|
||||
required: true,
|
||||
accept: '',
|
||||
name: 'file',
|
||||
'auto-upload': true,
|
||||
showTip: false,
|
||||
buttonText: '点击上传',
|
||||
fileSize: 2,
|
||||
sizeUnit: 'MB',
|
||||
'list-type': 'text',
|
||||
multiple: false,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/upload'
|
||||
}
|
||||
]
|
||||
|
||||
export const layoutComponents = [
|
||||
{
|
||||
layout: 'rowFormItem',
|
||||
tagIcon: 'row',
|
||||
type: 'default',
|
||||
justify: 'start',
|
||||
align: 'top',
|
||||
label: '行容器',
|
||||
layoutTree: true,
|
||||
children: [],
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/layout'
|
||||
},
|
||||
{
|
||||
layout: 'colFormItem',
|
||||
label: '按钮',
|
||||
changeTag: true,
|
||||
labelWidth: null,
|
||||
tag: 'el-button',
|
||||
tagIcon: 'button',
|
||||
span: 24,
|
||||
default: '主要按钮',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-search',
|
||||
size: 'medium',
|
||||
disabled: false,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/button'
|
||||
}
|
||||
]
|
||||
|
||||
// 组件rule的触发方式,无触发方式的组件不生成rule
|
||||
export const trigger = {
|
||||
'el-input': 'blur',
|
||||
'el-input-number': 'blur',
|
||||
'el-select': 'change',
|
||||
'el-radio-group': 'change',
|
||||
'el-checkbox-group': 'change',
|
||||
'el-cascader': 'change',
|
||||
'el-time-picker': 'change',
|
||||
'el-date-picker': 'change',
|
||||
'el-rate': 'change'
|
||||
}
|
||||
18
src/utils/generator/css.js
Normal file
18
src/utils/generator/css.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const styles = {
|
||||
'el-rate': '.el-rate{display: inline-block; vertical-align: text-top;}',
|
||||
'el-upload': '.el-upload__tip{line-height: 1.2;}'
|
||||
}
|
||||
|
||||
function addCss(cssList, el) {
|
||||
const css = styles[el.tag]
|
||||
css && cssList.indexOf(css) === -1 && cssList.push(css)
|
||||
if (el.children) {
|
||||
el.children.forEach(el2 => addCss(cssList, el2))
|
||||
}
|
||||
}
|
||||
|
||||
export function makeUpCss(conf) {
|
||||
const cssList = []
|
||||
conf.fields.forEach(el => addCss(cssList, el))
|
||||
return cssList.join('\n')
|
||||
}
|
||||
29
src/utils/generator/drawingDefault.js
Normal file
29
src/utils/generator/drawingDefault.js
Normal file
@@ -0,0 +1,29 @@
|
||||
export default [
|
||||
{
|
||||
layout: 'colFormItem',
|
||||
tagIcon: 'input',
|
||||
label: '手机号',
|
||||
vModel: 'mobile',
|
||||
formId: 6,
|
||||
tag: 'el-input',
|
||||
placeholder: '请输入手机号',
|
||||
defaultValue: '',
|
||||
span: 24,
|
||||
style: { width: '100%' },
|
||||
clearable: true,
|
||||
prepend: '',
|
||||
append: '',
|
||||
'prefix-icon': 'el-icon-mobile',
|
||||
'suffix-icon': '',
|
||||
maxlength: 11,
|
||||
'show-word-limit': true,
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
required: true,
|
||||
changeTag: true,
|
||||
regList: [{
|
||||
pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
|
||||
message: '手机号格式错误'
|
||||
}]
|
||||
}
|
||||
]
|
||||
359
src/utils/generator/html.js
Normal file
359
src/utils/generator/html.js
Normal file
@@ -0,0 +1,359 @@
|
||||
/* eslint-disable max-len */
|
||||
import { trigger } from './config'
|
||||
|
||||
let confGlobal
|
||||
let someSpanIsNot24
|
||||
|
||||
export function dialogWrapper(str) {
|
||||
return `<el-dialog v-bind="$attrs" v-on="$listeners" @open="onOpen" @close="onClose" title="Dialog Title">
|
||||
${str}
|
||||
<div slot="footer">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>`
|
||||
}
|
||||
|
||||
export function vueTemplate(str) {
|
||||
return `<template>
|
||||
<div>
|
||||
${str}
|
||||
</div>
|
||||
</template>`
|
||||
}
|
||||
|
||||
export function vueScript(str) {
|
||||
return `<script>
|
||||
${str}
|
||||
</script>`
|
||||
}
|
||||
|
||||
export function cssStyle(cssStr) {
|
||||
return `<style>
|
||||
${cssStr}
|
||||
</style>`
|
||||
}
|
||||
|
||||
function buildFormTemplate(conf, child, type) {
|
||||
let labelPosition = ''
|
||||
if (conf.labelPosition !== 'right') {
|
||||
labelPosition = `label-position="${conf.labelPosition}"`
|
||||
}
|
||||
const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : ''
|
||||
let str = `<el-form ref="${conf.formRef}" :model="${conf.formModel}" :rules="${conf.formRules}" size="${conf.size}" ${disabled} label-width="${conf.labelWidth}px" ${labelPosition}>
|
||||
${child}
|
||||
${buildFromBtns(conf, type)}
|
||||
</el-form>`
|
||||
if (someSpanIsNot24) {
|
||||
str = `<el-row :gutter="${conf.gutter}">
|
||||
${str}
|
||||
</el-row>`
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
function buildFromBtns(conf, type) {
|
||||
let str = ''
|
||||
if (conf.formBtns && type === 'file') {
|
||||
str = `<el-form-item size="large">
|
||||
<el-button type="primary" @click="submitForm">提交</el-button>
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
</el-form-item>`
|
||||
if (someSpanIsNot24) {
|
||||
str = `<el-col :span="24">
|
||||
${str}
|
||||
</el-col>`
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// span不为24的用el-col包裹
|
||||
function colWrapper(element, str) {
|
||||
if (someSpanIsNot24 || element.span !== 24) {
|
||||
return `<el-col :span="${element.span}">
|
||||
${str}
|
||||
</el-col>`
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
const layouts = {
|
||||
colFormItem(element) {
|
||||
let labelWidth = ''
|
||||
if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) {
|
||||
labelWidth = `label-width="${element.labelWidth}px"`
|
||||
}
|
||||
const required = !trigger[element.tag] && element.required ? 'required' : ''
|
||||
const tagDom = tags[element.tag] ? tags[element.tag](element) : null
|
||||
let str = `<el-form-item ${labelWidth} label="${element.label}" prop="${element.vModel}" ${required}>
|
||||
${tagDom}
|
||||
</el-form-item>`
|
||||
str = colWrapper(element, str)
|
||||
return str
|
||||
},
|
||||
rowFormItem(element) {
|
||||
const type = element.type === 'default' ? '' : `type="${element.type}"`
|
||||
const justify = element.type === 'default' ? '' : `justify="${element.justify}"`
|
||||
const align = element.type === 'default' ? '' : `align="${element.align}"`
|
||||
const gutter = element.gutter ? `gutter="${element.gutter}"` : ''
|
||||
const children = element.children.map(el => layouts[el.layout](el))
|
||||
let str = `<el-row ${type} ${justify} ${align} ${gutter}>
|
||||
${children.join('\n')}
|
||||
</el-row>`
|
||||
str = colWrapper(element, str)
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
const tags = {
|
||||
'el-button': el => {
|
||||
const {
|
||||
tag, disabled
|
||||
} = attrBuilder(el)
|
||||
const type = el.type ? `type="${el.type}"` : ''
|
||||
const icon = el.icon ? `icon="${el.icon}"` : ''
|
||||
const size = el.size ? `size="${el.size}"` : ''
|
||||
let child = buildElButtonChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}</${el.tag}>`
|
||||
},
|
||||
'el-input': el => {
|
||||
const {
|
||||
disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : ''
|
||||
const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : ''
|
||||
const readonly = el.readonly ? 'readonly' : ''
|
||||
const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : ''
|
||||
const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : ''
|
||||
const showPassword = el['show-password'] ? 'show-password' : ''
|
||||
const type = el.type ? `type="${el.type}"` : ''
|
||||
const autosize = el.autosize && el.autosize.minRows
|
||||
? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"`
|
||||
: ''
|
||||
let child = buildElInputChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${el.tag}>`
|
||||
},
|
||||
'el-input-number': el => {
|
||||
const { disabled, vModel, placeholder } = attrBuilder(el)
|
||||
const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : ''
|
||||
const min = el.min ? `:min='${el.min}'` : ''
|
||||
const max = el.max ? `:max='${el.max}'` : ''
|
||||
const step = el.step ? `:step='${el.step}'` : ''
|
||||
const stepStrictly = el['step-strictly'] ? 'step-strictly' : ''
|
||||
const precision = el.precision ? `:precision='${el.precision}'` : ''
|
||||
|
||||
return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-select': el => {
|
||||
const {
|
||||
disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const filterable = el.filterable ? 'filterable' : ''
|
||||
const multiple = el.multiple ? 'multiple' : ''
|
||||
let child = buildElSelectChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${el.tag}>`
|
||||
},
|
||||
'el-radio-group': el => {
|
||||
const { disabled, vModel } = attrBuilder(el)
|
||||
const size = `size="${el.size}"`
|
||||
let child = buildElRadioGroupChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${el.tag} ${vModel} ${size} ${disabled}>${child}</${el.tag}>`
|
||||
},
|
||||
'el-checkbox-group': el => {
|
||||
const { disabled, vModel } = attrBuilder(el)
|
||||
const size = `size="${el.size}"`
|
||||
const min = el.min ? `:min="${el.min}"` : ''
|
||||
const max = el.max ? `:max="${el.max}"` : ''
|
||||
let child = buildElCheckboxGroupChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${el.tag}>`
|
||||
},
|
||||
'el-switch': el => {
|
||||
const { disabled, vModel } = attrBuilder(el)
|
||||
const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : ''
|
||||
const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : ''
|
||||
const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : ''
|
||||
const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : ''
|
||||
const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : ''
|
||||
const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : ''
|
||||
|
||||
return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-cascader': el => {
|
||||
const {
|
||||
disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const options = el.options ? `:options="${el.vModel}Options"` : ''
|
||||
const props = el.props ? `:props="${el.vModel}Props"` : ''
|
||||
const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"'
|
||||
const filterable = el.filterable ? 'filterable' : ''
|
||||
const separator = el.separator === '/' ? '' : `separator="${el.separator}"`
|
||||
|
||||
return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-slider': el => {
|
||||
const { disabled, vModel } = attrBuilder(el)
|
||||
const min = el.min ? `:min='${el.min}'` : ''
|
||||
const max = el.max ? `:max='${el.max}'` : ''
|
||||
const step = el.step ? `:step='${el.step}'` : ''
|
||||
const range = el.range ? 'range' : ''
|
||||
const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : ''
|
||||
|
||||
return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-time-picker': el => {
|
||||
const {
|
||||
disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
|
||||
const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
|
||||
const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
|
||||
const isRange = el['is-range'] ? 'is-range' : ''
|
||||
const format = el.format ? `format="${el.format}"` : ''
|
||||
const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
|
||||
const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : ''
|
||||
|
||||
return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-date-picker': el => {
|
||||
const {
|
||||
disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
|
||||
const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
|
||||
const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
|
||||
const format = el.format ? `format="${el.format}"` : ''
|
||||
const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
|
||||
const type = el.type === 'date' ? '' : `type="${el.type}"`
|
||||
const readonly = el.readonly ? 'readonly' : ''
|
||||
|
||||
return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-rate': el => {
|
||||
const { disabled, vModel } = attrBuilder(el)
|
||||
const max = el.max ? `:max='${el.max}'` : ''
|
||||
const allowHalf = el['allow-half'] ? 'allow-half' : ''
|
||||
const showText = el['show-text'] ? 'show-text' : ''
|
||||
const showScore = el['show-score'] ? 'show-score' : ''
|
||||
|
||||
return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-color-picker': el => {
|
||||
const { disabled, vModel } = attrBuilder(el)
|
||||
const size = `size="${el.size}"`
|
||||
const showAlpha = el['show-alpha'] ? 'show-alpha' : ''
|
||||
const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : ''
|
||||
|
||||
return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${el.tag}>`
|
||||
},
|
||||
'el-upload': el => {
|
||||
const disabled = el.disabled ? ':disabled=\'true\'' : ''
|
||||
const action = el.action ? `:action="${el.vModel}Action"` : ''
|
||||
const multiple = el.multiple ? 'multiple' : ''
|
||||
const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : ''
|
||||
const accept = el.accept ? `accept="${el.accept}"` : ''
|
||||
const name = el.name !== 'file' ? `name="${el.name}"` : ''
|
||||
const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : ''
|
||||
const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"`
|
||||
const fileList = `:file-list="${el.vModel}fileList"`
|
||||
const ref = `ref="${el.vModel}"`
|
||||
let child = buildElUploadChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</${el.tag}>`
|
||||
}
|
||||
}
|
||||
|
||||
function attrBuilder(el) {
|
||||
return {
|
||||
vModel: `v-model="${confGlobal.formModel}.${el.vModel}"`,
|
||||
clearable: el.clearable ? 'clearable' : '',
|
||||
placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '',
|
||||
width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '',
|
||||
disabled: el.disabled ? ':disabled=\'true\'' : ''
|
||||
}
|
||||
}
|
||||
|
||||
// el-buttin 子级
|
||||
function buildElButtonChild(conf) {
|
||||
const children = []
|
||||
if (conf.default) {
|
||||
children.push(conf.default)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
// el-input innerHTML
|
||||
function buildElInputChild(conf) {
|
||||
const children = []
|
||||
if (conf.prepend) {
|
||||
children.push(`<template slot="prepend">${conf.prepend}</template>`)
|
||||
}
|
||||
if (conf.append) {
|
||||
children.push(`<template slot="append">${conf.append}</template>`)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
function buildElSelectChild(conf) {
|
||||
const children = []
|
||||
if (conf.options && conf.options.length) {
|
||||
children.push(`<el-option v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
function buildElRadioGroupChild(conf) {
|
||||
const children = []
|
||||
if (conf.options && conf.options.length) {
|
||||
const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio'
|
||||
const border = conf.border ? 'border' : ''
|
||||
children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
function buildElCheckboxGroupChild(conf) {
|
||||
const children = []
|
||||
if (conf.options && conf.options.length) {
|
||||
const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox'
|
||||
const border = conf.border ? 'border' : ''
|
||||
children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
function buildElUploadChild(conf) {
|
||||
const list = []
|
||||
if (conf['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>')
|
||||
else list.push(`<el-button size="small" type="primary" icon="el-icon-upload">${conf.buttonText}</el-button>`)
|
||||
if (conf.showTip) list.push(`<div slot="tip" class="el-upload__tip">只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件</div>`)
|
||||
return list.join('\n')
|
||||
}
|
||||
|
||||
export function makeUpHtml(conf, type) {
|
||||
const htmlList = []
|
||||
confGlobal = conf
|
||||
someSpanIsNot24 = conf.fields.some(item => item.span !== 24)
|
||||
conf.fields.forEach(el => {
|
||||
htmlList.push(layouts[el.layout](el))
|
||||
})
|
||||
const htmlStr = htmlList.join('\n')
|
||||
|
||||
let temp = buildFormTemplate(conf, htmlStr, type)
|
||||
if (type === 'dialog') {
|
||||
temp = dialogWrapper(temp)
|
||||
}
|
||||
confGlobal = null
|
||||
return temp
|
||||
}
|
||||
1
src/utils/generator/icon.json
Normal file
1
src/utils/generator/icon.json
Normal file
@@ -0,0 +1 @@
|
||||
["platform-eleme","eleme","delete-solid","delete","s-tools","setting","user-solid","user","phone","phone-outline","more","more-outline","star-on","star-off","s-goods","goods","warning","warning-outline","question","info","remove","circle-plus","success","error","zoom-in","zoom-out","remove-outline","circle-plus-outline","circle-check","circle-close","s-help","help","minus","plus","check","close","picture","picture-outline","picture-outline-round","upload","upload2","download","camera-solid","camera","video-camera-solid","video-camera","message-solid","bell","s-cooperation","s-order","s-platform","s-fold","s-unfold","s-operation","s-promotion","s-home","s-release","s-ticket","s-management","s-open","s-shop","s-marketing","s-flag","s-comment","s-finance","s-claim","s-custom","s-opportunity","s-data","s-check","s-grid","menu","share","d-caret","caret-left","caret-right","caret-bottom","caret-top","bottom-left","bottom-right","back","right","bottom","top","top-left","top-right","arrow-left","arrow-right","arrow-down","arrow-up","d-arrow-left","d-arrow-right","video-pause","video-play","refresh","refresh-right","refresh-left","finished","sort","sort-up","sort-down","rank","loading","view","c-scale-to-original","date","edit","edit-outline","folder","folder-opened","folder-add","folder-remove","folder-delete","folder-checked","tickets","document-remove","document-delete","document-copy","document-checked","document","document-add","printer","paperclip","takeaway-box","search","monitor","attract","mobile","scissors","umbrella","headset","brush","mouse","coordinate","magic-stick","reading","data-line","data-board","pie-chart","data-analysis","collection-tag","film","suitcase","suitcase-1","receiving","collection","files","notebook-1","notebook-2","toilet-paper","office-building","school","table-lamp","house","no-smoking","smoking","shopping-cart-full","shopping-cart-1","shopping-cart-2","shopping-bag-1","shopping-bag-2","sold-out","sell","present","box","bank-card","money","coin","wallet","discount","price-tag","news","guide","male","female","thumb","cpu","link","connection","open","turn-off","set-up","chat-round","chat-line-round","chat-square","chat-dot-round","chat-dot-square","chat-line-square","message","postcard","position","turn-off-microphone","microphone","close-notification","bangzhu","time","odometer","crop","aim","switch-button","full-screen","copy-document","mic","stopwatch","medal-1","medal","trophy","trophy-1","first-aid-kit","discover","place","location","location-outline","location-information","add-location","delete-location","map-location","alarm-clock","timer","watch-1","watch","lock","unlock","key","service","mobile-phone","bicycle","truck","ship","basketball","football","soccer","baseball","wind-power","light-rain","lightning","heavy-rain","sunrise","sunrise-1","sunset","sunny","cloudy","partly-cloudy","cloudy-and-sunny","moon","moon-night","dish","dish-1","food","chicken","fork-spoon","knife-fork","burger","tableware","sugar","dessert","ice-cream","hot-water","water-cup","coffee-cup","cold-drink","goblet","goblet-full","goblet-square","goblet-square-full","refrigerator","grape","watermelon","cherry","apple","pear","orange","coffee","ice-tea","ice-drink","milk-tea","potato-strips","lollipop","ice-cream-square","ice-cream-round"]
|
||||
236
src/utils/generator/js.js
Normal file
236
src/utils/generator/js.js
Normal file
@@ -0,0 +1,236 @@
|
||||
import { isArray } from 'util'
|
||||
import { exportDefault, titleCase } from '@/utils/index'
|
||||
import { trigger } from './config'
|
||||
|
||||
const units = {
|
||||
KB: '1024',
|
||||
MB: '1024 / 1024',
|
||||
GB: '1024 / 1024 / 1024'
|
||||
}
|
||||
let confGlobal
|
||||
const inheritAttrs = {
|
||||
file: '',
|
||||
dialog: 'inheritAttrs: false,'
|
||||
}
|
||||
|
||||
|
||||
export function makeUpJs(conf, type) {
|
||||
confGlobal = conf = JSON.parse(JSON.stringify(conf))
|
||||
const dataList = []
|
||||
const ruleList = []
|
||||
const optionsList = []
|
||||
const propsList = []
|
||||
const methodList = mixinMethod(type)
|
||||
const uploadVarList = []
|
||||
|
||||
conf.fields.forEach(el => {
|
||||
buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)
|
||||
})
|
||||
|
||||
const script = buildexport(
|
||||
conf,
|
||||
type,
|
||||
dataList.join('\n'),
|
||||
ruleList.join('\n'),
|
||||
optionsList.join('\n'),
|
||||
uploadVarList.join('\n'),
|
||||
propsList.join('\n'),
|
||||
methodList.join('\n')
|
||||
)
|
||||
confGlobal = null
|
||||
return script
|
||||
}
|
||||
|
||||
function buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) {
|
||||
buildData(el, dataList)
|
||||
buildRules(el, ruleList)
|
||||
|
||||
if (el.options && el.options.length) {
|
||||
buildOptions(el, optionsList)
|
||||
if (el.dataType === 'dynamic') {
|
||||
const model = `${el.vModel}Options`
|
||||
const options = titleCase(model)
|
||||
buildOptionMethod(`get${options}`, model, methodList)
|
||||
}
|
||||
}
|
||||
|
||||
if (el.props && el.props.props) {
|
||||
buildProps(el, propsList)
|
||||
}
|
||||
|
||||
if (el.action && el.tag === 'el-upload') {
|
||||
uploadVarList.push(
|
||||
`${el.vModel}Action: '${el.action}',
|
||||
${el.vModel}fileList: [],`
|
||||
)
|
||||
methodList.push(buildBeforeUpload(el))
|
||||
if (!el['auto-upload']) {
|
||||
methodList.push(buildSubmitUpload(el))
|
||||
}
|
||||
}
|
||||
|
||||
if (el.children) {
|
||||
el.children.forEach(el2 => {
|
||||
buildAttributes(el2, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function mixinMethod(type) {
|
||||
const list = []; const
|
||||
minxins = {
|
||||
file: confGlobal.formBtns ? {
|
||||
submitForm: `submitForm() {
|
||||
this.$refs['${confGlobal.formRef}'].validate(valid => {
|
||||
if(!valid) return
|
||||
// TODO 提交表单
|
||||
})
|
||||
},`,
|
||||
resetForm: `resetForm() {
|
||||
this.$refs['${confGlobal.formRef}'].resetFields()
|
||||
},`
|
||||
} : null,
|
||||
dialog: {
|
||||
onOpen: 'onOpen() {},',
|
||||
onClose: `onClose() {
|
||||
this.$refs['${confGlobal.formRef}'].resetFields()
|
||||
},`,
|
||||
close: `close() {
|
||||
this.$emit('update:visible', false)
|
||||
},`,
|
||||
handleConfirm: `handleConfirm() {
|
||||
this.$refs['${confGlobal.formRef}'].validate(valid => {
|
||||
if(!valid) return
|
||||
this.close()
|
||||
})
|
||||
},`
|
||||
}
|
||||
}
|
||||
|
||||
const methods = minxins[type]
|
||||
if (methods) {
|
||||
Object.keys(methods).forEach(key => {
|
||||
list.push(methods[key])
|
||||
})
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
function buildData(conf, dataList) {
|
||||
if (conf.vModel === undefined) return
|
||||
let defaultValue
|
||||
if (typeof (conf.defaultValue) === 'string' && !conf.multiple) {
|
||||
defaultValue = `'${conf.defaultValue}'`
|
||||
} else {
|
||||
defaultValue = `${JSON.stringify(conf.defaultValue)}`
|
||||
}
|
||||
dataList.push(`${conf.vModel}: ${defaultValue},`)
|
||||
}
|
||||
|
||||
function buildRules(conf, ruleList) {
|
||||
if (conf.vModel === undefined) return
|
||||
const rules = []
|
||||
if (trigger[conf.tag]) {
|
||||
if (conf.required) {
|
||||
const type = isArray(conf.defaultValue) ? 'type: \'array\',' : ''
|
||||
let message = isArray(conf.defaultValue) ? `请至少选择一个${conf.vModel}` : conf.placeholder
|
||||
if (message === undefined) message = `${conf.label}不能为空`
|
||||
rules.push(`{ required: true, ${type} message: '${message}', trigger: '${trigger[conf.tag]}' }`)
|
||||
}
|
||||
if (conf.regList && isArray(conf.regList)) {
|
||||
conf.regList.forEach(item => {
|
||||
if (item.pattern) {
|
||||
rules.push(`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${trigger[conf.tag]}' }`)
|
||||
}
|
||||
})
|
||||
}
|
||||
ruleList.push(`${conf.vModel}: [${rules.join(',')}],`)
|
||||
}
|
||||
}
|
||||
|
||||
function buildOptions(conf, optionsList) {
|
||||
if (conf.vModel === undefined) return
|
||||
if (conf.dataType === 'dynamic') { conf.options = [] }
|
||||
const str = `${conf.vModel}Options: ${JSON.stringify(conf.options)},`
|
||||
optionsList.push(str)
|
||||
}
|
||||
|
||||
function buildProps(conf, propsList) {
|
||||
if (conf.dataType === 'dynamic') {
|
||||
conf.valueKey !== 'value' && (conf.props.props.value = conf.valueKey)
|
||||
conf.labelKey !== 'label' && (conf.props.props.label = conf.labelKey)
|
||||
conf.childrenKey !== 'children' && (conf.props.props.children = conf.childrenKey)
|
||||
}
|
||||
const str = `${conf.vModel}Props: ${JSON.stringify(conf.props.props)},`
|
||||
propsList.push(str)
|
||||
}
|
||||
|
||||
function buildBeforeUpload(conf) {
|
||||
const unitNum = units[conf.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const
|
||||
returnList = []
|
||||
if (conf.fileSize) {
|
||||
rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${conf.fileSize}
|
||||
if(!isRightSize){
|
||||
this.$message.error('文件大小超过 ${conf.fileSize}${conf.sizeUnit}')
|
||||
}`
|
||||
returnList.push('isRightSize')
|
||||
}
|
||||
if (conf.accept) {
|
||||
acceptCode = `let isAccept = new RegExp('${conf.accept}').test(file.type)
|
||||
if(!isAccept){
|
||||
this.$message.error('应该选择${conf.accept}类型的文件')
|
||||
}`
|
||||
returnList.push('isAccept')
|
||||
}
|
||||
const str = `${conf.vModel}BeforeUpload(file) {
|
||||
${rightSizeCode}
|
||||
${acceptCode}
|
||||
return ${returnList.join('&&')}
|
||||
},`
|
||||
return returnList.length ? str : ''
|
||||
}
|
||||
|
||||
function buildSubmitUpload(conf) {
|
||||
const str = `submitUpload() {
|
||||
this.$refs['${conf.vModel}'].submit()
|
||||
},`
|
||||
return str
|
||||
}
|
||||
|
||||
function buildOptionMethod(methodName, model, methodList) {
|
||||
const str = `${methodName}() {
|
||||
// TODO 发起请求获取数据
|
||||
this.${model}
|
||||
},`
|
||||
methodList.push(str)
|
||||
}
|
||||
|
||||
function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) {
|
||||
const str = `${exportDefault}{
|
||||
${inheritAttrs[type]}
|
||||
components: {},
|
||||
props: [],
|
||||
data () {
|
||||
return {
|
||||
${conf.formModel}: {
|
||||
${data}
|
||||
},
|
||||
${conf.formRules}: {
|
||||
${rules}
|
||||
},
|
||||
${uploadVar}
|
||||
${selectOptions}
|
||||
${props}
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
watch: {},
|
||||
created () {},
|
||||
mounted () {},
|
||||
methods: {
|
||||
${methods}
|
||||
}
|
||||
}`
|
||||
return str
|
||||
}
|
||||
126
src/utils/generator/render.js
Normal file
126
src/utils/generator/render.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import { makeMap } from '@/utils/index'
|
||||
|
||||
// 参考https://github.com/vuejs/vue/blob/v2.6.10/src/platforms/web/server/util.js
|
||||
const isAttr = makeMap(
|
||||
'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,'
|
||||
+ 'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,'
|
||||
+ 'checked,cite,class,code,codebase,color,cols,colspan,content,http-equiv,'
|
||||
+ 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,'
|
||||
+ 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,for,'
|
||||
+ 'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,'
|
||||
+ 'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,'
|
||||
+ 'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,'
|
||||
+ 'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,'
|
||||
+ 'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,'
|
||||
+ 'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,'
|
||||
+ 'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,'
|
||||
+ 'target,title,type,usemap,value,width,wrap'
|
||||
)
|
||||
|
||||
function vModel(self, dataObject, defaultValue) {
|
||||
dataObject.props.value = defaultValue
|
||||
|
||||
dataObject.on.input = val => {
|
||||
self.$emit('input', val)
|
||||
}
|
||||
}
|
||||
|
||||
const componentChild = {
|
||||
'el-button': {
|
||||
default(h, conf, key) {
|
||||
return conf[key]
|
||||
},
|
||||
},
|
||||
'el-input': {
|
||||
prepend(h, conf, key) {
|
||||
return <template slot="prepend">{conf[key]}</template>
|
||||
},
|
||||
append(h, conf, key) {
|
||||
return <template slot="append">{conf[key]}</template>
|
||||
}
|
||||
},
|
||||
'el-select': {
|
||||
options(h, conf, key) {
|
||||
const list = []
|
||||
conf.options.forEach(item => {
|
||||
list.push(<el-option label={item.label} value={item.value} disabled={item.disabled}></el-option>)
|
||||
})
|
||||
return list
|
||||
}
|
||||
},
|
||||
'el-radio-group': {
|
||||
options(h, conf, key) {
|
||||
const list = []
|
||||
conf.options.forEach(item => {
|
||||
if (conf.optionType === 'button') list.push(<el-radio-button label={item.value}>{item.label}</el-radio-button>)
|
||||
else list.push(<el-radio label={item.value} border={conf.border}>{item.label}</el-radio>)
|
||||
})
|
||||
return list
|
||||
}
|
||||
},
|
||||
'el-checkbox-group': {
|
||||
options(h, conf, key) {
|
||||
const list = []
|
||||
conf.options.forEach(item => {
|
||||
if (conf.optionType === 'button') {
|
||||
list.push(<el-checkbox-button label={item.value}>{item.label}</el-checkbox-button>)
|
||||
} else {
|
||||
list.push(<el-checkbox label={item.value} border={conf.border}>{item.label}</el-checkbox>)
|
||||
}
|
||||
})
|
||||
return list
|
||||
}
|
||||
},
|
||||
'el-upload': {
|
||||
'list-type': (h, conf, key) => {
|
||||
const list = []
|
||||
if (conf['list-type'] === 'picture-card') {
|
||||
list.push(<i class="el-icon-plus"></i>)
|
||||
} else {
|
||||
list.push(<el-button size="small" type="primary" icon="el-icon-upload">{conf.buttonText}</el-button>)
|
||||
}
|
||||
if (conf.showTip) {
|
||||
list.push(<div slot="tip" class="el-upload__tip">只能上传不超过 {conf.fileSize}{conf.sizeUnit} 的{conf.accept}文件</div>)
|
||||
}
|
||||
return list
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
render(h) {
|
||||
const dataObject = {
|
||||
attrs: {},
|
||||
props: {},
|
||||
on: {},
|
||||
style: {}
|
||||
}
|
||||
const confClone = JSON.parse(JSON.stringify(this.conf))
|
||||
const children = []
|
||||
|
||||
const childObjs = componentChild[confClone.tag]
|
||||
if (childObjs) {
|
||||
Object.keys(childObjs).forEach(key => {
|
||||
const childFunc = childObjs[key]
|
||||
if (confClone[key]) {
|
||||
children.push(childFunc(h, confClone, key))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Object.keys(confClone).forEach(key => {
|
||||
const val = confClone[key]
|
||||
if (key === 'vModel') {
|
||||
vModel(this, dataObject, confClone.defaultValue)
|
||||
} else if (dataObject[key]) {
|
||||
dataObject[key] = val
|
||||
} else if (!isAttr(key)) {
|
||||
dataObject.props[key] = val
|
||||
} else {
|
||||
dataObject.attrs[key] = val
|
||||
}
|
||||
})
|
||||
return h(this.conf.tag, dataObject, children)
|
||||
},
|
||||
props: ['conf']
|
||||
}
|
||||
130
src/utils/htmlToPdf.js
Normal file
130
src/utils/htmlToPdf.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import HTML2canvas from 'html2canvas'
|
||||
import JSPDF from 'jspdf'
|
||||
|
||||
export default {
|
||||
install(Vue) {
|
||||
Vue.prototype.htmlToPdf = function (ele, title) {
|
||||
const dom = document.querySelector('#' + ele)
|
||||
HTML2canvas(dom, {
|
||||
useCORS: true,//解决网络图片跨域问题
|
||||
width: dom.width,
|
||||
height: dom.height,
|
||||
windowWidth: dom.scrollWidth,
|
||||
dpi: window.devicePixelRatio * 4, // 将分辨率提高到特定的DPI 提高四倍
|
||||
scale: 4, // 按比例增加分辨率
|
||||
}).then((canvas) => {
|
||||
// eslint-disable-next-line new-cap
|
||||
const pdf = new JSPDF('p', 'mm', 'a4'); // A4纸,纵向
|
||||
const ctx = canvas.getContext('2d');
|
||||
const a4w = 170;
|
||||
const a4h = 250; // A4大小,210mm x 297mm,四边各保留20mm的边距,显示区域170x257
|
||||
const imgHeight = Math.floor(a4h * canvas.width / a4w); // 按A4显示比例换算一页图像的像素高度
|
||||
let renderedHeight = 0;
|
||||
|
||||
while (renderedHeight < canvas.height) {
|
||||
const page = document.createElement('canvas');
|
||||
page.width = canvas.width;
|
||||
page.height = Math.min(imgHeight, canvas.height - renderedHeight);// 可能内容不足一页
|
||||
|
||||
// 用getImageData剪裁指定区域,并画到前面创建的canvas对象中
|
||||
page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0);
|
||||
pdf.addImage(page.toDataURL('image/jpeg', 1.0), 'JPEG', 20, 20, a4w, Math.min(a4h, a4w * page.height / page.width)); // 添加图像到页面,保留10mm边距
|
||||
|
||||
renderedHeight += imgHeight;
|
||||
if (renderedHeight < canvas.height) {
|
||||
pdf.addPage();// 如果后面还有内容,添加一个空页
|
||||
}
|
||||
// 预览pdf(这里我用的是事件总线把canvas传递过去展示,达到模拟pdf预览的效果,有用但效果不是很好,有需要的可以自行修改)
|
||||
//this.$EventBus.$emit('open-pdf', canvas);
|
||||
}
|
||||
// 保存文件
|
||||
pdf.save(`${title}.pdf`);
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
// install(Vue) {
|
||||
// Vue.prototype.htmlToPdf = function (name, title) {
|
||||
// html2Canvas(document.querySelector('#' + name), {
|
||||
// // allowTaint: true,
|
||||
// useCORS: true,
|
||||
// scale: 2, // 提升画面质量,但是会增加文件大小
|
||||
// dpi: window.devicePixelRatio * 1,
|
||||
// }).then((canvas) => {
|
||||
// const contentWidth = canvas.width
|
||||
// const contentHeight = canvas.height
|
||||
|
||||
// /* 导出不分页处理 */
|
||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
||||
|
||||
// const pdfWidth = (contentWidth + 10) / 2 * 0.75
|
||||
// const pdfHeight = (contentHeight + 200) / 2 * 0.75 // 500为底部留白
|
||||
|
||||
// const imgWidth = pdfWidth
|
||||
// const imgHeight = (contentHeight / 2 * 0.75) // 内容图片这里不需要留白的距离
|
||||
|
||||
// const PDF = new JsPDF('', 'pt', [ pdfWidth + 50, pdfHeight + 100, ])
|
||||
// PDF.addImage(pageData, 'jpeg', 33, 33, imgWidth, imgHeight)
|
||||
// PDF.save(title + '.pdf')
|
||||
// })
|
||||
// };
|
||||
// },
|
||||
|
||||
// install(Vue) {
|
||||
// Vue.prototype.htmlToPdf = async function (name, title) {
|
||||
// const ele = document.querySelector('#' + name)
|
||||
// const eleW = ele.offsetWidth// 获得该容器的宽
|
||||
// const eleH = ele.offsetHeight// 获得该容器的高
|
||||
|
||||
// const eleOffsetTop = ele.offsetTop // 获得该容器到文档顶部的距离
|
||||
// const eleOffsetLeft = ele.offsetLeft // 获得该容器到文档最左的距离
|
||||
|
||||
// var canvas = document.createElement('canvas')
|
||||
// var abs = 0
|
||||
|
||||
// const win_in = document.documentElement.clientWidth || document.body.clientWidth // 获得当前可视窗口的宽度(不包含滚动条)
|
||||
// const win_out = window.innerWidth // 获得当前窗口的宽度(包含滚动条)
|
||||
|
||||
// if (win_out > win_in) {
|
||||
// // abs = (win_o - win_i)/2; // 获得滚动条长度的一半
|
||||
// abs = (win_out - win_in) / 2 // 获得滚动条宽度的一半
|
||||
// // console.log(a, '新abs');
|
||||
// }
|
||||
|
||||
// canvas.width = eleW * 2 // 将画布宽&&高放大两倍
|
||||
// canvas.height = eleH * 2
|
||||
|
||||
// var context = canvas.getContext('2d')
|
||||
|
||||
// context.scale(2, 2)
|
||||
|
||||
// context.translate(-eleOffsetLeft - abs, -eleOffsetTop)
|
||||
// // 这里默认横向没有滚动条的情况,因为offset.left(),有无滚动条的时候存在差值,因此
|
||||
// // translate的时候,要把这个差值去掉
|
||||
|
||||
// var pdf = new JsPDF('', 'pt', 'a4')
|
||||
// const childrenBox = ele.children
|
||||
// for (let i = 0; i < childrenBox.length; i++) { // 循环传过来的Dom的字节点 每个子节点打印成一页pdf A4纸那么大
|
||||
// console.log(childrenBox,'childrenBox');
|
||||
// console.log(1111);
|
||||
// const res = await html2Canvas(childrenBox[i], {
|
||||
// dpi: 300,
|
||||
// // allowTaint: true, //允许 canvas 污染, allowTaint参数要去掉,否则是无法通过toDataURL导出canvas数据的
|
||||
// useCORS: true, // 允许canvas画布内 可以跨域请求外部链接图片, 允许跨域请求。
|
||||
// scale: 4, // 提升导出的文件的分辨率
|
||||
// })
|
||||
// var pageData = res.toDataURL('image/jpeg', 1.0)
|
||||
// var contentWidth = res.width
|
||||
// var contentHeight = res.height
|
||||
// var imgWidth = 555.28
|
||||
// var imgHeight = 552.28 / contentWidth * contentHeight
|
||||
// pdf.addImage(pageData, 'JPEG', 20, 20, imgWidth, imgHeight)
|
||||
// if (i < childrenBox.length - 1) {
|
||||
// pdf.addPage() // 避免最后多一个空白页
|
||||
// }
|
||||
// }
|
||||
// pdf.save(`${title}.pdf`);
|
||||
// };
|
||||
// },
|
||||
}
|
||||
|
||||
460
src/utils/index.js
Normal file
460
src/utils/index.js
Normal file
@@ -0,0 +1,460 @@
|
||||
import { parseTime } from './ruoyi'
|
||||
|
||||
/**
|
||||
* 表格时间格式化
|
||||
*/
|
||||
export function formatDate(cellValue) {
|
||||
if (cellValue == null || cellValue == "") return "";
|
||||
var date = new Date(cellValue)
|
||||
var year = date.getFullYear()
|
||||
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
|
||||
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
|
||||
var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
|
||||
var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
|
||||
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
|
||||
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
|
||||
}
|
||||
|
||||
export function formatDateByYearMonthDay(cellValue) {
|
||||
if (cellValue == null || cellValue == "") return "";
|
||||
var date = new Date(cellValue)
|
||||
var year = date.getFullYear()
|
||||
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
|
||||
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
|
||||
|
||||
return year + '-' + month + '-' + day
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} time
|
||||
* @param {string} option
|
||||
* @returns {string}
|
||||
*/
|
||||
export function formatTime(time, option) {
|
||||
if (('' + time).length === 10) {
|
||||
time = parseInt(time) * 1000
|
||||
} else {
|
||||
time = +time
|
||||
}
|
||||
const d = new Date(time)
|
||||
const now = Date.now()
|
||||
|
||||
const diff = (now - d) / 1000
|
||||
|
||||
if (diff < 30) {
|
||||
return '刚刚'
|
||||
} else if (diff < 3600) {
|
||||
// less 1 hour
|
||||
return Math.ceil(diff / 60) + '分钟前'
|
||||
} else if (diff < 3600 * 24) {
|
||||
return Math.ceil(diff / 3600) + '小时前'
|
||||
} else if (diff < 3600 * 24 * 2) {
|
||||
return '1天前'
|
||||
}
|
||||
if (option) {
|
||||
return parseTime(time, option)
|
||||
} else {
|
||||
return (
|
||||
d.getMonth() +
|
||||
1 +
|
||||
'月' +
|
||||
d.getDate() +
|
||||
'日' +
|
||||
d.getHours() +
|
||||
'时' +
|
||||
d.getMinutes() +
|
||||
'分'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function nowTime(time, fmt) {
|
||||
if (!time) return '';
|
||||
else {
|
||||
const date = new Date(time);
|
||||
const o = {
|
||||
'M+': date.getMonth() + 1,
|
||||
'd+': date.getDate(),
|
||||
'H+': date.getHours(),
|
||||
'm+': date.getMinutes(),
|
||||
's+': date.getSeconds(),
|
||||
'q+': Math.floor((date.getMonth() + 3) / 3),
|
||||
S: date.getMilliseconds(),
|
||||
};
|
||||
if (/(y+)/.test(fmt))
|
||||
fmt = fmt.replace(
|
||||
RegExp.$1,
|
||||
(date.getFullYear() + '').substr(4 - RegExp.$1.length)
|
||||
);
|
||||
for (const k in o) {
|
||||
if (new RegExp('(' + k + ')').test(fmt)) {
|
||||
fmt = fmt.replace(
|
||||
RegExp.$1,
|
||||
RegExp.$1.length === 1
|
||||
? o[k]
|
||||
: ('00' + o[k]).substr(('' + o[k]).length)
|
||||
);
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {string} url
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function getQueryObject(url) {
|
||||
url = url == null ? window.location.href : url
|
||||
const search = url.substring(url.lastIndexOf('?') + 1)
|
||||
const obj = {}
|
||||
const reg = /([^?&=]+)=([^?&=]*)/g
|
||||
search.replace(reg, (rs, $1, $2) => {
|
||||
const name = decodeURIComponent($1)
|
||||
let val = decodeURIComponent($2)
|
||||
val = String(val)
|
||||
obj[name] = val
|
||||
return rs
|
||||
})
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input value
|
||||
* @returns {number} output value
|
||||
*/
|
||||
export function byteLength(str) {
|
||||
// returns the byte length of an utf8 string
|
||||
let s = str.length
|
||||
for (var i = str.length - 1; i >= 0; i--) {
|
||||
const code = str.charCodeAt(i)
|
||||
if (code > 0x7f && code <= 0x7ff) s++
|
||||
else if (code > 0x7ff && code <= 0xffff) s += 2
|
||||
if (code >= 0xDC00 && code <= 0xDFFF) i--
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} actual
|
||||
* @returns {Array}
|
||||
*/
|
||||
export function cleanArray(actual) {
|
||||
const newArray = []
|
||||
for (let i = 0; i < actual.length; i++) {
|
||||
if (actual[i]) {
|
||||
newArray.push(actual[i])
|
||||
}
|
||||
}
|
||||
return newArray
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} json
|
||||
* @returns {Array}
|
||||
*/
|
||||
export function param(json) {
|
||||
if (!json) return ''
|
||||
return cleanArray(
|
||||
Object.keys(json).map(key => {
|
||||
if (json[key] === undefined) return ''
|
||||
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
|
||||
})
|
||||
).join('&')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function param2Obj(url) {
|
||||
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
|
||||
if (!search) {
|
||||
return {}
|
||||
}
|
||||
const obj = {}
|
||||
const searchArr = search.split('&')
|
||||
searchArr.forEach(v => {
|
||||
const index = v.indexOf('=')
|
||||
if (index !== -1) {
|
||||
const name = v.substring(0, index)
|
||||
const val = v.substring(index + 1, v.length)
|
||||
obj[name] = val
|
||||
}
|
||||
})
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} val
|
||||
* @returns {string}
|
||||
*/
|
||||
export function html2Text(val) {
|
||||
const div = document.createElement('div')
|
||||
div.innerHTML = val
|
||||
return div.textContent || div.innerText
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two objects, giving the last one precedence
|
||||
* @param {Object} target
|
||||
* @param {(Object|Array)} source
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function objectMerge(target, source) {
|
||||
if (typeof target !== 'object') {
|
||||
target = {}
|
||||
}
|
||||
if (Array.isArray(source)) {
|
||||
return source.slice()
|
||||
}
|
||||
Object.keys(source).forEach(property => {
|
||||
const sourceProperty = source[property]
|
||||
if (typeof sourceProperty === 'object') {
|
||||
target[property] = objectMerge(target[property], sourceProperty)
|
||||
} else {
|
||||
target[property] = sourceProperty
|
||||
}
|
||||
})
|
||||
return target
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} element
|
||||
* @param {string} className
|
||||
*/
|
||||
export function toggleClass(element, className) {
|
||||
if (!element || !className) {
|
||||
return
|
||||
}
|
||||
let classString = element.className
|
||||
const nameIndex = classString.indexOf(className)
|
||||
if (nameIndex === -1) {
|
||||
classString += '' + className
|
||||
} else {
|
||||
classString =
|
||||
classString.substr(0, nameIndex) +
|
||||
classString.substr(nameIndex + className.length)
|
||||
}
|
||||
element.className = classString
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} type
|
||||
* @returns {Date}
|
||||
*/
|
||||
export function getTime(type) {
|
||||
if (type === 'start') {
|
||||
return new Date().getTime() - 3600 * 1000 * 24 * 90
|
||||
} else {
|
||||
return new Date(new Date().toDateString())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Function} func
|
||||
* @param {number} wait
|
||||
* @param {boolean} immediate
|
||||
* @return {*}
|
||||
*/
|
||||
export function debounce(func, wait, immediate) {
|
||||
let timeout, args, context, timestamp, result
|
||||
|
||||
const later = function() {
|
||||
// 据上一次触发时间间隔
|
||||
const last = +new Date() - timestamp
|
||||
|
||||
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
|
||||
if (last < wait && last > 0) {
|
||||
timeout = setTimeout(later, wait - last)
|
||||
} else {
|
||||
timeout = null
|
||||
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
|
||||
if (!immediate) {
|
||||
result = func.apply(context, args)
|
||||
if (!timeout) context = args = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return function(...args) {
|
||||
context = this
|
||||
timestamp = +new Date()
|
||||
const callNow = immediate && !timeout
|
||||
// 如果延时不存在,重新设定延时
|
||||
if (!timeout) timeout = setTimeout(later, wait)
|
||||
if (callNow) {
|
||||
result = func.apply(context, args)
|
||||
context = args = null
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is just a simple version of deep copy
|
||||
* Has a lot of edge cases bug
|
||||
* If you want to use a perfect deep copy, use lodash's _.cloneDeep
|
||||
* @param {Object} source
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function deepClone(source) {
|
||||
if (!source && typeof source !== 'object') {
|
||||
throw new Error('error arguments', 'deepClone')
|
||||
}
|
||||
const targetObj = source.constructor === Array ? [] : {}
|
||||
Object.keys(source).forEach(keys => {
|
||||
if (source[keys] && typeof source[keys] === 'object') {
|
||||
targetObj[keys] = deepClone(source[keys])
|
||||
} else {
|
||||
targetObj[keys] = source[keys]
|
||||
}
|
||||
})
|
||||
return targetObj
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} arr
|
||||
* @returns {Array}
|
||||
*/
|
||||
export function uniqueArr(arr) {
|
||||
return Array.from(new Set(arr))
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
export function createUniqueString() {
|
||||
const timestamp = +new Date() + ''
|
||||
const randomNum = parseInt((1 + Math.random()) * 65536) + ''
|
||||
return (+(randomNum + timestamp)).toString(32)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an element has a class
|
||||
* @param {HTMLElement} elm
|
||||
* @param {string} cls
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function hasClass(ele, cls) {
|
||||
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
|
||||
}
|
||||
|
||||
/**
|
||||
* Add class to element
|
||||
* @param {HTMLElement} elm
|
||||
* @param {string} cls
|
||||
*/
|
||||
export function addClass(ele, cls) {
|
||||
if (!hasClass(ele, cls)) ele.className += ' ' + cls
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove class from element
|
||||
* @param {HTMLElement} elm
|
||||
* @param {string} cls
|
||||
*/
|
||||
export function removeClass(ele, cls) {
|
||||
if (hasClass(ele, cls)) {
|
||||
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
|
||||
ele.className = ele.className.replace(reg, ' ')
|
||||
}
|
||||
}
|
||||
|
||||
export function makeMap(str, expectsLowerCase) {
|
||||
const map = Object.create(null)
|
||||
const list = str.split(',')
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
map[list[i]] = true
|
||||
}
|
||||
return expectsLowerCase
|
||||
? val => map[val.toLowerCase()]
|
||||
: val => map[val]
|
||||
}
|
||||
|
||||
export const exportDefault = 'export default '
|
||||
|
||||
export const beautifierConf = {
|
||||
html: {
|
||||
indent_size: '2',
|
||||
indent_char: ' ',
|
||||
max_preserve_newlines: '-1',
|
||||
preserve_newlines: false,
|
||||
keep_array_indentation: false,
|
||||
break_chained_methods: false,
|
||||
indent_scripts: 'separate',
|
||||
brace_style: 'end-expand',
|
||||
space_before_conditional: true,
|
||||
unescape_strings: false,
|
||||
jslint_happy: false,
|
||||
end_with_newline: true,
|
||||
wrap_line_length: '110',
|
||||
indent_inner_html: true,
|
||||
comma_first: false,
|
||||
e4x: true,
|
||||
indent_empty_lines: true
|
||||
},
|
||||
js: {
|
||||
indent_size: '2',
|
||||
indent_char: ' ',
|
||||
max_preserve_newlines: '-1',
|
||||
preserve_newlines: false,
|
||||
keep_array_indentation: false,
|
||||
break_chained_methods: false,
|
||||
indent_scripts: 'normal',
|
||||
brace_style: 'end-expand',
|
||||
space_before_conditional: true,
|
||||
unescape_strings: false,
|
||||
jslint_happy: true,
|
||||
end_with_newline: true,
|
||||
wrap_line_length: '110',
|
||||
indent_inner_html: true,
|
||||
comma_first: false,
|
||||
e4x: true,
|
||||
indent_empty_lines: true
|
||||
}
|
||||
}
|
||||
|
||||
// 首字母大小
|
||||
export function titleCase(str) {
|
||||
return str.replace(/( |^)[a-z]/g, L => L.toUpperCase())
|
||||
}
|
||||
|
||||
// 下划转驼峰
|
||||
export function camelCase(str) {
|
||||
return str.replace(/_[a-z]/g, str1 => str1.substr(-1).toUpperCase())
|
||||
}
|
||||
|
||||
export function isNumberStr(str) {
|
||||
return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Excel 序列号转换为 JavaScript Date 对象
|
||||
* @param {number} serial - Excel 日期序列号
|
||||
* @returns {Date}
|
||||
*/
|
||||
export function excelDateToJSDate(serial) {
|
||||
const utc_days = Math.floor(serial - 25569); // 转换为 Unix 天数
|
||||
const utc_value = utc_days * 86400; // 秒数
|
||||
const date_info = new Date(utc_value * 1000); // 得到 UTC 时间
|
||||
|
||||
const fractional_day = serial % 1;
|
||||
const total_seconds = Math.floor(86400 * fractional_day);
|
||||
const seconds = total_seconds % 60;
|
||||
const minutes = Math.floor(total_seconds / 60) % 60;
|
||||
const hours = Math.floor(total_seconds / 3600);
|
||||
|
||||
// 使用 setUTCFullYear 避免本地时区干扰
|
||||
const result = new Date(Date.UTC(
|
||||
date_info.getUTCFullYear(),
|
||||
date_info.getUTCMonth(),
|
||||
date_info.getUTCDate(),
|
||||
hours,
|
||||
minutes,
|
||||
seconds
|
||||
));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
30
src/utils/jsencrypt.js
Normal file
30
src/utils/jsencrypt.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
|
||||
|
||||
// 密钥对生成 http://web.chacuo.net/netrsakeypair
|
||||
|
||||
const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' +
|
||||
'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
|
||||
|
||||
const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
|
||||
'7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
|
||||
'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
|
||||
'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
|
||||
'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
|
||||
'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
|
||||
'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
|
||||
'UP8iWi1Qw0Y='
|
||||
|
||||
// 加密
|
||||
export function encrypt(txt) {
|
||||
const encryptor = new JSEncrypt()
|
||||
encryptor.setPublicKey(publicKey) // 设置公钥
|
||||
return encryptor.encrypt(txt) // 对数据进行加密
|
||||
}
|
||||
|
||||
// 解密
|
||||
export function decrypt(txt) {
|
||||
const encryptor = new JSEncrypt()
|
||||
encryptor.setPrivateKey(privateKey) // 设置私钥
|
||||
return encryptor.decrypt(txt) // 对数据进行解密
|
||||
}
|
||||
|
||||
51
src/utils/permission.js
Normal file
51
src/utils/permission.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import store from '@/store'
|
||||
|
||||
/**
|
||||
* 字符权限校验
|
||||
* @param {Array} value 校验值
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function checkPermi(value) {
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const permissions = store.getters && store.getters.permissions
|
||||
const permissionDatas = value
|
||||
const all_permission = "*:*:*";
|
||||
|
||||
const hasPermission = permissions.some(permission => {
|
||||
return all_permission === permission || permissionDatas.includes(permission)
|
||||
})
|
||||
|
||||
if (!hasPermission) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色权限校验
|
||||
* @param {Array} value 校验值
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function checkRole(value) {
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const roles = store.getters && store.getters.roles
|
||||
const permissionRoles = value
|
||||
const super_admin = "admin";
|
||||
|
||||
const hasRole = roles.some(role => {
|
||||
return super_admin === role || permissionRoles.includes(role)
|
||||
})
|
||||
|
||||
if (!hasRole) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
console.error(`need roles! Like checkRole="['admin','editor']"`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
158
src/utils/request.js
Normal file
158
src/utils/request.js
Normal file
@@ -0,0 +1,158 @@
|
||||
import cache from '@/plugins/cache'
|
||||
import store from '@/store'
|
||||
import { getTokenKeySessionStorage } from '@/utils/auth'
|
||||
import errorCode from '@/utils/errorCode'
|
||||
import { blobValidate, tansParams } from "@/utils/ruoyi"
|
||||
import axios from 'axios'
|
||||
import { Loading, Message, MessageBox, Notification } from 'element-ui'
|
||||
import { saveAs } from 'file-saver'
|
||||
|
||||
let downloadLoadingInstance;
|
||||
// 是否显示重新登录
|
||||
export let isRelogin = { show: false };
|
||||
|
||||
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
|
||||
const defaultSettings = require('@/settings')
|
||||
// 创建axios实例
|
||||
const service = axios.create({
|
||||
// axios中请求配置有baseURL选项,表示请求URL公共部分
|
||||
baseURL: process.env.VUE_APP_BASE_API,
|
||||
// 超时
|
||||
timeout: 100000
|
||||
})
|
||||
|
||||
// request拦截器
|
||||
service.interceptors.request.use(config => {
|
||||
// 是否需要设置 token
|
||||
const isToken = (config.headers || {}).isToken === false
|
||||
// 是否需要防止数据重复提交
|
||||
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
|
||||
if (getTokenKeySessionStorage() && !isToken) {
|
||||
config.headers['Authorization'] = 'Bearer ' + getTokenKeySessionStorage() // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||
}
|
||||
// get请求映射params参数
|
||||
if (config.method === 'get' && config.params) {
|
||||
let url = config.url + '?' + tansParams(config.params);
|
||||
url = url.slice(0, -1);
|
||||
config.params = {};
|
||||
config.url = url;
|
||||
}
|
||||
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
|
||||
const requestObj = {
|
||||
url: config.url,
|
||||
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
|
||||
time: new Date().getTime()
|
||||
}
|
||||
const sessionObj = cache.session.getJSON('sessionObj')
|
||||
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
|
||||
cache.session.setJSON('sessionObj', requestObj)
|
||||
} else {
|
||||
const s_url = sessionObj.url; // 请求地址
|
||||
const s_data = sessionObj.data; // 请求数据
|
||||
const s_time = sessionObj.time; // 请求时间
|
||||
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
|
||||
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url && s_url !=='/affix/upload') {
|
||||
const message = '数据正在处理,请勿重复提交';
|
||||
console.log(`[${s_url}]: ` + message)
|
||||
return Promise.reject(new Error(message))
|
||||
} else {
|
||||
cache.session.setJSON('sessionObj', requestObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
return config
|
||||
}, error => {
|
||||
console.log(65,error)
|
||||
return Promise.reject(error)
|
||||
})
|
||||
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(res => {
|
||||
// 未设置状态码则默认成功状态
|
||||
const code = res.data.code || 200;
|
||||
// 获取错误信息
|
||||
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
||||
// 二进制数据则直接返回
|
||||
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
|
||||
return res.data
|
||||
}
|
||||
if (code === 401) {
|
||||
if (!isRelogin.show) {
|
||||
isRelogin.show = true;
|
||||
if(!defaultSettings.casEnable)
|
||||
{
|
||||
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
|
||||
isRelogin.show = false;
|
||||
store.dispatch('LogOut').then(() => {
|
||||
sessionStorage.clear();
|
||||
if (!defaultSettings.casEnable) {
|
||||
location.href = '/srs';
|
||||
}else{
|
||||
location.href = '/cas';
|
||||
}
|
||||
})
|
||||
}).catch(() => {
|
||||
isRelogin.show = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
|
||||
} else if (code === 500) {
|
||||
Message({ message: msg, type: 'error',duration:5000 })
|
||||
return res.data
|
||||
} else if (code === 601) {
|
||||
Message({ message: msg, type: 'warning' })
|
||||
return Promise.reject('error')
|
||||
} else if (code !== 200) {
|
||||
Notification.error({ title: msg })
|
||||
return Promise.reject('error')
|
||||
} else {
|
||||
return res.data
|
||||
}
|
||||
},
|
||||
error => {
|
||||
console.log('err' + error)
|
||||
let { message } = error;
|
||||
if (message == "Network Error") {
|
||||
message = "后端接口连接异常";
|
||||
Message({ message: message, type: 'error', duration: 5 * 1000 })
|
||||
} else if (message.includes("timeout")) {
|
||||
message = "系统接口请求超时";
|
||||
Message({ message: message, type: 'error', duration: 5 * 1000 })
|
||||
} else if (message.includes("Request failed with status code")) {
|
||||
message = "系统接口" + message.substr(message.length - 3) + "异常";
|
||||
}
|
||||
console.log("接口请求错误:"+message);
|
||||
//Message({ message: message, type: 'error', duration: 5 * 1000 })
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 通用下载方法
|
||||
export function download(url, params, filename, config) {
|
||||
downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })
|
||||
return service.post(url, params, {
|
||||
transformRequest: [(params) => { return tansParams(params) }],
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
responseType: 'blob',
|
||||
...config
|
||||
}).then(async (data) => {
|
||||
const isBlob = blobValidate(data);
|
||||
if (isBlob) {
|
||||
const blob = new Blob([data])
|
||||
saveAs(blob, filename)
|
||||
} else {
|
||||
const resText = await data.text();
|
||||
const rspObj = JSON.parse(resText);
|
||||
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
|
||||
Message.error(errMsg);
|
||||
}
|
||||
downloadLoadingInstance.close();
|
||||
}).catch((r) => {
|
||||
console.error(r)
|
||||
Message.error('下载文件出现错误,请联系管理员!')
|
||||
downloadLoadingInstance.close();
|
||||
})
|
||||
}
|
||||
|
||||
export default service
|
||||
262
src/utils/ruoyi.js
Normal file
262
src/utils/ruoyi.js
Normal file
@@ -0,0 +1,262 @@
|
||||
|
||||
|
||||
/**
|
||||
* 通用js方法封装处理 工具类
|
||||
*/
|
||||
|
||||
// 日期格式化
|
||||
export function parseTime(time, pattern) {
|
||||
if (arguments.length === 0 || !time) {
|
||||
return null
|
||||
}
|
||||
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
|
||||
let date
|
||||
if (typeof time === 'object') {
|
||||
date = time
|
||||
} else {
|
||||
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
|
||||
time = parseInt(time)
|
||||
} else if (typeof time === 'string') {
|
||||
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '');
|
||||
}
|
||||
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
||||
time = time * 1000
|
||||
}
|
||||
date = new Date(time)
|
||||
}
|
||||
const formatObj = {
|
||||
y: date.getFullYear(),
|
||||
m: date.getMonth() + 1,
|
||||
d: date.getDate(),
|
||||
h: date.getHours(),
|
||||
i: date.getMinutes(),
|
||||
s: date.getSeconds(),
|
||||
a: date.getDay()
|
||||
}
|
||||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
||||
let value = formatObj[key]
|
||||
// Note: getDay() returns 0 on Sunday
|
||||
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
|
||||
if (result.length > 0 && value < 10) {
|
||||
value = '0' + value
|
||||
}
|
||||
return value || 0
|
||||
})
|
||||
return time_str
|
||||
}
|
||||
|
||||
// 表单重置
|
||||
export function resetForm(refName) {
|
||||
if (this.$refs[refName]) {
|
||||
this.$refs[refName].resetFields();
|
||||
}
|
||||
}
|
||||
|
||||
// 添加日期范围
|
||||
export function addDateRange(params, dateRange, propName) {
|
||||
let search = params;
|
||||
search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
|
||||
dateRange = Array.isArray(dateRange) ? dateRange : [];
|
||||
if (typeof (propName) === 'undefined') {
|
||||
search.params['beginTime'] = dateRange[0];
|
||||
search.params['endTime'] = dateRange[1];
|
||||
} else {
|
||||
search.params['begin' + propName] = dateRange[0];
|
||||
search.params['end' + propName] = dateRange[1];
|
||||
}
|
||||
return search;
|
||||
}
|
||||
|
||||
// 回显数据字典
|
||||
export function selectDictLabel(datas, value) {
|
||||
if (value === undefined) {
|
||||
return "";
|
||||
}
|
||||
var actions = [];
|
||||
Object.keys(datas).some((key) => {
|
||||
if (datas[key].value == ('' + value)) {
|
||||
actions.push(datas[key].label);
|
||||
return true;
|
||||
}
|
||||
})
|
||||
if (actions.length === 0) {
|
||||
actions.push(value);
|
||||
}
|
||||
return actions.join('');
|
||||
}
|
||||
|
||||
// 回显数据字典(字符串、数组)
|
||||
export function selectDictLabels(datas, value, separator) {
|
||||
if (value === undefined || value.length ===0) {
|
||||
return "";
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
value = value.join(",");
|
||||
}
|
||||
var actions = [];
|
||||
var currentSeparator = undefined === separator ? "," : separator;
|
||||
var temp = value.split(currentSeparator);
|
||||
Object.keys(value.split(currentSeparator)).some((val) => {
|
||||
var match = false;
|
||||
Object.keys(datas).some((key) => {
|
||||
if (datas[key].value == ('' + temp[val])) {
|
||||
actions.push(datas[key].label + currentSeparator);
|
||||
match = true;
|
||||
}
|
||||
})
|
||||
if (!match) {
|
||||
actions.push(temp[val] + currentSeparator);
|
||||
}
|
||||
})
|
||||
return actions.join('').substring(0, actions.join('').length - 1);
|
||||
}
|
||||
|
||||
// 字符串格式化(%s )
|
||||
export function sprintf(str) {
|
||||
var args = arguments, flag = true, i = 1;
|
||||
str = str.replace(/%s/g, function () {
|
||||
var arg = args[i++];
|
||||
if (typeof arg === 'undefined') {
|
||||
flag = false;
|
||||
return '';
|
||||
}
|
||||
return arg;
|
||||
});
|
||||
return flag ? str : '';
|
||||
}
|
||||
|
||||
// 转换字符串,undefined,null等转化为""
|
||||
export function parseStrEmpty(str) {
|
||||
if (!str || str == "undefined" || str == "null") {
|
||||
return "";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
// 数据合并
|
||||
export function mergeRecursive(source, target) {
|
||||
for (var p in target) {
|
||||
try {
|
||||
if (target[p].constructor == Object) {
|
||||
source[p] = mergeRecursive(source[p], target[p]);
|
||||
} else {
|
||||
source[p] = target[p];
|
||||
}
|
||||
} catch (e) {
|
||||
source[p] = target[p];
|
||||
}
|
||||
}
|
||||
return source;
|
||||
};
|
||||
|
||||
/**
|
||||
* 构造树型结构数据
|
||||
* @param {*} data 数据源
|
||||
* @param {*} id id字段 默认 'id'
|
||||
* @param {*} parentId 父节点字段 默认 'parentId'
|
||||
* @param {*} children 孩子节点字段 默认 'children'
|
||||
*/
|
||||
export function handleTree(data, id, parentId, children) {
|
||||
let config = {
|
||||
id: id || 'id',
|
||||
parentId: parentId || 'parentId',
|
||||
childrenList: children || 'children'
|
||||
};
|
||||
|
||||
var childrenListMap = {};
|
||||
var nodeIds = {};
|
||||
var tree = [];
|
||||
|
||||
for (let d of data) {
|
||||
let parentId = d[config.parentId];
|
||||
if (childrenListMap[parentId] == null) {
|
||||
childrenListMap[parentId] = [];
|
||||
}
|
||||
nodeIds[d[config.id]] = d;
|
||||
childrenListMap[parentId].push(d);
|
||||
}
|
||||
|
||||
for (let d of data) {
|
||||
let parentId = d[config.parentId];
|
||||
if (nodeIds[parentId] == null) {
|
||||
tree.push(d);
|
||||
}
|
||||
}
|
||||
|
||||
for (let t of tree) {
|
||||
adaptToChildrenList(t);
|
||||
}
|
||||
|
||||
function adaptToChildrenList(o) {
|
||||
if (childrenListMap[o[config.id]] !== null) {
|
||||
o[config.childrenList] = childrenListMap[o[config.id]];
|
||||
}
|
||||
if (o[config.childrenList]) {
|
||||
for (let c of o[config.childrenList]) {
|
||||
adaptToChildrenList(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数处理
|
||||
* @param {*} params 参数
|
||||
*/
|
||||
export function tansParams(params) {
|
||||
let result = ''
|
||||
for (const propName of Object.keys(params)) {
|
||||
const value = params[propName];
|
||||
var part = encodeURIComponent(propName) + "=";
|
||||
if (value !== null && value !== "" && typeof (value) !== "undefined") {
|
||||
if (typeof value === 'object') {
|
||||
for (const key of Object.keys(value)) {
|
||||
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
|
||||
let params = propName + '[' + key + ']';
|
||||
var subPart = encodeURIComponent(params) + "=";
|
||||
result += subPart + encodeURIComponent(value[key]) + "&";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result += part + encodeURIComponent(value) + "&";
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 获取两个日期之间的所有月份
|
||||
export function getMonth(quitTime, reentryTime) {
|
||||
// 解析日期
|
||||
const start = new Date(quitTime);
|
||||
const end = new Date(reentryTime);
|
||||
|
||||
// 初始化结果数组
|
||||
const months = [];
|
||||
|
||||
// 确保开始日期早于结束日期
|
||||
if (start > end) {
|
||||
[start, end] = [end, start];
|
||||
}
|
||||
|
||||
// 循环遍历所有月份
|
||||
let currentDate = new Date(start);
|
||||
while (currentDate <= end) {
|
||||
// 添加当前月份到结果数组
|
||||
months.push({
|
||||
year: currentDate.getFullYear(),
|
||||
month: currentDate.getMonth() + 1
|
||||
});
|
||||
|
||||
// 进入下一个月
|
||||
currentDate.setMonth(currentDate.getMonth() + 1);
|
||||
}
|
||||
|
||||
return months;
|
||||
}
|
||||
|
||||
// 验证是否为blob格式
|
||||
export function blobValidate(data) {
|
||||
return data.type !== 'application/json'
|
||||
}
|
||||
58
src/utils/scroll-to.js
Normal file
58
src/utils/scroll-to.js
Normal file
@@ -0,0 +1,58 @@
|
||||
Math.easeInOutQuad = function(t, b, c, d) {
|
||||
t /= d / 2
|
||||
if (t < 1) {
|
||||
return c / 2 * t * t + b
|
||||
}
|
||||
t--
|
||||
return -c / 2 * (t * (t - 2) - 1) + b
|
||||
}
|
||||
|
||||
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
|
||||
var requestAnimFrame = (function() {
|
||||
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
|
||||
})()
|
||||
|
||||
/**
|
||||
* Because it's so fucking difficult to detect the scrolling element, just move them all
|
||||
* @param {number} amount
|
||||
*/
|
||||
function move(amount) {
|
||||
document.documentElement.scrollTop = amount
|
||||
document.body.parentNode.scrollTop = amount
|
||||
document.body.scrollTop = amount
|
||||
}
|
||||
|
||||
function position() {
|
||||
return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} to
|
||||
* @param {number} duration
|
||||
* @param {Function} callback
|
||||
*/
|
||||
export function scrollTo(to, duration, callback) {
|
||||
const start = position()
|
||||
const change = to - start
|
||||
const increment = 20
|
||||
let currentTime = 0
|
||||
duration = (typeof (duration) === 'undefined') ? 500 : duration
|
||||
var animateScroll = function() {
|
||||
// increment the time
|
||||
currentTime += increment
|
||||
// find the value with the quadratic in-out easing function
|
||||
var val = Math.easeInOutQuad(currentTime, start, change, duration)
|
||||
// move the document.body
|
||||
move(val)
|
||||
// do the animation unless its over
|
||||
if (currentTime < duration) {
|
||||
requestAnimFrame(animateScroll)
|
||||
} else {
|
||||
if (callback && typeof (callback) === 'function') {
|
||||
// the animation is done so lets callback
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
animateScroll()
|
||||
}
|
||||
19
src/utils/tool.js
Normal file
19
src/utils/tool.js
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
export const uuid = () => {
|
||||
var s = [];
|
||||
var hexDigits = "0123456789abcdef";
|
||||
for (var i = 0; i < 36; i++) {
|
||||
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
|
||||
}
|
||||
// s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
|
||||
// s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
|
||||
s[8] = s[13] = s[18] = s[23] = "";
|
||||
|
||||
return s.join("");
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default {
|
||||
uuid
|
||||
}
|
||||
83
src/utils/validate.js
Normal file
83
src/utils/validate.js
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* @param {string} path
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isExternal(path) {
|
||||
return /^(https?:|mailto:|tel:)/.test(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validUsername(str) {
|
||||
const valid_map = ['admin', 'editor']
|
||||
return valid_map.indexOf(str.trim()) >= 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validURL(url) {
|
||||
const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
|
||||
return reg.test(url)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validLowerCase(str) {
|
||||
const reg = /^[a-z]+$/
|
||||
return reg.test(str)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validUpperCase(str) {
|
||||
const reg = /^[A-Z]+$/
|
||||
return reg.test(str)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validAlphabets(str) {
|
||||
const reg = /^[A-Za-z]+$/
|
||||
return reg.test(str)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} email
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validEmail(email) {
|
||||
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||
return reg.test(email)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isString(str) {
|
||||
if (typeof str === 'string' || str instanceof String) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} arg
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isArray(arg) {
|
||||
if (typeof Array.isArray === 'undefined') {
|
||||
return Object.prototype.toString.call(arg) === '[object Array]'
|
||||
}
|
||||
return Array.isArray(arg)
|
||||
}
|
||||
Reference in New Issue
Block a user