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 { ElMessage, ElMessageBox } from 'element-plus'; |
|||
import router from '@/router'; |
|||
import { useRouter } from 'vue-router' |
|||
|
|||
|
|||
const routerPinia = useRouter() |
|||
// 创建 axios 实例
|
|||
/** |
|||
@ 作者: 秦东 |
|||
@ 时间: 2025-12-09 10:58:34 |
|||
@ 功能: 创建axios实例 |
|||
*/ |
|||
const service = axios.create({ |
|||
baseURL: import.meta.env.VITE_APP_BASE_API, |
|||
timeout: 50000, |
|||
headers: { 'Content-Type': 'application/json;charset=utf-8' } |
|||
}); |
|||
|
|||
// 请求拦截器
|
|||
/** |
|||
@ 作者: 秦东 |
|||
@ 时间: 2025-12-09 11:00:05 |
|||
@ 功能: 请求拦截 |
|||
*/ |
|||
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; |
|||
} |
|||
return config; |
|||
}, |
|||
(error: any) => { |
|||
return Promise.reject(error); |
|||
} |
|||
); |
|||
(config: InternalAxiosRequestConfig) => { |
|||
const userStore = useUserStoreHook(); |
|||
|
|||
// 响应拦截器
|
|||
service.interceptors.response.use( |
|||
(response: AxiosResponse) => { |
|||
const { code, msg } = response.data; |
|||
if (code === 0 || code === 200 || code === 10001) { |
|||
return response.data; |
|||
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; |
|||
} |
|||
|
|||
// 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: "确定", |
|||
type: "warning", |
|||
}).then(() => { |
|||
localStorage.clear(); |
|||
// window.location.href = "/login";
|
|||
routerPinia.push({path:"/login"}) |
|||
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 { |
|||
} |
|||
// 响应数据为二进制流处理(Excel导出)
|
|||
if (response.data instanceof ArrayBuffer) { |
|||
return response; |
|||
} |
|||
// 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); |
|||
} |
|||
); |
|||
|
|||
// 导出 axios 实例
|
|||
export default service; |
|||
) |
|||
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