Compare commits
11 Commits
e459afbb59
...
bbb9b4e6b1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbb9b4e6b1 | ||
|
|
9b89d24d2e | ||
|
|
7cc3274ebc | ||
|
|
4b4b49011e | ||
|
|
543a82120a | ||
|
|
722437b24f | ||
|
|
a55999220c | ||
|
|
3c3c9405a0 | ||
|
|
8d254ed319 | ||
|
|
4437c69bb3 | ||
|
|
a296ab3c9d |
5
.env.development
Normal file
5
.env.development
Normal file
@ -0,0 +1,5 @@
|
||||
# 开发环境:空字符串(走Vite代理)
|
||||
VITE_API_BASE_URL='http://127.0.0.1:8501'
|
||||
VITE_ADMIN_BASE_URL='http://127.0.0.1:8501'
|
||||
VITE_HEAT_API_BASE_URL='http://192.168.0.207:8000'
|
||||
VITE_STRENGTH_API_BASE_URL='http://127.0.0.1:8501'
|
||||
0
.env.production
Normal file
0
.env.production
Normal file
@ -24,7 +24,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-21 12:51:42
|
||||
* @LastEditTime: 2022-09-24 16:29:19
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -71,5 +71,6 @@ module.exports = {
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/no-unused-components': 'warn',
|
||||
'vue/no-setup-props-destructure': 'off',
|
||||
'vue/script-setup-uses-vars': 'off',
|
||||
},
|
||||
}
|
||||
|
||||
@ -2,9 +2,10 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="icon" href="/ai_tu.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vue3-Element-Admin</title>
|
||||
<!-- <title>Vue3-Element-Admin</title>-->
|
||||
<title>凌空天行 AI大模型 应用系统</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
19
mock/menu.js
19
mock/menu.js
@ -24,7 +24,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-07-26 16:43:22
|
||||
* @LastEditTime: 2022-09-27 18:51:22
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -42,7 +42,7 @@ export default [
|
||||
{
|
||||
name: 'testList',
|
||||
title: '列表',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'testAdd',
|
||||
title: '添加',
|
||||
@ -51,10 +51,13 @@ export default [
|
||||
name: 'testEdit',
|
||||
title: '编辑',
|
||||
},
|
||||
{
|
||||
name: 'testAuth',
|
||||
title: '权限测试',
|
||||
]
|
||||
},
|
||||
|
||||
// {
|
||||
// name: 'testAuth',
|
||||
// title: '权限测试',
|
||||
// },
|
||||
{
|
||||
name: 'test-cache',
|
||||
title: '该页面可缓存',
|
||||
@ -83,12 +86,6 @@ export default [
|
||||
},
|
||||
]
|
||||
|
||||
if (query.role === 'admin')
|
||||
childs.push({
|
||||
name: 'testNoAuth',
|
||||
title: '权限页面',
|
||||
})
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取菜单成功',
|
||||
|
||||
26
mock/test.js
26
mock/test.js
@ -3,7 +3,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-21 09:39:02
|
||||
* @LastEditTime: 2022-09-25 12:27:50
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -44,4 +44,28 @@ export default [
|
||||
data: null,
|
||||
},
|
||||
},
|
||||
// 请求用户列表
|
||||
{
|
||||
url: '/api/test/users',
|
||||
method: 'post',
|
||||
timeout: 1000,
|
||||
response: () => {
|
||||
// 响应内容
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取成功',
|
||||
data: {
|
||||
'list|10': [
|
||||
{
|
||||
'id|+1': 1,
|
||||
nickName: '@cname()',
|
||||
userEmail: '@email()',
|
||||
'status|1': [0, 1],
|
||||
},
|
||||
],
|
||||
'total|50-1000': 1,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
8374
package-lock.json
generated
8374
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vue3-element-admin",
|
||||
"version": "2.0.1",
|
||||
"version": "3.0.0",
|
||||
"author": {
|
||||
"name": "huzhushan",
|
||||
"email": "huzhushan@126.com",
|
||||
@ -21,19 +21,21 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"vue": "^3.0.5",
|
||||
"vue": "^3.2.33",
|
||||
"vue-router": "^4.0.5",
|
||||
"vuex": "^4.0.0"
|
||||
"vuex": "^4.0.0",
|
||||
"pinia": "^2.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ehutch79/vite-eslint": "0.0.1",
|
||||
"@vitejs/plugin-vue": "^1.1.5",
|
||||
"@vue/compiler-sfc": "^3.0.5",
|
||||
"@vitejs/plugin-vue": "^1.2.3",
|
||||
"@vue/compiler-sfc": "^3.1.2",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"autoprefixer": "^10.2.5",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"crypto-js": "^4.0.0",
|
||||
"element-plus": "^1.0.2-beta.71",
|
||||
"element-plus": "^2.2.13",
|
||||
"vue-i18n": "^9.0.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-vue": "^7.0.0-0",
|
||||
@ -42,7 +44,7 @@
|
||||
"mockjs": "^1.1.0",
|
||||
"prettier": "^1.19.1",
|
||||
"sass": "^1.41.1",
|
||||
"vite": "^2.1.0",
|
||||
"vite": "^2.3.7",
|
||||
"vite-plugin-mock": "^2.3.0",
|
||||
"vite-plugin-svg-icons": "^0.4.0"
|
||||
},
|
||||
|
||||
BIN
public/ai_tu.png
Normal file
BIN
public/ai_tu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 412 KiB |
22
src/App.vue
22
src/App.vue
@ -24,15 +24,14 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-18 13:17:44
|
||||
* @LastEditTime: 2022-09-26 12:14:10
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
-->
|
||||
|
||||
<template>
|
||||
<el-config-provider :locale="locale">
|
||||
<el-config-provider :locale="locales[lang]">
|
||||
<router-view />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
@ -40,16 +39,22 @@
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
import { ElConfigProvider } from 'element-plus'
|
||||
|
||||
import zhCn from 'element-plus/lib/locale/lang/zh-cn'
|
||||
import localeZH from 'element-plus/es/locale/lang/zh-cn'
|
||||
import localeEN from 'element-plus/es/locale/lang/en'
|
||||
import useLang from '@/i18n/useLang'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
[ElConfigProvider.name]: ElConfigProvider,
|
||||
},
|
||||
data() {
|
||||
setup() {
|
||||
const { lang } = useLang()
|
||||
return {
|
||||
locale: zhCn,
|
||||
lang,
|
||||
locales: {
|
||||
'zh-cn': localeZH,
|
||||
en: localeEN,
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
@ -63,5 +68,8 @@ body,
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
* {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
16
src/api/convert.js
Normal file
16
src/api/convert.js
Normal file
@ -0,0 +1,16 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 调用外部接口转换MinIO路径(传递完整data对象)
|
||||
* @param {Object} uploadData - uploadToMinIO返回的完整data
|
||||
* @param {String} type - 'heat' 或 'strength'
|
||||
*/
|
||||
export function convertToDownloadUrl(uploadData, type) {
|
||||
// ✅ 绝对不能带域名,必须是相对路径
|
||||
const url =
|
||||
type === 'heat'
|
||||
? '/heat-api/api/calculate' // ✅ 正确:相对路径
|
||||
: '/strength-api/api/convert/download-url'
|
||||
|
||||
return request({ url, method: 'post', data: uploadData, timeout: 600000 })
|
||||
}
|
||||
30
src/api/document.js
Normal file
30
src/api/document.js
Normal file
@ -0,0 +1,30 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 搜索文档(分页)
|
||||
*/
|
||||
export function searchDocuments(params) {
|
||||
return request({
|
||||
url: '/api/document/search/summary/paged',
|
||||
method: 'get',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文档详情
|
||||
*/
|
||||
export function getDocumentDetail(id) {
|
||||
return request({
|
||||
url: `/api/document/detail/${id}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 下载文档(根据fileNo获取MinIO URL)
|
||||
export function downloadDocument(fileNo) {
|
||||
return request({
|
||||
url: `/api/document/download/search/${fileNo}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
31
src/api/excel.js
Normal file
31
src/api/excel.js
Normal file
@ -0,0 +1,31 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function uploadToMinIOjgmodel(formData) {
|
||||
return request({
|
||||
url: '/api/upload/folderjgmodel',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传 Excel 文件到后端
|
||||
* @param {File} file - Excel 文件对象
|
||||
* @returns {Promise} - 返回上传结果
|
||||
*/
|
||||
export function uploadExcelFile(file) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
return request({
|
||||
url: '/api/document/upload',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
timeout: 60000, // 60秒超时,Excel 解析可能需要较长时间
|
||||
})
|
||||
}
|
||||
15
src/api/heat.js
Normal file
15
src/api/heat.js
Normal file
@ -0,0 +1,15 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// uploadToMinIO 增加超时和进度监听
|
||||
export function uploadToMinIO(formData, onProgress) {
|
||||
return request({
|
||||
url: '/api/upload/folder',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
timeout: 600000, // ✅ 增加超时时间到10分钟
|
||||
onUploadProgress: onProgress || function() {}, // ✅ 进度回调
|
||||
})
|
||||
}
|
||||
28
src/api/image.js
Normal file
28
src/api/image.js
Normal file
@ -0,0 +1,28 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 上传图片
|
||||
*/
|
||||
export function uploadImage(file) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
return request.post('/api/images/upload', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊搜索图片
|
||||
*/
|
||||
export function searchImages(keyword) {
|
||||
return request.get('/api/images/search', {
|
||||
params: { keyword },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原图高清 URL
|
||||
*/
|
||||
export function getOriginImage(id) {
|
||||
return request.get(`/api/images/view/${id}`)
|
||||
}
|
||||
@ -14,7 +14,8 @@ import request from '@/utils/request'
|
||||
// 登录接口
|
||||
export const Login = data => {
|
||||
return request({
|
||||
url: '/api/login',
|
||||
// url: '/api/login',
|
||||
url: '/admin/system/index/login',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
@ -23,7 +24,16 @@ export const Login = data => {
|
||||
// 获取登录用户信息
|
||||
export const GetUserinfo = () => {
|
||||
return request({
|
||||
url: '/api/userinfo',
|
||||
// url: '/api/userinfo',
|
||||
url: '/admin/system/index/getUserInfo',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 请求验证码的函数
|
||||
export const GetValidateCode = () => {
|
||||
return request({
|
||||
url: '/admin/system/index/generateValidateCode',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
;``
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @version:
|
||||
@ -14,7 +15,7 @@ import request from '@/utils/request'
|
||||
// 获取菜单
|
||||
export const GetMenus = params => {
|
||||
return request({
|
||||
url: '/api/menus',
|
||||
url: '/admin/system/index/menus',
|
||||
method: 'get',
|
||||
params,
|
||||
})
|
||||
|
||||
48
src/api/rag.js
Normal file
48
src/api/rag.js
Normal file
@ -0,0 +1,48 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 提交RAG入库任务
|
||||
* @param {string} folderId - 文件夹ID
|
||||
* @returns {Promise} 任务提交结果
|
||||
*/
|
||||
// 提交RAG入库任务(带角色参数)
|
||||
export function submitRagIngestTask(folderId, userRoleCode = 'default') {
|
||||
return request({
|
||||
url: '/api/rag/submit',
|
||||
method: 'post',
|
||||
data: {
|
||||
folderId,
|
||||
userRoleCode,
|
||||
},
|
||||
timeout: 50000, // 30秒超时
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询RAG入库进度
|
||||
* @param {string} taskId - 任务ID
|
||||
* @returns {Promise} 进度信息
|
||||
*/
|
||||
export function getRagIngestProgress(taskId, userRoleCode = 'default') {
|
||||
return request({
|
||||
url: `/api/rag/progress/${taskId}`,
|
||||
method: 'get',
|
||||
params: { userRoleCode }, // 作为查询参数
|
||||
timeout: 50000,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件夹到MinIO
|
||||
* @param {FormData} formData - 包含files, relativePaths, sender, folderName
|
||||
* @returns {Promise} 上传结果
|
||||
*/
|
||||
export function uploadFolder(formData) {
|
||||
return request({
|
||||
url: '/api/upload/folder',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
timeout: 60000 * 10,
|
||||
})
|
||||
}
|
||||
20
src/api/rasa.js
Normal file
20
src/api/rasa.js
Normal file
@ -0,0 +1,20 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 调用 Rasa AI 接口
|
||||
// export const sendToAI = (data) => {
|
||||
// return request({
|
||||
// url: '/api/rasa/webhook',
|
||||
// method: 'post',
|
||||
// data
|
||||
// })
|
||||
// }
|
||||
|
||||
// 修改后
|
||||
export function sendToAI(data) {
|
||||
return request({
|
||||
url: '/api/chat/message', // 新地址
|
||||
method: 'post',
|
||||
data,
|
||||
timeout: 600000, // ✅ 增加超时时间到10分钟
|
||||
})
|
||||
}
|
||||
10
src/api/sysFRole.js
Normal file
10
src/api/sysFRole.js
Normal file
@ -0,0 +1,10 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取当前用户角色
|
||||
export function getUserRoles(userId) {
|
||||
return request({
|
||||
url: '/api/user/current-user-roles',
|
||||
method: 'get',
|
||||
params: { userId }, // ✅ 将用户ID作为查询参数传递
|
||||
})
|
||||
}
|
||||
28
src/api/sysMenu.js
Normal file
28
src/api/sysMenu.js
Normal file
@ -0,0 +1,28 @@
|
||||
import request from '@/utils/request'
|
||||
const api_name = '/admin/system/sysMenu'
|
||||
|
||||
// 分页列表
|
||||
export const FindNodes = () => {
|
||||
return request({
|
||||
url: `${api_name}/findNodes`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 保存信息
|
||||
export const SaveSysMenu = sysMenu => {
|
||||
return request({
|
||||
url: `${api_name}/saveSysMenu`,
|
||||
method: 'post',
|
||||
data: sysMenu,
|
||||
})
|
||||
}
|
||||
|
||||
// 修改信息
|
||||
export const UpdateSysMenu = sysMenu => {
|
||||
return request({
|
||||
url: `${api_name}/updateSysMenu`,
|
||||
method: 'put',
|
||||
data: sysMenu,
|
||||
})
|
||||
}
|
||||
65
src/api/sysRole.js
Normal file
65
src/api/sysRole.js
Normal file
@ -0,0 +1,65 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取菜单
|
||||
export const GetSysRoleListByPage = (queryDto, page, limit) => {
|
||||
return request({
|
||||
// url: '/api/menus',
|
||||
url: `/admin/system/sysRole/getSysRoleListByPage/${page}/${limit}`,
|
||||
method: 'get',
|
||||
params: queryDto,
|
||||
})
|
||||
}
|
||||
|
||||
//修改
|
||||
export const UpdateSysRole = sysRole => {
|
||||
return request({
|
||||
// url: '/api/menus',
|
||||
url: `/admin/system/sysRole/updateSysRole`,
|
||||
method: 'put',
|
||||
data: sysRole,
|
||||
})
|
||||
}
|
||||
|
||||
//添加
|
||||
export const SaveSysRole = sysRole => {
|
||||
return request({
|
||||
// url: '/api/menus',
|
||||
url: `/admin/system/sysRole/saveSysRole`,
|
||||
method: 'post',
|
||||
data: sysRole,
|
||||
})
|
||||
}
|
||||
|
||||
//删除
|
||||
export const DeleteSysRoleById = roleId => {
|
||||
return request({
|
||||
// url: '/api/menus',
|
||||
url: `/admin/system/sysRole/deleteSysRoleById/${roleId}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
||||
// 查询所有的角色数据
|
||||
export const GetAllRoleList = userId => {
|
||||
return request({
|
||||
url: `/admin/system/sysRole/findAllRoles/${userId}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 查询指定角色所对应的菜单id
|
||||
export const GetSysRoleMenuIds = roleId => {
|
||||
return request({
|
||||
url: '/admin/system/sysRoleMenu/findSysRoleMenuByRoleId/' + roleId,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 根据角色分配菜单请求方法
|
||||
export const DoAssignMenuIdToSysRole = assignMenuDto => {
|
||||
return request({
|
||||
url: '/admin/system/sysRoleMenu/doAssign',
|
||||
method: 'post',
|
||||
data: assignMenuDto,
|
||||
})
|
||||
}
|
||||
45
src/api/sysUser.js
Normal file
45
src/api/sysUser.js
Normal file
@ -0,0 +1,45 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 登录接口
|
||||
export const GetSysUserListByPage = (page, limit, queryDto) => {
|
||||
return request({
|
||||
url: `/admin/system/sysUser/getSysUserListByPage/${page}/${limit}`,
|
||||
method: 'get',
|
||||
params: queryDto,
|
||||
})
|
||||
}
|
||||
|
||||
// 修改
|
||||
export const UpdateSysUser = sysUser => {
|
||||
return request({
|
||||
url: `/admin/system/sysUser/updateSysUser`,
|
||||
method: 'put',
|
||||
data: sysUser,
|
||||
})
|
||||
}
|
||||
|
||||
// 添加
|
||||
export const SaveSysUser = sysUser => {
|
||||
return request({
|
||||
url: `/admin/system/sysUser/saveSysUser`,
|
||||
method: 'post',
|
||||
data: sysUser,
|
||||
})
|
||||
}
|
||||
|
||||
// 根据id删除用户
|
||||
export const DeleteSysUserById = userId => {
|
||||
return request({
|
||||
url: `/admin/system/sysUser/deleteById/${userId}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
||||
// 给用户分配角色请求
|
||||
export const DoAssignRoleToUser = assginRoleDto => {
|
||||
return request({
|
||||
url: '/admin/system/sysUserRole/doAssign',
|
||||
method: 'post',
|
||||
data: assginRoleDto,
|
||||
})
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 16:35:04
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-21 09:37:00
|
||||
* @LastEditTime: 2022-09-25 11:50:39
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -18,3 +18,12 @@ export const TestError = () => {
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 用户列表
|
||||
export const getUsers = data => {
|
||||
return request({
|
||||
url: '/api/test/users',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
BIN
src/assets/ai_tu.png
Normal file
BIN
src/assets/ai_tu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 412 KiB |
@ -1,11 +1,14 @@
|
||||
/* 改变主题色变量 */
|
||||
$--colors: (
|
||||
/**如果需要修改其它变量,可以在以下文件中查找
|
||||
* https://github.com/element-plus/element-plus/blob/dev/packages/theme-chalk/src/common/var.scss
|
||||
*/
|
||||
|
||||
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
|
||||
$colors: (
|
||||
'primary': (
|
||||
'base': $mainColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
/* 改变 icon 字体路径变量,必需 */
|
||||
$--font-path: 'element-plus/lib/theme-chalk/fonts';
|
||||
@import 'element-plus/packages/theme-chalk/src/reset';
|
||||
@import 'element-plus/packages/theme-chalk/src/index';
|
||||
@use "element-plus/theme-chalk/src/reset.scss" as *;
|
||||
@use "element-plus/theme-chalk/src/index.scss" as *;
|
||||
|
||||
1
src/assets/svg/icon-home.svg
Normal file
1
src/assets/svg/icon-home.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M510.72 41.856l435.2 361.024a32 32 0 0 1-36.16 52.48l-4.736-3.2-41.024-34.048V832c0 48.832-32.64 90.752-76.864 95.552l-8.448 0.448H245.312c-45.696 0-80.96-39.04-84.928-86.912L160 832V407.808l-55.04 44.608a32 32 0 0 1-40.96-0.64l-4.032-4.16a32 32 0 0 1 0.64-40.96l4.096-4.032L510.784 41.856z m-0.512 82.688L223.232 356.672a31.808 31.808 0 0 1 0.256 1.152l0.512 5.76V832c0 16.768 8.64 28.992 17.92 31.552l3.392 0.448h533.376c9.216 0 18.88-10.432 20.928-25.92L800 832V364.992L510.208 124.544z" /></svg>
|
||||
|
After Width: | Height: | Size: 766 B |
1
src/assets/svg/language.svg
Normal file
1
src/assets/svg/language.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1664017268288" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2638" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M848.805886 805.572222c70.998007-81.260745 109.779266-184.217628 109.779266-293.14448 0-119.204939-46.421262-231.277434-130.713041-315.569212C744.876861 113.862257 634.94103 67.61598 517.788843 66.213028c-1.924839-0.599657-10.290367-0.592494-12.227486 0.01535C388.878868 67.945485 279.434224 114.159016 196.73471 196.85853 113.863281 279.730982 67.630307 389.460106 66.095347 506.415818c-0.428765 1.64957-0.436952 8.601912-0.021489 10.226922 1.082658 117.628024 47.364751 228.058113 130.660852 311.354214 84.291778 84.291778 196.36325 130.713041 315.569212 130.713041 119.204939 0 231.277434-46.421262 315.569212-130.713041 6.139837-6.139837 12.054547-12.444427 17.789155-18.871813 0.50756-0.453325 1.001817-0.928139 1.471514-1.440815C847.750857 807.012014 848.295256 806.299793 848.805886 805.572222zM107.447151 532.043499l187.501418 0c1.322112 65.678862 9.253758 127.264499 22.505573 182.112688-61.690014 16.687054-100.819197 38.371936-121.076566 51.906184C144.30971 701.336206 111.676475 620.35687 107.447151 532.043499zM195.881272 259.408121c20.090571 13.556761 59.242266 35.461653 121.340579 52.260248-12.998035 54.127781-20.827351 114.778116-22.243607 179.432649L107.525945 491.101018C112.076588 403.731134 144.437623 323.612399 195.881272 259.408121zM917.081898 491.099994 729.628576 491.099994c-1.415232-64.630996-9.240455-125.260865-22.229281-179.37432 61.95505-16.693194 101.235682-38.444591 121.56673-52.020794C880.270505 323.860039 912.537396 403.866211 917.081898 491.099994zM688.677908 491.099994 532.167319 491.099994 532.167319 335.061149c52.209082-1.094938 97.103572-6.453992 135.272893-14.033621C680.000272 373.163955 687.286212 430.896844 688.677908 491.099994zM532.167319 294.115598 532.167319 109.918435c36.84107 10.398838 72.779583 49.205679 100.926644 110.015649 8.810666 19.035542 16.645099 39.641859 23.464411 61.521169C621.531626 288.227494 580.261687 293.062616 532.167319 294.115598zM491.223814 110.273523l0 183.805236c-47.504944-1.12666-88.378863-6.001691-123.120109-12.802584 6.807033-21.812795 14.623046-42.35976 23.409153-61.344137C419.351903 159.792333 454.809463 121.175827 491.223814 110.273523zM491.223814 335.040682l0 156.059312L335.928912 491.099994c1.391696-60.213383 8.679683-117.955482 21.243837-170.099073C395.008472 328.536548 439.487499 333.887416 491.223814 335.040682zM335.893096 532.043499l155.330718 0 0 158.667719c-51.609425 1.194198-96.019891 6.563486-133.821845 14.103206C344.576873 651.927913 337.193719 593.243349 335.893096 532.043499zM491.223814 731.672118l0 182.909843c-36.415374-10.902304-71.871911-49.51881-99.709933-109.659539-8.679683-18.752086-16.409738-39.034015-23.157419-60.551074C402.9964 737.645157 443.773106 732.820268 491.223814 731.672118zM532.167319 914.937049 532.167319 731.608673c47.904033 1.025353 89.103364 5.862521 124.116809 12.656251-6.755868 21.555945-14.497179 41.87369-23.190165 60.656475C604.946902 865.73137 569.008388 904.538211 532.167319 914.937049zM532.167319 690.660052 532.167319 532.043499l156.546406 0c-1.298576 61.096497-8.66024 119.68487-21.445428 172.502819C629.154233 697.013761 584.319096 691.710988 532.167319 690.660052zM729.659275 532.043499l187.501418 0c-4.221138 88.138386-36.732599 168.973436-88.620363 233.635131-20.469194-13.668301-59.635215-35.298947-121.30374-51.868321C720.43724 659.049101 728.33921 597.585237 729.659275 532.043499zM801.518906 228.742704c-18.329461 11.570523-52.309366 29.355585-104.858186 43.493583-19.295462-63.056128-46.110177-115.004267-78.06189-150.97655C689.00025 140.410913 751.833297 178.097234 801.518906 228.742704zM406.007991 121.259738c-31.905664 35.920094-58.690704 87.768973-77.979002 150.702304-52.40351-14.241352-86.370113-32.099069-104.581893-43.587728C273.076422 177.914062 335.777463 140.364865 406.007991 121.259738zM223.917816 796.963147c18.284435-11.535731 52.098565-29.230742 104.332207-43.335994 19.271926 62.60485 45.976124 114.186645 77.757968 149.968593C335.99952 884.550994 273.472442 847.181899 223.917816 796.963147zM618.59883 903.595746c31.801287-35.803437 58.517765-87.426165 77.792761-150.08218 51.984978 14.023388 85.972047 31.631418 104.533798 43.208081C751.3329 847.061149 688.718841 884.521319 618.59883 903.595746z" p-id="2639"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@ -17,19 +17,16 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-23 14:56:06
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-23 15:00:31
|
||||
* @LastEditTime: 2022-09-27 16:07:53
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
|
||||
import { computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useAccount } from '@/pinia/modules/account'
|
||||
|
||||
export const useUserinfo = () => {
|
||||
const store = useStore()
|
||||
const userinfo = computed(() => store.state.account.userinfo)
|
||||
|
||||
const { userinfo } = storeToRefs(useAccount())
|
||||
return { userinfo }
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-21 09:18:32
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-21 12:45:38
|
||||
* @LastEditTime: 2022-09-27 17:50:59
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -111,18 +111,20 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import { useErrorlog } from '@/pinia/modules/errorLog'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ErrorLog',
|
||||
setup() {
|
||||
const dialogTableVisible = ref(false)
|
||||
const store = useStore()
|
||||
const errorLogs = computed(() => store.state.errorLog.logs)
|
||||
const errorStore = useErrorlog()
|
||||
const { logs: errorLogs } = storeToRefs(errorStore)
|
||||
const { clearErrorLog } = errorStore
|
||||
const clearAll = () => {
|
||||
dialogTableVisible.value = false
|
||||
store.dispatch('errorLog/clearErrorLog')
|
||||
clearErrorLog()
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-29 15:04:03
|
||||
* @LastEditTime: 2022-09-25 11:53:47
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -45,7 +45,7 @@
|
||||
<el-form-item
|
||||
v-for="item in search.fields"
|
||||
:key="item.name"
|
||||
:label="item.label"
|
||||
:label="$t(item.label)"
|
||||
:prop="item.name"
|
||||
>
|
||||
<slot v-if="item.type === 'custom'" :name="item.slot" />
|
||||
@ -55,13 +55,13 @@
|
||||
:filterable="!!item.filterable"
|
||||
:multiple="!!item.multiple"
|
||||
clearable
|
||||
:placeholder="`请选择${item.label}`"
|
||||
:placeholder="$t(item.label)"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
>
|
||||
<el-option
|
||||
v-for="option of item.options"
|
||||
:key="option.value"
|
||||
:label="option.name"
|
||||
:label="$t(option.name)"
|
||||
:value="option.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
@ -75,7 +75,7 @@
|
||||
:key="option.value"
|
||||
:label="option.value"
|
||||
>
|
||||
{{ option.name }}
|
||||
{{ $t(option.name) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
<el-radio-group
|
||||
@ -88,7 +88,7 @@
|
||||
:key="option.value"
|
||||
:label="option.value"
|
||||
>
|
||||
{{ option.name }}
|
||||
{{ $t(option.name) }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-checkbox-group
|
||||
@ -101,7 +101,7 @@
|
||||
:key="option.value"
|
||||
:label="option.value"
|
||||
>
|
||||
{{ option.name }}
|
||||
{{ $t(option.name) }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
<el-checkbox-group
|
||||
@ -114,7 +114,7 @@
|
||||
:key="option.value"
|
||||
:label="option.value"
|
||||
>
|
||||
{{ option.name }}
|
||||
{{ $t(option.name) }}
|
||||
</el-checkbox-button>
|
||||
</el-checkbox-group>
|
||||
<el-date-picker
|
||||
@ -124,7 +124,7 @@
|
||||
format="YYYY-MM-DD"
|
||||
clearable
|
||||
@change="handleDateChange($event, item, 'YYYY-MM-DD')"
|
||||
:placeholder="`请选择${item.label}`"
|
||||
:placeholder="$t(item.label)"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
></el-date-picker>
|
||||
<el-date-picker
|
||||
@ -134,7 +134,7 @@
|
||||
clearable
|
||||
@change="handleDateChange($event, item, 'YYYY-MM-DD HH:mm:ss')"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
:placeholder="`请选择${item.label}`"
|
||||
:placeholder="$t(item.label)"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
></el-date-picker>
|
||||
<el-date-picker
|
||||
@ -143,11 +143,11 @@
|
||||
type="daterange"
|
||||
format="YYYY-MM-DD"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:start-placeholder="$t('public.startdate')"
|
||||
:end-placeholder="$t('public.enddate')"
|
||||
clearable
|
||||
@change="handleRangeChange($event, item, 'YYYY-MM-DD')"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
:style="{ ...item.style }"
|
||||
></el-date-picker>
|
||||
<el-date-picker
|
||||
v-else-if="item.type === 'datetimerange'"
|
||||
@ -155,16 +155,16 @@
|
||||
type="datetimerange"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
range-separator="-"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
:start-placeholder="$t('public.starttime')"
|
||||
:end-placeholder="$t('public.endtime')"
|
||||
clearable
|
||||
@change="handleRangeChange($event, item, 'YYYY-MM-DD HH:mm:ss')"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
:style="{ ...item.style }"
|
||||
></el-date-picker>
|
||||
<el-input-number
|
||||
v-else-if="item.type === 'number'"
|
||||
v-model="searchModel[item.name]"
|
||||
:placeholder="`请输入${item.label}`"
|
||||
:placeholder="$t(item.label)"
|
||||
controls-position="right"
|
||||
:min="item.min"
|
||||
:max="item.max"
|
||||
@ -176,7 +176,7 @@
|
||||
type="textarea"
|
||||
clearable
|
||||
v-model="searchModel[item.name]"
|
||||
:placeholder="`请输入${item.label}`"
|
||||
:placeholder="$t(item.label)"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
></el-input>
|
||||
<el-input
|
||||
@ -184,16 +184,16 @@
|
||||
:maxlength="item.maxlength"
|
||||
v-model="searchModel[item.name]"
|
||||
clearable
|
||||
:placeholder="`请输入${item.label}`"
|
||||
:placeholder="$t(item.label)"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item class="search-btn">
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
|
||||
查询
|
||||
<el-button type="primary" icon="Search" @click="handleSearch">
|
||||
{{ $t('public.search') }}
|
||||
</el-button>
|
||||
<el-button @click="handleReset" icon="el-icon-refresh-right">
|
||||
重置
|
||||
<el-button @click="handleReset" icon="RefreshRight">
|
||||
{{ $t('public.reset') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@ -224,6 +224,7 @@
|
||||
:filter-method="item.filters && filterHandler"
|
||||
:show-overflow-tooltip="!item.wrap"
|
||||
v-bind="item"
|
||||
:label="item.label ? $t(item.label) : ''"
|
||||
>
|
||||
<template #header="scope" v-if="!!item.labelSlot">
|
||||
<slot :name="item.labelSlot" v-bind="scope"></slot>
|
||||
@ -511,6 +512,9 @@ export default defineComponent({
|
||||
:deep(.el-input-number .el-input__inner) {
|
||||
text-align: left;
|
||||
}
|
||||
:deep(.el-range-editor.el-input__wrapper) {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.head {
|
||||
@ -530,7 +534,7 @@ export default defineComponent({
|
||||
.pagination {
|
||||
padding: 0 20px 20px;
|
||||
background: #fff;
|
||||
text-align: right;
|
||||
justify-content: flex-end;
|
||||
:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
@ -27,19 +27,19 @@
|
||||
* @version:
|
||||
* @Date: 2021-09-01 13:58:08
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-29 10:10:32
|
||||
* @LastEditTime: 2022-09-27 18:31:22
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
|
||||
import store from '@/store'
|
||||
import { useAccount } from '@/pinia/modules/account'
|
||||
|
||||
export const Permission = app => {
|
||||
app.directive('permission', {
|
||||
mounted: function(el, binding) {
|
||||
const permissionList = store.state.account.permissionList || []
|
||||
const { permissionList } = useAccount()
|
||||
|
||||
if (
|
||||
binding.value &&
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-21 09:18:32
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-21 12:48:49
|
||||
* @LastEditTime: 2022-09-27 15:53:02
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -21,7 +21,8 @@
|
||||
*/
|
||||
|
||||
import { nextTick } from 'vue'
|
||||
import store from '@/store'
|
||||
import { useErrorlog } from './pinia/modules/errorLog'
|
||||
// import store from '@/store'
|
||||
|
||||
// 判断环境,决定是否开启错误监控
|
||||
// - import.meta.env.DEV 布尔值,代表开发环境
|
||||
@ -34,7 +35,7 @@ export default app => {
|
||||
if (flag) {
|
||||
app.config.errorHandler = function(err, vm, info) {
|
||||
nextTick(() => {
|
||||
store.dispatch('errorLog/addErrorLog', {
|
||||
useErrorlog().addErrorLog({
|
||||
err,
|
||||
// vm, // 这里不保存vm,否则渲染错误日志的时候控制台会有警告
|
||||
info,
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-09-18 09:32:01
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-18 17:54:29
|
||||
* @LastEditTime: 2022-09-24 14:43:31
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -23,4 +23,3 @@
|
||||
|
||||
export { default as SvgIcon } from '@/components/SvgIcon/index.vue'
|
||||
export { default as ProTable } from '@/components/ProTable/index.vue'
|
||||
export { default as ElSelectTree } from '@/components/SelectTree/index.vue'
|
||||
|
||||
@ -27,23 +27,23 @@
|
||||
* @version:
|
||||
* @Date: 2021-08-20 11:15:27
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-18 17:25:32
|
||||
* @LastEditTime: 2022-09-27 16:15:56
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
|
||||
import { useTags } from '@/pinia/modules/tags'
|
||||
import { reactive, toRefs, getCurrentInstance } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
|
||||
// 关闭当前标签
|
||||
export default () => {
|
||||
const instance = getCurrentInstance()
|
||||
const store = useStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const { delTag } = useTags()
|
||||
const state = reactive({
|
||||
/**
|
||||
* @param {String} fullPath 要跳转到那个页面的地址
|
||||
@ -52,7 +52,7 @@ export default () => {
|
||||
* @return {*}
|
||||
*/
|
||||
closeTag({ fullPath, reload, f5 } = {}) {
|
||||
store.dispatch('tags/delTag', route)
|
||||
delTag(route)
|
||||
fullPath ? router.push(fullPath) : router.back()
|
||||
reload &&
|
||||
setTimeout(() => {
|
||||
|
||||
19
src/i18n/index.js
Normal file
19
src/i18n/index.js
Normal file
@ -0,0 +1,19 @@
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
const getMessage = modules => {
|
||||
return Object.entries(modules).reduce((module, [path, mod]) => {
|
||||
const moduleName = path.replace(/^\.\/locales\/[\w-]+\/(.*)\.\w+$/, '$1')
|
||||
module[moduleName] = mod.default
|
||||
return module
|
||||
}, {})
|
||||
}
|
||||
|
||||
export default createI18n({
|
||||
locale: localStorage.getItem('__VEA__lang') || 'zh-cn',
|
||||
messages: {
|
||||
'zh-cn': getMessage(import.meta.globEager('./locales/zh-cn/**/*.js')),
|
||||
en: getMessage(import.meta.globEager('./locales/en/**/*.js')),
|
||||
},
|
||||
legacy: false,
|
||||
globalInjection: true,
|
||||
})
|
||||
6
src/i18n/locales/en/error.js
Normal file
6
src/i18n/locales/en/error.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
noauth: 'No access',
|
||||
servererror: 'Server error',
|
||||
notfound: 'Page not found',
|
||||
backhome: 'Back to Home',
|
||||
}
|
||||
10
src/i18n/locales/en/login.js
Normal file
10
src/i18n/locales/en/login.js
Normal file
@ -0,0 +1,10 @@
|
||||
export default {
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
login: 'Login',
|
||||
logining: 'Login...',
|
||||
loginsuccess: 'Success',
|
||||
'rules-username': 'Please input username',
|
||||
'rules-password': 'Please input password',
|
||||
'rules-regpassword': '6 to 12 characters in length',
|
||||
}
|
||||
16
src/i18n/locales/en/menu.js
Normal file
16
src/i18n/locales/en/menu.js
Normal file
@ -0,0 +1,16 @@
|
||||
export default {
|
||||
homepage: 'Homepage',
|
||||
dashboard: 'Dashboard',
|
||||
test: 'Test page',
|
||||
testList: 'List',
|
||||
testAdd: 'Add',
|
||||
testEdit: 'Edit',
|
||||
testAuth: 'Auth',
|
||||
testNoAuth: 'No auth',
|
||||
'test-cache': 'Cache',
|
||||
'test-no-cache': 'No Cache',
|
||||
nest: 'Nest page',
|
||||
nestPage1: 'Page1',
|
||||
nestPage2: 'Page2',
|
||||
'test-error-log': 'Error log',
|
||||
}
|
||||
22
src/i18n/locales/en/public.js
Normal file
22
src/i18n/locales/en/public.js
Normal file
@ -0,0 +1,22 @@
|
||||
export default {
|
||||
sure: 'Sure',
|
||||
search: 'Search',
|
||||
reset: 'Reset',
|
||||
edit: 'Edit',
|
||||
add: 'Add',
|
||||
delete: 'Delete',
|
||||
save: 'Save',
|
||||
cancel: 'Cancel',
|
||||
yes: 'Yes',
|
||||
no: 'No',
|
||||
status: 'Status',
|
||||
operate: 'Operate',
|
||||
enabled: 'Enabled',
|
||||
disabled: 'Disabled',
|
||||
male: 'Male',
|
||||
female: 'Female',
|
||||
startdate: 'From',
|
||||
enddate: 'To',
|
||||
starttime: 'From',
|
||||
endtime: 'To',
|
||||
}
|
||||
8
src/i18n/locales/en/tags.js
Normal file
8
src/i18n/locales/en/tags.js
Normal file
@ -0,0 +1,8 @@
|
||||
export default {
|
||||
refresh: 'Refresh',
|
||||
close: 'Close',
|
||||
other: 'Close other',
|
||||
left: 'Close left',
|
||||
right: 'Close right',
|
||||
all: 'Close all',
|
||||
}
|
||||
32
src/i18n/locales/en/test/list.js
Normal file
32
src/i18n/locales/en/test/list.js
Normal file
@ -0,0 +1,32 @@
|
||||
export default {
|
||||
title: 'List',
|
||||
batchDelete: 'Batch delete',
|
||||
add: 'Add one',
|
||||
refresh: 'Refresh',
|
||||
index: 'Index',
|
||||
name: 'Nickname',
|
||||
email: 'Email',
|
||||
desc: 'Description',
|
||||
publish: 'Published',
|
||||
nopublish: 'Unpublished',
|
||||
gender: 'Gender',
|
||||
city: 'City',
|
||||
bj: 'Beijing',
|
||||
sh: 'Shanghai',
|
||||
gz: 'Guangzhou',
|
||||
sz: 'Shenzhen',
|
||||
hobby: 'Hobby',
|
||||
eat: 'Eat',
|
||||
sleep: 'Sleep',
|
||||
bit: 'Beat',
|
||||
fruit: 'Fruit',
|
||||
apple: 'Apple',
|
||||
banana: 'Banana',
|
||||
orange: 'Orange',
|
||||
grape: 'Grape',
|
||||
date: 'Date',
|
||||
daterange: 'Date range',
|
||||
time: 'Time',
|
||||
timerange: 'Time range',
|
||||
num: 'Number',
|
||||
}
|
||||
21
src/i18n/locales/en/topbar.js
Normal file
21
src/i18n/locales/en/topbar.js
Normal file
@ -0,0 +1,21 @@
|
||||
export default {
|
||||
center: 'User center',
|
||||
password: 'Modify password',
|
||||
logout: 'Logout',
|
||||
'lock-title': 'Lock screen',
|
||||
'lock-password': 'Password',
|
||||
'lock-rules-password': 'Please input Screen password',
|
||||
'lock-locked': 'Screen Locked',
|
||||
'lock-lock': 'Unlock',
|
||||
'lock-relogin': 'Re-login',
|
||||
'lock-rules-password2': 'Screen password or User password',
|
||||
'lock-rules-password3': 'Password error',
|
||||
'lock-error': 'Your account has been logged out, please log in directly',
|
||||
'lock-week0': 'Sunday',
|
||||
'lock-week1': 'Monday',
|
||||
'lock-week2': 'Tuesday',
|
||||
'lock-week3': 'Wednesday',
|
||||
'lock-week4': 'Thursday',
|
||||
'lock-week5': 'Friday',
|
||||
'lock-week6': 'Saturday',
|
||||
}
|
||||
6
src/i18n/locales/zh-cn/error.js
Normal file
6
src/i18n/locales/zh-cn/error.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
noauth: '您无权访问此页面',
|
||||
servererror: '服务器出错了',
|
||||
notfound: '您访问的页面不存在',
|
||||
backhome: '返回首页',
|
||||
}
|
||||
10
src/i18n/locales/zh-cn/login.js
Normal file
10
src/i18n/locales/zh-cn/login.js
Normal file
@ -0,0 +1,10 @@
|
||||
export default {
|
||||
username: '用户名',
|
||||
password: '密码',
|
||||
login: '登录',
|
||||
logining: '登录中...',
|
||||
loginsuccess: '登录成功',
|
||||
'rules-username': '请输入用户名',
|
||||
'rules-password': '请输入密码',
|
||||
'rules-regpassword': '长度在 6 到 12 个字符',
|
||||
}
|
||||
16
src/i18n/locales/zh-cn/menu.js
Normal file
16
src/i18n/locales/zh-cn/menu.js
Normal file
@ -0,0 +1,16 @@
|
||||
export default {
|
||||
homepage: '首页',
|
||||
dashboard: '工作台',
|
||||
test: '测试页面',
|
||||
testList: '列表',
|
||||
testAdd: '添加',
|
||||
testEdit: '编辑',
|
||||
testAuth: '权限测试',
|
||||
testNoAuth: '权限页面',
|
||||
'test-cache': '该页面可缓存',
|
||||
'test-no-cache': '该页面不缓存',
|
||||
nest: '二级页面',
|
||||
nestPage1: 'Page1',
|
||||
nestPage2: 'Page2',
|
||||
'test-error-log': '测试错误日志',
|
||||
}
|
||||
22
src/i18n/locales/zh-cn/public.js
Normal file
22
src/i18n/locales/zh-cn/public.js
Normal file
@ -0,0 +1,22 @@
|
||||
export default {
|
||||
sure: '确定',
|
||||
search: '搜索',
|
||||
reset: '重置',
|
||||
edit: '编辑',
|
||||
add: '新增',
|
||||
delete: '删除',
|
||||
save: '保存',
|
||||
cancel: '取消',
|
||||
yes: '是',
|
||||
no: '否',
|
||||
status: '状态',
|
||||
operate: '操作',
|
||||
enabled: '启用',
|
||||
disabled: '禁用',
|
||||
male: '男',
|
||||
female: '女',
|
||||
startdate: '开始日期',
|
||||
enddate: '结束日期',
|
||||
starttime: '开始时间',
|
||||
endtime: '结束时间',
|
||||
}
|
||||
8
src/i18n/locales/zh-cn/tags.js
Normal file
8
src/i18n/locales/zh-cn/tags.js
Normal file
@ -0,0 +1,8 @@
|
||||
export default {
|
||||
refresh: '刷新',
|
||||
close: '关闭',
|
||||
other: '关闭其它',
|
||||
left: '关闭左侧',
|
||||
right: '关闭右侧',
|
||||
all: '关闭全部',
|
||||
}
|
||||
32
src/i18n/locales/zh-cn/test/list.js
Normal file
32
src/i18n/locales/zh-cn/test/list.js
Normal file
@ -0,0 +1,32 @@
|
||||
export default {
|
||||
title: '列表',
|
||||
batchDelete: '批量删除',
|
||||
add: '添加一条',
|
||||
refresh: '刷新',
|
||||
index: '序号',
|
||||
name: '昵称',
|
||||
email: '邮箱',
|
||||
desc: '描述',
|
||||
publish: '已发布',
|
||||
nopublish: '未发布',
|
||||
gender: '性别',
|
||||
city: '城市',
|
||||
bj: '北京',
|
||||
sh: '上海',
|
||||
gz: '广州',
|
||||
sz: '深圳',
|
||||
hobby: '爱好',
|
||||
eat: '吃饭',
|
||||
sleep: '睡觉',
|
||||
bit: '打豆豆',
|
||||
fruit: '水果',
|
||||
apple: '苹果',
|
||||
banana: '香蕉',
|
||||
orange: '橘子',
|
||||
grape: '葡萄',
|
||||
date: '日期',
|
||||
daterange: '日期范围',
|
||||
time: '时间',
|
||||
timerange: '时间范围',
|
||||
num: '数量',
|
||||
}
|
||||
21
src/i18n/locales/zh-cn/topbar.js
Normal file
21
src/i18n/locales/zh-cn/topbar.js
Normal file
@ -0,0 +1,21 @@
|
||||
export default {
|
||||
center: '个人中心',
|
||||
password: '修改密码',
|
||||
logout: '退出登录',
|
||||
'lock-title': '锁定屏幕',
|
||||
'lock-password': '锁屏密码',
|
||||
'lock-rules-password': '请输入锁屏密码',
|
||||
'lock-locked': '屏幕已锁定',
|
||||
'lock-lock': '解锁',
|
||||
'lock-relogin': '重新登录',
|
||||
'lock-rules-password2': '请输入锁屏密码或登录密码',
|
||||
'lock-rules-password3': '密码错误',
|
||||
'lock-error': '您的账号已退出,请直接登录',
|
||||
'lock-week0': '星期日',
|
||||
'lock-week1': '星期一',
|
||||
'lock-week2': '星期二',
|
||||
'lock-week3': '星期三',
|
||||
'lock-week4': '星期四',
|
||||
'lock-week5': '星期五',
|
||||
'lock-week6': '星期六',
|
||||
}
|
||||
16
src/i18n/useLang.js
Normal file
16
src/i18n/useLang.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
export default function useLang() {
|
||||
const i18n = useI18n()
|
||||
const lang = computed(() => i18n.locale.value)
|
||||
const changeLang = value => {
|
||||
i18n.locale.value = value
|
||||
localStorage.setItem('__VEA__lang', value)
|
||||
}
|
||||
return {
|
||||
i18n,
|
||||
lang,
|
||||
changeLang,
|
||||
}
|
||||
}
|
||||
1516
src/layout/chat-layout.css
Normal file
1516
src/layout/chat-layout.css
Normal file
File diff suppressed because it is too large
Load Diff
2370
src/layout/chat_layout.vue
Normal file
2370
src/layout/chat_layout.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-21 12:46:17
|
||||
* @LastEditTime: 2022-09-27 16:27:54
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -42,15 +42,15 @@
|
||||
</router-view>
|
||||
</template>
|
||||
<script>
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import { useTags } from '@/pinia/modules/tags'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const cacheList = computed(() => store.state.tags.cacheList)
|
||||
const { cacheList } = storeToRefs(useTags())
|
||||
const key = computed(() => route.fullPath)
|
||||
|
||||
return {
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-21 12:46:37
|
||||
* @LastEditTime: 2022-09-24 19:33:12
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -32,9 +32,9 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<i v-if="isElementIcon" :class="`icon ${icon}`" />
|
||||
<svg-icon class="icon" v-else-if="!!icon" :name="icon" />
|
||||
<span>{{ title }}</span>
|
||||
<svg-icon class="icon" v-if="isCustomSvg" :name="icon" />
|
||||
<component :is="icon" v-else-if="!!icon" class="icon" />
|
||||
<span>{{ $t(title) }}</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -43,10 +43,10 @@ import { computed, defineComponent } from 'vue'
|
||||
export default defineComponent({
|
||||
props: ['title', 'icon'],
|
||||
setup({ icon }) {
|
||||
const isElementIcon = computed(() => icon && icon.startsWith('el-icon'))
|
||||
const isCustomSvg = computed(() => icon && icon.startsWith('icon-'))
|
||||
|
||||
return {
|
||||
isElementIcon,
|
||||
isCustomSvg,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@ -34,8 +34,9 @@
|
||||
|
||||
<template>
|
||||
<div class="brand">
|
||||
<img class="logo" src="~@/assets/logo.svg" @click="goHome" />
|
||||
<div class="title">Vue3 Element Admin</div>
|
||||
<!-- <img class="logo" src="~@/assets/logo.svg" @click="goHome" />-->
|
||||
<img class="logo" src="~@/assets/ai_tu.png" @click="goHome" />
|
||||
<div class="title">凌空天行</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-07-26 16:02:28
|
||||
* @LastEditTime: 2022-09-27 16:45:42
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -40,7 +40,7 @@
|
||||
class="menu"
|
||||
:mode="mode"
|
||||
:collapse="collapse"
|
||||
:uniqueOpened="true"
|
||||
:uniqueOpened="false"
|
||||
:router="true"
|
||||
:default-active="activePath"
|
||||
:background-color="variables.menuBg"
|
||||
@ -54,9 +54,11 @@
|
||||
<script>
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import Submenu from './Submenu.vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useRoute } from 'vue-router'
|
||||
import config from './config/menu.module.scss'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useMenus } from '@/pinia/modules/menu'
|
||||
import { useApp } from '@/pinia/modules/app'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -74,11 +76,17 @@ export default defineComponent({
|
||||
},
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const store = useStore()
|
||||
const { menus } = storeToRefs(useMenus())
|
||||
const appStore = useApp()
|
||||
const { matchedRoutes } = storeToRefs(appStore)
|
||||
|
||||
return {
|
||||
menus: computed(() => store.state.menu.menus),
|
||||
activePath: computed(() => route.path),
|
||||
menus,
|
||||
activePath: computed(() => {
|
||||
return matchedRoutes.value.length > 1
|
||||
? matchedRoutes.value[1].path
|
||||
: route.path
|
||||
}),
|
||||
variables: computed(() => config),
|
||||
}
|
||||
},
|
||||
@ -87,15 +95,15 @@ export default defineComponent({
|
||||
<style lang="scss">
|
||||
// menu hover
|
||||
.el-menu-item,
|
||||
.el-submenu__title {
|
||||
.el-sub-menu__title {
|
||||
&:hover {
|
||||
background-color: $menuHover !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-submenu {
|
||||
.el-sub-menu {
|
||||
.el-menu-item,
|
||||
.el-submenu .el-submenu__title {
|
||||
.el-sub-menu .el-sub-menu__title {
|
||||
background-color: $subMenuBg !important;
|
||||
|
||||
&:hover {
|
||||
@ -112,7 +120,7 @@ export default defineComponent({
|
||||
|
||||
.el-menu--collapse {
|
||||
.el-menu-item.is-active,
|
||||
.el-submenu.is-active > .el-submenu__title {
|
||||
.el-sub-menu.is-active > .el-sub-menu__title {
|
||||
position: relative;
|
||||
background-color: $collapseMenuActiveBg !important;
|
||||
color: $collapseMenuActiveColor !important;
|
||||
@ -128,19 +136,20 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
.el-submenu__title i {
|
||||
.el-sub-menu__title i {
|
||||
color: $arrowColor;
|
||||
}
|
||||
|
||||
// 水平菜单
|
||||
.el-menu--horizontal {
|
||||
.el-menu-item,
|
||||
.el-submenu .el-submenu__title {
|
||||
.el-sub-menu .el-sub-menu__title {
|
||||
height: $horizontalMenuHeight;
|
||||
line-height: $horizontalMenuHeight;
|
||||
border-bottom: none;
|
||||
}
|
||||
.el-menu-item.is-active,
|
||||
.el-submenu.is-active .el-submenu__title {
|
||||
.el-sub-menu.is-active .el-sub-menu__title {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-21 12:46:55
|
||||
* @LastEditTime: 2022-09-24 16:44:28
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -24,7 +24,7 @@
|
||||
<el-menu-item v-if="!menu.children" :index="menu.url">
|
||||
<item :icon="menu.icon" :title="menu.title" />
|
||||
</el-menu-item>
|
||||
<el-submenu v-else :index="menu.url">
|
||||
<el-sub-menu v-else :index="menu.url">
|
||||
<template #title>
|
||||
<item :icon="menu.icon" :title="menu.title" />
|
||||
</template>
|
||||
@ -34,7 +34,7 @@
|
||||
:is-nest="true"
|
||||
:menu="submenu"
|
||||
/>
|
||||
</el-submenu>
|
||||
</el-sub-menu>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-07-23 09:36:42
|
||||
* @LastEditTime: 2022-09-27 18:45:07
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -44,10 +44,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import { useApp } from '@/pinia/modules/app'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import Logo from './Logo.vue'
|
||||
import Menus from './Menus.vue'
|
||||
import { useStore } from 'vuex'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -55,12 +56,13 @@ export default defineComponent({
|
||||
Menus,
|
||||
},
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const collapse = computed(() => !!store.state.app.sidebar.collapse)
|
||||
const device = computed(() => store.state.app.device)
|
||||
const appStore = useApp()
|
||||
const { sidebar, device } = storeToRefs(appStore)
|
||||
const { setCollapse } = appStore
|
||||
const collapse = computed(() => sidebar.value.collapse)
|
||||
|
||||
const closeSidebar = () => {
|
||||
store.commit('app/setCollapse', 1)
|
||||
setCollapse(1)
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@ -26,23 +26,24 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-29 10:52:41
|
||||
* @LastEditTime: 2022-09-27 16:52:30
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
|
||||
import { useTags } from '@/pinia/modules/tags'
|
||||
import { onMounted, onBeforeUnmount, reactive, toRefs, nextTick } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import { isAffix } from './useTags'
|
||||
|
||||
export const useContextMenu = tagList => {
|
||||
const store = useStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const tagsStore = useTags()
|
||||
|
||||
const state = reactive({
|
||||
visible: false,
|
||||
top: 0,
|
||||
@ -58,7 +59,7 @@ export const useContextMenu = tagList => {
|
||||
state.visible = false
|
||||
},
|
||||
refreshSelectedTag(tag) {
|
||||
store.commit('tags/DEL_CACHE_LIST', tag)
|
||||
tagsStore.deCacheList(tag)
|
||||
const { fullPath } = tag
|
||||
nextTick(() => {
|
||||
router.replace({
|
||||
@ -72,13 +73,13 @@ export const useContextMenu = tagList => {
|
||||
const closedTagIndex = tagList.value.findIndex(
|
||||
item => item.fullPath === tag.fullPath
|
||||
)
|
||||
store.dispatch('tags/delTag', tag)
|
||||
tagsStore.delTag(tag)
|
||||
if (isActive(tag)) {
|
||||
toLastTag(closedTagIndex - 1)
|
||||
}
|
||||
},
|
||||
closeOtherTags() {
|
||||
store.dispatch('tags/delOtherTags', state.selectedTag)
|
||||
tagsStore.delOtherTags(state.selectedTag)
|
||||
router.push(state.selectedTag)
|
||||
},
|
||||
closeLeftTags() {
|
||||
@ -103,11 +104,11 @@ export const useContextMenu = tagList => {
|
||||
direction === 'left'
|
||||
? tagList.value.slice(0, index)
|
||||
: tagList.value.slice(index + 1)
|
||||
store.dispatch('tags/delSomeTags', needToClose)
|
||||
tagsStore.delSomeTags(needToClose)
|
||||
router.push(state.selectedTag)
|
||||
},
|
||||
closeAllTags() {
|
||||
store.dispatch('tags/delAllTags')
|
||||
tagsStore.delAllTags()
|
||||
router.push('/')
|
||||
},
|
||||
})
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-21 12:47:07
|
||||
* @LastEditTime: 2022-08-13 14:50:23
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -35,15 +35,28 @@ import { ref } from 'vue'
|
||||
|
||||
export const useScrollbar = tagsItem => {
|
||||
const scrollContainer = ref(null)
|
||||
const scrollLeft = ref(0)
|
||||
|
||||
const doScroll = val => {
|
||||
scrollLeft.value = val
|
||||
scrollContainer.value.setScrollLeft(scrollLeft.value)
|
||||
}
|
||||
|
||||
const handleScroll = e => {
|
||||
const $wrap = scrollContainer.value.wrap$
|
||||
if ($wrap.offsetWidth + scrollLeft.value > $wrap.children[0].scrollWidth) {
|
||||
doScroll($wrap.children[0].scrollWidth - $wrap.offsetWidth)
|
||||
return
|
||||
} else if (scrollLeft.value < 0) {
|
||||
doScroll(0)
|
||||
return
|
||||
}
|
||||
const eventDelta = e.wheelDelta || -e.deltaY
|
||||
scrollContainer.value.wrap.scrollLeft -= eventDelta / 4
|
||||
doScroll(scrollLeft.value - eventDelta / 4)
|
||||
}
|
||||
|
||||
const moveToTarget = currentTag => {
|
||||
const containerWidth = scrollContainer.value.scrollbar.offsetWidth
|
||||
const scrollWrapper = scrollContainer.value.wrap
|
||||
const $wrap = scrollContainer.value.wrap$
|
||||
const tagList = tagsItem.value
|
||||
|
||||
let firstTag = null
|
||||
@ -54,15 +67,15 @@ export const useScrollbar = tagsItem => {
|
||||
lastTag = tagList[tagList.length - 1]
|
||||
}
|
||||
if (firstTag === currentTag) {
|
||||
scrollWrapper.scrollLeft = 0
|
||||
doScroll(0)
|
||||
} else if (lastTag === currentTag) {
|
||||
scrollWrapper.scrollLeft = scrollWrapper.scrollWidth - containerWidth
|
||||
doScroll($wrap.children[0].scrollWidth - $wrap.offsetWidth)
|
||||
} else {
|
||||
const el = currentTag.$el.nextElementSibling
|
||||
scrollWrapper.scrollLeft =
|
||||
el.offsetLeft + el.offsetWidth > containerWidth
|
||||
? el.offsetLeft - el.offsetWidth
|
||||
: 0
|
||||
|
||||
el.offsetLeft + el.offsetWidth > $wrap.offsetWidth
|
||||
? doScroll(el.offsetLeft - el.offsetWidth)
|
||||
: doScroll(0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -24,28 +24,29 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-29 17:13:31
|
||||
* @LastEditTime: 2022-09-27 18:28:33
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useTags as useTagsbar } from '@/pinia/modules/tags'
|
||||
import { useScrollbar } from './useScrollbar'
|
||||
import { watch, computed, ref, nextTick, onBeforeMount } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
|
||||
export const isAffix = tag => {
|
||||
return !!tag.meta && !!tag.meta.affix
|
||||
}
|
||||
|
||||
export const useTags = () => {
|
||||
const store = useStore()
|
||||
const tagStore = useTagsbar()
|
||||
const { tagList } = storeToRefs(tagStore)
|
||||
const { addTag, delTag, saveActivePosition, updateTagList } = tagStore
|
||||
const router = useRouter()
|
||||
const route = router.currentRoute
|
||||
const routes = computed(() => router.getRoutes())
|
||||
const tagList = computed(() => store.state.tags.tagList)
|
||||
|
||||
const tagsItem = ref([])
|
||||
|
||||
@ -71,24 +72,31 @@ export const useTags = () => {
|
||||
|
||||
for (const tag of affixTags) {
|
||||
if (tag.name) {
|
||||
store.dispatch('tags/addTag', tag)
|
||||
addTag(tag)
|
||||
}
|
||||
}
|
||||
// 不在路由中的所有标签,需要删除
|
||||
const noUseTags = tagList.value.filter(tag =>
|
||||
routes.value.every(route => route.name !== tag.name)
|
||||
)
|
||||
noUseTags.forEach(tag => {
|
||||
delTag(tag)
|
||||
})
|
||||
}
|
||||
|
||||
const addTag = () => {
|
||||
const addTagList = () => {
|
||||
const tag = route.value
|
||||
if (!!tag.name && tag.matched[0].components.default.name === 'layout') {
|
||||
store.dispatch('tags/addTag', tag)
|
||||
addTag(tag)
|
||||
}
|
||||
}
|
||||
|
||||
const saveActivePosition = tag => {
|
||||
const saveTagPosition = tag => {
|
||||
const index = tagList.value.findIndex(
|
||||
item => item.fullPath === tag.fullPath
|
||||
)
|
||||
|
||||
store.dispatch('tags/saveActivePosition', Math.max(0, index))
|
||||
saveActivePosition(Math.max(0, index))
|
||||
}
|
||||
|
||||
const moveToCurrentTag = () => {
|
||||
@ -98,7 +106,7 @@ export const useTags = () => {
|
||||
scrollbar.moveToTarget(tag)
|
||||
|
||||
if (tag.to.fullPath !== route.value.fullPath) {
|
||||
store.dispatch('tags/updateTagList', route.value)
|
||||
updateTagList(route.value)
|
||||
}
|
||||
break
|
||||
}
|
||||
@ -108,13 +116,13 @@ export const useTags = () => {
|
||||
|
||||
onBeforeMount(() => {
|
||||
initTags()
|
||||
addTag()
|
||||
addTagList()
|
||||
moveToCurrentTag()
|
||||
})
|
||||
|
||||
watch(route, (newRoute, oldRoute) => {
|
||||
saveActivePosition(oldRoute) // 保存标签的位置
|
||||
addTag()
|
||||
saveTagPosition(oldRoute) // 保存标签的位置
|
||||
addTagList()
|
||||
moveToCurrentTag()
|
||||
})
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-18 17:50:46
|
||||
* @LastEditTime: 2022-09-24 20:38:36
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -43,12 +43,15 @@
|
||||
@click.middle="closeTag(tag)"
|
||||
@contextmenu.prevent="openMenu(tag, $event)"
|
||||
>
|
||||
<span class="title">{{ tag.title }}</span>
|
||||
<span
|
||||
<span class="title">{{ $t(tag.title) }}</span>
|
||||
|
||||
<el-icon
|
||||
v-if="!isAffix(tag)"
|
||||
class="el-icon-close"
|
||||
@click.prevent.stop="closeTag(tag)"
|
||||
/>
|
||||
>
|
||||
<Close />
|
||||
</el-icon>
|
||||
</div>
|
||||
</router-link>
|
||||
</el-scrollbar>
|
||||
@ -58,12 +61,14 @@
|
||||
:style="{ left: left + 'px', top: top + 'px' }"
|
||||
class="contextmenu"
|
||||
>
|
||||
<li @click="refreshSelectedTag(selectedTag)">刷新</li>
|
||||
<li v-if="!isAffix(selectedTag)" @click="closeTag(selectedTag)">关闭</li>
|
||||
<li @click="closeOtherTags">关闭其他</li>
|
||||
<li @click="closeLeftTags">关闭左侧</li>
|
||||
<li @click="closeRightTags">关闭右侧</li>
|
||||
<li @click="closeAllTags">关闭全部</li>
|
||||
<li @click="refreshSelectedTag(selectedTag)">{{ $t('tags.refresh') }}</li>
|
||||
<li v-if="!isAffix(selectedTag)" @click="closeTag(selectedTag)">
|
||||
{{ $t('tags.close') }}
|
||||
</li>
|
||||
<li @click="closeOtherTags">{{ $t('tags.other') }}</li>
|
||||
<li @click="closeLeftTags">{{ $t('tags.left') }}</li>
|
||||
<li @click="closeRightTags">{{ $t('tags.right') }}</li>
|
||||
<li @click="closeAllTags">{{ $t('tags.all') }}</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
@ -71,7 +76,7 @@
|
||||
import { defineComponent, computed, getCurrentInstance } from 'vue'
|
||||
import { useTags } from './hooks/useTags'
|
||||
import { useContextMenu } from './hooks/useContextMenu'
|
||||
import { useStore } from 'vuex'
|
||||
import { useLayoutsettings } from '@/pinia/modules/layoutSettings'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Tagsbar',
|
||||
@ -80,9 +85,8 @@ export default defineComponent({
|
||||
instance.appContext.config.globalProperties.$tagsbar = this
|
||||
},
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const defaultSettings = computed(() => store.state.layoutSettings)
|
||||
const isTagsbarShow = computed(() => defaultSettings.value.tagsbar.isShow)
|
||||
const defaultSettings = useLayoutsettings()
|
||||
const isTagsbarShow = computed(() => defaultSettings.tagsbar.isShow)
|
||||
|
||||
const tags = useTags()
|
||||
const contextMenu = useContextMenu(tags.tagList)
|
||||
@ -156,7 +160,7 @@ export default defineComponent({
|
||||
margin-left: 8px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: 2px;
|
||||
vertical-align: -2px;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-07-23 17:22:14
|
||||
* @LastEditTime: 2022-09-27 17:38:48
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -50,37 +50,68 @@
|
||||
:class="{ no_link: index === breadcrumbs.length - 1 }"
|
||||
:to="index < breadcrumbs.length - 1 ? item.path : ''"
|
||||
>
|
||||
{{ item.meta.title }}
|
||||
{{ $t(item.meta.title) }}
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent, computed, ref, onBeforeMount, watch } from 'vue'
|
||||
import { useApp } from '@/pinia/modules/app'
|
||||
import { useLayoutsettings } from '@/pinia/modules/layoutSettings'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import {
|
||||
defineComponent,
|
||||
computed,
|
||||
ref,
|
||||
onBeforeMount,
|
||||
watch,
|
||||
getCurrentInstance,
|
||||
} from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
|
||||
export default defineComponent({
|
||||
setup(props, { emit }) {
|
||||
const store = useStore()
|
||||
const device = computed(() => store.state.app.device)
|
||||
const { proxy } = getCurrentInstance()
|
||||
const appStore = useApp()
|
||||
const { device } = storeToRefs(appStore)
|
||||
const router = useRouter()
|
||||
const route = router.currentRoute // 这里不使用useRoute获取当前路由,否则下面watch监听路由的时候会有警告
|
||||
const breadcrumbs = ref([])
|
||||
const defaultSettings = computed(() => store.state.layoutSettings)
|
||||
const defaultSettings = useLayoutsettings()
|
||||
const isHorizontalMenu = computed(
|
||||
() => defaultSettings.value.menus.mode === 'horizontal'
|
||||
() => defaultSettings.menus.mode === 'horizontal'
|
||||
)
|
||||
|
||||
const allRoutes = router.getRoutes()
|
||||
|
||||
const recursionArr = (matched, route) => {
|
||||
const parent = route.meta.parent
|
||||
if (parent) {
|
||||
const parentRoute = allRoutes.find(item => item.name === parent)
|
||||
if (parentRoute && parentRoute.meta && parentRoute.meta.title) {
|
||||
matched.splice(
|
||||
matched.findIndex(item => item.name === route.name),
|
||||
0,
|
||||
parentRoute
|
||||
)
|
||||
recursionArr(matched, parentRoute)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getBreadcrumbs = route => {
|
||||
const home = [{ path: '/', meta: { title: '首页' } }]
|
||||
const home = [{ path: '/', meta: { title: proxy.$t('menu.homepage') } }]
|
||||
if (route.name === 'home') {
|
||||
appStore.setMatchedRoutes(home)
|
||||
return home
|
||||
} else {
|
||||
const matched = route.matched.filter(
|
||||
item => !!item.meta && !!item.meta.title
|
||||
)
|
||||
|
||||
return [...home, ...matched]
|
||||
// return [...home, ...matched]
|
||||
recursionArr(matched, route)
|
||||
appStore.setMatchedRoutes(matched)
|
||||
return matched
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,6 +122,7 @@ export default defineComponent({
|
||||
watch(
|
||||
route,
|
||||
newRoute => {
|
||||
route.value.meta.truetitle = proxy.$t(route.value.meta.title)
|
||||
breadcrumbs.value = getBreadcrumbs(newRoute)
|
||||
emit('on-breadcrumbs-change', breadcrumbs.value.length > 1)
|
||||
},
|
||||
@ -118,9 +150,9 @@ export default defineComponent({
|
||||
::v-deep(.is-link) {
|
||||
font-weight: normal;
|
||||
}
|
||||
::v-deep(.el-breadcrumb__item) {
|
||||
float: none;
|
||||
}
|
||||
// ::v-deep(.el-breadcrumb__item) {
|
||||
// float: none;
|
||||
// }
|
||||
.no_link {
|
||||
::v-deep(.el-breadcrumb__inner) {
|
||||
color: #97a8be !important;
|
||||
|
||||
94
src/layout/components/Topbar/ChangeLang.vue
Normal file
94
src/layout/components/Topbar/ChangeLang.vue
Normal file
@ -0,0 +1,94 @@
|
||||
<!--
|
||||
* _oo0oo_
|
||||
* o8888888o
|
||||
* 88" . "88
|
||||
* (| -_- |)
|
||||
* 0\ = /0
|
||||
* ___/`---'\___
|
||||
* .' \\| |// '.
|
||||
* / \\||| : |||// \
|
||||
* / _||||| -:- |||||- \
|
||||
* | | \\\ - /// | |
|
||||
* | \_| ''\---/'' |_/ |
|
||||
* \ .-\__ '-' ___/-. /
|
||||
* ___'. .' /--.--\ `. .'___
|
||||
* ."" '< `.___\_<|>_/___.' >' "".
|
||||
* | | : `- \`.;`\ _ /`;.`/ - ` : | |
|
||||
* \ \ `_. \_ __\ /__ _/ .-` / /
|
||||
* =====`-.____`.___ \_____/___.-`___.-'=====
|
||||
* `=---='
|
||||
*
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* 佛祖保佑 永不宕机 永无BUG
|
||||
*
|
||||
* 佛曰:
|
||||
* 写字楼里写字间,写字间里程序员;
|
||||
* 程序人员写程序,又拿程序换酒钱。
|
||||
* 酒醒只在网上坐,酒醉还来网下眠;
|
||||
* 酒醉酒醒日复日,网上网下年复年。
|
||||
* 但愿老死电脑间,不愿鞠躬老板前;
|
||||
* 奔驰宝马贵者趣,公交自行程序员。
|
||||
* 别人笑我忒疯癫,我笑自己命太贱;
|
||||
* 不见满街漂亮妹,哪个归得程序员?
|
||||
*
|
||||
* @Descripttion:
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2022-09-26 11:53:15
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
-->
|
||||
|
||||
<template>
|
||||
<el-dropdown trigger="hover">
|
||||
<div class="change-lang">
|
||||
<svg-icon name="language" class="icon"></svg-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
@click="changeLang(item.value)"
|
||||
v-for="item in langlist"
|
||||
:key="item.value"
|
||||
>
|
||||
{{ item.name }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
<script setup>
|
||||
import useLang from '@/i18n/useLang'
|
||||
const langlist = [
|
||||
{
|
||||
name: '简体中文',
|
||||
value: 'zh-cn',
|
||||
},
|
||||
{
|
||||
name: 'English',
|
||||
value: 'en',
|
||||
},
|
||||
]
|
||||
const { changeLang } = useLang()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.change-lang {
|
||||
padding: 0 16px;
|
||||
height: 48px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -18,7 +18,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-21 12:47:40
|
||||
* @LastEditTime: 2022-09-27 18:43:44
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -26,25 +26,30 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<i
|
||||
class="fold-btn el-icon-s-fold"
|
||||
<el-icon
|
||||
:size="20"
|
||||
class="fold-btn"
|
||||
:class="{ collapse: collapse }"
|
||||
@click="handleToggleMenu"
|
||||
></i>
|
||||
>
|
||||
<Fold />
|
||||
</el-icon>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useApp } from '@/pinia/modules/app'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const collapse = computed(() => !!store.state.app.sidebar.collapse)
|
||||
const appStore = useApp()
|
||||
const { sidebar } = storeToRefs(appStore)
|
||||
const { setCollapse } = appStore
|
||||
const handleToggleMenu = () => {
|
||||
store.commit('app/setCollapse', +!collapse.value)
|
||||
setCollapse(+!sidebar.value.collapse)
|
||||
}
|
||||
return {
|
||||
collapse,
|
||||
collapse: computed(() => sidebar.value.collapse),
|
||||
handleToggleMenu,
|
||||
}
|
||||
},
|
||||
@ -54,11 +59,7 @@ export default defineComponent({
|
||||
.fold-btn {
|
||||
line-height: 48px;
|
||||
padding: 0 10px;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
&.collapse {
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-23 14:15:50
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-28 09:23:23
|
||||
* @LastEditTime: 2022-09-27 17:55:16
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -52,17 +52,24 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<el-dropdown-item @click="dialogVisible = true">锁定屏幕</el-dropdown-item>
|
||||
<el-dropdown-item @click="dialogVisible = true">
|
||||
{{ $t('topbar.lock-title') }}
|
||||
</el-dropdown-item>
|
||||
<el-dialog
|
||||
title="锁定屏幕"
|
||||
:title="$t('topbar.lock-title')"
|
||||
v-model="dialogVisible"
|
||||
width="640px"
|
||||
custom-class="lock-modal"
|
||||
append-to-body
|
||||
>
|
||||
<Avatar />
|
||||
<el-form :model="lockModel" :rules="lockRules" ref="lockForm">
|
||||
<el-form-item label="锁屏密码" prop="password">
|
||||
<el-form
|
||||
:model="lockModel"
|
||||
:rules="lockRules"
|
||||
ref="lockForm"
|
||||
label-width="90px"
|
||||
>
|
||||
<el-form-item :label="$t('topbar.lock-password')" prop="password">
|
||||
<el-input
|
||||
type="password"
|
||||
v-model.trim="lockModel.password"
|
||||
@ -71,13 +78,8 @@
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
size="small"
|
||||
class="submit-btn"
|
||||
type="primary"
|
||||
@click="submitForm"
|
||||
>
|
||||
锁定屏幕
|
||||
<el-button class="submit-btn" type="primary" @click="submitForm">
|
||||
{{ $t('topbar.lock-title') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@ -85,25 +87,27 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, reactive, ref } from 'vue'
|
||||
import { defineComponent, getCurrentInstance, reactive, ref } from 'vue'
|
||||
import Avatar from '@/components/Avatar/index.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import { useApp } from '@/pinia/modules/app'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
Avatar,
|
||||
},
|
||||
setup() {
|
||||
const { proxy } = getCurrentInstance()
|
||||
const router = useRouter()
|
||||
const store = useStore()
|
||||
const dialogVisible = ref(false)
|
||||
const lockForm = ref(null)
|
||||
const lockModel = reactive({
|
||||
password: '',
|
||||
})
|
||||
const lockRules = reactive({
|
||||
password: [{ required: true, message: '请输入锁屏密码' }],
|
||||
password: [
|
||||
{ required: true, message: proxy.$t('topbar.lock-rules-password') },
|
||||
],
|
||||
})
|
||||
const submitForm = () => {
|
||||
lockForm.value.validate(valid => {
|
||||
@ -112,7 +116,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
// 对密码加密并跟token保存在一起
|
||||
store.dispatch('app/setScreenCode', lockModel.password)
|
||||
useApp().setScreenCode(lockModel.password)
|
||||
|
||||
// 跳转到锁屏页面
|
||||
router.push('/lock?redirect=' + router.currentRoute.value.fullPath)
|
||||
@ -135,8 +139,3 @@ export default defineComponent({
|
||||
max-width: 90%;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-18 15:39:30
|
||||
* @LastEditTime: 2022-09-27 17:56:21
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -45,7 +45,7 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<el-dropdown trigger="click">
|
||||
<el-dropdown trigger="hover">
|
||||
<div class="userinfo">
|
||||
<template v-if="!userinfo">
|
||||
<i class="el-icon-user" />
|
||||
@ -58,27 +58,28 @@
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||
<el-dropdown-item>修改密码</el-dropdown-item>
|
||||
<el-dropdown-item>{{ $t('topbar.center') }}</el-dropdown-item>
|
||||
<el-dropdown-item>{{ $t('topbar.password') }}</el-dropdown-item>
|
||||
<lock-modal />
|
||||
<el-dropdown-item @click="logout">退出登录</el-dropdown-item>
|
||||
<el-dropdown-item @click="logout">
|
||||
{{ $t('topbar.logout') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useUserinfo } from '@/components/Avatar/hooks/useUserinfo'
|
||||
import LockModal from './LockModal.vue'
|
||||
import { useApp } from '@/pinia/modules/app'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
LockModal,
|
||||
},
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const router = useRouter()
|
||||
|
||||
const { userinfo } = useUserinfo()
|
||||
@ -86,7 +87,7 @@ export default defineComponent({
|
||||
// 退出
|
||||
const logout = () => {
|
||||
// 清除token
|
||||
store.dispatch('app/clearToken')
|
||||
useApp().clearToken()
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-21 09:18:32
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-07-23 16:49:39
|
||||
* @LastEditTime: 2022-09-27 18:36:16
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -58,6 +58,7 @@
|
||||
<div class="action">
|
||||
<error-log />
|
||||
<userinfo />
|
||||
<change-lang />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -67,8 +68,11 @@ import Logo from '@/layout/components/Sidebar/Logo.vue'
|
||||
import Hamburger from './Hamburger.vue'
|
||||
import Breadcrumbs from './Breadcrumbs.vue'
|
||||
import Userinfo from './Userinfo.vue'
|
||||
import ErrorLog from '@/components/ErrorLog/index.vue'
|
||||
import { useStore } from 'vuex'
|
||||
import ChangeLang from './ChangeLang.vue'
|
||||
// import ErrorLog from '@/components/ErrorLog/index.vue'
|
||||
import { useLayoutsettings } from '@/pinia/modules/layoutSettings'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useApp } from '@/pinia/modules/app'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -76,16 +80,16 @@ export default defineComponent({
|
||||
Hamburger,
|
||||
Breadcrumbs,
|
||||
Userinfo,
|
||||
ErrorLog,
|
||||
ChangeLang,
|
||||
// ErrorLog,
|
||||
},
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const defaultSettings = computed(() => store.state.layoutSettings)
|
||||
const defaultSettings = useLayoutsettings()
|
||||
|
||||
const device = computed(() => store.state.app.device)
|
||||
const { device } = storeToRefs(useApp())
|
||||
|
||||
const isHorizontalMenu = computed(
|
||||
() => defaultSettings.value.menus.mode === 'horizontal'
|
||||
() => defaultSettings.menus.mode === 'horizontal'
|
||||
)
|
||||
|
||||
const isShowLogo = computed(
|
||||
@ -95,7 +99,7 @@ export default defineComponent({
|
||||
const isShowHamburger = computed(() => !isHorizontalMenu.value)
|
||||
|
||||
const isShowBreadcrumbs = computed(
|
||||
() => defaultSettings.value.breadcrumbs.isShow && !isHorizontalMenu.value
|
||||
() => defaultSettings.breadcrumbs.isShow && !isHorizontalMenu.value
|
||||
)
|
||||
|
||||
return {
|
||||
|
||||
@ -24,22 +24,23 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-29 11:31:50
|
||||
* @LastEditTime: 2022-09-27 19:02:14
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
|
||||
import { onBeforeMount, onBeforeUnmount /*watch*/ } from 'vue'
|
||||
// import { useRouter } from 'vue-router';
|
||||
import { useStore } from 'vuex'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useApp } from '@/pinia/modules/app'
|
||||
import { onBeforeMount, onBeforeUnmount, computed } from 'vue'
|
||||
|
||||
const WIDTH = 768
|
||||
export const useResizeHandler = () => {
|
||||
const store = useStore()
|
||||
// const router = useRouter();
|
||||
// const route = router.currentRoute;
|
||||
const appStore = useApp()
|
||||
const { sidebar } = storeToRefs(appStore)
|
||||
const { setDevice, setCollapse } = appStore
|
||||
const collapse = computed(() => sidebar.value.collapse)
|
||||
|
||||
const isMobile = () => {
|
||||
return window.innerWidth < WIDTH
|
||||
@ -47,11 +48,11 @@ export const useResizeHandler = () => {
|
||||
|
||||
const resizeHandler = () => {
|
||||
if (isMobile()) {
|
||||
store.commit('app/setDevice', 'mobile')
|
||||
store.commit('app/setCollapse', 1)
|
||||
setDevice('mobile')
|
||||
setCollapse(1)
|
||||
} else {
|
||||
store.commit('app/setDevice', 'desktop')
|
||||
store.commit('app/setCollapse', 0)
|
||||
setDevice('desktop')
|
||||
setCollapse(collapse.value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,11 +64,4 @@ export const useResizeHandler = () => {
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', resizeHandler)
|
||||
})
|
||||
|
||||
// // 监听路由的时候不能使用useRoute获取路由,否则会有警告
|
||||
// watch(route, () => {
|
||||
// if (store.state.app.device === 'mobile' && !store.state.app.sidebar.collapse) {
|
||||
// store.commit('app/setCollapse', 1)
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-18 14:58:53
|
||||
* @LastEditTime: 2022-09-27 18:31:47
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -53,6 +53,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, computed } from 'vue'
|
||||
import Sidebar from './components/Sidebar/index.vue'
|
||||
@ -62,7 +63,8 @@ import Tagsbar from './components/Tagsbar/index.vue'
|
||||
import Breadcrumbs from './components/Topbar/Breadcrumbs.vue'
|
||||
import Content from './components/Content/index.vue'
|
||||
import { useResizeHandler } from './hooks/useResizeHandler'
|
||||
import { useStore } from 'vuex'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useLayoutsettings } from '@/pinia/modules/layoutSettings'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'layout',
|
||||
@ -76,16 +78,13 @@ export default defineComponent({
|
||||
},
|
||||
setup() {
|
||||
useResizeHandler()
|
||||
const store = useStore()
|
||||
const defaultSettings = computed(() => store.state.layoutSettings)
|
||||
const isFluid = computed(() => defaultSettings.value.layout.isFluid)
|
||||
const isTopbarFixed = computed(() => defaultSettings.value.topbar.isFixed)
|
||||
const isMenusShow = computed(() => defaultSettings.value.menus.isShow)
|
||||
const isHorizontalMenu = computed(
|
||||
() => defaultSettings.value.menus.mode === 'horizontal'
|
||||
)
|
||||
const defaultSettings = useLayoutsettings()
|
||||
const isFluid = defaultSettings.layout.isFluid
|
||||
const isTopbarFixed = defaultSettings.topbar.isFixed
|
||||
const isMenusShow = defaultSettings.menus.isShow
|
||||
const isHorizontalMenu = defaultSettings.menus.mode === 'horizontal'
|
||||
const isBreadcrumbsShow = computed(
|
||||
() => isHorizontalMenu.value && defaultSettings.value.breadcrumbs.isShow
|
||||
() => isHorizontalMenu && defaultSettings.breadcrumbs.isShow
|
||||
)
|
||||
const paddingFlag = ref(true)
|
||||
const handleBreadcrumbsChange = boo => {
|
||||
|
||||
26
src/main.js
26
src/main.js
@ -27,7 +27,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-29 10:08:00
|
||||
* @LastEditTime: 2022-09-27 19:04:15
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -42,15 +42,22 @@ const app = createApp(App)
|
||||
// 引入element-plus
|
||||
import ElementPlus from 'element-plus'
|
||||
import './assets/style/element-variables.scss'
|
||||
// 引入中文语言包
|
||||
import 'dayjs/locale/zh-cn'
|
||||
import locale from 'element-plus/lib/locale/lang/zh-cn'
|
||||
|
||||
// 国际化
|
||||
import i18n from '@/i18n'
|
||||
|
||||
// 全局注册element-plus/icons-vue
|
||||
import * as ICONS from '@element-plus/icons-vue'
|
||||
Object.entries(ICONS).forEach(([key, component]) => {
|
||||
// app.component(key === 'PieChart' ? 'PieChartIcon' : key, component)
|
||||
app.component(key, component)
|
||||
})
|
||||
|
||||
// 引入路由
|
||||
import router from './router'
|
||||
|
||||
// 引入store
|
||||
import store from './store'
|
||||
// 引入pinia
|
||||
import pinia from './pinia'
|
||||
|
||||
// 权限控制
|
||||
import './permission'
|
||||
@ -73,9 +80,8 @@ import useErrorHandler from './error-log'
|
||||
useErrorHandler(app)
|
||||
|
||||
app
|
||||
.use(ElementPlus, {
|
||||
locale,
|
||||
})
|
||||
.use(store)
|
||||
.use(i18n)
|
||||
.use(ElementPlus)
|
||||
.use(pinia)
|
||||
.use(router)
|
||||
.mount('#app')
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-29 09:47:46
|
||||
* @LastEditTime: 2022-09-27 16:35:06
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -34,90 +34,87 @@
|
||||
*/
|
||||
|
||||
import { ElLoading } from 'element-plus'
|
||||
import router, { asyncRoutes } from '@/router'
|
||||
import store from '@/store'
|
||||
import { TOKEN } from '@/store/modules/app' // TOKEN变量名
|
||||
import router from '@/router'
|
||||
import { TOKEN } from './pinia/modules/app'
|
||||
import { nextTick } from 'vue'
|
||||
import { useApp } from './pinia/modules/app'
|
||||
import { useAccount } from './pinia/modules/account'
|
||||
import { useMenus } from './pinia/modules/menu'
|
||||
|
||||
const getPageTitle = title => {
|
||||
const appTitle = store.state.app.title
|
||||
if (title) {
|
||||
return `${title} - ${appTitle}`
|
||||
}
|
||||
return appTitle
|
||||
const { title: appTitle } = useApp()
|
||||
return title ? `${title} - ${appTitle}` : appTitle
|
||||
}
|
||||
|
||||
// 白名单,里面是路由对象的name
|
||||
// 白名单
|
||||
const WhiteList = ['login', 'lock']
|
||||
let loadingInstance = null
|
||||
|
||||
// vue-router4的路由守卫不再是通过next放行,而是通过return返回true或false或者一个路由地址
|
||||
router.beforeEach(async to => {
|
||||
document.title = getPageTitle(!!to.meta && to.meta.title)
|
||||
// 1. 白名单直接放行
|
||||
if (WhiteList.includes(to.name)) return true
|
||||
|
||||
if (WhiteList.includes(to.name)) {
|
||||
return true
|
||||
}
|
||||
// 2. 未登录强制跳转登录页
|
||||
if (!window.localStorage[TOKEN]) {
|
||||
return {
|
||||
name: 'login',
|
||||
query: {
|
||||
redirect: to.fullPath, // redirect是指登录之后可以跳回到redirect指定的页面
|
||||
},
|
||||
query: { redirect: to.fullPath },
|
||||
replace: true,
|
||||
}
|
||||
} else {
|
||||
// 获取用户角色信息,根据角色判断权限
|
||||
let userinfo = store.state.account.userinfo
|
||||
}
|
||||
|
||||
const { userinfo, getUserinfo } = useAccount()
|
||||
|
||||
// 3. 首次加载:获取用户信息
|
||||
if (!userinfo) {
|
||||
const loadingInstance = ElLoading.service({
|
||||
lock: true,
|
||||
text: '正在加载数据,请稍候~',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
try {
|
||||
// 获取用户信息
|
||||
userinfo = await store.dispatch('account/getUserinfo')
|
||||
loadingInstance.close()
|
||||
} catch (err) {
|
||||
loadingInstance.close()
|
||||
await getUserinfo()
|
||||
} catch {
|
||||
loadingInstance?.close()
|
||||
return false
|
||||
}
|
||||
|
||||
// 删除所有动态路由
|
||||
asyncRoutes.forEach(item => {
|
||||
router.removeRoute(item.name)
|
||||
})
|
||||
return to.fullPath
|
||||
// 获取完用户信息后,如果当前不是 /chat_view,强制跳转
|
||||
// 这里的 to 可能是登录页(带 redirect)或其他页面
|
||||
if (to.path !== '/chat_view') {
|
||||
return '/chat_view'
|
||||
}
|
||||
return true // 已在目标页,直接放行
|
||||
}
|
||||
|
||||
// 生成菜单(如果你的项目有动态菜单,在此处会添加动态路由)
|
||||
if (store.state.menu.menus.length <= 0) {
|
||||
const loadingInstance = ElLoading.service({
|
||||
lock: true,
|
||||
text: '正在加载数据,请稍候~',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
// 4. 生成动态菜单(如有)
|
||||
const { menus, generateMenus } = useMenus()
|
||||
if (menus.length <= 0) {
|
||||
try {
|
||||
await store.dispatch('menu/generateMenus', userinfo)
|
||||
loadingInstance.close()
|
||||
return to.fullPath // 添加动态路由后,必须加这一句触发重定向,否则会404
|
||||
} catch (err) {
|
||||
loadingInstance.close()
|
||||
await generateMenus()
|
||||
return to.fullPath // 生成后重新触发导航
|
||||
} catch {
|
||||
loadingInstance?.close()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否处于锁屏状态
|
||||
// 5. 锁屏检查
|
||||
if (to.name !== 'lock') {
|
||||
const { authorization } = store.state.app
|
||||
if (!!authorization && !!authorization.screenCode) {
|
||||
const { authorization } = useApp()
|
||||
if (authorization?.screenCode) {
|
||||
return {
|
||||
name: 'lock',
|
||||
query: {
|
||||
redirect: to.path,
|
||||
},
|
||||
query: { redirect: to.path },
|
||||
replace: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 已登录且非首次,允许自由访问
|
||||
return true
|
||||
})
|
||||
|
||||
router.afterEach(to => {
|
||||
loadingInstance?.close()
|
||||
if (router.currentRoute.value.name === to.name) {
|
||||
nextTick(() => {
|
||||
document.title = getPageTitle(to.meta?.truetitle)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@ -14,22 +14,15 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-21 12:48:11
|
||||
* @LastEditTime: 2022-09-27 14:52:09
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
|
||||
import { createStore } from 'vuex'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
const modulesFiles = import.meta.globEager('./modules/*.js')
|
||||
const modules = Object.entries(modulesFiles).reduce((modules, [path, mod]) => {
|
||||
const moduleName = path.replace(/^\.\/modules\/(.*)\.\w+$/, '$1')
|
||||
modules[moduleName] = mod.default
|
||||
return modules
|
||||
}, {})
|
||||
const pinia = createPinia()
|
||||
|
||||
export default createStore({
|
||||
modules,
|
||||
})
|
||||
export default pinia
|
||||
@ -3,38 +3,32 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-29 10:10:14
|
||||
* @LastEditTime: 2022-09-27 14:57:06
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
import { defineStore } from 'pinia'
|
||||
import { GetUserinfo } from '@/api/login'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
export const useAccount = defineStore('account', {
|
||||
state: () => ({
|
||||
userinfo: null,
|
||||
permissionList: [],
|
||||
},
|
||||
mutations: {
|
||||
// 保存用户信息
|
||||
setUserinfo(state, data) {
|
||||
state.userinfo = data
|
||||
},
|
||||
// 清除用户信息
|
||||
clearUserinfo(state) {
|
||||
state.userinfo = null
|
||||
},
|
||||
},
|
||||
}),
|
||||
actions: {
|
||||
// 清除用户信息
|
||||
clearUserinfo() {
|
||||
this.userinfo = null
|
||||
},
|
||||
// 获取用户信息
|
||||
async getUserinfo({ commit }) {
|
||||
async getUserinfo() {
|
||||
const { code, data } = await GetUserinfo()
|
||||
if (+code === 200) {
|
||||
commit('setUserinfo', data)
|
||||
this.userinfo = data
|
||||
return Promise.resolve(data)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
@ -3,66 +3,72 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-18 15:40:50
|
||||
* @LastEditTime: 2022-09-27 15:42:35
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
|
||||
import { defineStore } from 'pinia'
|
||||
import { getItem, setItem, removeItem } from '@/utils/storage' //getItem和setItem是封装的操作localStorage的方法
|
||||
import { AesEncryption } from '@/utils/encrypt'
|
||||
import { toRaw } from 'vue'
|
||||
import { useAccount } from './account'
|
||||
import { useTags } from './tags'
|
||||
import { useMenus } from './menu'
|
||||
export const TOKEN = 'VEA-TOKEN'
|
||||
const COLLAPSE = 'VEA-COLLAPSE'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
title: 'Vue3 Element Admin',
|
||||
export const useApp = defineStore('app', {
|
||||
state: () => ({
|
||||
// title: 'Vue3 Element Admin',
|
||||
title: '凌空天行 AI大模型 应用系统',
|
||||
authorization: getItem(TOKEN),
|
||||
sidebar: {
|
||||
collapse: getItem(COLLAPSE),
|
||||
},
|
||||
device: 'desktop',
|
||||
},
|
||||
mutations: {
|
||||
setToken(state, data) {
|
||||
state.authorization = data
|
||||
// 保存到localStorage
|
||||
setItem(TOKEN, data)
|
||||
},
|
||||
clearToken(state) {
|
||||
state.authorization = ''
|
||||
|
||||
removeItem(TOKEN)
|
||||
},
|
||||
setCollapse(state, data) {
|
||||
state.sidebar.collapse = data
|
||||
matchedRoutes: [],
|
||||
}),
|
||||
actions: {
|
||||
setCollapse(data) {
|
||||
this.sidebar.collapse = data
|
||||
// 保存到localStorage
|
||||
setItem(COLLAPSE, data)
|
||||
},
|
||||
clearCollapse(state) {
|
||||
state.sidebar.collapse = ''
|
||||
|
||||
clearCollapse() {
|
||||
this.sidebar.collapse = ''
|
||||
removeItem(COLLAPSE)
|
||||
},
|
||||
setDevice(state, device) {
|
||||
state.device = device
|
||||
setDevice(device) {
|
||||
this.device = device
|
||||
},
|
||||
setMatchedRoutes(payload) {
|
||||
this.matchedRoutes = payload
|
||||
},
|
||||
actions: {
|
||||
clearToken({ commit }) {
|
||||
setToken(data) {
|
||||
this.authorization = data
|
||||
// 保存到localStorage
|
||||
setItem(TOKEN, data)
|
||||
},
|
||||
initToken(data) {
|
||||
this.clearToken()
|
||||
this.setToken(data)
|
||||
},
|
||||
clearToken() {
|
||||
// 清除token
|
||||
commit('clearToken')
|
||||
this.authorization = ''
|
||||
removeItem(TOKEN)
|
||||
// 清除用户信息
|
||||
commit('account/clearUserinfo', '', { root: true })
|
||||
useAccount().clearUserinfo()
|
||||
// 清除标签栏
|
||||
commit('tags/CLEAR_ALL_TAGS', '', { root: true })
|
||||
useTags().clearAllTags()
|
||||
// 清空menus
|
||||
commit('menu/SET_MENUS', [], { root: true })
|
||||
useMenus().setMenus([])
|
||||
},
|
||||
setScreenCode({ commit, state }, password) {
|
||||
const authorization = toRaw(state.authorization)
|
||||
setScreenCode(password) {
|
||||
const authorization = toRaw(this.authorization)
|
||||
|
||||
if (!password) {
|
||||
try {
|
||||
@ -70,7 +76,10 @@ export default {
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
commit('setToken', authorization)
|
||||
|
||||
this.authorization = authorization
|
||||
// 保存到localStorage
|
||||
setItem(TOKEN, authorization)
|
||||
|
||||
return
|
||||
}
|
||||
@ -78,10 +87,13 @@ export default {
|
||||
// 对密码加密
|
||||
const screenCode = new AesEncryption().encryptByAES(password)
|
||||
|
||||
commit('setToken', {
|
||||
const res = {
|
||||
...authorization,
|
||||
screenCode,
|
||||
})
|
||||
}
|
||||
this.authorization = res
|
||||
// 保存到localStorage
|
||||
setItem(TOKEN, res)
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
29
src/pinia/modules/errorLog.js
Normal file
29
src/pinia/modules/errorLog.js
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @version:
|
||||
* @Date: 2021-04-21 09:18:32
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2022-09-27 15:45:36
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useErrorlog = defineStore('errorLog', {
|
||||
state: () => ({
|
||||
logs: [],
|
||||
}),
|
||||
actions: {
|
||||
addErrorLog(log) {
|
||||
// 可以根据需要将错误上报给服务器
|
||||
// ....code.......
|
||||
|
||||
this.logs.push(log)
|
||||
},
|
||||
clearErrorLog() {
|
||||
this.logs.splice(0)
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -27,24 +27,24 @@
|
||||
* @version:
|
||||
* @Date: 2021-07-23 16:10:49
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-18 15:03:26
|
||||
* @LastEditTime: 2022-09-27 15:47:50
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
|
||||
import { defineStore } from 'pinia'
|
||||
import { getItem, setItem } from '@/utils/storage' //getItem和setItem是封装的操作localStorage的方法
|
||||
import defaultSettings from '@/default-settings'
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: getItem('defaultSettings') || defaultSettings,
|
||||
mutations: {
|
||||
SAVE_SETTINGS(state, data) {
|
||||
|
||||
export const useLayoutsettings = defineStore('layoutSettings', {
|
||||
state: () => getItem('defaultSettings') || defaultSettings,
|
||||
actions: {
|
||||
saveSettings(data) {
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
state[key] = value
|
||||
this[key] = value
|
||||
})
|
||||
setItem('defaultSettings', data)
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
151
src/pinia/modules/menu.js
Normal file
151
src/pinia/modules/menu.js
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2022-09-27 16:41:46
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
import { defineStore } from 'pinia'
|
||||
import { fixedRoutes, asyncRoutes } from '@/router'
|
||||
import { GetMenus } from '@/api/menu'
|
||||
import router from '@/router'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export const useMenus = defineStore('menu', () => {
|
||||
const generateUrl = (path, parentPath) => {
|
||||
return path.startsWith('/')
|
||||
? path
|
||||
: path
|
||||
? `${parentPath}/${path}`
|
||||
: parentPath
|
||||
}
|
||||
|
||||
const getFilterRoutes = (targetRoutes, ajaxRoutes) => {
|
||||
const filterRoutes = []
|
||||
|
||||
ajaxRoutes.forEach(item => {
|
||||
const target = targetRoutes.find(target => target.name === item.name)
|
||||
|
||||
if (target) {
|
||||
const { children: targetChildren, ...rest } = target
|
||||
const route = {
|
||||
...rest,
|
||||
}
|
||||
|
||||
if (item.children) {
|
||||
route.children = getFilterRoutes(targetChildren, item.children)
|
||||
}
|
||||
|
||||
filterRoutes.push(route)
|
||||
}
|
||||
})
|
||||
|
||||
return filterRoutes
|
||||
}
|
||||
|
||||
const getFilterMenus = (arr, parentPath = '') => {
|
||||
const menus = []
|
||||
|
||||
arr.forEach(item => {
|
||||
if (!item.hidden) {
|
||||
const menu = {
|
||||
url: generateUrl(item.path, parentPath),
|
||||
title: item.meta.title,
|
||||
icon: item.icon,
|
||||
}
|
||||
if (item.children) {
|
||||
if (item.children.filter(child => !child.hidden).length <= 0) {
|
||||
delete menu.children
|
||||
} else {
|
||||
menu.children = getFilterMenus(item.children, menu.url)
|
||||
}
|
||||
}
|
||||
menus.push(menu)
|
||||
}
|
||||
})
|
||||
|
||||
return menus
|
||||
}
|
||||
|
||||
// 扁平化树形数据
|
||||
const flattenTree = tree => {
|
||||
let result = []
|
||||
tree.forEach(node => {
|
||||
result.push(node) // 将当前节点添加到结果数组中
|
||||
if (node.children && node.children.length) {
|
||||
// 如果有子节点,将子节点添加到结果数组中
|
||||
result = result.concat(flattenTree(node.children))
|
||||
delete node.children
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
const optimizeRoutes = (arr, parentPath = '', parentName = '') => {
|
||||
return arr.map(obj => {
|
||||
const item = {
|
||||
...obj,
|
||||
}
|
||||
item.path = item.path.startsWith('/')
|
||||
? item.path
|
||||
: parentPath
|
||||
? `${parentPath}/${item.path}`
|
||||
: item.path
|
||||
if (parentName) {
|
||||
item.meta.parent = parentName
|
||||
}
|
||||
if (item.children) {
|
||||
item.children = optimizeRoutes(item.children, item.path, item.name)
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
|
||||
const formatRoutes = routes => {
|
||||
return routes.map(route => ({
|
||||
...route,
|
||||
children: flattenTree(optimizeRoutes(route.children || [])),
|
||||
}))
|
||||
}
|
||||
|
||||
const menus = ref([])
|
||||
const setMenus = data => {
|
||||
menus.value = data
|
||||
}
|
||||
const generateMenus = async () => {
|
||||
// // 方式一:只有固定菜单
|
||||
const menus = getFilterMenus(fixedRoutes)
|
||||
// commit('SET_MENUS', menus)
|
||||
// setMenus(menus)
|
||||
|
||||
// 方式二:有动态菜单
|
||||
// 从后台获取菜单
|
||||
const { code, data } = await GetMenus()
|
||||
|
||||
if (+code === 200) {
|
||||
// 添加路由之前先删除所有动态路由
|
||||
asyncRoutes.forEach(item => {
|
||||
router.removeRoute(item.name)
|
||||
})
|
||||
// 过滤出需要添加的动态路由
|
||||
const filterRoutes = getFilterRoutes(asyncRoutes, data)
|
||||
|
||||
// 生成菜单
|
||||
const menus = getFilterMenus([...fixedRoutes, ...filterRoutes])
|
||||
setMenus(menus)
|
||||
|
||||
// 添加动态路由,由于只做了二级路由,所以需要将三级之后的children提到二级
|
||||
const arr = formatRoutes(filterRoutes)
|
||||
arr.forEach(route => router.addRoute(route))
|
||||
}
|
||||
}
|
||||
return {
|
||||
menus,
|
||||
setMenus,
|
||||
generateMenus,
|
||||
}
|
||||
})
|
||||
113
src/pinia/modules/tags.js
Normal file
113
src/pinia/modules/tags.js
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2022-09-27 16:49:31
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
import { defineStore } from 'pinia'
|
||||
import { getItem, setItem, removeItem } from '@/utils/storage' //getItem和setItem是封装的操作localStorage的方法
|
||||
const TAGLIST = 'VEA-TAGLIST'
|
||||
|
||||
export const useTags = defineStore('tags', {
|
||||
state: () => ({
|
||||
tagList: getItem(TAGLIST) || [],
|
||||
cacheList: [],
|
||||
activePosition: -1,
|
||||
}),
|
||||
actions: {
|
||||
saveActivePosition(index) {
|
||||
this.activePosition = index
|
||||
},
|
||||
addTag({ path, fullPath, name, meta, params, query }) {
|
||||
if (this.tagList.some(v => v.path === path)) return false
|
||||
// 添加tagList
|
||||
const target = Object.assign(
|
||||
{},
|
||||
{ path, fullPath, name, meta, params, query },
|
||||
{
|
||||
title: meta.title || '未命名',
|
||||
fullPath: fullPath || path,
|
||||
}
|
||||
)
|
||||
if (this.activePosition === -1) {
|
||||
if (name === 'home') {
|
||||
this.tagList.unshift(target)
|
||||
} else {
|
||||
this.tagList.push(target)
|
||||
}
|
||||
} else {
|
||||
this.tagList.splice(this.activePosition + 1, 0, target)
|
||||
}
|
||||
// 保存到localStorage
|
||||
setItem(TAGLIST, this.tagList)
|
||||
|
||||
// 添加cacheList
|
||||
if (this.cacheList.includes(name)) return
|
||||
if (!meta.noCache) {
|
||||
this.cacheList.push(name)
|
||||
}
|
||||
},
|
||||
deTagList(tag) {
|
||||
// 删除tagList
|
||||
this.tagList = this.tagList.filter(v => v.path !== tag.path)
|
||||
// 保存到localStorage
|
||||
setItem(TAGLIST, this.tagList)
|
||||
},
|
||||
deCacheList(tag) {
|
||||
// 删除cacheList
|
||||
this.cacheList = this.cacheList.filter(v => v !== tag.name)
|
||||
},
|
||||
delTag(tag) {
|
||||
// 删除tagList
|
||||
this.deTagList(tag)
|
||||
|
||||
// 删除cacheList
|
||||
this.deCacheList(tag)
|
||||
},
|
||||
delOtherTags(tag) {
|
||||
this.tagList = this.tagList.filter(
|
||||
v => !!v.meta.affix || v.path === tag.path
|
||||
)
|
||||
// 保存到localStorage
|
||||
setItem(TAGLIST, this.tagList)
|
||||
|
||||
this.cacheList = this.cacheList.filter(v => v === tag.name)
|
||||
},
|
||||
delSomeTags(tags) {
|
||||
this.tagList = this.tagList.filter(
|
||||
v => !!v.meta.affix || tags.every(tag => tag.path !== v.path)
|
||||
)
|
||||
// 保存到localStorage
|
||||
setItem(TAGLIST, this.tagList)
|
||||
|
||||
this.cacheList = this.cacheList.filter(v =>
|
||||
tags.every(tag => tag.name !== v)
|
||||
)
|
||||
},
|
||||
delAllTags() {
|
||||
this.tagList = this.tagList.filter(v => !!v.meta.affix)
|
||||
// 保存到localStorage
|
||||
removeItem(TAGLIST)
|
||||
this.cacheList = []
|
||||
},
|
||||
updateTagList(tag) {
|
||||
const index = this.tagList.findIndex(v => v.path === tag.path)
|
||||
if (index > -1) {
|
||||
this.tagList[index] = Object.assign({}, this.tagList[index], tag)
|
||||
// 保存到localStorage
|
||||
setItem(TAGLIST, this.tagList)
|
||||
}
|
||||
},
|
||||
clearAllTags() {
|
||||
this.cacheList = []
|
||||
this.tagList = []
|
||||
// 保存到localStorage
|
||||
removeItem(TAGLIST)
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -32,13 +32,20 @@ import error from './modules/error'
|
||||
import login from './modules/login'
|
||||
import lock from './modules/lock'
|
||||
import home from './modules/home'
|
||||
import system from './modules/system'
|
||||
|
||||
import test from './modules/test'
|
||||
|
||||
import lktx_jiegou_chat from './modules/lktx_jiegou_chat'
|
||||
import chat from './modules/chat'
|
||||
import jg_gcb from './modules/jg_gcb'
|
||||
|
||||
/* 菜单栏的路由 */
|
||||
// 固定菜单
|
||||
export const fixedRoutes = [...home]
|
||||
export const fixedRoutes = [...home, ...chat]
|
||||
// export const fixedRoutes = [ ]
|
||||
// 动态菜单
|
||||
export const asyncRoutes = [...test]
|
||||
export const asyncRoutes = [...test, ...system, ...jg_gcb]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
|
||||
25
src/router/modules/chat.js
Normal file
25
src/router/modules/chat.js
Normal file
@ -0,0 +1,25 @@
|
||||
const Layout = () => import('@/layout/chat_layout.vue')
|
||||
|
||||
export default [
|
||||
{
|
||||
path: '/chat',
|
||||
component: Layout,
|
||||
name: 'chat',
|
||||
meta: {
|
||||
title: 'chat',
|
||||
},
|
||||
icon: 'Setting',
|
||||
children: [
|
||||
{
|
||||
path: '/chat_view',
|
||||
name: 'chat_view',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: 'chat界面',
|
||||
affix: false,
|
||||
},
|
||||
icon: 'User',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
@ -3,16 +3,17 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-18 16:23:58
|
||||
* @LastEditTime: 2022-09-27 18:14:03
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
import store from '@/store'
|
||||
|
||||
import { useAccount } from '@/pinia/modules/account'
|
||||
|
||||
const checkUserinfo = (code, fullPath) => {
|
||||
const userinfo = store.state.account.userinfo
|
||||
const { userinfo } = useAccount()
|
||||
if (userinfo) {
|
||||
return `/error/${code === '404' ? fullPath : code}`
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-07-26 14:37:08
|
||||
* @LastEditTime: 2022-09-24 19:27:21
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -19,16 +19,16 @@ export default [
|
||||
component: Layout,
|
||||
name: 'Dashboard',
|
||||
meta: {
|
||||
title: '工作台',
|
||||
title: 'menu.dashboard',
|
||||
},
|
||||
icon: 'home',
|
||||
icon: 'icon-home',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'home',
|
||||
component: Home,
|
||||
meta: {
|
||||
title: '首页',
|
||||
title: 'menu.homepage',
|
||||
affix: true,
|
||||
},
|
||||
},
|
||||
|
||||
50
src/router/modules/jg_gcb.js
Normal file
50
src/router/modules/jg_gcb.js
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2022-09-24 19:27:21
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
// home.js
|
||||
const Layout = () => import('@/layout/index.vue')
|
||||
const jg_knowledge_files_add = () =>
|
||||
import('@/views/jg_gcb/jg_knowledge_files_add.vue')
|
||||
const jg_model_file_add = () => import('@/views/jg_gcb/jg_model_file_add.vue')
|
||||
|
||||
export default [
|
||||
{
|
||||
path: '/jg_gcb',
|
||||
component: Layout,
|
||||
name: 'jg_gcb',
|
||||
meta: {
|
||||
title: '结构工程部-工具集合',
|
||||
},
|
||||
icon: 'Setting',
|
||||
children: [
|
||||
{
|
||||
path: '/jg_knowledge_files_add',
|
||||
name: 'jg_knowledge_files_add',
|
||||
component: jg_knowledge_files_add,
|
||||
meta: {
|
||||
title: 'JG-知识库文件新增管理',
|
||||
affix: false,
|
||||
},
|
||||
icon: 'User',
|
||||
},
|
||||
{
|
||||
path: '/jg_model_file_add',
|
||||
name: 'jg_model_file_add',
|
||||
component: jg_model_file_add,
|
||||
meta: {
|
||||
title: 'JG-模型文件新增',
|
||||
affix: false,
|
||||
},
|
||||
icon: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
48
src/router/modules/lktx_jiegou_chat.js
Normal file
48
src/router/modules/lktx_jiegou_chat.js
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2022-09-24 19:27:21
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
// home.js
|
||||
const Layout = () => import('@/layout/chat_layout.vue')
|
||||
const first = () => import('@/views/lktx_jiegou/first.vue')
|
||||
const second = () => import('@/views/lktx_jiegou/second.vue')
|
||||
export default [
|
||||
{
|
||||
path: '/lktx_jiegou',
|
||||
component: Layout,
|
||||
name: 'lktx_jiegou',
|
||||
meta: {
|
||||
title: '结构组工具集合',
|
||||
},
|
||||
icon: 'Setting',
|
||||
children: [
|
||||
{
|
||||
path: '/first',
|
||||
name: 'first',
|
||||
component: first,
|
||||
meta: {
|
||||
title: '结构组-知识库',
|
||||
affix: false,
|
||||
},
|
||||
icon: 'User',
|
||||
},
|
||||
{
|
||||
path: '/second',
|
||||
name: 'second',
|
||||
component: second,
|
||||
meta: {
|
||||
title: '图片检索',
|
||||
affix: false,
|
||||
},
|
||||
icon: 'Service',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
59
src/router/modules/system.js
Normal file
59
src/router/modules/system.js
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2022-09-24 19:27:21
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
// home.js
|
||||
const Layout = () => import('@/layout/index.vue')
|
||||
const sysUser = () => import('@/views/system/sysUser.vue')
|
||||
const sysRole = () => import('@/views/system/sysRole.vue')
|
||||
const sysMenu = () => import('@/views/system/sysMenu.vue')
|
||||
export default [
|
||||
{
|
||||
path: '/system',
|
||||
component: Layout,
|
||||
name: 'system',
|
||||
meta: {
|
||||
title: '系统管理',
|
||||
},
|
||||
icon: 'Setting',
|
||||
children: [
|
||||
{
|
||||
path: '/sysUser',
|
||||
name: 'sysUser',
|
||||
component: sysUser,
|
||||
meta: {
|
||||
title: '用户管理',
|
||||
affix: false,
|
||||
},
|
||||
icon: 'User',
|
||||
},
|
||||
{
|
||||
path: '/sysRole',
|
||||
name: 'sysRole',
|
||||
component: sysRole,
|
||||
meta: {
|
||||
title: '角色管理',
|
||||
affix: false,
|
||||
},
|
||||
icon: 'Service',
|
||||
},
|
||||
{
|
||||
path: '/sysMenu',
|
||||
name: 'sysMenu',
|
||||
component: sysMenu,
|
||||
meta: {
|
||||
title: '菜单管理',
|
||||
affix: false,
|
||||
},
|
||||
icon: 'Document',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
@ -3,7 +3,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-21 09:18:32
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-07-26 16:30:58
|
||||
* @LastEditTime: 2022-09-27 18:51:35
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -28,24 +28,24 @@ export default [
|
||||
component: Layout,
|
||||
name: 'test',
|
||||
meta: {
|
||||
title: '测试页面',
|
||||
title: 'menu.test',
|
||||
},
|
||||
icon: 'el-icon-location',
|
||||
icon: 'Location',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'testList',
|
||||
component: List,
|
||||
meta: {
|
||||
title: '列表',
|
||||
},
|
||||
title: 'menu.testList',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'add',
|
||||
name: 'testAdd',
|
||||
component: Add,
|
||||
meta: {
|
||||
title: '添加',
|
||||
title: 'menu.testAdd',
|
||||
},
|
||||
hidden: true, // 不在菜单中显示
|
||||
},
|
||||
@ -54,33 +54,36 @@ export default [
|
||||
name: 'testEdit',
|
||||
component: Edit,
|
||||
meta: {
|
||||
title: '编辑',
|
||||
title: 'menu.testEdit',
|
||||
},
|
||||
hidden: true, // 不在菜单中显示
|
||||
},
|
||||
{
|
||||
path: 'auth',
|
||||
name: 'testAuth',
|
||||
component: Auth,
|
||||
meta: {
|
||||
title: '权限测试',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'noauth',
|
||||
name: 'testNoAuth',
|
||||
component: NoAuth,
|
||||
meta: {
|
||||
title: '权限页面',
|
||||
},
|
||||
hidden: true,
|
||||
],
|
||||
},
|
||||
|
||||
// {
|
||||
// path: 'auth',
|
||||
// name: 'testAuth',
|
||||
// component: Auth,
|
||||
// meta: {
|
||||
// title: 'menu.testAuth',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: 'noauth',
|
||||
// name: 'testNoAuth',
|
||||
// component: NoAuth,
|
||||
// meta: {
|
||||
// title: 'menu.testNoAuth',
|
||||
// },
|
||||
// hidden: true,
|
||||
// },
|
||||
{
|
||||
path: 'cache',
|
||||
name: 'test-cache',
|
||||
component: Iscache,
|
||||
meta: {
|
||||
title: '该页面可缓存',
|
||||
title: 'menu.test-cache',
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -88,7 +91,7 @@ export default [
|
||||
name: 'test-no-cache',
|
||||
component: Nocache,
|
||||
meta: {
|
||||
title: '该页面不缓存',
|
||||
title: 'menu.test-no-cache',
|
||||
noCache: true, // 不缓存页面
|
||||
},
|
||||
},
|
||||
@ -98,7 +101,7 @@ export default [
|
||||
component: Nest,
|
||||
redirect: '/test/nest/page1',
|
||||
meta: {
|
||||
title: '二级菜单',
|
||||
title: 'menu.nest',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@ -106,7 +109,7 @@ export default [
|
||||
name: 'nestPage1',
|
||||
component: NestPage1,
|
||||
meta: {
|
||||
title: 'page1',
|
||||
title: 'menu.nestPage1',
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -114,7 +117,7 @@ export default [
|
||||
name: 'nestPage2',
|
||||
component: NestPage2,
|
||||
meta: {
|
||||
title: 'page2',
|
||||
title: 'menu.nestPage2',
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -124,7 +127,7 @@ export default [
|
||||
name: 'test-error-log',
|
||||
component: ErrorLog,
|
||||
meta: {
|
||||
title: '测试错误日志',
|
||||
title: 'menu.test-error-log',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @version:
|
||||
* @Date: 2021-04-21 09:18:32
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-21 09:34:13
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
const state = {
|
||||
logs: [],
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
ADD_ERROR_LOG: (state, log) => {
|
||||
state.logs.push(log)
|
||||
},
|
||||
CLEAR_ERROR_LOG: state => {
|
||||
state.logs.splice(0)
|
||||
},
|
||||
}
|
||||
|
||||
const actions = {
|
||||
addErrorLog({ commit }, log) {
|
||||
// 可以根据需要将错误上报给服务器
|
||||
// ....code.......
|
||||
|
||||
// 触发mutations
|
||||
commit('ADD_ERROR_LOG', log)
|
||||
},
|
||||
clearErrorLog({ commit }) {
|
||||
commit('CLEAR_ERROR_LOG')
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
mutations,
|
||||
actions,
|
||||
}
|
||||
@ -1,109 +0,0 @@
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-07-26 18:22:01
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
import { fixedRoutes, asyncRoutes } from '@/router'
|
||||
import { GetMenus } from '@/api/menu'
|
||||
import router from '@/router'
|
||||
|
||||
// const hasPermission = (role, route) => {
|
||||
// if (!!route.meta && !!route.meta.roles && !route.meta.roles.includes(role)) {
|
||||
// return false
|
||||
// }
|
||||
// return true
|
||||
// }
|
||||
|
||||
const generateUrl = (path, parentPath) => {
|
||||
return path.startsWith('/')
|
||||
? path
|
||||
: path
|
||||
? `${parentPath}/${path}`
|
||||
: parentPath
|
||||
}
|
||||
|
||||
const getFilterRoutes = (targetRoutes, ajaxRoutes) => {
|
||||
const filterRoutes = []
|
||||
|
||||
ajaxRoutes.forEach(item => {
|
||||
const target = targetRoutes.find(target => target.name === item.name)
|
||||
|
||||
if (target) {
|
||||
const { children: targetChildren, ...rest } = target
|
||||
const route = {
|
||||
...rest,
|
||||
}
|
||||
|
||||
if (item.children) {
|
||||
route.children = getFilterRoutes(targetChildren, item.children)
|
||||
}
|
||||
|
||||
filterRoutes.push(route)
|
||||
}
|
||||
})
|
||||
|
||||
return filterRoutes
|
||||
}
|
||||
|
||||
const getFilterMenus = (arr, parentPath = '') => {
|
||||
const menus = []
|
||||
|
||||
arr.forEach(item => {
|
||||
if (!item.hidden) {
|
||||
const menu = {
|
||||
url: generateUrl(item.path, parentPath),
|
||||
title: item.meta.title,
|
||||
icon: item.icon,
|
||||
}
|
||||
if (item.children) {
|
||||
if (item.children.filter(child => !child.hidden).length <= 1) {
|
||||
menu.url = generateUrl(item.children[0].path, menu.url)
|
||||
} else {
|
||||
menu.children = getFilterMenus(item.children, menu.url)
|
||||
}
|
||||
}
|
||||
menus.push(menu)
|
||||
}
|
||||
})
|
||||
|
||||
return menus
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
menus: [],
|
||||
},
|
||||
mutations: {
|
||||
SET_MENUS(state, data) {
|
||||
state.menus = data
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async generateMenus({ commit }, userinfo) {
|
||||
// // 方式一:只有固定菜单
|
||||
// const menus = getFilterMenus(fixedRoutes)
|
||||
// commit('SET_MENUS', menus)
|
||||
|
||||
// 方式二:有动态菜单
|
||||
// 从后台获取菜单
|
||||
const { code, data } = await GetMenus({ role: userinfo.role })
|
||||
|
||||
if (+code === 200) {
|
||||
// 过滤出需要添加的动态路由
|
||||
const filterRoutes = getFilterRoutes(asyncRoutes, data)
|
||||
filterRoutes.forEach(route => router.addRoute(route))
|
||||
|
||||
// 生成菜单
|
||||
const menus = getFilterMenus([...fixedRoutes, ...filterRoutes])
|
||||
commit('SET_MENUS', menus)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -1,150 +0,0 @@
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-18 15:36:04
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
import { getItem, setItem, removeItem } from '@/utils/storage' //getItem和setItem是封装的操作localStorage的方法
|
||||
|
||||
const TAGLIST = 'VEA-TAGLIST'
|
||||
|
||||
const state = {
|
||||
tagList: getItem(TAGLIST) || [],
|
||||
cacheList: [],
|
||||
activePosition: -1,
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
ADD_TAG_LIST: (state, { path, fullPath, name, meta, params, query }) => {
|
||||
if (state.tagList.some(v => v.path === path)) return false
|
||||
|
||||
const target = Object.assign(
|
||||
{},
|
||||
{ path, fullPath, name, meta, params, query },
|
||||
{
|
||||
title: meta.title || '未命名',
|
||||
fullPath: fullPath || path,
|
||||
}
|
||||
)
|
||||
if (state.activePosition === -1) {
|
||||
if (name === 'home') {
|
||||
state.tagList.unshift(target)
|
||||
} else {
|
||||
state.tagList.push(target)
|
||||
}
|
||||
} else {
|
||||
state.tagList.splice(state.activePosition + 1, 0, target)
|
||||
}
|
||||
|
||||
// 保存到localStorage
|
||||
setItem(TAGLIST, state.tagList)
|
||||
},
|
||||
ADD_CACHE_LIST: (state, tag) => {
|
||||
if (state.cacheList.includes(tag.name)) return
|
||||
if (!tag.meta.noCache) {
|
||||
state.cacheList.push(tag.name)
|
||||
}
|
||||
},
|
||||
|
||||
DEL_TAG_LIST: (state, tag) => {
|
||||
state.tagList = state.tagList.filter(v => v.path !== tag.path)
|
||||
// 保存到localStorage
|
||||
setItem(TAGLIST, state.tagList)
|
||||
},
|
||||
DEL_CACHE_LIST: (state, tag) => {
|
||||
state.cacheList = state.cacheList.filter(v => v !== tag.name)
|
||||
},
|
||||
|
||||
DEL_OTHER_TAG_LIST: (state, tag) => {
|
||||
state.tagList = state.tagList.filter(
|
||||
v => !!v.meta.affix || v.path === tag.path
|
||||
)
|
||||
// 保存到localStorage
|
||||
setItem(TAGLIST, state.tagList)
|
||||
},
|
||||
DEL_OTHER_CACHE_LIST: (state, tag) => {
|
||||
state.cacheList = state.cacheList.filter(v => v === tag.name)
|
||||
},
|
||||
|
||||
DEL_SOME_TAG_LIST: (state, tags) => {
|
||||
state.tagList = state.tagList.filter(
|
||||
v => !!v.meta.affix || tags.every(tag => tag.path !== v.path)
|
||||
)
|
||||
// 保存到localStorage
|
||||
setItem(TAGLIST, state.tagList)
|
||||
},
|
||||
|
||||
DEL_SOME_CACHE_LIST: (state, tags) => {
|
||||
state.cacheList = state.cacheList.filter(v =>
|
||||
tags.every(tag => tag.name !== v)
|
||||
)
|
||||
},
|
||||
|
||||
DEL_ALL_TAG_LIST: state => {
|
||||
state.tagList = state.tagList.filter(v => !!v.meta.affix)
|
||||
// 保存到localStorage
|
||||
removeItem(TAGLIST)
|
||||
},
|
||||
DEL_ALL_CACHE_LIST: state => {
|
||||
state.cacheList = []
|
||||
},
|
||||
CLEAR_ALL_TAGS: state => {
|
||||
state.cacheList = []
|
||||
state.tagList = []
|
||||
// 保存到localStorage
|
||||
removeItem(TAGLIST)
|
||||
},
|
||||
UPDATE_TAG_LIST: (state, tag) => {
|
||||
const index = state.tagList.findIndex(v => v.path === tag.path)
|
||||
if (index > -1) {
|
||||
state.tagList[index] = Object.assign({}, state.tagList[index], tag)
|
||||
// 保存到localStorage
|
||||
setItem(TAGLIST, state.tagList)
|
||||
}
|
||||
},
|
||||
|
||||
SAVE_ACTIVE_POSITION: (state, index) => {
|
||||
state.activePosition = index
|
||||
},
|
||||
}
|
||||
|
||||
const actions = {
|
||||
saveActivePosition({ commit }, index) {
|
||||
commit('SAVE_ACTIVE_POSITION', index)
|
||||
},
|
||||
addTag({ commit }, tag) {
|
||||
commit('ADD_TAG_LIST', tag)
|
||||
commit('ADD_CACHE_LIST', tag)
|
||||
},
|
||||
delTag({ commit }, tag) {
|
||||
commit('DEL_TAG_LIST', tag)
|
||||
commit('DEL_CACHE_LIST', tag)
|
||||
},
|
||||
delOtherTags({ commit }, tag) {
|
||||
commit('DEL_OTHER_TAG_LIST', tag)
|
||||
commit('DEL_OTHER_CACHE_LIST', tag)
|
||||
},
|
||||
delSomeTags({ commit }, tags) {
|
||||
commit('DEL_SOME_TAG_LIST', tags)
|
||||
commit('DEL_SOME_CACHE_LIST', tags)
|
||||
},
|
||||
delAllTags({ commit }) {
|
||||
commit('DEL_ALL_TAG_LIST')
|
||||
commit('DEL_ALL_CACHE_LIST')
|
||||
},
|
||||
updateTagList({ commit }, { path, fullPath, name, meta, params, query }) {
|
||||
commit('UPDATE_TAG_LIST', { path, fullPath, name, meta, params, query })
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
mutations,
|
||||
actions,
|
||||
}
|
||||
@ -22,7 +22,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-09-18 15:44:39
|
||||
* @LastEditTime: 2022-09-27 18:17:20
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -31,26 +31,55 @@
|
||||
|
||||
import axios from 'axios'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import store from '@/store'
|
||||
import router from '@/router'
|
||||
import { useApp } from '@/pinia/modules/app'
|
||||
|
||||
const service = axios.create({
|
||||
baseURL: '/',
|
||||
baseURL: '',
|
||||
// baseURL: 'http://localhost:8501',
|
||||
timeout: 10000,
|
||||
withCredentials: true,
|
||||
})
|
||||
|
||||
// 拦截请求
|
||||
// 拦截请求(合并版)
|
||||
service.interceptors.request.use(
|
||||
config => {
|
||||
const { authorization } = store.state.app
|
||||
// ✅ 1. 添加 Token(你的原逻辑)
|
||||
const { authorization } = useApp()
|
||||
if (authorization) {
|
||||
config.headers.Authorization = `Bearer ${authorization.token}`
|
||||
config.headers.token = `${authorization.token}`
|
||||
}
|
||||
|
||||
// ✅ 2. 生产环境替换代理路径(跨域方案核心)
|
||||
if (!import.meta.env.DEV) {
|
||||
const { url } = config
|
||||
|
||||
if (url.startsWith('/heat-api')) {
|
||||
config.url = url.replace(
|
||||
'/heat-api',
|
||||
import.meta.env.VITE_HEAT_API_BASE_URL
|
||||
)
|
||||
} else if (url.startsWith('/strength-api')) {
|
||||
config.url = url.replace(
|
||||
'/strength-api',
|
||||
import.meta.env.VITE_STRENGTH_API_BASE_URL
|
||||
)
|
||||
} else if (url.startsWith('/api')) {
|
||||
config.url = url.replace('/api', import.meta.env.VITE_API_BASE_URL)
|
||||
} else if (url.startsWith('/admin')) {
|
||||
config.url = url.replace('/admin', import.meta.env.VITE_ADMIN_BASE_URL)
|
||||
} else if (url.startsWith('/system')) {
|
||||
config.url = url.replace('/system', import.meta.env.VITE_API_BASE_URL)
|
||||
}
|
||||
|
||||
console.log(`[PROD] ${config.method?.toUpperCase()} ${config.url}`)
|
||||
} else {
|
||||
console.log(`[DEV] ${config.method?.toUpperCase()} ${config.url}`)
|
||||
}
|
||||
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
// console.log(error);
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
@ -67,7 +96,7 @@ service.interceptors.response.use(
|
||||
// 响应拦截器中的 error 就是那个响应的错误对象
|
||||
if (error.response && error.response.status === 401) {
|
||||
// 校验是否有 refresh_token
|
||||
const { authorization } = store.state.app
|
||||
const { authorization, clearToken, setToken } = useApp()
|
||||
if (!authorization || !authorization.refresh_token) {
|
||||
if (router.currentRoute.value.name === 'login') {
|
||||
return Promise.reject(error)
|
||||
@ -75,7 +104,7 @@ service.interceptors.response.use(
|
||||
const redirect = encodeURIComponent(window.location.href)
|
||||
router.push(`/login?redirect=${redirect}`)
|
||||
// 清除token
|
||||
store.dispatch('app/clearToken')
|
||||
clearToken()
|
||||
setTimeout(() => {
|
||||
ElMessage.closeAll()
|
||||
try {
|
||||
@ -99,7 +128,7 @@ service.interceptors.response.use(
|
||||
})
|
||||
// 如果获取成功,则把新的 token 更新到容器中
|
||||
// console.log('刷新 token 成功', res)
|
||||
store.commit('app/setToken', {
|
||||
setToken({
|
||||
token: res.data.data.token, // 最新获取的可用 token
|
||||
refresh_token: authorization.refresh_token, // 还是原来的 refresh_token
|
||||
})
|
||||
@ -113,7 +142,7 @@ service.interceptors.response.use(
|
||||
const redirect = encodeURIComponent(window.location.href)
|
||||
router.push(`/login?redirect=${redirect}`)
|
||||
// 清除token
|
||||
store.dispatch('app/clearToken')
|
||||
clearToken()
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
6
src/views/chat/chat_view.vue
Normal file
6
src/views/chat/chat_view.vue
Normal file
@ -0,0 +1,6 @@
|
||||
<!-- first.vue -->
|
||||
<template>
|
||||
<el-radio-button label="chat_view" size="default">
|
||||
知识库
|
||||
</el-radio-button>
|
||||
</template>
|
||||
@ -3,7 +3,7 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-21 09:33:01
|
||||
* @LastEditTime: 2022-09-24 21:52:50
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
@ -14,19 +14,19 @@
|
||||
<template v-if="error === '403'">
|
||||
<span class="code-403">403</span>
|
||||
<svg-icon name="error-icons-403" class="error-img" />
|
||||
<h2 class="title">您无权访问此页面</h2>
|
||||
<h2 class="title">{{ $t('error.noauth') }}</h2>
|
||||
</template>
|
||||
<template v-else-if="error === '500'">
|
||||
<svg-icon name="error-icons-500" class="error-img" />
|
||||
<h2 class="title">服务器出错了</h2>
|
||||
<h2 class="title">{{ $t('error.servererror') }}</h2>
|
||||
</template>
|
||||
<template v-else-if="error === '404'">
|
||||
<svg-icon name="error-icons-404" class="error-img" />
|
||||
<h2 class="title">您访问的页面不存在</h2>
|
||||
<h2 class="title">{{ $t('error.notfound') }}</h2>
|
||||
</template>
|
||||
|
||||
<router-link to="/">
|
||||
<el-button type="primary">返回首页</el-button>
|
||||
<el-button type="primary">{{ $t('error.backhome') }}</el-button>
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -3,27 +3,12 @@
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-23 15:16:12
|
||||
* @LastEditTime: 2022-09-24 18:18:43
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
-->
|
||||
<template>
|
||||
<div class="home">home</div>
|
||||
home
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'home',
|
||||
setup() {},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.home {
|
||||
color: $mainColor;
|
||||
}
|
||||
</style>
|
||||
|
||||
456
src/views/jg_gcb/jg_knowledge_files_add.vue
Normal file
456
src/views/jg_gcb/jg_knowledge_files_add.vue
Normal file
@ -0,0 +1,456 @@
|
||||
<template>
|
||||
<div class="knowledge-ingest">
|
||||
<!-- 第一步:上传区域 -->
|
||||
<div class="section upload-section">
|
||||
<h3>步骤1: 上传文件夹到MinIO</h3>
|
||||
|
||||
<!-- 文件夹选择 -->
|
||||
<input
|
||||
type="file"
|
||||
ref="folderInput"
|
||||
style="display: none"
|
||||
webkitdirectory
|
||||
multiple
|
||||
@change="handleFolderSelect"
|
||||
:disabled="processCompleted"
|
||||
/>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="$refs.folderInput.click()"
|
||||
:disabled="processCompleted"
|
||||
>
|
||||
选择文件夹
|
||||
</el-button>
|
||||
|
||||
<!-- 文件列表预览 -->
|
||||
<div v-if="selectedFiles.length > 0" class="file-list">
|
||||
<h4>待上传文件(共 {{ selectedFiles.length }} 个)</h4>
|
||||
<el-tree
|
||||
:data="fileTree"
|
||||
:props="{ label: 'name', children: 'children' }"
|
||||
default-expand-all
|
||||
class="tree-view"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 上传按钮 -->
|
||||
<el-button
|
||||
type="success"
|
||||
:loading="uploading"
|
||||
@click="uploadFolder"
|
||||
:disabled="selectedFiles.length === 0 || uploading || processCompleted"
|
||||
class="action-btn"
|
||||
>
|
||||
{{ uploading ? '上传中...' : '开始上传' }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 第二步:RAG入库区域 -->
|
||||
<div v-if="showIngestSection" class="section ingest-section">
|
||||
<el-divider />
|
||||
<h3>步骤2: RAG知识库入库</h3>
|
||||
|
||||
<el-alert title="上传成功!" type="success" :closable="false" show-icon>
|
||||
<div class="upload-info">
|
||||
<span>
|
||||
文件夹ID:
|
||||
<strong>{{ uploadResult.folderId }}</strong>
|
||||
</span>
|
||||
<span>
|
||||
文件数量:
|
||||
<strong>{{ uploadResult.fileCount }}</strong>
|
||||
</span>
|
||||
<span>
|
||||
存储桶:
|
||||
<strong>{{ uploadResult.bucketName }}</strong>
|
||||
</span>
|
||||
</div>
|
||||
</el-alert>
|
||||
|
||||
<el-button
|
||||
type="warning"
|
||||
:loading="ingesting"
|
||||
@click="startRagIngest"
|
||||
:disabled="ingesting || ingestCompleted || processCompleted"
|
||||
class="action-btn"
|
||||
>
|
||||
{{
|
||||
ingesting ? '入库中...' : ingestCompleted ? '已完成' : '开始RAG入库'
|
||||
}}
|
||||
</el-button>
|
||||
|
||||
<!-- ✅ 进度条显示条件:进行中 或 已完成 -->
|
||||
<div v-if="ingesting || ingestCompleted" class="progress-area">
|
||||
<h4>RAG入库进度</h4>
|
||||
<el-progress
|
||||
:percentage="ingestProgress"
|
||||
:status="ingestStatus"
|
||||
:stroke-width="20"
|
||||
:text-inside="true"
|
||||
/>
|
||||
<p v-if="currentFile" class="current-file">
|
||||
正在处理: {{ currentFile }}
|
||||
</p>
|
||||
|
||||
<!-- ✅ 完成提示 -->
|
||||
<div v-if="ingestCompleted" class="completion-status">
|
||||
<el-icon class="success-icon" :size="48" color="#67C23A">
|
||||
<CircleCheckFilled />
|
||||
</el-icon>
|
||||
<p class="complete-text">✅ RAG入库已完成!</p>
|
||||
<p class="complete-detail">
|
||||
共处理 {{ uploadResult.fileCount }} 个文件
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ✅ 修改后的刷新按钮区域 -->
|
||||
<div v-if="processCompleted" class="refresh-section">
|
||||
<el-divider />
|
||||
<el-button
|
||||
type="primary"
|
||||
size="large"
|
||||
@click="resetAllData"
|
||||
class="refresh-btn"
|
||||
>
|
||||
<el-icon><RefreshRight /></el-icon>
|
||||
开始新流程
|
||||
</el-button>
|
||||
<p class="refresh-hint">点击按钮将重置所有数据,开始新的上传入库流程</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ElMessage, ElIcon } from 'element-plus'
|
||||
import { CircleCheckFilled, RefreshRight } from '@element-plus/icons-vue'
|
||||
import {
|
||||
submitRagIngestTask,
|
||||
getRagIngestProgress,
|
||||
uploadFolder,
|
||||
} from '@/api/rag'
|
||||
import { getUserRoles } from '@/api/sysFRole'
|
||||
import { GetUserinfo } from '@/api/login'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ElIcon,
|
||||
CircleCheckFilled,
|
||||
RefreshRight,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
selectedFiles: [],
|
||||
fileTree: [],
|
||||
uploading: false,
|
||||
uploadResult: null,
|
||||
ingesting: false,
|
||||
ingestProgress: 0,
|
||||
ingestStatus: '',
|
||||
currentFile: '',
|
||||
taskId: null,
|
||||
ingestCompleted: false,
|
||||
processCompleted: false, // ✅ 新增:标记整个流程是否结束
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
showIngestSection() {
|
||||
// ✅ 流程结束后仍然显示,只是按钮禁用
|
||||
return Boolean(this.uploadResult?.folderId) && !this.uploading
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleFolderSelect(event) {
|
||||
// ✅ 流程结束后禁用选择
|
||||
if (this.processCompleted) return
|
||||
|
||||
const files = Array.from(event.target.files)
|
||||
if (files.length === 0) return
|
||||
|
||||
// ✅ 检查文件大小(例如:限制单个文件 100MB)
|
||||
const MAX_FILE_SIZE = 200 * 1024 * 1024 * 1024 // 100MB
|
||||
const oversizedFiles = files.filter(f => f.size > MAX_FILE_SIZE)
|
||||
|
||||
if (oversizedFiles.length > 0) {
|
||||
// 显示错误提示(自动换行)
|
||||
ElMessage.error({
|
||||
message: `以下文件超过 2GB 限制:\n${oversizedFiles
|
||||
.map(f => f.name)
|
||||
.join('\n')}`,
|
||||
duration: 5000, // 显示5秒
|
||||
showClose: true,
|
||||
dangerouslyUseHTMLString: true, // 支持HTML(可选)
|
||||
})
|
||||
|
||||
// 清空文件输入框和已选文件
|
||||
this.$refs.folderInput.value = null
|
||||
this.selectedFiles = []
|
||||
this.fileTree = []
|
||||
return // 中断后续操作
|
||||
}
|
||||
|
||||
this.selectedFiles = files.map(file => ({
|
||||
file: file,
|
||||
relativePath: file.webkitRelativePath,
|
||||
size: file.size,
|
||||
}))
|
||||
this.buildFileTree()
|
||||
},
|
||||
|
||||
buildFileTree() {
|
||||
const tree = { name: '根目录', children: {} }
|
||||
this.selectedFiles.forEach(({ relativePath }) => {
|
||||
const parts = relativePath.split('/').filter(p => p)
|
||||
let current = tree.children
|
||||
parts.forEach(part => {
|
||||
if (!current[part]) {
|
||||
current[part] = { name: part, children: {} }
|
||||
}
|
||||
current = current[part].children
|
||||
})
|
||||
})
|
||||
this.fileTree = this.convertToTreeData(tree.children)
|
||||
},
|
||||
|
||||
convertToTreeData(children) {
|
||||
return Object.keys(children).map(key => {
|
||||
const node = children[key]
|
||||
return {
|
||||
name: node.name,
|
||||
children:
|
||||
Object.keys(node.children).length > 0
|
||||
? this.convertToTreeData(node.children)
|
||||
: undefined,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
async uploadFolder() {
|
||||
// ✅ 流程结束后禁用上传
|
||||
if (this.processCompleted) return
|
||||
|
||||
this.uploading = true
|
||||
this.uploadResult = null
|
||||
|
||||
const formData = new FormData()
|
||||
this.selectedFiles.forEach(({ file, relativePath }) => {
|
||||
formData.append('files', file)
|
||||
formData.append('relativePaths', relativePath)
|
||||
})
|
||||
|
||||
const folderName = this.$refs.folderInput.files[0].webkitRelativePath.split(
|
||||
'/'
|
||||
)[0]
|
||||
formData.append('sender', 'user')
|
||||
formData.append('folderName', folderName)
|
||||
|
||||
try {
|
||||
const res = await uploadFolder(formData)
|
||||
if (res.code === 200 && res.data) {
|
||||
this.uploadResult = res.data
|
||||
ElMessage.success(`上传成功!共 ${res.data.fileCount} 个文件`)
|
||||
} else {
|
||||
throw new Error(res.message || `上传失败 (code: ${res.code})`)
|
||||
}
|
||||
} catch (error) {
|
||||
const msg = error.response?.data?.message || error.message || '上传失败'
|
||||
ElMessage.error(msg)
|
||||
} finally {
|
||||
this.uploading = false
|
||||
}
|
||||
},
|
||||
|
||||
async startRagIngest() {
|
||||
// ✅ 流程结束后禁用入库
|
||||
if (this.processCompleted) return
|
||||
|
||||
if (!this.uploadResult?.folderId) {
|
||||
ElMessage.warning('请先完成文件上传')
|
||||
return
|
||||
}
|
||||
|
||||
this.ingesting = true
|
||||
this.ingestCompleted = false
|
||||
this.ingestProgress = 0
|
||||
this.ingestStatus = ''
|
||||
this.currentFile = ''
|
||||
|
||||
try {
|
||||
const userInfoRes = await GetUserinfo()
|
||||
const userId = userInfoRes.data.id
|
||||
|
||||
const roleRes = await getUserRoles(userId)
|
||||
const userRoles = roleRes.data || []
|
||||
|
||||
const userRoleCode = userRoles.some(r => r?.roleCode === 'admin')
|
||||
? 'admin'
|
||||
: userRoles[0]?.roleCode || 'default'
|
||||
|
||||
const res = await submitRagIngestTask(
|
||||
this.uploadResult.folderId,
|
||||
userRoleCode
|
||||
)
|
||||
|
||||
if (res.code === 200 && res.data) {
|
||||
this.taskId = res.data.taskId
|
||||
ElMessage.success(`入库任务已提交,角色: ${userRoleCode}`)
|
||||
this.pollIngestProgress(this.taskId, userRoleCode)
|
||||
} else {
|
||||
throw new Error(res.message || '提交失败')
|
||||
}
|
||||
} catch (error) {
|
||||
const msg = error.response?.data?.message || error.message || '入库失败'
|
||||
ElMessage.error(msg)
|
||||
this.ingesting = false
|
||||
}
|
||||
},
|
||||
|
||||
pollIngestProgress(taskId, userRoleCode) {
|
||||
let retryCount = 0
|
||||
const maxRetries = 10
|
||||
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
const res = await getRagIngestProgress(taskId, userRoleCode)
|
||||
|
||||
const { code, data } = res
|
||||
if (code === 200 && data) {
|
||||
this.ingestProgress = data.progress || 0
|
||||
this.currentFile = data.currentFile || ''
|
||||
|
||||
switch (data.status) {
|
||||
case 'completed':
|
||||
this.ingestStatus = 'success'
|
||||
clearInterval(interval)
|
||||
this.ingesting = false
|
||||
this.ingestCompleted = true
|
||||
this.processCompleted = true // ✅ 标记整个流程结束
|
||||
ElMessage.success('RAG入库完成!')
|
||||
// ✅ 可以添加一个提示
|
||||
ElMessage.info('请点击"开始新流程"按钮进行下一次操作')
|
||||
break
|
||||
case 'failed':
|
||||
this.ingestStatus = 'exception'
|
||||
clearInterval(interval)
|
||||
this.ingesting = false
|
||||
ElMessage.error(`入库失败: ${data.errorMessage}`)
|
||||
break
|
||||
default:
|
||||
retryCount = 0
|
||||
}
|
||||
} else {
|
||||
throw new Error('查询进度失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('轮询错误:', error)
|
||||
if (retryCount++ >= maxRetries) {
|
||||
clearInterval(interval)
|
||||
this.ingesting = false
|
||||
ElMessage.error('进度查询失败')
|
||||
}
|
||||
}
|
||||
}, 1500)
|
||||
},
|
||||
|
||||
// ✅ 新增:刷新页面方法
|
||||
refreshPage() {
|
||||
// 方式1: 使用 Vue Router (推荐)
|
||||
this.$router.go(0)
|
||||
|
||||
// 方式2: 使用 location.reload()
|
||||
// location.reload()
|
||||
|
||||
// 方式3: 重置所有数据(不刷新页面)
|
||||
// this.resetAllData()
|
||||
},
|
||||
|
||||
// ✅ 修改:重置所有数据而不刷新页面
|
||||
resetAllData() {
|
||||
// 重置所有数据
|
||||
this.selectedFiles = []
|
||||
this.fileTree = []
|
||||
this.uploading = false
|
||||
this.uploadResult = null
|
||||
this.ingesting = false
|
||||
this.ingestProgress = 0
|
||||
this.ingestStatus = ''
|
||||
this.currentFile = ''
|
||||
this.taskId = null
|
||||
this.ingestCompleted = false
|
||||
this.processCompleted = false
|
||||
|
||||
// 清空文件输入框
|
||||
if (this.$refs.folderInput) {
|
||||
this.$refs.folderInput.value = null
|
||||
}
|
||||
|
||||
// 显示成功提示
|
||||
ElMessage.success('页面已重置,可以开始新流程')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
@import 'knowledge-ingest.css';
|
||||
|
||||
/* ✅ 新增:完成状态样式 */
|
||||
.complete-text {
|
||||
margin-top: 10px;
|
||||
font-size: 16px;
|
||||
color: #67c23a;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.complete-detail {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
text-align: center;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.completion-status {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
background: #f0f9ff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #b3e19d;
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* ✅ 新增:刷新按钮区域样式 */
|
||||
.refresh-section {
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
background: #fef0f0;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #fbc4c4;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.refresh-hint {
|
||||
color: #f56c6c;
|
||||
font-size: 12px;
|
||||
margin: 5px 0 0 0;
|
||||
}
|
||||
|
||||
/* 禁用状态按钮 */
|
||||
.action-btn:disabled,
|
||||
input[type='file']:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
</style>
|
||||
96
src/views/jg_gcb/jg_model_file_add.css
Normal file
96
src/views/jg_gcb/jg_model_file_add.css
Normal file
@ -0,0 +1,96 @@
|
||||
.upload-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 30px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.upload-container h2 {
|
||||
text-align: center;
|
||||
color: #303133;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.upload-section {
|
||||
margin-bottom: 40px;
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.upload-section h3 {
|
||||
margin: 0 0 15px 0;
|
||||
color: #409EFF;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.upload-drop-zone {
|
||||
width: 100%;
|
||||
min-height: 150px;
|
||||
border: 2px dashed #dcdfe6;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.upload-drop-zone:hover {
|
||||
border-color: #409EFF;
|
||||
background: #f0f7ff;
|
||||
}
|
||||
|
||||
.upload-drop-zone.is-dragover {
|
||||
border-color: #409EFF;
|
||||
background: #e6f0ff;
|
||||
}
|
||||
|
||||
.upload-drop-zone .el-icon {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.upload-drop-zone p {
|
||||
margin: 5px 0;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.upload-drop-zone .upload-tip {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.upload-progress {
|
||||
margin-top: 15px;
|
||||
padding: 10px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.folder-info, .file-info {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.folder-info p, .file-info p {
|
||||
margin: 8px 0;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
/* 深度选择器用于影响子组件样式 */
|
||||
.upload-section :deep(.el-button) {
|
||||
width: 100%;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.upload-container {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
333
src/views/jg_gcb/jg_model_file_add.vue
Normal file
333
src/views/jg_gcb/jg_model_file_add.vue
Normal file
@ -0,0 +1,333 @@
|
||||
<template>
|
||||
<div class="upload-container">
|
||||
<h2>文件上传管理中心</h2>
|
||||
|
||||
<!-- 文件夹上传区域 -->
|
||||
<div class="upload-section">
|
||||
<h3>📁 文件夹上传(结构+文件)</h3>
|
||||
<div
|
||||
class="upload-drop-zone"
|
||||
:class="{ 'is-dragover': folderDragover }"
|
||||
@click="selectFolder"
|
||||
@drop.prevent="handleFolderDrop"
|
||||
@dragover.prevent="folderDragover = true"
|
||||
@dragleave.prevent="folderDragover = false"
|
||||
>
|
||||
<input
|
||||
ref="folderInput"
|
||||
type="file"
|
||||
webkitdirectory
|
||||
directory
|
||||
multiple
|
||||
style="display: none"
|
||||
@change="handleFolderSelect"
|
||||
/>
|
||||
<div v-if="!selectedFolder">
|
||||
<el-icon :size="48" color="#409EFF"><FolderOpened /></el-icon>
|
||||
<p>点击或拖拽文件夹到此处上传</p>
|
||||
<p class="upload-tip">支持保留文件夹结构,将自动上传到MinIO</p>
|
||||
</div>
|
||||
<div v-else class="folder-info">
|
||||
<el-icon :size="32" color="#67C23A"><SuccessFilled /></el-icon>
|
||||
<p>
|
||||
已选择文件夹:
|
||||
<strong>{{ selectedFolder.name }}</strong>
|
||||
</p>
|
||||
<p>文件数量: {{ selectedFolder.files.length }} 个</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 进度条 -->
|
||||
<div v-if="folderUploading" class="upload-progress">
|
||||
<el-progress
|
||||
:percentage="uploadProgress"
|
||||
:status="uploadProgress === 100 ? 'success' : ''"
|
||||
:stroke-width="15"
|
||||
>
|
||||
<span>{{ uploadStatusText }}</span>
|
||||
</el-progress>
|
||||
</div>
|
||||
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="uploadFolder"
|
||||
:loading="folderUploading"
|
||||
:disabled="!selectedFolder || folderUploading"
|
||||
>
|
||||
{{ folderUploading ? '上传中...' : '确认上传文件夹' }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- Excel文件上传区域 -->
|
||||
<div class="upload-section">
|
||||
<h3>📊 Excel文件上传</h3>
|
||||
<div
|
||||
class="upload-drop-zone"
|
||||
:class="{ 'is-dragover': excelDragover }"
|
||||
@click="selectExcel"
|
||||
@drop.prevent="handleExcelDrop"
|
||||
@dragover.prevent="excelDragover = true"
|
||||
@dragleave.prevent="excelDragover = false"
|
||||
>
|
||||
<input
|
||||
ref="excelInput"
|
||||
type="file"
|
||||
accept=".xlsx,.xls"
|
||||
style="display: none"
|
||||
@change="handleExcelSelect"
|
||||
/>
|
||||
<div v-if="!selectedExcel">
|
||||
<el-icon :size="48" color="#409EFF"><Document /></el-icon>
|
||||
<p>点击或拖拽Excel文件到此处上传</p>
|
||||
<p class="upload-tip">仅支持 .xlsx 格式,数据将导入系统</p>
|
||||
</div>
|
||||
<div v-else class="file-info">
|
||||
<el-icon :size="32" color="#67C23A"><SuccessFilled /></el-icon>
|
||||
<p>
|
||||
已选择文件:
|
||||
<strong>{{ selectedExcel.name }}</strong>
|
||||
</p>
|
||||
<p>文件大小: {{ formatFileSize(selectedExcel.size) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<el-button
|
||||
type="success"
|
||||
@click="uploadExcel"
|
||||
:loading="excelUploading"
|
||||
:disabled="!selectedExcel || excelUploading"
|
||||
>
|
||||
{{ excelUploading ? '上传解析中...' : '确认上传Excel' }}
|
||||
</el-button>
|
||||
|
||||
<!-- 新增:Excel上传结果详情 -->
|
||||
<el-alert
|
||||
v-if="showResult && resultDetails"
|
||||
:title="resultDetails"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
style="margin-top: 15px; font-size: 13px;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 全局上传结果提示 -->
|
||||
<el-alert
|
||||
v-if="showResult"
|
||||
:title="resultMessage"
|
||||
:type="resultType"
|
||||
:closable="false"
|
||||
show-icon
|
||||
style="margin-top: 20px"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { FolderOpened, Document, SuccessFilled } from '@element-plus/icons-vue'
|
||||
import { uploadToMinIOjgmodel, uploadExcelFile } from '@/api/excel'
|
||||
|
||||
// 用户信息
|
||||
const username = ref('admin')
|
||||
|
||||
// 文件夹上传状态
|
||||
const folderInput = ref(null)
|
||||
const folderDragover = ref(false)
|
||||
const selectedFolder = ref(null)
|
||||
const folderUploading = ref(false)
|
||||
const uploadProgress = ref(0)
|
||||
const uploadStatusText = ref('准备上传...')
|
||||
const BATCH_SIZE = 10
|
||||
|
||||
// Excel上传状态
|
||||
const excelInput = ref(null)
|
||||
const excelDragover = ref(false)
|
||||
const selectedExcel = ref(null)
|
||||
const excelUploading = ref(false)
|
||||
|
||||
// 结果提示(修复:添加了 resultDetails 定义)
|
||||
const showResult = ref(false)
|
||||
const resultMessage = ref('')
|
||||
const resultType = ref('success')
|
||||
const resultDetails = ref('') // 新增:存储详细结果信息
|
||||
|
||||
// 文件夹相关方法
|
||||
const selectFolder = () => folderInput.value.click()
|
||||
|
||||
const handleFolderSelect = event => {
|
||||
const files = Array.from(event.target.files)
|
||||
if (files.length === 0) return
|
||||
|
||||
const firstPath = files[0].webkitRelativePath || files[0].name
|
||||
selectedFolder.value = {
|
||||
name: firstPath.split('/')[0],
|
||||
files: files,
|
||||
}
|
||||
}
|
||||
|
||||
const handleFolderDrop = event => {
|
||||
folderDragover.value = false
|
||||
const items = event.dataTransfer.items
|
||||
if (!items) return
|
||||
|
||||
const entries = Array.from(items)
|
||||
.map(item => item.webkitGetAsEntry())
|
||||
.filter(Boolean)
|
||||
|
||||
processEntries(entries)
|
||||
}
|
||||
|
||||
const processEntries = async (entries, path = '') => {
|
||||
const files = []
|
||||
for (const entry of entries) {
|
||||
if (entry.isFile) {
|
||||
const file = await new Promise(resolve => entry.file(resolve))
|
||||
file.webkitRelativePath = path ? `${path}/${entry.name}` : entry.name
|
||||
files.push(file)
|
||||
} else if (entry.isDirectory) {
|
||||
const dirReader = entry.createReader()
|
||||
const dirEntries = await new Promise(resolve =>
|
||||
dirReader.readEntries(resolve)
|
||||
)
|
||||
files.push(
|
||||
...(await processEntries(
|
||||
dirEntries,
|
||||
path ? `${path}/${entry.name}` : entry.name
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
if (path && files.length > 0) {
|
||||
selectedFolder.value = { name: path.split('/')[0], files }
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
const uploadFolder = async () => {
|
||||
if (!selectedFolder.value) {
|
||||
ElMessage.warning('请先选择文件夹')
|
||||
return
|
||||
}
|
||||
|
||||
folderUploading.value = true
|
||||
showResult.value = false
|
||||
uploadProgress.value = 0
|
||||
|
||||
try {
|
||||
const { name: folderName, files } = selectedFolder.value
|
||||
const totalBatches = Math.ceil(files.length / BATCH_SIZE)
|
||||
let uploadedFiles = 0
|
||||
let failedFiles = 0
|
||||
|
||||
for (let i = 0; i < totalBatches; i++) {
|
||||
const batchFiles = files.slice(i * BATCH_SIZE, (i + 1) * BATCH_SIZE)
|
||||
uploadStatusText.value = `上传中... (${i + 1}/${totalBatches})`
|
||||
|
||||
const formData = new FormData()
|
||||
formData.append('sender', username.value)
|
||||
formData.append('folderName', folderName)
|
||||
batchFiles.forEach(file => formData.append('files', file))
|
||||
|
||||
const res = await uploadToMinIOjgmodel(formData)
|
||||
if (res.code === 200) {
|
||||
uploadedFiles += batchFiles.length
|
||||
} else {
|
||||
failedFiles += batchFiles.length
|
||||
}
|
||||
uploadProgress.value = Math.round(((i + 1) / totalBatches) * 100)
|
||||
}
|
||||
|
||||
uploadProgress.value = 100
|
||||
if (failedFiles === 0) {
|
||||
resultMessage.value = `文件夹上传成功!共 ${uploadedFiles} 个文件`
|
||||
resultType.value = 'success'
|
||||
selectedFolder.value = null
|
||||
folderInput.value.value = ''
|
||||
} else {
|
||||
resultMessage.value = `上传完成!成功: ${uploadedFiles}, 失败: ${failedFiles}`
|
||||
resultType.value = 'warning'
|
||||
}
|
||||
} catch (error) {
|
||||
resultMessage.value = `上传异常: ${error.message}`
|
||||
resultType.value = 'error'
|
||||
} finally {
|
||||
folderUploading.value = false
|
||||
showResult.value = true
|
||||
setTimeout(() => {
|
||||
uploadProgress.value = 0
|
||||
uploadStatusText.value = '准备上传...'
|
||||
}, 3000)
|
||||
}
|
||||
}
|
||||
|
||||
// Excel相关方法
|
||||
const selectExcel = () => excelInput.value.click()
|
||||
|
||||
const handleExcelSelect = event => {
|
||||
const file = event.target.files[0]
|
||||
if (!file) return
|
||||
validateAndSetExcel(file)
|
||||
}
|
||||
|
||||
const handleExcelDrop = event => {
|
||||
excelDragover.value = false
|
||||
const file = event.dataTransfer.files[0]
|
||||
if (file) validateAndSetExcel(file)
|
||||
}
|
||||
|
||||
const validateAndSetExcel = file => {
|
||||
if (!file.name.toLowerCase().endsWith('.xlsx')) {
|
||||
ElMessage.error('请上传.xlsx格式的Excel文件')
|
||||
excelInput.value.value = ''
|
||||
return
|
||||
}
|
||||
selectedExcel.value = file
|
||||
}
|
||||
|
||||
const uploadExcel = async () => {
|
||||
if (!selectedExcel.value) {
|
||||
ElMessage.warning('请先选择Excel文件')
|
||||
return
|
||||
}
|
||||
|
||||
excelUploading.value = true
|
||||
showResult.value = false
|
||||
resultDetails.value = '' // 重置详情
|
||||
|
||||
try {
|
||||
const res = await uploadExcelFile(selectedExcel.value)
|
||||
if (res.code === 200) {
|
||||
resultMessage.value = 'Excel上传成功!'
|
||||
resultType.value = 'success'
|
||||
resultDetails.value = res.data || '数据已成功导入系统'
|
||||
selectedExcel.value = null
|
||||
excelInput.value.value = ''
|
||||
} else {
|
||||
resultMessage.value = '上传失败'
|
||||
resultType.value = 'error'
|
||||
resultDetails.value = res.message || '解析失败,请检查文件格式'
|
||||
}
|
||||
} catch (error) {
|
||||
resultMessage.value = '上传异常'
|
||||
resultType.value = 'error'
|
||||
resultDetails.value = error.message || '网络错误或服务器无响应'
|
||||
} finally {
|
||||
excelUploading.value = false
|
||||
showResult.value = true
|
||||
}
|
||||
}
|
||||
|
||||
// 工具函数
|
||||
const formatFileSize = bytes => {
|
||||
if (bytes === 0) return '0 B'
|
||||
const k = 1024
|
||||
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import 'jg_model_file_add.css';
|
||||
</style>
|
||||
87
src/views/jg_gcb/knowledge-ingest.css
Normal file
87
src/views/jg_gcb/knowledge-ingest.css
Normal file
@ -0,0 +1,87 @@
|
||||
.knowledge-ingest {
|
||||
padding: 20px;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.knowledge-ingest .section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.knowledge-ingest .section h3 {
|
||||
color: #303133;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #409EFF;
|
||||
}
|
||||
|
||||
.knowledge-ingest .file-list {
|
||||
margin: 20px 0;
|
||||
background: #f5f7fa;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.knowledge-ingest .file-list h4 {
|
||||
margin: 0 0 10px 0;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.knowledge-ingest .file-list .tree-view {
|
||||
background: white;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.knowledge-ingest .action-btn {
|
||||
margin-top: 15px;
|
||||
margin-right: 10px;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.knowledge-ingest .upload-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.knowledge-ingest .upload-info span {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.knowledge-ingest .upload-info strong {
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
.knowledge-ingest .progress-area {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.knowledge-ingest .progress-area h4 {
|
||||
margin-bottom: 10px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.knowledge-ingest .progress-area .current-file {
|
||||
margin-top: 10px;
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
word-break: break-all;
|
||||
background: #f4f4f5;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.knowledge-ingest .el-descriptions {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.knowledge-ingest .el-alert {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.knowledge-ingest .el-divider {
|
||||
margin: 30px 0;
|
||||
}
|
||||
6
src/views/lktx_jiegou/first.vue
Normal file
6
src/views/lktx_jiegou/first.vue
Normal file
@ -0,0 +1,6 @@
|
||||
<!-- first.vue -->
|
||||
<template>
|
||||
<el-radio-button label="first" size="default">
|
||||
知识库
|
||||
</el-radio-button>
|
||||
</template>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user