22 changed files with 14547 additions and 245 deletions
@ -0,0 +1,5 @@ |
|||||
|
//统一配置 |
||||
|
VITE_APP_PORT = 17777 |
||||
|
VITE_APP_TITLE = '管理平台' |
||||
|
VITE_APP_SYSTEM_KEY = 'hengxingaokeApp1' |
||||
|
VITE_APP_CSS_URL = '/public/' |
||||
|
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,63 @@ |
|||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2025-12-07 11:07:32 |
||||
|
@ 备注: 创建任意位数的随机字符串 |
||||
|
*/ |
||||
|
const generateRandomString = (length: number = 16): string => { |
||||
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' |
||||
|
const array = new Uint8Array(length) |
||||
|
crypto.getRandomValues(array) |
||||
|
return Array.from(array, byte => characters[byte % characters.length]).join('') |
||||
|
// let randomString = '';
|
||||
|
// for (let i = 0; i < length; i++) {
|
||||
|
// const randomIndex = Math.floor(Math.random() * characters.length);
|
||||
|
// randomString += characters.charAt(randomIndex);
|
||||
|
// }
|
||||
|
// return randomString;
|
||||
|
} |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2025-12-08 16:15:12 |
||||
|
@ 功能: 获取UUID |
||||
|
*/ |
||||
|
const uuid = () => { |
||||
|
const s: string[] = [] |
||||
|
const hexDigits = '123456789abcdef' |
||||
|
for (let 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((parseInt(s[19]!, 16) & 0x3) | 0x8, 1) // bits 6-7 of the clock_seq_hi_and_reserved to 01
|
||||
|
s[8] = s[13] = s[18] = s[23] = '-' |
||||
|
const uuid = s.join('') |
||||
|
return uuid |
||||
|
} |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2025-12-08 16:15:48 |
||||
|
@ 功能: 获取简单随机数 |
||||
|
*/ |
||||
|
const randomString = (length: number) => { |
||||
|
var str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' |
||||
|
var result = '' |
||||
|
for (var i = length; i > 0; --i) { |
||||
|
result += str[Math.floor(Math.random() * str.length)] |
||||
|
} |
||||
|
return result |
||||
|
} |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2025-12-08 16:16:12 |
||||
|
@ 功能: 中间画线驼峰 |
||||
|
*/ |
||||
|
const toHump = (str: string) => { |
||||
|
if (!str) return str |
||||
|
return str |
||||
|
.replace(/\-(\w)/g, function (all: string, letter: string) { |
||||
|
return letter.toUpperCase() |
||||
|
}) |
||||
|
.replace(/(\s|^)[a-z]/g, function (char: string) { |
||||
|
return char.toUpperCase() |
||||
|
}) |
||||
|
} |
||||
|
export { generateRandomString, uuid, randomString, toHump } |
||||
@ -0,0 +1,7 @@ |
|||||
|
import * as CryptoJS from 'crypto-js' |
||||
|
|
||||
|
const strToHexSub = (str: string): string => { |
||||
|
return CryptoJS.enc.Utf8.parse(str).toString(CryptoJS.enc.Hex) |
||||
|
} |
||||
|
|
||||
|
export { strToHexSub } |
||||
@ -0,0 +1,79 @@ |
|||||
|
import { SM4 as sm4 } from 'gm-crypto' |
||||
|
import * as CryptoJS from 'crypto-js' |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2025-12-07 10:26:51 |
||||
|
@ 备注: 将字符串转换成16进制 |
||||
|
*/ |
||||
|
const strToHexMethod = (str: string): string => { |
||||
|
return CryptoJS.enc.Utf8.parse(str).toString(CryptoJS.enc.Hex) |
||||
|
} |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2025-12-07 10:29:48 |
||||
|
@ 备注: 判断是不是12位16进制参数 |
||||
|
*/ |
||||
|
const isValidHex32 = (str: string): boolean => { |
||||
|
return /^[0-9a-fA-F]{32}$/.test(str) |
||||
|
} |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2025-12-07 10:28:33 |
||||
|
@ 备注: 初始化SM4密钥参数 |
||||
|
*/ |
||||
|
const appSystemKey = strToHexMethod(import.meta.env.VITE_APP_SYSTEM_KEY) |
||||
|
const sm4TokenKey = strToHexMethod(import.meta.env.VITE_APP_SM4_APP_KEY) |
||||
|
|
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2025-12-07 10:30:40 |
||||
|
@ 备注: SM4加密方法 |
||||
|
*/ |
||||
|
const sm4EncryptMethod = (data: string, customKey: string): string => { |
||||
|
let ivSetup = sm4TokenKey |
||||
|
if (customKey) { |
||||
|
ivSetup = strToHexMethod(customKey) |
||||
|
if (!isValidHex32(ivSetup)) { |
||||
|
ivSetup = sm4TokenKey |
||||
|
} |
||||
|
} |
||||
|
return sm4.encrypt(data, appSystemKey, { |
||||
|
iv: ivSetup, |
||||
|
mode: sm4.constants.CBC, |
||||
|
inputEncoding: 'utf8', |
||||
|
outputEncoding: 'hex', |
||||
|
padding: 1 |
||||
|
} as never) |
||||
|
} |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2025-12-07 10:31:22 |
||||
|
@ 备注: SM4解密方法 |
||||
|
*/ |
||||
|
const sm4DecryptMethod = (data: string, customKey: string): string => { |
||||
|
let ivSetup = sm4TokenKey |
||||
|
if (customKey) { |
||||
|
ivSetup = strToHexMethod(customKey) |
||||
|
if (!isValidHex32(ivSetup)) { |
||||
|
ivSetup = sm4TokenKey |
||||
|
} |
||||
|
} |
||||
|
// console.log('SM4解密方法----解密结构---data--->', data)
|
||||
|
console.log('SM4解密方法----解密结构----customKey-->', customKey) |
||||
|
console.log('SM4解密方法----解密结构--ivSetup---->', ivSetup) |
||||
|
console.log('SM4解密方法----解密结构---sm4TokenKey--->', sm4TokenKey) |
||||
|
console.log('SM4解密方法----解密结构---appSystemKey--->', appSystemKey) |
||||
|
|
||||
|
console.log('SM4解密方法----解密结构---appSystemKey.length--->', appSystemKey.length) |
||||
|
console.log('SM4解密方法----解密结构---ivSetup.length--->', ivSetup.length) |
||||
|
|
||||
|
|
||||
|
return sm4.decrypt(data, appSystemKey, { |
||||
|
iv: ivSetup, |
||||
|
mode: sm4.constants.CBC, |
||||
|
inputEncoding: 'hex', |
||||
|
outputEncoding: 'utf8', |
||||
|
padding: 1 |
||||
|
} as never) |
||||
|
} |
||||
|
export { sm4EncryptMethod, sm4DecryptMethod } |
||||
@ -0,0 +1,347 @@ |
|||||
|
/** |
||||
|
* 数字 |
||||
|
*/ |
||||
|
const REG_NUMBER: string = '.*\\d+.*' |
||||
|
/** |
||||
|
* 小写字母 |
||||
|
*/ |
||||
|
const REG_UPPERCASE: string = '.*[A-Z]+.*' |
||||
|
/** |
||||
|
* 大写字母 |
||||
|
*/ |
||||
|
const REG_LOWERCASE: string = '.*[a-z]+.*' |
||||
|
/** |
||||
|
* 特殊符号(~!@#$%^&*()_+|<>,.?/:;'[]{}\) |
||||
|
*/ |
||||
|
const REG_SYMBOL: string = '.*[~!@#$%^&*()_+|<>,.?/:;\'\\[\\]{}]+".*' |
||||
|
/** |
||||
|
* 键盘字符表(小写) |
||||
|
* 非shift键盘字符表 |
||||
|
*/ |
||||
|
const CHAR_TABLE1: string[][] = [ |
||||
|
['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\0'], |
||||
|
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\'], |
||||
|
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', "'", '\0', '\0'], |
||||
|
['z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', '\0', '\0', '\0'] |
||||
|
] |
||||
|
/** |
||||
|
* shift键盘的字符表 |
||||
|
*/ |
||||
|
const CHAR_TABLE2: string[][] = [ |
||||
|
['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\0'], |
||||
|
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '{', '}', '|'], |
||||
|
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ':', '"', '\0', '\0'], |
||||
|
['z', 'x', 'c', 'v', 'b', 'n', 'm', '<', '>', '?', '\0', '\0', '\0'] |
||||
|
] |
||||
|
|
||||
|
/** |
||||
|
* 校验密码是否符合条件 |
||||
|
* @param password 密码 |
||||
|
* @param username 用户名 |
||||
|
*/ |
||||
|
export const checkPasswordRule = (password: string, username: string) => { |
||||
|
if (password === '' || password.length < 8 || password.length > 32) { |
||||
|
// console.log("长度小于8,或大于32");
|
||||
|
return '密码长度应大于8小于32' |
||||
|
} |
||||
|
if (password.indexOf(username) !== -1) { |
||||
|
// console.log("包含用户名");
|
||||
|
return '请勿包含用户名' |
||||
|
} |
||||
|
if (isContinuousChar(password)) { |
||||
|
// console.log("包含3个及以上相同或字典连续字符");
|
||||
|
return '请勿包含3个及以上相同或连续的字符' |
||||
|
} |
||||
|
if (isKeyBoardContinuousChar(password)) { |
||||
|
// console.log("包含3个及以上键盘连续字符");
|
||||
|
return '请勿包含3个及以上键盘连续字符' |
||||
|
} |
||||
|
let i: number = 0 |
||||
|
if (password.match(REG_NUMBER)) i++ |
||||
|
if (password.match(REG_LOWERCASE)) i++ |
||||
|
if (password.match(REG_UPPERCASE)) i++ |
||||
|
if (password.match(REG_SYMBOL)) i++ |
||||
|
if (i < 2) { |
||||
|
// console.log(("数字、小写字母、大写字母、特殊字符,至少包含两种"));
|
||||
|
return '数字、小写字母、大写字母、特殊字符,至少包含两种' |
||||
|
} |
||||
|
// console.log(i);
|
||||
|
return '校验通过' |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 是否包含3个及以上相同或字典连续字符 |
||||
|
*/ |
||||
|
const isContinuousChar = (password: string) => { |
||||
|
const chars: string[] = password.split('') |
||||
|
|
||||
|
// 遍历所有可能的三个连续字符组合
|
||||
|
for (let i = 0; i < chars.length - 2; i++) { |
||||
|
// 添加类型断言,确保字符不是undefined
|
||||
|
const char1 = chars[i] as string |
||||
|
const char2 = chars[i + 1] as string |
||||
|
const char3 = chars[i + 2] as string |
||||
|
|
||||
|
// 获取字符的ASCII码
|
||||
|
const n1 = char1.charCodeAt(0) |
||||
|
const n2 = char2.charCodeAt(0) |
||||
|
const n3 = char3.charCodeAt(0) |
||||
|
|
||||
|
// 判断重复字符
|
||||
|
if (n1 === n2 && n1 === n3) { |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
// 判断连续字符: 正序(如abc)和倒序(如cba)
|
||||
|
if ((n1 + 1 === n2 && n2 + 1 === n3) || (n1 - 1 === n2 && n2 - 1 === n3)) { |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false |
||||
|
} |
||||
|
/** |
||||
|
* 是否包含3个及以上键盘连续字符 |
||||
|
* @param password 待匹配的字符串 |
||||
|
*/ |
||||
|
const isKeyBoardContinuousChar = (password: string) => { |
||||
|
if (password === '') { |
||||
|
return false |
||||
|
} |
||||
|
//考虑大小写,都转换成小写字母
|
||||
|
const lpStrChars: string[] = password.toLowerCase().split('') |
||||
|
// 获取字符串长度
|
||||
|
const nStrLen: number = lpStrChars.length |
||||
|
// 定义位置数组:row - 行,col - column 列,初始化为-1
|
||||
|
const pRowCharPos: number[] = new Array(nStrLen).fill(-1) |
||||
|
const pColCharPos: number[] = new Array(nStrLen).fill(-1) |
||||
|
|
||||
|
for (let i = 0; i < nStrLen; i++) { |
||||
|
// 添加类型断言,确保字符不是undefined
|
||||
|
const chLower: string = lpStrChars[i] as string |
||||
|
|
||||
|
// 检索在表1中的位置,构建位置数组
|
||||
|
for (let nRowTable1Idx = 0; nRowTable1Idx < 4; nRowTable1Idx++) { |
||||
|
for (let nColTable1Idx = 0; nColTable1Idx < 13; nColTable1Idx++) { |
||||
|
// 获取当前字符,跳过\0字符,使用可选链和空值合并确保类型安全
|
||||
|
const tableChar = CHAR_TABLE1[nRowTable1Idx]?.[nColTable1Idx] ?? '' |
||||
|
if (tableChar && tableChar !== '\0' && chLower === tableChar) { |
||||
|
pRowCharPos[i] = nRowTable1Idx |
||||
|
pColCharPos[i] = nColTable1Idx |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 在表1中没找到,到表二中去找,找到则continue
|
||||
|
// 添加非空检查
|
||||
|
const currentColPos = pColCharPos[i] |
||||
|
if (currentColPos !== undefined && currentColPos >= 0) { |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
// 检索在表2中的位置,构建位置数组
|
||||
|
for (let nRowTable2Idx = 0; nRowTable2Idx < 4; nRowTable2Idx++) { |
||||
|
for (let nColTable2Idx = 0; nColTable2Idx < 13; nColTable2Idx++) { |
||||
|
// 获取当前字符,跳过\0字符,使用可选链和空值合并确保类型安全
|
||||
|
const tableChar = CHAR_TABLE2[nRowTable2Idx]?.[nColTable2Idx] ?? '' |
||||
|
if (tableChar && tableChar !== '\0' && chLower === tableChar) { |
||||
|
pRowCharPos[i] = nRowTable2Idx |
||||
|
pColCharPos[i] = nColTable2Idx |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 匹配坐标连线
|
||||
|
for (let j = 1; j <= nStrLen - 2; j++) { |
||||
|
// 获取当前三个字符的位置,添加类型断言
|
||||
|
const row1 = pRowCharPos[j - 1] as number |
||||
|
const row2 = pRowCharPos[j] as number |
||||
|
const row3 = pRowCharPos[j + 1] as number |
||||
|
const col1 = pColCharPos[j - 1] as number |
||||
|
const col2 = pColCharPos[j] as number |
||||
|
const col3 = pColCharPos[j + 1] as number |
||||
|
|
||||
|
//同一行
|
||||
|
if (row1 === row2 && row2 === row3) { |
||||
|
// 键盘行正向连续(asd)或者键盘行反向连续(dsa)
|
||||
|
if ( |
||||
|
(col1 + 1 === col2 && col2 + 1 === col3) || |
||||
|
(col3 + 1 === col2 && col2 + 1 === col1) |
||||
|
) { |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//同一列
|
||||
|
if (col1 === col2 && col2 === col3) { |
||||
|
//键盘列连续(qaz)或者键盘列反向连续(zaq)
|
||||
|
if ( |
||||
|
(row1 + 1 === row2 && row2 + 1 === row3) || |
||||
|
(row1 - 1 === row2 && row2 - 1 === row3) |
||||
|
) { |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 密码强度校验 |
||||
|
*/ |
||||
|
/** |
||||
|
* 长度 |
||||
|
* @param str |
||||
|
*/ |
||||
|
const length = (str: string) => { |
||||
|
if (str.length < 5) { |
||||
|
return 5 |
||||
|
} else if (str.length < 8) { |
||||
|
return 10 |
||||
|
} else { |
||||
|
return 25 |
||||
|
} |
||||
|
} |
||||
|
/** |
||||
|
* 字母 |
||||
|
* @param str |
||||
|
*/ |
||||
|
const letters = (str: string) => { |
||||
|
let count1 = 0, |
||||
|
count2 = 0 |
||||
|
for (let i = 0; i < str.length; i++) { |
||||
|
if (str.charAt(i) >= 'a' && str.charAt(i) <= 'z') { |
||||
|
count1++ |
||||
|
} |
||||
|
if (str.charAt(i) >= 'A' && str.charAt(i) <= 'Z') { |
||||
|
count2++ |
||||
|
} |
||||
|
} |
||||
|
if (count1 === 0 && count2 === 0) { |
||||
|
return 0 |
||||
|
} |
||||
|
if (count1 !== 0 && count2 !== 0) { |
||||
|
return 20 |
||||
|
} |
||||
|
return 10 |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 数字 |
||||
|
* @param str |
||||
|
*/ |
||||
|
const numbers = (str: string) => { |
||||
|
let count = 0 |
||||
|
for (let i = 0; i < str.length; i++) { |
||||
|
if (str.charAt(i) >= '0' && str.charAt(i) <= '9') { |
||||
|
count++ |
||||
|
} |
||||
|
} |
||||
|
if (count === 0) { |
||||
|
return 0 |
||||
|
} |
||||
|
if (count === 1) { |
||||
|
return 10 |
||||
|
} |
||||
|
return 20 |
||||
|
} |
||||
|
/** |
||||
|
* 符号 |
||||
|
* @param str |
||||
|
*/ |
||||
|
const symbols = (str: string) => { |
||||
|
let count = 0 |
||||
|
for (let i = 0; i < str.length; i++) { |
||||
|
if ( |
||||
|
(str.charCodeAt(i) >= 0x21 && str.charCodeAt(i) <= 0x2f) || |
||||
|
(str.charCodeAt(i) >= 0x3a && str.charCodeAt(i) <= 0x40) || |
||||
|
(str.charCodeAt(i) >= 0x5b && str.charCodeAt(i) <= 0x60) || |
||||
|
(str.charCodeAt(i) >= 0x7b && str.charCodeAt(i) <= 0x7e) |
||||
|
) { |
||||
|
count++ |
||||
|
} |
||||
|
} |
||||
|
if (count === 0) { |
||||
|
return 0 |
||||
|
} |
||||
|
if (count === 1) { |
||||
|
return 10 |
||||
|
} |
||||
|
return 25 |
||||
|
} |
||||
|
/** |
||||
|
* 得分机制 |
||||
|
* @param str |
||||
|
*/ |
||||
|
const rewards = (str: string) => { |
||||
|
let letter = letters(str) //字母
|
||||
|
let number = numbers(str) //数字
|
||||
|
let symbol = symbols(str) //符号
|
||||
|
if (letter > 0 && number > 0 && symbol === 0) { |
||||
|
//字母和数字
|
||||
|
return 2 |
||||
|
} |
||||
|
if (letter === 10 && number > 0 && symbol > 0) { |
||||
|
//字母、数字和符号
|
||||
|
return 3 |
||||
|
} |
||||
|
if (letter === 20 && number > 0 && symbol > 0) { |
||||
|
//大小写字母、数字和符号
|
||||
|
return 5 |
||||
|
} |
||||
|
return 0 |
||||
|
} |
||||
|
/** |
||||
|
* 最终评分 |
||||
|
* @param str |
||||
|
*/ |
||||
|
export const level = (str: string) => { |
||||
|
let lengths = length(str) //长度
|
||||
|
let letter = letters(str) //字母
|
||||
|
let number = numbers(str) //数字
|
||||
|
let symbol = symbols(str) //符号
|
||||
|
let reward = rewards(str) //奖励
|
||||
|
let sum = lengths + letter + number + symbol + reward |
||||
|
console.log(sum) |
||||
|
if (sum >= 80) { |
||||
|
return '非常强' //非常安全
|
||||
|
} else if (sum >= 60) { |
||||
|
return '强' //非常强
|
||||
|
} else if (sum >= 40) { |
||||
|
return '一般' //一般
|
||||
|
} else if (sum >= 25) { |
||||
|
return '弱' //弱
|
||||
|
} else { |
||||
|
return '非常弱' //非常弱
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export const checkPwdRule = (password: string) => { |
||||
|
if (password === '' || password.length < 8 || password.length > 32) { |
||||
|
// console.log("长度小于8,或大于32");
|
||||
|
return '密码长度应大于8小于32' |
||||
|
} |
||||
|
if (isContinuousChar(password)) { |
||||
|
// console.log("包含3个及以上相同或字典连续字符");
|
||||
|
return '请勿包含3个及以上相同或连续的字符' |
||||
|
} |
||||
|
if (isKeyBoardContinuousChar(password)) { |
||||
|
// console.log("包含3个及以上键盘连续字符");
|
||||
|
return '请勿包含3个及以上键盘连续字符' |
||||
|
} |
||||
|
let i: number = 0 |
||||
|
if (password.match(REG_NUMBER)) i++ |
||||
|
if (password.match(REG_LOWERCASE)) i++ |
||||
|
if (password.match(REG_UPPERCASE)) i++ |
||||
|
if (password.match(REG_SYMBOL)) i++ |
||||
|
if (i < 2) { |
||||
|
// console.log(("数字、小写字母、大写字母、特殊字符,至少包含两种"));
|
||||
|
return '数字、小写字母、大写字母、特殊字符,至少包含两种' |
||||
|
} |
||||
|
// console.log(i);
|
||||
|
return '校验通过' |
||||
|
} |
||||
@ -1,96 +1,159 @@ |
|||||
import axios, { InternalAxiosRequestConfig, AxiosResponse } from 'axios'; |
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2025-12-09 10:57:37 |
||||
|
@ 功能: 封装axios请求 |
||||
|
*/ |
||||
|
import axios, { AxiosError, type InternalAxiosRequestConfig } from 'axios' |
||||
|
import { ElMessage, ElMessageBox } from 'element-plus' |
||||
|
import { generateRandomString } from '@/utils/encryptionAndDecryption/randNumber' |
||||
|
import { sm4DecryptMethod, sm4EncryptMethod } from '@/utils/encryptionAndDecryption/sm4Utils' |
||||
import { useUserStoreHook } from '@/store/modules/user'; |
import { useUserStoreHook } from '@/store/modules/user'; |
||||
import { ElMessage, ElMessageBox } from 'element-plus'; |
|
||||
import router from '@/router'; |
|
||||
import { useRouter } from 'vue-router' |
import { useRouter } from 'vue-router' |
||||
|
|
||||
|
|
||||
const routerPinia = useRouter() |
const routerPinia = useRouter() |
||||
// 创建 axios 实例
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2025-12-09 10:58:34 |
||||
|
@ 功能: 创建axios实例 |
||||
|
*/ |
||||
const service = axios.create({ |
const service = axios.create({ |
||||
baseURL: import.meta.env.VITE_APP_BASE_API, |
baseURL: import.meta.env.VITE_APP_BASE_API, |
||||
timeout: 50000, |
timeout: 50000, |
||||
headers: { 'Content-Type': 'application/json;charset=utf-8' } |
headers: { 'Content-Type': 'application/json;charset=utf-8' } |
||||
}); |
}); |
||||
|
|
||||
// 请求拦截器
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2025-12-09 11:00:05 |
||||
|
@ 功能: 请求拦截 |
||||
|
*/ |
||||
service.interceptors.request.use( |
service.interceptors.request.use( |
||||
(config: InternalAxiosRequestConfig) => { |
(config: InternalAxiosRequestConfig) => { |
||||
const userStore = useUserStoreHook(); |
const userStore = useUserStoreHook(); |
||||
//config.headers["Content-Type"] = "application/json;charset=utf-8";
|
|
||||
if (userStore.tokenIng) { |
|
||||
config.headers.Authorization = userStore.tokenIng; |
|
||||
} |
|
||||
if (userStore.userKey) { |
|
||||
config.headers["user-key"] = userStore.userKey; |
|
||||
} |
|
||||
if (userStore.userToken) { |
|
||||
config.headers["user-token"] = userStore.userToken; |
|
||||
} |
|
||||
return config; |
|
||||
}, |
|
||||
(error: any) => { |
|
||||
return Promise.reject(error); |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
// 响应拦截器
|
if (userStore.tokenIng) { |
||||
service.interceptors.response.use( |
config.headers.Authorization = userStore.tokenIng; |
||||
(response: AxiosResponse) => { |
} |
||||
const { code, msg } = response.data; |
if (userStore.userKey) { |
||||
if (code === 0 || code === 200 || code === 10001) { |
config.headers["user-key"] = userStore.userKey; |
||||
return response.data; |
} |
||||
|
if (userStore.userToken) { |
||||
|
config.headers["user-token"] = userStore.userToken; |
||||
|
} |
||||
|
|
||||
|
// if (config.headers['content-type'] === 'application/json') {
|
||||
|
let { data, headers } = config |
||||
|
//获取16位随机数
|
||||
|
let randomString = generateRandomString(16) |
||||
|
config.headers['Auth-key'] = randomString |
||||
|
if (data) { |
||||
|
// 加密请求数据
|
||||
|
config.data = { |
||||
|
data: sm4EncryptMethod(JSON.stringify(data), randomString) |
||||
|
} |
||||
|
} |
||||
|
console.log('请求拦截---------->', randomString) |
||||
|
console.log('请求拦截----headers------>', headers) |
||||
|
console.log('请求拦截----data------>', config.data) |
||||
|
console.log('请求拦截----config------>', config) |
||||
|
// }
|
||||
|
|
||||
|
// console.log('请求拦截----content-type------>', config.headers['Content-Type'])
|
||||
|
// console.log('请求拦截----config------>', config.headers)
|
||||
|
return config |
||||
|
}, |
||||
|
(error: AxiosError) => { |
||||
|
return Promise.reject(error) |
||||
} |
} |
||||
if (code === 7 || code === 300 || code === 301 || code === 302){ |
) |
||||
ElMessageBox.confirm("身份令牌已失效!请重新登录!", "提示5", { |
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2025-12-09 21:00:20 |
||||
|
@ 备注: 响应拦截 |
||||
|
*/ |
||||
|
service.interceptors.response.use( |
||||
|
response => { |
||||
|
|
||||
|
let { data, headers } = response |
||||
|
let authKey = headers['auth-key'] |
||||
|
console.log('行营结果----authKey------>', authKey) |
||||
|
// 解密响应数据
|
||||
|
if (authKey) { |
||||
|
let jsonData = sm4DecryptMethod(data.data, authKey) |
||||
|
response.data.data = JSON.parse(jsonData) |
||||
|
} |
||||
|
console.log('行营结果----解密结构------>', headers['auth-key'], response) |
||||
|
const { code, msg } = response.data; |
||||
|
if (code === 0 || code === 200 || code === 10001) { |
||||
|
return response.data; |
||||
|
} |
||||
|
if (code === 7 || code === 300 || code === 301 || code === 302){ |
||||
|
ElMessageBox.confirm("身份令牌已失效!请重新登录!", "提示5", { |
||||
confirmButtonText: "确定", |
confirmButtonText: "确定", |
||||
type: "warning", |
type: "warning", |
||||
}).then(() => { |
}).then(() => { |
||||
localStorage.clear(); |
localStorage.clear(); |
||||
// window.location.href = "/login";
|
window.location.href = "/login"; |
||||
routerPinia.push({path:"/login"}) |
// routerPinia.push({path:"/login"})
|
||||
}); |
}); |
||||
return response.data; |
return response.data; |
||||
} |
} |
||||
// 响应数据为二进制流处理(Excel导出)
|
// 响应数据为二进制流处理(Excel导出)
|
||||
if (response.data instanceof ArrayBuffer) { |
if (response.data instanceof ArrayBuffer) { |
||||
return response; |
return response; |
||||
} |
} |
||||
|
|
||||
// ElMessage.error(msg || '系统出错');
|
|
||||
return Promise.reject(new Error(msg || 'Error')); |
|
||||
}, |
|
||||
(error: any) => { |
|
||||
if (error.response.data) { |
|
||||
const { code, msg } = error.response.data; |
|
||||
// token 过期,重新登录
|
|
||||
if (code === 'A0230') { |
|
||||
ElMessageBox.confirm('当前页面已失效,请重新登录', '提示6', { |
|
||||
confirmButtonText: '确定', |
|
||||
type: 'warning' |
|
||||
}).then(() => { |
|
||||
localStorage.clear(); |
|
||||
// window.location.href = '/login';
|
|
||||
router.push({path:"/login"}) |
|
||||
}); |
|
||||
}else if(code === 7 || code === 300 || code === 301 || code === 302){ |
|
||||
ElMessageBox.confirm("身份令牌已失效!请重新登录!", "提示7", { |
|
||||
confirmButtonText: "确定", |
|
||||
type: "warning", |
|
||||
}).then(() => { |
|
||||
localStorage.clear(); |
|
||||
// window.location.href = "/login";
|
|
||||
routerPinia.push({path:"/login"}) |
|
||||
}); |
|
||||
}else if (code === 10001 || code === 10002 || code === 10003) { |
|
||||
return Promise.reject(error.message); |
|
||||
} else { |
|
||||
// ElMessage.error(msg || '系统出错');
|
// ElMessage.error(msg || '系统出错');
|
||||
} |
return Promise.reject(new Error(msg || 'Error')); |
||||
|
}, |
||||
|
(error: any) => { |
||||
|
if (error.response) { |
||||
|
const status = error.response.status |
||||
|
switch (status) { |
||||
|
case 401: |
||||
|
// 处理401错误,例如跳转到登录页
|
||||
|
break |
||||
|
case 403: |
||||
|
// 处理403错误,例如提示无权限
|
||||
|
break |
||||
|
case 404: |
||||
|
// 处理404错误,例如提示资源不存在
|
||||
|
break |
||||
|
case 500: |
||||
|
// 处理500错误,例如提示服务器错误
|
||||
|
break |
||||
|
default: |
||||
|
// 处理其他错误
|
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
if (error.response.data) { |
||||
|
const { code, msg } = error.response?.data; |
||||
|
// token 过期,重新登录
|
||||
|
if (code === 'A0230') { |
||||
|
ElMessageBox.confirm('当前页面已失效,请重新登录', '提示6', { |
||||
|
confirmButtonText: '确定', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
localStorage.clear(); |
||||
|
window.location.href = '/login'; |
||||
|
// router.push({path:"/login"})
|
||||
|
}); |
||||
|
}else if(code === 7 || code === 300 || code === 301 || code === 302){ |
||||
|
ElMessageBox.confirm("身份令牌已失效!请重新登录!", "提示7", { |
||||
|
confirmButtonText: "确定", |
||||
|
type: "warning", |
||||
|
}).then(() => { |
||||
|
localStorage.clear(); |
||||
|
window.location.href = "/login"; |
||||
|
// routerPinia.push({path:"/login"})
|
||||
|
}); |
||||
|
}else if (code === 10001 || code === 10002 || code === 10003) { |
||||
|
return Promise.reject(error.message); |
||||
|
} else { |
||||
|
// ElMessage.error(msg || '系统出错');
|
||||
|
} |
||||
|
} |
||||
|
return Promise.reject(error.message); |
||||
} |
} |
||||
return Promise.reject(error.message); |
) |
||||
} |
export default service |
||||
); |
|
||||
|
|
||||
// 导出 axios 实例
|
|
||||
export default service; |
|
||||
|
|||||
@ -0,0 +1,130 @@ |
|||||
|
import axios, { InternalAxiosRequestConfig, AxiosResponse } from 'axios'; |
||||
|
import { useUserStoreHook } from '@/store/modules/user'; |
||||
|
import { ElMessage, ElMessageBox } from 'element-plus'; |
||||
|
import router from '@/router'; |
||||
|
import { useRouter } from 'vue-router' |
||||
|
import { generateRandomString } from '@/utils/encryptionAndDecryption/randNumber' |
||||
|
|
||||
|
|
||||
|
const routerPinia = useRouter() |
||||
|
// 创建 axios 实例
|
||||
|
const service = axios.create({ |
||||
|
baseURL: import.meta.env.VITE_APP_BASE_API, |
||||
|
timeout: 50000, |
||||
|
headers: { 'Content-Type': 'application/json;charset=utf-8' } |
||||
|
}); |
||||
|
|
||||
|
// 请求拦截器
|
||||
|
service.interceptors.request.use( |
||||
|
(config: InternalAxiosRequestConfig) => { |
||||
|
const userStore = useUserStoreHook(); |
||||
|
//config.headers["Content-Type"] = "application/json;charset=utf-8";
|
||||
|
if (userStore.tokenIng) { |
||||
|
config.headers.Authorization = userStore.tokenIng; |
||||
|
} |
||||
|
if (userStore.userKey) { |
||||
|
config.headers["user-key"] = userStore.userKey; |
||||
|
} |
||||
|
if (userStore.userToken) { |
||||
|
config.headers["user-token"] = userStore.userToken; |
||||
|
} |
||||
|
|
||||
|
let { data, headers } = config |
||||
|
console.log('请求拦截----data---1--->', data) |
||||
|
|
||||
|
//获取16位随机数
|
||||
|
let randomString = generateRandomString(16) |
||||
|
config.headers['Auth-key'] = randomString |
||||
|
if (data) { |
||||
|
console.log('请求拦截----data---2--->', data) |
||||
|
// 加密请求数据
|
||||
|
config.data = { |
||||
|
data: sm4EncryptMethod(JSON.stringify(data), randomString) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
console.log('请求拦截---------->', randomString) |
||||
|
console.log('请求拦截----headers------>', headers) |
||||
|
console.log('请求拦截----data---3--->', data) |
||||
|
console.log('请求拦截----data------>', config.data) |
||||
|
console.log('请求拦截----config------>', config) |
||||
|
|
||||
|
|
||||
|
return config; |
||||
|
}, |
||||
|
(error: any) => { |
||||
|
return Promise.reject(error); |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
// 响应拦截器
|
||||
|
service.interceptors.response.use( |
||||
|
(response: AxiosResponse) => { |
||||
|
|
||||
|
let { data, headers } = response |
||||
|
let authKey = headers['auth-key'] |
||||
|
console.log('行营结果----authKey------>', authKey) |
||||
|
if (authKey) { |
||||
|
let jsonData = sm4DecryptMethod(data.data, authKey) |
||||
|
response.data.data = JSON.parse(jsonData) |
||||
|
} |
||||
|
const { code, msg } = response.data; |
||||
|
console.log('行营结果----解密结构------>', headers['auth-key'], response) |
||||
|
// console.error("--------code--------->",code)
|
||||
|
// console.error("--------msg--------->",msg)
|
||||
|
if (code === 0 || code === 200 || code === 10001) { |
||||
|
return response.data; |
||||
|
} |
||||
|
if (code === 7 || code === 300 || code === 301 || code === 302){ |
||||
|
ElMessageBox.confirm("身份令牌已失效!请重新登录!", "提示5", { |
||||
|
confirmButtonText: "确定", |
||||
|
type: "warning", |
||||
|
}).then(() => { |
||||
|
localStorage.clear(); |
||||
|
window.location.href = "/login"; |
||||
|
// routerPinia.push({path:"/login"})
|
||||
|
}); |
||||
|
return response.data; |
||||
|
} |
||||
|
// 响应数据为二进制流处理(Excel导出)
|
||||
|
if (response.data instanceof ArrayBuffer) { |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
// ElMessage.error(msg || '系统出错');
|
||||
|
return Promise.reject(new Error(msg || 'Error')); |
||||
|
}, |
||||
|
(error: any) => { |
||||
|
if (error.response.data) { |
||||
|
const { code, msg } = error.response.data; |
||||
|
// token 过期,重新登录
|
||||
|
if (code === 'A0230') { |
||||
|
ElMessageBox.confirm('当前页面已失效,请重新登录', '提示6', { |
||||
|
confirmButtonText: '确定', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
localStorage.clear(); |
||||
|
window.location.href = '/login'; |
||||
|
// router.push({path:"/login"})
|
||||
|
}); |
||||
|
}else if(code === 7 || code === 300 || code === 301 || code === 302){ |
||||
|
ElMessageBox.confirm("身份令牌已失效!请重新登录!", "提示7", { |
||||
|
confirmButtonText: "确定", |
||||
|
type: "warning", |
||||
|
}).then(() => { |
||||
|
localStorage.clear(); |
||||
|
window.location.href = "/login"; |
||||
|
// routerPinia.push({path:"/login"})
|
||||
|
}); |
||||
|
}else if (code === 10001 || code === 10002 || code === 10003) { |
||||
|
return Promise.reject(error.message); |
||||
|
} else { |
||||
|
// ElMessage.error(msg || '系统出错');
|
||||
|
} |
||||
|
} |
||||
|
return Promise.reject(error.message); |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
// 导出 axios 实例
|
||||
|
export default service; |
||||
@ -0,0 +1,659 @@ |
|||||
|
<!-- eslint-disable vue/no-side-effects-in-computed-properties --> |
||||
|
<!-- |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2026-01-19 13:21:15 |
||||
|
@ 备注: 编辑状态下的添加 |
||||
|
--> |
||||
|
<script lang='ts' setup> |
||||
|
import { conditionInfo, nodeFlow } from "@/api/DesignForm/type"; |
||||
|
import { echoTableFormPage,realTimeUpdateFlow } from "@/api/taskapi/management"; |
||||
|
import { judgeSubmitCancel,startRunFlow,gainAppPageInfo,getFieldRecord } from "@/api/DesignForm/requestapi"; |
||||
|
import { string2json, stringToObj } from "@/utils/DesignForm/form"; |
||||
|
import { appPageDataInit } from "@/api/date/type"; |
||||
|
import { |
||||
|
notAsA_BasisForJudgment, |
||||
|
asAnApprovalActionControl, |
||||
|
fixedValueControl, |
||||
|
timeControl, |
||||
|
timeEquation, |
||||
|
submitButton, |
||||
|
afreshSubmitButton, |
||||
|
} from "@/utils/workflow/const"; |
||||
|
import { nodePoweInfo } from "@/api/taskapi/types"; |
||||
|
import request from "@/utils/request"; |
||||
|
import SvgIcon from "@/components/SvgIcon/index.vue"; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
show:{ |
||||
|
type: Boolean, |
||||
|
default: true, |
||||
|
}, |
||||
|
versionid: { |
||||
|
type: String, |
||||
|
default: "", |
||||
|
}, |
||||
|
versiontitle: { |
||||
|
type: String, |
||||
|
default: "", |
||||
|
}, |
||||
|
drawerwith: { |
||||
|
type: Number, |
||||
|
default: 0, |
||||
|
}, |
||||
|
pickAppMenu: { |
||||
|
type: Object, |
||||
|
default() { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
}) |
||||
|
const formBodyRef = ref() |
||||
|
const drawBodyWidth = ref(props.drawerwith) |
||||
|
const emits = defineEmits(["update:show", "searchquery"]); |
||||
|
const nodeKey = ref<string>(""); |
||||
|
const currterNodePower = ref<nodePoweInfo[]>([]) |
||||
|
const flowMap = ref<any[]>(); //工作流 |
||||
|
const flowFactor = reactive<conditionInfo[]>([]); //表单组件 |
||||
|
const pageType = ref<number>(1) |
||||
|
const aiConfigArea =ref(false) |
||||
|
const drawLoading = ref(false) |
||||
|
const isFlowTable = ref(false); //判断是不是流程表单 |
||||
|
const openOrClose = computed({ |
||||
|
get: () => { |
||||
|
drawBodyWidth.value = props.drawerwith; |
||||
|
return props.show |
||||
|
}, |
||||
|
set: (val) => { |
||||
|
emits("update:show", val); |
||||
|
drawBodyWidth.value = 0; |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const pageBody = ref("") |
||||
|
const drawTitle = computed(() => props.versiontitle) |
||||
|
const drawbox = computed({ |
||||
|
get: () => { |
||||
|
let pageContWidth = props.drawerwith |
||||
|
let extraW=0; |
||||
|
if (aiConfigArea.value){ |
||||
|
extraW=300 //ai 问答区域的宽度 |
||||
|
} |
||||
|
if (isFlowTable.value) { |
||||
|
extraW = 300+extraW; |
||||
|
} |
||||
|
if (aiConfigArea.value && isFlowTable.value){ |
||||
|
pageType.value = 3 |
||||
|
pageBody.value = "treePage" |
||||
|
pageContWidth = pageContWidth - (extraW/2) |
||||
|
}else if ((!aiConfigArea.value && isFlowTable.value) || (aiConfigArea.value && !isFlowTable.value)){ |
||||
|
pageType.value = 2 |
||||
|
pageBody.value = "twoPage" |
||||
|
pageContWidth = pageContWidth - (extraW/2) |
||||
|
}else{ |
||||
|
pageType.value = 1 |
||||
|
pageBody.value = "" |
||||
|
} |
||||
|
console.error("1---------------->",pageContWidth) |
||||
|
return pageContWidth+extraW; |
||||
|
}, |
||||
|
set: (val: number) => { |
||||
|
return val; |
||||
|
}, |
||||
|
}); |
||||
|
let qrCodeImgInside = ""; |
||||
|
const appInitData = ref<appPageDataInit>(); |
||||
|
const mastesformjson = ref(""); // |
||||
|
const versionId = ref<string>(""); //表单版本号 |
||||
|
const versionTitle = ref<string>(""); //表单名称 |
||||
|
const changeFlowPick = ref<string[]>([]) |
||||
|
const purviewAry = ref<any[]>([]); |
||||
|
const qrCodeInsideDialogFlag = ref(false); |
||||
|
|
||||
|
//获取工作流条件 |
||||
|
const gainFlowChart = reactive<nodeFlow>({ |
||||
|
id: "0", |
||||
|
}); |
||||
|
|
||||
|
const state = reactive<any>({ |
||||
|
type: 1, // 1新增;2修改;3查看(表单模式) ;4查看; 5设计 |
||||
|
formData: { |
||||
|
list: [], |
||||
|
form: {}, |
||||
|
config: {}, |
||||
|
powerstr: {}, |
||||
|
aiConfig:[] |
||||
|
}, |
||||
|
dict: {}, |
||||
|
formId: props.versionid, |
||||
|
id: 0, |
||||
|
loading: true, |
||||
|
}); |
||||
|
//app表单 |
||||
|
const stateForm = reactive<any>({ |
||||
|
type: 1, // 1新增;2修改;3查看(表单模式) ;4查看; 5设计 |
||||
|
formData: { |
||||
|
list: [], |
||||
|
form: {}, |
||||
|
config: {}, |
||||
|
powerstr: {}, |
||||
|
aiConfig:[] |
||||
|
}, |
||||
|
dict: {}, |
||||
|
id: 0, |
||||
|
formId: "1", |
||||
|
versionId: 0, |
||||
|
loading: true, |
||||
|
}); |
||||
|
let gainAppPageInfoPromise: any[] = []; |
||||
|
let gainAppPageInfoData: any = {}; |
||||
|
let getFieldRecordPromise: any[] = []; |
||||
|
let objMastesform: any; |
||||
|
|
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2026-01-19 15:30:44 |
||||
|
@ 功能: 获取表单数据 |
||||
|
*/ |
||||
|
const getTaskFormData = () => { |
||||
|
drawLoading.value = true |
||||
|
echoTableFormPage({ id: props.versionid.toString() }) |
||||
|
.then(({ data }) => { |
||||
|
console.log("表单数据",data) |
||||
|
let extraW=0; |
||||
|
if (data.tableFormPage.flowIsOpen == 1 && data.tableFormPage.flowkeystr != "0") { |
||||
|
isFlowTable.value = true; |
||||
|
extraW = 300 |
||||
|
// drawbox.value = 800 + 350; |
||||
|
} else { |
||||
|
isFlowTable.value = false; |
||||
|
// drawbox.value = 800; |
||||
|
} |
||||
|
if(data.tableFormPage.powerAry && Array.isArray(data.tableFormPage.powerAry) && data.tableFormPage.powerAry.length > 0){ |
||||
|
currterNodePower.value = data.tableFormPage.powerAry |
||||
|
} |
||||
|
changeFlowPick.value = data.channerUpdate |
||||
|
nodeKey.value = data.flowPage.nodeKey?data.flowPage.nodeKey:""; |
||||
|
purviewAry.value = data.tableFormPage.powerAry; |
||||
|
gainFlowChart.id = data.tableFormPage.flowkeystr; |
||||
|
console.log("表单数据--->1", data.flowPage.flowList); |
||||
|
flowMap.value = data.flowPage.flowList; |
||||
|
state.id = props.versionid; |
||||
|
state.formData = stringToObj(data.tableFormPage.mastesform); |
||||
|
state.formData.config.hideField = []; //暂时的修复方法:接口返回的config中没有hideField属性,手动添加 |
||||
|
state.dict = string2json(data.tableFormPage.dict); |
||||
|
state.formData.powerstr = string2json(data.tableFormPage.powerstr); |
||||
|
judgeSubmitCancel({ name: data.tableFormPage.mastesformjson }).then( |
||||
|
(datajud: any) => { |
||||
|
if (datajud.code == 0) { |
||||
|
if (datajud.data == 3 || datajud.data == 4) { |
||||
|
state.formData.list.push(submitButton); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
); |
||||
|
if(state.formData.aiConfig.length>0){ |
||||
|
aiConfigArea.value=true |
||||
|
extraW = extraW + 300 |
||||
|
} |
||||
|
|
||||
|
if (aiConfigArea.value && isFlowTable.value){ |
||||
|
pageType.value = 3 |
||||
|
pageBody.value = "treePage" |
||||
|
drawBodyWidth.value = drawBodyWidth.value + extraW - (extraW/2) |
||||
|
}else if ((!aiConfigArea.value && isFlowTable.value) || (aiConfigArea.value && !isFlowTable.value)){ |
||||
|
pageType.value = 2 |
||||
|
pageBody.value = "twoPage" |
||||
|
drawBodyWidth.value = drawBodyWidth.value + extraW- (extraW/2) |
||||
|
}else{ |
||||
|
pageType.value = 1 |
||||
|
pageBody.value = "" |
||||
|
drawBodyWidth.value = drawBodyWidth.value |
||||
|
} |
||||
|
|
||||
|
drawLoading.value = false |
||||
|
|
||||
|
console.error("2---------------->",formBodyRef.value?.clientWidth) |
||||
|
}) |
||||
|
.finally(()=>{}) |
||||
|
} |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2026-01-19 15:38:53 |
||||
|
@ 功能: 初始化数据 |
||||
|
*/ |
||||
|
const initData = () => { |
||||
|
state.formData = { |
||||
|
list: [], |
||||
|
form: {}, |
||||
|
config: {}, |
||||
|
}; |
||||
|
state.dict = {}; |
||||
|
state.formId = 0; |
||||
|
state.id = 0; |
||||
|
state.loading = true; |
||||
|
let aryLen = flowFactor.length; |
||||
|
console.log("改变表单值--flowMap.value--1->", flowMap.value); |
||||
|
flowMap.value = []; |
||||
|
console.log("改变表单值--flowMap.value--->", flowMap.value); |
||||
|
if (aryLen > 0) flowFactor.splice(0, aryLen); |
||||
|
} |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2026-01-19 15:38:13 |
||||
|
@ 功能: 初始化表单 |
||||
|
*/ |
||||
|
const initLoadData = () => { |
||||
|
console.log("获取初始化表单数据-------------->", props.pickAppMenu); |
||||
|
if (props.pickAppMenu.type != 1) { |
||||
|
gainAppPageInfoPromise.push( |
||||
|
gainAppPageInfo({ id: props.pickAppMenu.id }).then((data:any) => { |
||||
|
gainAppPageInfoData = data; |
||||
|
}) |
||||
|
) |
||||
|
|
||||
|
Promise.all(gainAppPageInfoPromise).then(() => { |
||||
|
objMastesform = stringToObj(gainAppPageInfoData.data.appForm.mastesform); |
||||
|
for (const element of objMastesform.list) { |
||||
|
if (element.type == "table") { |
||||
|
//console.log(element); |
||||
|
for (const item of element.list) { |
||||
|
if (item.options) { |
||||
|
if ( |
||||
|
item.type == "radio" || |
||||
|
item.type == "select" || |
||||
|
item.type == "checkbox" |
||||
|
) { |
||||
|
console.log(item.config.optionsType); |
||||
|
if (item.config.optionsType == 3) { |
||||
|
let paramx: string = "" + item.control.optionsValue3Field; |
||||
|
getFieldRecordPromise.push( |
||||
|
getFieldRecord(paramx).then(({ data }) => { |
||||
|
//console.log(data) |
||||
|
item.options = data; |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
Promise.all(getFieldRecordPromise).then(() => { |
||||
|
console.log(gainAppPageInfoData); |
||||
|
let objMastesformStr = JSON.stringify(objMastesform); |
||||
|
gainAppPageInfoData.data.appForm.mastesform = objMastesform; |
||||
|
gainAppPageInfoData.data.appForm.mastesformjson = objMastesformStr; |
||||
|
|
||||
|
appInitData.value = gainAppPageInfoData.data; |
||||
|
// console.log("获取初始化表单数据---!",appInitData.value) |
||||
|
mastesformjson.value = gainAppPageInfoData.data.appForm.mastesformjson; |
||||
|
// isFlow.value = gainAppPageInfoData.data.appForm.flowIsOpen; |
||||
|
|
||||
|
versionId.value = gainAppPageInfoData.data.appForm.id.toString(); |
||||
|
versionTitle.value = gainAppPageInfoData.data.appForm.name; |
||||
|
|
||||
|
if (gainAppPageInfoData.data.page) { |
||||
|
// console.log("data.data.mastesform", data.data) |
||||
|
stateForm.id = gainAppPageInfoData.data.appForm.version.toString(); |
||||
|
stateForm.formId = gainAppPageInfoData.data.appForm.cfid.toString(); |
||||
|
stateForm.versionId = gainAppPageInfoData.data.appForm.id.toString(); |
||||
|
stateForm.formData = objMastesform; |
||||
|
stateForm.formData.config.hideField = []; //暂时的修复方法:接口返回的config中没有hideField属性,手动添加 |
||||
|
stateForm.dict = string2json(gainAppPageInfoData.data.appForm.dict); |
||||
|
stateForm.formData.powerstr = string2json( |
||||
|
gainAppPageInfoData.data.appForm.powerstr |
||||
|
); |
||||
|
// console.log("data.data.mastesform", stateForm.formData) |
||||
|
|
||||
|
judgeSubmitCancel({ |
||||
|
name: gainAppPageInfoData.data.appForm.mastesformjson, |
||||
|
}).then((datajud: any) => { |
||||
|
if (datajud.code == 0) { |
||||
|
if (datajud.data == 3 || datajud.data == 4) { |
||||
|
stateForm.formData.list.push(submitButton); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
}) |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2026-01-19 15:34:26 |
||||
|
@ 功能: 监听开关 |
||||
|
*/ |
||||
|
watch(() => openOrClose,(val) => { |
||||
|
if(val){ |
||||
|
getTaskFormData() |
||||
|
initLoadData() |
||||
|
}else{ |
||||
|
initData() |
||||
|
} |
||||
|
},{ |
||||
|
deep:true |
||||
|
}) |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2026-01-19 16:16:16 |
||||
|
@ 功能: 扫码填单 |
||||
|
*/ |
||||
|
const showFillFormQrCode = () => { |
||||
|
//查看二维码展示弹窗图片,图片带有分组,app,表单名称,和二维码图片 |
||||
|
//在此获取必要的参数并请求后台,获取二维码图片,当图片返回成功时,显示弹窗 |
||||
|
//必要的参数为cfid |
||||
|
if ( |
||||
|
state.formData.form.qrCodeFlag == true && |
||||
|
state.formData.form.qrCodeInside == true |
||||
|
) { |
||||
|
getQrCodeImgInside().then(({ data }) => { |
||||
|
qrCodeImgInside = data; |
||||
|
if (qrCodeInsideDialogFlag.value == false) { |
||||
|
//console.log(qrCodeImgInside) |
||||
|
qrCodeInsideDialogFlag.value = true; |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
alert("请先开启表单二维码功能"); |
||||
|
} |
||||
|
} |
||||
|
const getQrCodeImgInside = () => { |
||||
|
return request({ |
||||
|
url: "/javasys/lowCode/QrCode/getQrCodeImgInside", |
||||
|
method: "post", |
||||
|
data: { |
||||
|
cfid: state.formId, |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-09 16:50:01 |
||||
|
@ 功能: 关闭操作 |
||||
|
*/ |
||||
|
const closeAppSubmit = () => { |
||||
|
flowMap.value = []; |
||||
|
emits("searchquery"); |
||||
|
emits("update:show", false); |
||||
|
}; |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2026-01-19 16:36:28 |
||||
|
@ 功能: 判断怎么分屏 |
||||
|
*/ |
||||
|
// const pageBody = ():string => { |
||||
|
// let pageClass = "" |
||||
|
// switch(pageType.value){ |
||||
|
// case 2: |
||||
|
// pageClass = "twoPage"; |
||||
|
// break; |
||||
|
// case 3: |
||||
|
// pageClass = "treePage"; |
||||
|
// break; |
||||
|
// default: |
||||
|
// pageClass = "" |
||||
|
// break; |
||||
|
// } |
||||
|
// console.log("判断怎么分屏------------->",pageClass) |
||||
|
// console.log("判断怎么分屏------1------->",pageType.value) |
||||
|
// console.log("判断怎么分屏------2------->",pageType.value) |
||||
|
// return pageClass; |
||||
|
// } |
||||
|
|
||||
|
provide('flowNodePower', currterNodePower) |
||||
|
provide('currentNodeKey', nodeKey) |
||||
|
</script> |
||||
|
<template> |
||||
|
<div class="drawerClass"> |
||||
|
<el-drawer |
||||
|
v-model="openOrClose" |
||||
|
:title="drawTitle" |
||||
|
:close-on-click-modal="false" |
||||
|
:close-on-press-escape="false" |
||||
|
:destroy-on-close="true" |
||||
|
:size="drawBodyWidth" |
||||
|
|
||||
|
> |
||||
|
<template #header> |
||||
|
<div class="drawHeader"> |
||||
|
<el-text size="large">{{ drawTitle }}</el-text> |
||||
|
<el-button v-if="stateForm.formData.form.qrCodeFlag == true && stateForm.formData.form.qrCodeInside == true" size="small" @click="showFillFormQrCode">扫码填单</el-button> |
||||
|
</div> |
||||
|
</template> |
||||
|
<div v-loading="drawLoading" :class="aiConfigArea&&isFlowTable?'drawBody treePage':((!aiConfigArea&&isFlowTable)||(aiConfigArea&&!isFlowTable))?'drawBody twoPage':'drawBody'"> |
||||
|
<el-card v-if="aiConfigArea" shadow="always"> |
||||
|
<template #header> |
||||
|
<div class="card-header"> |
||||
|
<div class="svgBox"><SvgIcon icon-class="aiRoboot" size="25" /></div> |
||||
|
AI智能问答助手 |
||||
|
</div> |
||||
|
</template> |
||||
|
<el-scrollbar class="scroBox "> |
||||
|
|
||||
|
<div class="ai-conversation"> |
||||
|
<div class="message ai-message"> |
||||
|
<strong>AI助手:</strong>您好!我是您的智能助手。我可以帮您解答关于表单填写和流程审批的问题。请问有什么可以帮您的? |
||||
|
</div> |
||||
|
|
||||
|
<div class="message user-message"> |
||||
|
<strong>用户:</strong>如何填写项目申请表中的预算部分? |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
|
||||
|
</el-scrollbar> |
||||
|
<template #footer> |
||||
|
<div class="bootemAi"> |
||||
|
<el-input placeholder="请输入您的问题..." /> |
||||
|
|
||||
|
<el-button class="ai-send-btn"><SvgIcon icon-class="fbsk" size="" /></el-button> |
||||
|
</div> |
||||
|
</template> |
||||
|
</el-card> |
||||
|
<el-card shadow="always"> |
||||
|
<template #header> |
||||
|
<div class="card-header"> |
||||
|
<div class="svgBox"><SvgIcon icon-class="edit" size="25" /></div> |
||||
|
项目申请表 |
||||
|
</div> |
||||
|
</template> |
||||
|
<el-scrollbar class="formBody"> |
||||
|
<p v-for="o in 400" :key="o" class="text item">{{ 'List item ' + o }}</p> |
||||
|
</el-scrollbar> |
||||
|
</el-card> |
||||
|
<el-card v-if="isFlowTable" shadow="always"> |
||||
|
<template #header> |
||||
|
<div class="card-header"> |
||||
|
<div class="svgBox"><SvgIcon icon-class="liuChengBiaoDan" size="25" /></div> |
||||
|
审批流程 |
||||
|
</div> |
||||
|
</template> |
||||
|
<el-scrollbar class="flowBody"> |
||||
|
<p v-for="o in 400" :key="o" class="text item">{{ 'List item ' + o }}</p> |
||||
|
</el-scrollbar> |
||||
|
<template #footer> |
||||
|
<div class="bootemWorkFlow"> |
||||
|
<el-input type="textarea" :rows="2" style="width: 100%" placeholder="请输入审批意见"></el-input> |
||||
|
<div class="bootemWorkFlowBut"> |
||||
|
<el-button class="btn approve-btn"><SvgIcon icon-class="kxdg" size="" style="margin-right: 5px" />通过审批</el-button> |
||||
|
<el-button class="btn reject-btn"><SvgIcon icon-class="cwkx" size="" style="margin-right: 5px" />驳回申请</el-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
</el-card> |
||||
|
</div> |
||||
|
</el-drawer> |
||||
|
</div> |
||||
|
</template> |
||||
|
<style lang='scss' scoped> |
||||
|
.drawerClass{ |
||||
|
:deep .el-drawer__header{ |
||||
|
border-bottom: 1px solid #ECECEC; |
||||
|
} |
||||
|
} |
||||
|
.drawHeader { |
||||
|
width: 100%; |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
} |
||||
|
.drawBody{ |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
padding: 10px 10px; |
||||
|
:deep .el-card{ |
||||
|
border-radius: 15px; |
||||
|
} |
||||
|
:deep .el-card__header{ |
||||
|
padding: 15px 15px; |
||||
|
} |
||||
|
:deep .el-card__body{ |
||||
|
padding: 0; |
||||
|
} |
||||
|
:deep .el-card__footer{ |
||||
|
padding: 10px 10px; |
||||
|
} |
||||
|
} |
||||
|
.twoPage{ |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr minmax(150px, 250px); /* 左右最小150px,最大250px,中间自适应 */ |
||||
|
grid-template-rows: auto; |
||||
|
gap: 10px; |
||||
|
max-width: 100%; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
.treePage{ |
||||
|
display: grid; |
||||
|
grid-template-columns: minmax(150px, 250px) 1fr minmax(150px, 250px); /* 左右最小150px,最大250px,中间自适应 */ |
||||
|
grid-template-rows: auto; |
||||
|
gap: 10px; |
||||
|
max-width: 100%; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
.svgBox{ |
||||
|
background: linear-gradient(135deg, #0020C2, #4d6cff); |
||||
|
color: white; |
||||
|
width: 40px; |
||||
|
height: 40px; |
||||
|
border-radius: 50%; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
font-size: 1.2rem; |
||||
|
} |
||||
|
.card-header{ |
||||
|
font-size: 1.4rem; |
||||
|
font-weight: 700; |
||||
|
color: #0020C2; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 10px; |
||||
|
} |
||||
|
.scroBox{ |
||||
|
padding: 10px 15px; |
||||
|
height: calc(100vh - 200px); |
||||
|
} |
||||
|
.formBody{ |
||||
|
padding: 10px 15px; |
||||
|
height: calc(100vh - 145px); |
||||
|
} |
||||
|
.flowBody{ |
||||
|
padding: 10px 15px; |
||||
|
height: calc(100vh - 260px); |
||||
|
} |
||||
|
|
||||
|
.bootemWorkFlow{ |
||||
|
width: 100%; |
||||
|
text-align: center; |
||||
|
.bootemWorkFlowBut{ |
||||
|
width: 100%; |
||||
|
padding: 10px 0; |
||||
|
text-align: center; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.btn { |
||||
|
padding: 14px 16px; |
||||
|
border-radius: 10px; |
||||
|
font-weight: 600; |
||||
|
font-size: 1rem; |
||||
|
cursor: pointer; |
||||
|
transition: all 0.3s; |
||||
|
border: none; |
||||
|
} |
||||
|
.btn:hover { |
||||
|
transform: translateY(-2px); |
||||
|
box-shadow: 0 5px 15px rgba(0, 32, 194, 0.15); |
||||
|
} |
||||
|
.approve-btn { |
||||
|
background: linear-gradient(135deg, #00cc66, #33dd88); |
||||
|
color: white; |
||||
|
} |
||||
|
.reject-btn { |
||||
|
background: linear-gradient(135deg, #ff3366, #ff5588); |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.bootemAi{ |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 50px; /* 左右最小150px,最大250px,中间自适应 */ |
||||
|
grid-template-rows: auto; |
||||
|
gap: 10px; |
||||
|
max-width: 100%; |
||||
|
margin: 0 auto; |
||||
|
:deep .el-input__wrapper{ |
||||
|
border-radius: 10px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.ai-send-btn { |
||||
|
background: linear-gradient(135deg, #0020C2, #4d6cff); |
||||
|
color: white; |
||||
|
border: none; |
||||
|
width: 46px; |
||||
|
border-radius: 10px; |
||||
|
cursor: pointer; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
transition: all 0.3s; |
||||
|
} |
||||
|
|
||||
|
.ai-send-btn:hover { |
||||
|
background: linear-gradient(135deg, #0019a0, #3a5aff); |
||||
|
transform: translateY(-2px); |
||||
|
} |
||||
|
.ai-conversation { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 15px; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
.message { |
||||
|
padding: 12px 16px; |
||||
|
border-radius: 12px; |
||||
|
max-width: 90%; |
||||
|
line-height: 1.5; |
||||
|
} |
||||
|
|
||||
|
.user-message { |
||||
|
background: #eef2ff; |
||||
|
align-self: flex-end; |
||||
|
border-top-right-radius: 4px; |
||||
|
border: 1px solid rgba(0, 32, 194, 0.2); |
||||
|
} |
||||
|
|
||||
|
.ai-message { |
||||
|
background: linear-gradient(to right, #f0f5ff, #ffffff); |
||||
|
align-self: flex-start; |
||||
|
border-top-left-radius: 4px; |
||||
|
border: 1px solid rgba(0, 32, 194, 0.1); |
||||
|
} |
||||
|
</style> |
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Loading…
Reference in new issue