注册全局组件SVG,集成pinia,封装axios
This commit is contained in:
3
frontend/.env.development
Normal file
3
frontend/.env.development
Normal file
@@ -0,0 +1,3 @@
|
||||
NODE_ENV='development'
|
||||
VITE_TITLE=""
|
||||
VITE_URL="http://localhost:8081"
|
||||
3
frontend/.env.production
Normal file
3
frontend/.env.production
Normal file
@@ -0,0 +1,3 @@
|
||||
NODE_ENV='production'
|
||||
VITE_TITLE=""
|
||||
VITE_URL="http://www.test.com"
|
||||
3
frontend/.eslintignore
Normal file
3
frontend/.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
// .eslintignore 配置, 防止校验打包的产物
|
||||
dist
|
||||
node_modules
|
||||
@@ -9,14 +9,15 @@
|
||||
"build-staging": "vite build --mode staging",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --build --force",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"axios": "^1.7.3",
|
||||
"element-plus": "^2.7.8",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia": "^2.2.1",
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"vue": "^3.4.29",
|
||||
"vue-router": "^4.3.3"
|
||||
},
|
||||
@@ -36,6 +37,7 @@
|
||||
"typescript": "~5.4.0",
|
||||
"vite": "^5.3.1",
|
||||
"vite-plugin-node-polyfills": "^0.22.0",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vue-tsc": "^2.0.21"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ defineOptions({
|
||||
import { ElConfigProvider } from 'element-plus'
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||
|
||||
document.getElementById('loadingPage').remove()
|
||||
document.getElementById('loadingPage')?.remove()
|
||||
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* 主进程与渲染进程通信频道定义
|
||||
* Definition of communication channels between main process and rendering process
|
||||
@@ -7,7 +6,4 @@ const ipcApiRoute = {
|
||||
test: 'controller.example.test',
|
||||
}
|
||||
|
||||
export {
|
||||
ipcApiRoute
|
||||
}
|
||||
|
||||
export { ipcApiRoute }
|
||||
|
||||
57
frontend/src/api/user/index.ts
Normal file
57
frontend/src/api/user/index.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import request from '@/utils/http'
|
||||
import { useUserInfoStore } from '@/stores/user'
|
||||
import { sm3Digest } from '@/assets/commjs/sm3.js'
|
||||
import { sm2, encrypt } from '@/assets/commjs/sm2.js'
|
||||
|
||||
// 获取公钥
|
||||
export const publicKey = (params?: any) => {
|
||||
if (!params) {
|
||||
const userInfo = useUserInfoStore()
|
||||
params = {
|
||||
loginName: encrypt(userInfo.loginName),
|
||||
}
|
||||
}
|
||||
return request({
|
||||
url: '/user/generateSm2Key',
|
||||
method: 'get',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
export const pwdSm3 = async (pwd: any, loginName?: string) => {
|
||||
let publicKeyStr = await publicKey(
|
||||
loginName ? { loginName: encrypt(loginName) } : false,
|
||||
)
|
||||
let sm3Pwd = sm3Digest(pwd) //SM3加密
|
||||
return sm2(sm3Pwd + '|' + pwd, publicKeyStr.data, 0)
|
||||
}
|
||||
|
||||
//登录获取token
|
||||
export const login = async (params: any) => {
|
||||
params.password = await pwdSm3(params.password, params.username)
|
||||
params.username = encrypt(params.username)
|
||||
return request({
|
||||
url: '/pqs-auth/oauth/token',
|
||||
method: 'post',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
//获取用户信息
|
||||
export const getUserById = () => {
|
||||
const userInfo = useUserInfoStore()
|
||||
return request({
|
||||
url: '/user-boot/user/getUserById?id=' + userInfo.userIndex,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 刷新token
|
||||
export function refreshToken(): Promise<any> {
|
||||
const userInfo = useUserInfoStore()
|
||||
return login({
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: userInfo.refresh_token,
|
||||
username: userInfo.loginName,
|
||||
})
|
||||
}
|
||||
3734
frontend/src/assets/commjs/sm2.js
Normal file
3734
frontend/src/assets/commjs/sm2.js
Normal file
File diff suppressed because it is too large
Load Diff
257
frontend/src/assets/commjs/sm3.js
Normal file
257
frontend/src/assets/commjs/sm3.js
Normal file
@@ -0,0 +1,257 @@
|
||||
/**
|
||||
* 国密摘要算法(SM3)
|
||||
* @param str:raw string
|
||||
* @return the 256-bit hex string produced by SM3 from a raw string
|
||||
*/
|
||||
function sm3Digest(str) {
|
||||
//1. 转换为二进制数组
|
||||
var binArr = str2bin(str2rstr_utf8(str));
|
||||
//2. 填充
|
||||
var groupNum = alignSM3(binArr, str.length);
|
||||
//3. 迭代压缩
|
||||
var v = new Array(8);//初始值
|
||||
v[0] = 0x7380166f;
|
||||
v[1] = 0x4914b2b9;
|
||||
v[2] = 0x172442d7;
|
||||
v[3] = 0xda8a0600;
|
||||
v[4] = 0xa96f30bc;
|
||||
v[5] = 0x163138aa;
|
||||
v[6] = 0xe38dee4d;
|
||||
v[7] = 0xb0fb0e4e;
|
||||
//按 512bit 分组进行压缩
|
||||
for (var i = 0; i < groupNum; i++) {
|
||||
v = compress(v, binArr, i);
|
||||
}
|
||||
return word2str(v, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数组转换为字符串。数组长度不定,每个元素为 32bit 的数字。
|
||||
* @param words:数组,每个元素为 32bit 的数字
|
||||
* @param seperator:在每个数组元素转换得到的字符串之间的分隔符
|
||||
*/
|
||||
function word2str(words, seperator) {
|
||||
var prefix = Array(8).join('0');
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
//若 hex 不足 8 位,则高位补 0
|
||||
words[i] = (prefix + (words[i] >>> 0).toString(16)).slice(-8);
|
||||
}
|
||||
|
||||
return words.join(seperator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为二进制数组,默认字符串编码为 UTF-8,且范围在 0x00~0xFF 内。
|
||||
* 若某些字符的编码超过此范围,则会只保留最低字节。加密可正常进行,但加密结果有误。
|
||||
* 每个数组元素包含 4 个字符,即 32 bit。
|
||||
* @param 字符串
|
||||
* @return 数组,长度为(字符串长度 / 4),每个元素为 32bit 的数字
|
||||
*/
|
||||
function str2bin(str) {
|
||||
var binary = new Array(str.length >> 2);
|
||||
for (var i = 0; i < str.length * 8; i += 8) {
|
||||
binary[i >> 5] |= (str.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
|
||||
}
|
||||
return binary;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对明文的二进制串进行填充
|
||||
* <pre>
|
||||
* | 满足 mod 512 = 448 | 固定 64 位 |
|
||||
* | 明文二进制 |填充部分|明文二进制串的长度的二进制表示|
|
||||
* xxxxxxxxxxxx 10.....0 0...........................xx
|
||||
* </pre>
|
||||
* @param arr:数组,每个元素为 32bit 的数字
|
||||
* @param strLen:明文字符串长度
|
||||
* @return 数组,每个元素为 32bit 的数字,数组长度为 16 的倍数(包括 16)
|
||||
*/
|
||||
function alignSM3(arr, strLen) {
|
||||
//在明文二进制串后面拼接 1000 0000
|
||||
arr[strLen >> 2] |= 0x80 << (24 - strLen % 4 * 8);
|
||||
var groupNum = ((strLen + 8) >> 6) + 1;//以 512bit 为一组,总的组数
|
||||
var wordNum = groupNum * 16;//一个 word 32bit,总的 word 数
|
||||
|
||||
for (var i = (strLen >> 2) + 1; i < wordNum; i++) {
|
||||
arr[i] = 0;
|
||||
}
|
||||
arr[wordNum - 1] = strLen * 8;//在末尾填上明文的二进制长度
|
||||
|
||||
return groupNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩函数中的置换函数
|
||||
*/
|
||||
function p0(x) {
|
||||
return x ^ bitRol(x, 9) ^ bitRol(x, 17);
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩函数中的置换函数
|
||||
*/
|
||||
function p1(x) {
|
||||
return x ^ bitRol(x, 15) ^ bitRol(x, 23);
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环左移
|
||||
*/
|
||||
function bitRol(input, n) {
|
||||
return (input << n) | (input >>> (32 - n));
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩函数
|
||||
*/
|
||||
function compress(v, binArr, i) {
|
||||
//将消息分组扩展成 132 个字
|
||||
var w1 = new Array(68);
|
||||
var w2 = new Array(64);
|
||||
for (var j = 0; j < 68; j++) {
|
||||
if (j < 16) {
|
||||
w1[j] = binArr[i * 16 + j];
|
||||
} else {
|
||||
w1[j] = p1(w1[j-16] ^ w1[j-9] ^ bitRol(w1[j-3], 15)) ^ bitRol(w1[j-13], 7) ^ w1[j-6];
|
||||
}
|
||||
}
|
||||
for (var j = 0; j < 64; j++) {
|
||||
w2[j] = w1[j] ^ w1[j+4];
|
||||
}
|
||||
|
||||
//压缩
|
||||
var a = v[0];
|
||||
var b = v[1];
|
||||
var c = v[2];
|
||||
var d = v[3];
|
||||
var e = v[4];
|
||||
var f = v[5];
|
||||
var g = v[6];
|
||||
var h = v[7];
|
||||
var ss1;
|
||||
var ss2;
|
||||
var tt1;
|
||||
var tt2;
|
||||
for (var j = 0; j < 64; j++) {
|
||||
ss1 = bitRol(addAll(bitRol(a, 12) , e , bitRol(t(j), j)), 7);
|
||||
ss2 = ss1 ^ bitRol(a, 12);
|
||||
tt1 = addAll(ff(a, b, c, j) , d , ss2 , w2[j]);
|
||||
tt2 = addAll(gg(e, f, g, j) , h , ss1 , w1[j]);
|
||||
d = c;
|
||||
c = bitRol(b, 9);
|
||||
b = a;
|
||||
a = tt1;
|
||||
h = g;
|
||||
g = bitRol(f, 19);
|
||||
f = e;
|
||||
e = p0(tt2);
|
||||
}
|
||||
v[0] ^= a;
|
||||
v[1] ^= b;
|
||||
v[2] ^= c;
|
||||
v[3] ^= d;
|
||||
v[4] ^= e;
|
||||
v[5] ^= f;
|
||||
v[6] ^= g;
|
||||
v[7] ^= h;
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* 常量 T 随 j 的不同而不同
|
||||
*/
|
||||
function t(j) {
|
||||
if (0 <= j && j < 16) {
|
||||
return 0x79CC4519;
|
||||
} else if (j < 64) {
|
||||
return 0x7A879D8A;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 布尔函数,随 j 的变化取不同的表达式
|
||||
*/
|
||||
function ff(x, y, z, j) {
|
||||
if (0 <= j && j < 16) {
|
||||
return x ^ y ^ z;
|
||||
} else if (j < 64) {
|
||||
return (x & y) | (x & z) | (y & z);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 布尔函数,随 j 的变化取不同的表达式
|
||||
*/
|
||||
function gg(x, y, z, j) {
|
||||
if (0 <= j && j < 16) {
|
||||
return x ^ y ^ z;
|
||||
} else if (j < 64) {
|
||||
return (x & y) | (~x & z);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 两数相加
|
||||
* 避免某些 js 引擎的 32 位加法的 bug
|
||||
*/
|
||||
function safe_add(x, y) {
|
||||
var lsw = ( x & 0xFFFF ) + (y & 0xFFFF);
|
||||
var msw = ( x >> 16 ) + (y >> 16) + (lsw >> 16);
|
||||
return (msw << 16) | ( lsw & 0xFFFF );
|
||||
}
|
||||
|
||||
/**
|
||||
* 将所有参数相加
|
||||
*/
|
||||
function addAll() {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
sum = safe_add(sum, arguments[i]);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* UTF-16 --> UTF-8
|
||||
*/
|
||||
function str2rstr_utf8(input) {
|
||||
var output = "" ;
|
||||
var i = -1 ;
|
||||
var x, y ;
|
||||
|
||||
while(++ i < input.length) {
|
||||
//按 UTF-16 解码
|
||||
x = input.charCodeAt(i);
|
||||
y = i + 1 < input.length ? input .charCodeAt (i + 1) : 0 ;
|
||||
if( 0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF ) {
|
||||
x = 0x10000 + ((x & 0x03FF) << 10 ) + (y & 0x03FF);
|
||||
i++;
|
||||
}
|
||||
|
||||
//按 UTF-8 编码
|
||||
if( x <= 0x7F ) {
|
||||
output += String.fromCharCode(x);
|
||||
}
|
||||
else if(x <= 0x7FF) {
|
||||
output += String.fromCharCode(
|
||||
0xC0 | ((x >>> 6 ) & 0x1F),
|
||||
0x80 | ( x & 0x3F ));
|
||||
} else if(x <= 0xFFFF) {
|
||||
output += String.fromCharCode(
|
||||
0xE0 | ((x >>> 12) & 0x0F ),
|
||||
0x80 | ((x >>> 6 ) & 0x3F),
|
||||
0x80 | ( x & 0x3F ));
|
||||
} else if(x <= 0x1FFFFF) {
|
||||
output += String.fromCharCode(
|
||||
0xF0 | ((x >>> 18) & 0x07 ),
|
||||
0x80 | ((x >>> 12) & 0x3F),
|
||||
0x80 | ((x >>> 6 ) & 0x3F),
|
||||
0x80 | ( x & 0x3F ));
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
export {
|
||||
sm3Digest
|
||||
}
|
||||
|
||||
1
frontend/src/assets/icons/wind.svg
Normal file
1
frontend/src/assets/icons/wind.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1723085514271" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1125" width="200" height="200"><path d="M765.22 393.6H184.15c-8.28 0-15-6.72-15-15s6.72-15 15-15h581.07c37.38 0 67.8-30.41 67.8-67.8s-30.41-67.8-67.8-67.8-67.8 30.41-67.8 67.8c0 8.28-6.72 15-15 15s-15-6.72-15-15c0-53.93 43.87-97.8 97.8-97.8s97.8 43.87 97.8 97.8-43.87 97.8-97.8 97.8zM783.99 809.79c-43.58 0-79.03-35.45-79.03-79.03 0-8.28 6.72-15 15-15s15 6.72 15 15c0 27.04 22 49.03 49.03 49.03s49.03-22 49.03-49.03-22-49.03-49.03-49.03H297.67c-8.28 0-15-6.72-15-15s6.72-15 15-15h486.32c43.58 0 79.03 35.45 79.03 79.03s-35.45 79.03-79.03 79.03z" fill="#333333" p-id="1126"></path><path d="M481.66 306.63H312.79c-8.28 0-15-6.72-15-15s6.72-15 15-15h168.87c8.28 0 15 6.72 15 15s-6.72 15-15 15zM227.46 681.49h-43c-8.28 0-15-6.72-15-15s6.72-15 15-15h43c8.28 0 15 6.72 15 15s-6.72 15-15 15zM777.81 598.8h-43c-8.28 0-15-6.72-15-15s6.72-15 15-15h43c8.28 0 15 6.72 15 15s-6.72 15-15 15zM383.78 761H241.87c-8.28 0-15-6.72-15-15s6.72-15 15-15h141.91c8.28 0 15 6.72 15 15s-6.72 15-15 15zM848.02 452.99H613.98c-8.28 0-15-6.72-15-15s6.72-15 15-15h234.04c8.28 0 15 6.72 15 15s-6.72 15-15 15z" fill="#333333" p-id="1127"></path><path d="M492 535.33m-243.33 0a243.33 243.33 0 1 0 486.66 0 243.33 243.33 0 1 0-486.66 0Z" fill="#FFFFFF" p-id="1128"></path><path d="M492 793.67c-69 0-133.88-26.87-182.67-75.66-48.79-48.79-75.66-113.67-75.66-182.67s26.87-133.88 75.66-182.67C358.12 303.88 423 277.01 492 277.01s133.88 26.87 182.67 75.66c48.79 48.79 75.66 113.67 75.66 182.67s-26.87 133.88-75.66 182.67C625.88 766.8 561 793.67 492 793.67zM492 307c-60.99 0-118.33 23.75-161.46 66.88-43.13 43.13-66.88 100.47-66.88 161.46s23.75 118.33 66.88 161.46c43.13 43.13 100.47 66.88 161.46 66.88s118.33-23.75 161.46-66.88 66.88-100.47 66.88-161.46-23.75-118.33-66.88-161.46S552.99 307 492 307z" fill="#333333" p-id="1129"></path><path d="M583.85 517.56l-58.52-11.99 24.71-97.1c5.39-21.17-20.94-35.62-35.9-19.71L393.3 517.17c-11.35 12.06-5.06 31.89 11.16 35.22l58.52 11.99-24.71 97.1c-5.39 21.17 20.94 35.62 35.9 19.71l120.84-128.41c11.35-12.06 5.06-31.89-11.16-35.22z" fill="#52E217" p-id="1130"></path><path d="M458.92 702.95c-5.94 0-11.96-1.5-17.55-4.57-14.68-8.06-21.77-24.38-17.64-40.6l20.83-81.86-43.12-8.83c-13.02-2.67-23.19-11.82-27.21-24.49s-0.97-26.02 8.13-35.7L503.2 378.49c11.48-12.2 29.05-14.97 43.73-6.92 14.68 8.06 21.77 24.38 17.64 40.6l-20.83 81.86 43.12 8.83c13.02 2.67 23.19 11.82 27.21 24.49s0.97 26.02-8.13 35.7L485.1 691.46c-7.1 7.54-16.53 11.48-26.17 11.49z m70.43-305.96c-1.33 0-2.84 0.5-4.3 2.04L404.21 527.44c-2.15 2.29-1.79 4.79-1.38 6.07 0.41 1.29 1.55 3.54 4.63 4.17l58.52 11.99c4.02 0.82 7.52 3.25 9.7 6.73 2.18 3.48 2.84 7.69 1.83 11.66l-24.71 97.1c-1.02 4.02 1.44 6.05 3 6.91 1.56 0.86 4.6 1.84 7.44-1.18l120.84-128.41c2.15-2.29 1.79-4.79 1.38-6.07-0.41-1.29-1.55-3.54-4.63-4.17l-58.52-11.99c-4.02-0.82-7.52-3.25-9.7-6.73a14.993 14.993 0 0 1-1.83-11.66l24.71-97.1c1.02-4.02-1.44-6.05-3-6.91-0.76-0.42-1.87-0.87-3.14-0.87z" fill="#333333" p-id="1131"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
19
frontend/src/components/index.ts
Normal file
19
frontend/src/components/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { App, Component } from 'vue'
|
||||
|
||||
// 当组件很多的时候,可以使用
|
||||
import { SvgIcon } from './staticExtend/SvgIcon'
|
||||
|
||||
// 这个地方将合并到对象中
|
||||
const Components: {
|
||||
[propName: string]: Component
|
||||
} = { SvgIcon }
|
||||
|
||||
// 批量注册全局组件
|
||||
export default {
|
||||
install: (app: App) => {
|
||||
Object.keys(Components).forEach((key) => {
|
||||
app.component(key, Components[key])
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
2
frontend/src/components/staticExtend/SvgIcon/index.ts
Normal file
2
frontend/src/components/staticExtend/SvgIcon/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import SvgIcon from './src/SvgIcon.vue'
|
||||
export { SvgIcon }
|
||||
89
frontend/src/components/staticExtend/SvgIcon/src/SvgIcon.vue
Normal file
89
frontend/src/components/staticExtend/SvgIcon/src/SvgIcon.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @name SvgIcon
|
||||
* @description svg图标组件
|
||||
* 支持定义名称、颜色、大小、旋转
|
||||
* @example <SvgIcon name="icon-name" color="#fff" size="20" spin />
|
||||
*/
|
||||
<template>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
:class="['svg-icon', spin && 'svg-icon-spin']"
|
||||
:style="getStyle"
|
||||
>
|
||||
<use :xlink:href="symbolId" :fill="color" />
|
||||
</svg>
|
||||
</template>
|
||||
<script setup lang='ts'>
|
||||
defineOptions({
|
||||
name: 'SvgIcon',
|
||||
})
|
||||
import { computed } from 'vue'
|
||||
import type { CSSProperties } from 'vue'
|
||||
// 定义组件对外暴露的props
|
||||
const props = defineProps({
|
||||
prefix: {
|
||||
type: String,
|
||||
default: 'icon',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 20,
|
||||
},
|
||||
spin: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
//计算属性获取symbolId
|
||||
const symbolId = computed(() => {
|
||||
return `#${props.prefix}-${props.name}`
|
||||
})
|
||||
|
||||
//计算属性获取svg样式
|
||||
const getStyle = computed((): CSSProperties => {
|
||||
const { size } = props
|
||||
let s = `${size}`
|
||||
// 确保size为px单位
|
||||
s = `${s.replace('px', '')}px`
|
||||
return {
|
||||
width: s,
|
||||
height: s,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
.svg-icon {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.svg-icon-spin {
|
||||
animation: loadingCircle 1.2s infinite linear;
|
||||
}
|
||||
|
||||
/* 旋转动画 */
|
||||
@keyframes loadingCircle {
|
||||
0% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -1,2 +1 @@
|
||||
/*文件说明:本文件用来定义一些本地缓存的key*/
|
||||
|
||||
|
||||
@@ -1,37 +1,38 @@
|
||||
|
||||
import {createApp} from 'vue'
|
||||
import { createApp } from 'vue'
|
||||
// element-plus
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
|
||||
|
||||
// 使用pinia
|
||||
import { createPinia } from 'pinia'
|
||||
import pinia from '@/stores'
|
||||
|
||||
import App from './App.vue'
|
||||
import Router from './router/index';
|
||||
import Router from './router/index'
|
||||
|
||||
// 引入tailwindcss
|
||||
import '@/assets/styles/tailMain.css'
|
||||
|
||||
// 导入全局注册的组件
|
||||
import 'virtual:svg-icons-register'
|
||||
import registerGlobComp from '@/components'
|
||||
|
||||
//创建实例
|
||||
const app = createApp(App)
|
||||
const setupAll = async () => {
|
||||
app
|
||||
.use(Router) // 使用路由
|
||||
.use(ElementPlus) // 使用ele-plus组件
|
||||
.use(createPinia()) // 使用pinia
|
||||
.use(pinia) // 使用pinia
|
||||
.use(registerGlobComp) // 使用全局自定义组件
|
||||
|
||||
// 自动引入图标
|
||||
Object.keys(ElementPlusIconsVue).forEach((key) => {
|
||||
app.component(key, ElementPlusIconsVue[key]);
|
||||
});
|
||||
|
||||
//待路由初始化完毕后,挂载app
|
||||
await Router.isReady()
|
||||
}
|
||||
|
||||
|
||||
//挂载app
|
||||
setupAll().then(() => {
|
||||
app.mount('#app')
|
||||
})
|
||||
|
||||
|
||||
@@ -6,15 +6,11 @@ const Router = createRouter({
|
||||
routes: routerMap,
|
||||
})
|
||||
|
||||
|
||||
Router.beforeEach((to, from, next) => {
|
||||
next()
|
||||
})
|
||||
|
||||
// 路由加载后
|
||||
Router.afterEach(() => {
|
||||
|
||||
})
|
||||
|
||||
Router.afterEach(() => {})
|
||||
|
||||
export default Router
|
||||
|
||||
@@ -8,7 +8,7 @@ const constantRouterMap = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'login',
|
||||
component: Login
|
||||
component: Login,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
14
frontend/src/stores/constant/cacheKey.ts
Normal file
14
frontend/src/stores/constant/cacheKey.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 本地缓存Key
|
||||
*/
|
||||
|
||||
// 用户信息
|
||||
export const USER_INFO = 'userInfo'
|
||||
|
||||
// WEB端布局配置
|
||||
export const STORE_CONFIG = 'storeConfig'
|
||||
|
||||
// 后台标签页
|
||||
export const STORE_TAB_VIEW_CONFIG = 'storeTabViewConfig'
|
||||
// 字典
|
||||
export const DICT_DATA = 'dictData'
|
||||
3
frontend/src/stores/index.ts
Normal file
3
frontend/src/stores/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { createPinia } from 'pinia'
|
||||
const pinia = createPinia()
|
||||
export default pinia
|
||||
95
frontend/src/stores/interface/index.ts
Normal file
95
frontend/src/stores/interface/index.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
// 变量名对应含义请在 /stores/* 里边找
|
||||
import type { RouteRecordRaw, RouteLocationNormalized } from 'vue-router'
|
||||
|
||||
export interface Layout {
|
||||
showDrawer: boolean
|
||||
shrink: boolean
|
||||
layoutMode: string
|
||||
mainAnimation: string
|
||||
isDark: boolean
|
||||
menuWidth: number
|
||||
menuDefaultIcon: string
|
||||
menuCollapse: boolean
|
||||
menuUniqueOpened: boolean
|
||||
menuShowTopBar: boolean
|
||||
elementUiPrimary: string[]
|
||||
tableHeaderBackground: string[]
|
||||
tableHeaderColor: string[]
|
||||
tableCurrent: string[]
|
||||
menuBackground: string[]
|
||||
menuColor: string[]
|
||||
menuActiveBackground: string[]
|
||||
menuActiveColor: string[]
|
||||
menuTopBarBackground: string[]
|
||||
headerBarTabColor: string[]
|
||||
headerBarBackground: string[]
|
||||
headerBarHoverBackground: string[]
|
||||
headerBarTabActiveBackground: string[]
|
||||
headerBarTabActiveColor: string[]
|
||||
}
|
||||
|
||||
export interface NavTabs {
|
||||
activeIndex: number
|
||||
activeRoute: RouteLocationNormalized | null
|
||||
tabsView: RouteLocationNormalized[]
|
||||
tabFullScreen: boolean
|
||||
tabsViewRoutes: RouteRecordRaw[]
|
||||
authNode: Map<string, string[]>
|
||||
}
|
||||
|
||||
// 用户信息
|
||||
export interface UserInfo {
|
||||
access_token: string
|
||||
token_type: string
|
||||
refresh_token: string
|
||||
expires_in: number
|
||||
scope: string
|
||||
nickname: string
|
||||
userType: number
|
||||
deptIndex: string
|
||||
userIndex: string
|
||||
client_id: string
|
||||
headSculpture: any
|
||||
jti: string
|
||||
name: string
|
||||
deptId: string
|
||||
phone: string
|
||||
email: string
|
||||
limitIpStart: string
|
||||
limitIpEnd: string
|
||||
limitTime: string
|
||||
casualUser: number
|
||||
type: number
|
||||
smsNotice: number
|
||||
emailNotice: number
|
||||
role: string[]
|
||||
devCode: any
|
||||
id: string
|
||||
loginName: string
|
||||
state: number
|
||||
registerTime: string
|
||||
loginTime: string
|
||||
deptName: string
|
||||
areaId: string
|
||||
areaName: string
|
||||
deptLevel: number
|
||||
roleList: string[]
|
||||
roleCode: string[]
|
||||
}
|
||||
|
||||
// 字典数据
|
||||
export interface DictData {
|
||||
basic: BasicDictData[]
|
||||
area: BasicDictData[]
|
||||
areaTree: BasicDictData[]
|
||||
userList: string[]
|
||||
}
|
||||
|
||||
export interface BasicDictData {
|
||||
name: string
|
||||
id: string
|
||||
code: string
|
||||
value: null
|
||||
sort: number | null
|
||||
children?: BasicDictData[]
|
||||
}
|
||||
75
frontend/src/stores/user.ts
Normal file
75
frontend/src/stores/user.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { USER_INFO } from '@/stores/constant/cacheKey'
|
||||
import type { UserInfo } from '@/stores/interface'
|
||||
|
||||
export const useUserInfoStore = defineStore('userInfo', {
|
||||
// 行为
|
||||
actions: {
|
||||
dataFill(state: UserInfo) {
|
||||
this.$state = { ...this.$state, ...state }
|
||||
},
|
||||
removeToken() {
|
||||
this.access_token = ''
|
||||
this.refresh_token = ''
|
||||
},
|
||||
setToken(token: string, type: 'auth' | 'refresh') {
|
||||
const field = type == 'auth' ? 'token' : 'refresh_token'
|
||||
this[field] = token
|
||||
},
|
||||
getToken(type: 'auth' | 'refresh' = 'auth') {
|
||||
if (type === 'auth') {
|
||||
if (this.token_type && this.access_token) {
|
||||
return this.token_type + ' ' + this.access_token
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
} else {
|
||||
return this.refresh_token
|
||||
}
|
||||
},
|
||||
},
|
||||
// 状态
|
||||
state: (): UserInfo => {
|
||||
return {
|
||||
access_token: '',
|
||||
token_type: '',
|
||||
refresh_token: '',
|
||||
expires_in: 0,
|
||||
scope: '',
|
||||
nickname: '',
|
||||
userType: 0,
|
||||
deptIndex: '',
|
||||
userIndex: '',
|
||||
client_id: '',
|
||||
headSculpture: '',
|
||||
jti: '',
|
||||
name: '',
|
||||
deptId: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
limitIpStart: '',
|
||||
limitIpEnd: '',
|
||||
limitTime: '',
|
||||
casualUser: 0,
|
||||
type: 0,
|
||||
smsNotice: 0,
|
||||
emailNotice: 0,
|
||||
role: [],
|
||||
devCode: '',
|
||||
id: '',
|
||||
loginName: '',
|
||||
state: 0,
|
||||
registerTime: '',
|
||||
loginTime: '',
|
||||
deptName: '',
|
||||
areaId: '',
|
||||
areaName: '',
|
||||
deptLevel: 0,
|
||||
roleList: [],
|
||||
roleCode: [],
|
||||
}
|
||||
},
|
||||
persist: {
|
||||
key: USER_INFO,
|
||||
},
|
||||
})
|
||||
33
frontend/src/types/global.d.ts
vendored
Normal file
33
frontend/src/types/global.d.ts
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
interface Window {
|
||||
XEUtils: Record<string, any>
|
||||
existLoading: boolean
|
||||
lazy: number
|
||||
unique: number
|
||||
tokenRefreshing: boolean
|
||||
requests: Function[]
|
||||
eventSource: EventSource
|
||||
loadLangHandle: Record<string, any>
|
||||
}
|
||||
|
||||
interface anyObj {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
interface treeData {
|
||||
id?: string
|
||||
value?: string
|
||||
name?: string
|
||||
label?: string
|
||||
children: treeData[]
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
|
||||
interface ApiResponse<T = any> {
|
||||
code: number
|
||||
data: T
|
||||
msg: string
|
||||
time: number
|
||||
}
|
||||
|
||||
type ApiPromise<T = any> = Promise<ApiResponse<T>>
|
||||
229
frontend/src/utils/http/index.ts
Normal file
229
frontend/src/utils/http/index.ts
Normal file
@@ -0,0 +1,229 @@
|
||||
import type { AxiosRequestConfig, Method } from 'axios'
|
||||
import axios from 'axios'
|
||||
import { ElLoading, ElNotification, type LoadingOptions } from 'element-plus'
|
||||
import { refreshToken } from '@/api/user'
|
||||
import router from '@/router/index'
|
||||
import { useUserInfoStore } from '@/stores/user'
|
||||
|
||||
window.requests = []
|
||||
window.tokenRefreshing = false
|
||||
const pendingMap = new Map()
|
||||
const loadingInstance: LoadingInstance = {
|
||||
target: null,
|
||||
count: 0,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建`Axios`
|
||||
* 默认开启`reductDataFormat(简洁响应)`,返回类型为`ApiPromise`
|
||||
* 关闭`reductDataFormat`,返回类型则为`AxiosPromise`
|
||||
*/
|
||||
function createAxios<Data = any, T = ApiPromise<Data>>(
|
||||
axiosConfig: AxiosRequestConfig,
|
||||
options: Options = {},
|
||||
loading: LoadingOptions = {},
|
||||
): T {
|
||||
const userInfo = useUserInfoStore()
|
||||
|
||||
const Axios = axios.create({
|
||||
baseURL: import.meta.env.VITE_URL,
|
||||
timeout: 1000 * 60 * 5,
|
||||
headers: {},
|
||||
responseType: 'json',
|
||||
})
|
||||
|
||||
options = Object.assign(
|
||||
{
|
||||
CancelDuplicateRequest: true, // 是否开启取消重复请求, 默认为 true
|
||||
loading: false, // 是否开启loading层效果, 默认为false
|
||||
reductDataFormat: true, // 是否开启简洁的数据结构响应, 默认为true
|
||||
showErrorMessage: true, // 是否开启接口错误信息展示,默认为true
|
||||
showCodeMessage: true, // 是否开启code不为1时的信息提示, 默认为true
|
||||
showSuccessMessage: false, // 是否开启code为1时的信息提示, 默认为false
|
||||
anotherToken: '', // 当前请求使用另外的用户token
|
||||
},
|
||||
options,
|
||||
)
|
||||
|
||||
// 请求拦截
|
||||
Axios.interceptors.request.use(
|
||||
config => {
|
||||
removePending(config)
|
||||
options.CancelDuplicateRequest && addPending(config)
|
||||
// 创建loading实例
|
||||
if (options.loading) {
|
||||
loadingInstance.count++
|
||||
if (loadingInstance.count === 1) {
|
||||
loadingInstance.target = ElLoading.service(loading)
|
||||
}
|
||||
}
|
||||
// 自动携带token
|
||||
if (config.headers) {
|
||||
const token = userInfo.getToken()
|
||||
if (token) {
|
||||
;(config.headers as any).Authorization = token
|
||||
} else {
|
||||
config.headers.Authorization = 'Basic bmpjbnRlc3Q6bmpjbnBxcw=='
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
// 响应拦截
|
||||
Axios.interceptors.response.use(
|
||||
response => {
|
||||
removePending(response.config)
|
||||
options.loading && closeLoading(options) // 关闭loading
|
||||
|
||||
if (
|
||||
response.data.code === 'A0000' ||
|
||||
response.data.type === 'application/json'
|
||||
) {
|
||||
return options.reductDataFormat ? response.data : response
|
||||
} else if (response.data.code == 'A0202') {
|
||||
if (!window.tokenRefreshing) {
|
||||
window.tokenRefreshing = true
|
||||
return refreshToken()
|
||||
.then(res => {
|
||||
userInfo.setToken(res.data.token, 'auth')
|
||||
response.headers.Authorization = `${res.data.token}`
|
||||
window.requests.forEach(cb => cb(res.data.token))
|
||||
window.requests = []
|
||||
return Axios(response.config)
|
||||
})
|
||||
.catch(err => {
|
||||
userInfo.removeToken()
|
||||
router.push({ name: 'login' })
|
||||
return Promise.reject(err)
|
||||
})
|
||||
.finally(() => {
|
||||
window.tokenRefreshing = false
|
||||
})
|
||||
} else {
|
||||
return new Promise(resolve => {
|
||||
// 用函数形式将 resolve 存入,等待刷新后再执行
|
||||
window.requests.push((token: string) => {
|
||||
response.headers.Authorization = `${token}`
|
||||
resolve(Axios(response.config))
|
||||
})
|
||||
})
|
||||
}
|
||||
} else if (response.data.code == 'A0024') {
|
||||
userInfo.removeToken()
|
||||
router.push({ name: 'login' })
|
||||
return Promise.reject(response.data)
|
||||
} else {
|
||||
if (options.showCodeMessage) {
|
||||
ElNotification({
|
||||
type: 'error',
|
||||
message: response.data.message || '未知错误',
|
||||
})
|
||||
}
|
||||
return Promise.reject(response.data)
|
||||
}
|
||||
},
|
||||
error => {
|
||||
error.config && removePending(error.config)
|
||||
options.loading && closeLoading(options) // 关闭loading
|
||||
return Promise.reject(error) // 错误继续返回给到具体页面
|
||||
},
|
||||
)
|
||||
return Axios(axiosConfig) as T
|
||||
}
|
||||
|
||||
export default createAxios
|
||||
|
||||
/**
|
||||
* 关闭Loading层实例
|
||||
*/
|
||||
function closeLoading(options: Options) {
|
||||
if (options.loading && loadingInstance.count > 0) loadingInstance.count--
|
||||
if (loadingInstance.count === 0) {
|
||||
loadingInstance.target.close()
|
||||
loadingInstance.target = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 储存每个请求的唯一cancel回调, 以此为标识
|
||||
*/
|
||||
function addPending(config: AxiosRequestConfig) {
|
||||
const pendingKey = getPendingKey(config)
|
||||
config.cancelToken =
|
||||
config.cancelToken ||
|
||||
new axios.CancelToken(cancel => {
|
||||
if (!pendingMap.has(pendingKey)) {
|
||||
pendingMap.set(pendingKey, cancel)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除重复的请求
|
||||
*/
|
||||
function removePending(config: AxiosRequestConfig) {
|
||||
const pendingKey = getPendingKey(config)
|
||||
if (pendingMap.has(pendingKey)) {
|
||||
const cancelToken = pendingMap.get(pendingKey)
|
||||
cancelToken(pendingKey)
|
||||
pendingMap.delete(pendingKey)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成每个请求的唯一key
|
||||
*/
|
||||
function getPendingKey(config: AxiosRequestConfig) {
|
||||
let { data } = config
|
||||
const { url, method, params, headers } = config
|
||||
if (typeof data === 'string') data = JSON.parse(data) // response里面返回的config.data是个字符串对象
|
||||
return [
|
||||
url,
|
||||
method,
|
||||
headers && (headers as anyObj).Authorization ? (headers as anyObj).Authorization : '',
|
||||
headers && (headers as anyObj)['ba-user-token'] ? (headers as anyObj)['ba-user-token'] : '',
|
||||
JSON.stringify(params),
|
||||
JSON.stringify(data),
|
||||
].join('&')
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据请求方法组装请求数据/参数
|
||||
*/
|
||||
export function requestPayload(method: Method, data: anyObj) {
|
||||
if (method == 'GET') {
|
||||
return {
|
||||
params: data,
|
||||
}
|
||||
} else if (method == 'POST') {
|
||||
return {
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface LoadingInstance {
|
||||
target: any
|
||||
count: number
|
||||
}
|
||||
|
||||
interface Options {
|
||||
// 是否开启取消重复请求, 默认为 true
|
||||
CancelDuplicateRequest?: boolean
|
||||
// 是否开启loading层效果, 默认为false
|
||||
loading?: boolean
|
||||
// 是否开启简洁的数据结构响应, 默认为true
|
||||
reductDataFormat?: boolean
|
||||
// 是否开启code不为A0000时的信息提示, 默认为true
|
||||
showCodeMessage?: boolean
|
||||
// 是否开启code为0时的信息提示, 默认为false
|
||||
showSuccessMessage?: boolean
|
||||
// 当前请求使用另外的用户token
|
||||
anotherToken?: string
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
const Renderer = (window.require && window.require('electron')) || window.electron || {};
|
||||
const Renderer =
|
||||
(window.require && window.require('electron')) || window.electron || {}
|
||||
|
||||
/**
|
||||
* ipc
|
||||
@@ -20,14 +21,11 @@ const Renderer = (window.require && window.require('electron')) || window.electr
|
||||
/**
|
||||
* ipc
|
||||
*/
|
||||
const ipc = Renderer.ipcRenderer || undefined;
|
||||
const ipc = Renderer.ipcRenderer || undefined
|
||||
|
||||
/**
|
||||
* 是否为EE环境
|
||||
*/
|
||||
const isEE = ipc ? true : false;
|
||||
|
||||
export {
|
||||
Renderer, ipc, isEE
|
||||
};
|
||||
const isEE = ipc ? true : false
|
||||
|
||||
export { Renderer, ipc, isEE }
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
<script setup lang='ts'>
|
||||
defineOptions({
|
||||
name: "componentName"
|
||||
})
|
||||
import {ref, reactive} from 'vue'
|
||||
let select = ref(1)
|
||||
|
||||
defineExpose({ select })
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,32 +1,22 @@
|
||||
<template>
|
||||
<div ref="loginContent" class="text-xl text-red-600">
|
||||
<!--用户名密码,暂时不做图形验证码-->
|
||||
<h1 ref="loginTitle">123</h1>
|
||||
<Child ref="child"/>
|
||||
<svg-icon name="wind" color="#fff" size="100" spin > </svg-icon>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
defineOptions({
|
||||
name: "Login"
|
||||
})
|
||||
import {ref, reactive,onMounted} from 'vue'
|
||||
import Child from './Child.vue'
|
||||
defineOptions(
|
||||
{
|
||||
name: "CustomLogin"
|
||||
}
|
||||
)
|
||||
import {ref, onMounted} from 'vue'
|
||||
let loginContent = ref()
|
||||
let loginTitle = ref()
|
||||
let child = ref()
|
||||
|
||||
function showLog(){
|
||||
console.log(child.value)
|
||||
}
|
||||
|
||||
showLog()
|
||||
|
||||
console.log(loginTitle.value);
|
||||
console.log(child.value);
|
||||
onMounted(() => {
|
||||
console.log(loginTitle.value);
|
||||
console.log(child.value);
|
||||
console.log(loginTitle.value);
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ["index.html", "./src/**/*.{html,js,ts,jsx,tsx,vue}"],
|
||||
content: ['index.html', './src/**/*.{html,js,ts,jsx,tsx,vue}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"include": [
|
||||
"env.d.ts",
|
||||
"src/**/*",
|
||||
"src/**/*.vue"
|
||||
],
|
||||
"exclude": [
|
||||
"src/**/__tests__/*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": false,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,36 @@
|
||||
}
|
||||
],
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"declaration": true,
|
||||
"useDefineForClassFields": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "Node",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"allowJs": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["esnext", "DOM"],
|
||||
"skipLibCheck": true,
|
||||
"noEmit": false,
|
||||
"types": [
|
||||
"node"
|
||||
]
|
||||
}
|
||||
"node",
|
||||
"element-plus/global"
|
||||
],
|
||||
"baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
|
||||
"paths": { //路径映射,相对于baseUrl
|
||||
"@/*": ["src/*"]
|
||||
},
|
||||
"allowSyntheticDefaultImports": true // 允许默认导入
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"vite.config.ts"
|
||||
],
|
||||
"exclude": ["node_modules", "dist", "**/*.js"]
|
||||
}
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": true,
|
||||
"noEmit": false,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"]
|
||||
|
||||
@@ -1,19 +1,31 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import {defineConfig, loadEnv} from 'vite'
|
||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import path from 'path'
|
||||
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
export default defineConfig((config) => {
|
||||
// 根据当前工作目录中的 `mode` 加载 .env 文件
|
||||
// 设置第三个参数为 '' 来加载所有环境变量,而不管是否有 `VITE_` 前缀。
|
||||
const {command, mode} = config
|
||||
const env = loadEnv(mode, process.cwd(), '')
|
||||
return {
|
||||
plugins: [
|
||||
vue(),
|
||||
// svg图标配置,可以使用svg图标
|
||||
createSvgIconsPlugin({
|
||||
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
|
||||
symbolId: 'icon-[dir]-[name]',
|
||||
}),
|
||||
],
|
||||
// 基础配置
|
||||
base: './',
|
||||
publicDir: 'public',
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src')
|
||||
}
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
@@ -30,7 +42,6 @@ export default defineConfig({
|
||||
assetsDir: 'assets',
|
||||
assetsInlineLimit: 4096,
|
||||
cssCodeSplit: true,
|
||||
brotliSize: false,
|
||||
sourcemap: false,
|
||||
minify: 'terser',
|
||||
terserOptions: {
|
||||
@@ -41,4 +52,5 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user