第一次提交
This commit is contained in:
parent
9b89d24d2e
commit
bbb9b4e6b1
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
@ -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>
|
||||
|
||||
7184
package-lock.json
generated
7184
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
BIN
public/ai_tu.png
Normal file
BIN
public/ai_tu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 412 KiB |
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,
|
||||
})
|
||||
}
|
||||
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 |
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
@ -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>
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
class="menu"
|
||||
:mode="mode"
|
||||
:collapse="collapse"
|
||||
:uniqueOpened="true"
|
||||
:uniqueOpened="false"
|
||||
:router="true"
|
||||
:default-active="activePath"
|
||||
:background-color="variables.menuBg"
|
||||
|
||||
@ -69,7 +69,7 @@ import Hamburger from './Hamburger.vue'
|
||||
import Breadcrumbs from './Breadcrumbs.vue'
|
||||
import Userinfo from './Userinfo.vue'
|
||||
import ChangeLang from './ChangeLang.vue'
|
||||
import ErrorLog from '@/components/ErrorLog/index.vue'
|
||||
// import ErrorLog from '@/components/ErrorLog/index.vue'
|
||||
import { useLayoutsettings } from '@/pinia/modules/layoutSettings'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useApp } from '@/pinia/modules/app'
|
||||
@ -81,7 +81,7 @@ export default defineComponent({
|
||||
Breadcrumbs,
|
||||
Userinfo,
|
||||
ChangeLang,
|
||||
ErrorLog,
|
||||
// ErrorLog,
|
||||
},
|
||||
setup() {
|
||||
const defaultSettings = useLayoutsettings()
|
||||
|
||||
@ -53,6 +53,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, computed } from 'vue'
|
||||
import Sidebar from './components/Sidebar/index.vue'
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
|
||||
import { ElLoading } from 'element-plus'
|
||||
import router from '@/router'
|
||||
import { TOKEN } from './pinia/modules/app' // TOKEN变量名
|
||||
import { TOKEN } from './pinia/modules/app'
|
||||
import { nextTick } from 'vue'
|
||||
import { useApp } from './pinia/modules/app'
|
||||
import { useAccount } from './pinia/modules/account'
|
||||
@ -43,84 +43,78 @@ import { useMenus } from './pinia/modules/menu'
|
||||
|
||||
const getPageTitle = title => {
|
||||
const { title: appTitle } = useApp()
|
||||
if (title) {
|
||||
return `${title} - ${appTitle}`
|
||||
}
|
||||
return appTitle
|
||||
return title ? `${title} - ${appTitle}` : appTitle
|
||||
}
|
||||
|
||||
// 白名单,里面是路由对象的name
|
||||
// 白名单
|
||||
const WhiteList = ['login', 'lock']
|
||||
|
||||
let loadingInstance = null
|
||||
|
||||
// vue-router4的路由守卫不再是通过next放行,而是通过return返回true或false或者一个路由地址
|
||||
router.beforeEach(async to => {
|
||||
loadingInstance = ElLoading.service({
|
||||
lock: true,
|
||||
// text: '正在加载数据,请稍候~',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
// 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 {
|
||||
const { userinfo, getUserinfo } = useAccount()
|
||||
// 获取用户角色信息,根据角色判断权限
|
||||
if (!userinfo) {
|
||||
try {
|
||||
// 获取用户信息
|
||||
await getUserinfo()
|
||||
} catch (err) {
|
||||
loadingInstance.close()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return to.fullPath
|
||||
const { userinfo, getUserinfo } = useAccount()
|
||||
|
||||
// 3. 首次加载:获取用户信息
|
||||
if (!userinfo) {
|
||||
try {
|
||||
await getUserinfo()
|
||||
} catch {
|
||||
loadingInstance?.close()
|
||||
return false
|
||||
}
|
||||
|
||||
// 生成菜单(如果你的项目有动态菜单,在此处会添加动态路由)
|
||||
const { menus, generateMenus } = useMenus()
|
||||
if (menus.length <= 0) {
|
||||
try {
|
||||
await generateMenus()
|
||||
return to.fullPath // 添加动态路由后,必须加这一句触发重定向,否则会404
|
||||
} catch (err) {
|
||||
loadingInstance.close()
|
||||
return false
|
||||
}
|
||||
// 获取完用户信息后,如果当前不是 /chat_view,强制跳转
|
||||
// 这里的 to 可能是登录页(带 redirect)或其他页面
|
||||
if (to.path !== '/chat_view') {
|
||||
return '/chat_view'
|
||||
}
|
||||
return true // 已在目标页,直接放行
|
||||
}
|
||||
|
||||
// 判断是否处于锁屏状态
|
||||
if (to.name !== 'lock') {
|
||||
const { authorization } = useApp()
|
||||
if (!!authorization && !!authorization.screenCode) {
|
||||
return {
|
||||
name: 'lock',
|
||||
query: {
|
||||
redirect: to.path,
|
||||
},
|
||||
replace: true,
|
||||
}
|
||||
// 4. 生成动态菜单(如有)
|
||||
const { menus, generateMenus } = useMenus()
|
||||
if (menus.length <= 0) {
|
||||
try {
|
||||
await generateMenus()
|
||||
return to.fullPath // 生成后重新触发导航
|
||||
} catch {
|
||||
loadingInstance?.close()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 锁屏检查
|
||||
if (to.name !== 'lock') {
|
||||
const { authorization } = useApp()
|
||||
if (authorization?.screenCode) {
|
||||
return {
|
||||
name: 'lock',
|
||||
query: { redirect: to.path },
|
||||
replace: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 已登录且非首次,允许自由访问
|
||||
return true
|
||||
})
|
||||
|
||||
router.afterEach(to => {
|
||||
loadingInstance.close()
|
||||
loadingInstance?.close()
|
||||
if (router.currentRoute.value.name === to.name) {
|
||||
nextTick(() => {
|
||||
document.title = getPageTitle(!!to.meta && to.meta.truetitle)
|
||||
document.title = getPageTitle(to.meta?.truetitle)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@ -22,7 +22,8 @@ const COLLAPSE = 'VEA-COLLAPSE'
|
||||
|
||||
export const useApp = defineStore('app', {
|
||||
state: () => ({
|
||||
title: 'Vue3 Element Admin',
|
||||
// title: 'Vue3 Element Admin',
|
||||
title: '凌空天行 AI大模型 应用系统',
|
||||
authorization: getItem(TOKEN),
|
||||
sidebar: {
|
||||
collapse: getItem(COLLAPSE),
|
||||
|
||||
@ -118,8 +118,9 @@ export const useMenus = defineStore('menu', () => {
|
||||
}
|
||||
const generateMenus = async () => {
|
||||
// // 方式一:只有固定菜单
|
||||
// const menus = getFilterMenus(fixedRoutes)
|
||||
const menus = getFilterMenus(fixedRoutes)
|
||||
// commit('SET_MENUS', menus)
|
||||
// setMenus(menus)
|
||||
|
||||
// 方式二:有动态菜单
|
||||
// 从后台获取菜单
|
||||
|
||||
@ -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',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
@ -35,22 +35,51 @@ 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 => {
|
||||
// ✅ 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)
|
||||
}
|
||||
)
|
||||
|
||||
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>
|
||||
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>
|
||||
6
src/views/lktx_jiegou/second.vue
Normal file
6
src/views/lktx_jiegou/second.vue
Normal file
@ -0,0 +1,6 @@
|
||||
<!-- second.vue -->
|
||||
<template>
|
||||
<el-radio-button label="second" size="default">
|
||||
图片检索
|
||||
</el-radio-button>
|
||||
</template>
|
||||
@ -12,7 +12,8 @@
|
||||
<template>
|
||||
<div class="login">
|
||||
<el-form class="form" :model="model" :rules="rules" ref="loginForm">
|
||||
<h1 class="title">Vue3 Element Admin</h1>
|
||||
<!-- <h1 class="title">Vue3 Element Admin</h1>-->
|
||||
<h1 class="title">凌空天行 AI大模型 应用系统</h1>
|
||||
<el-form-item prop="userName">
|
||||
<el-input
|
||||
class="text"
|
||||
@ -32,6 +33,20 @@
|
||||
:placeholder="$t('login.password')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 页面结构 -->
|
||||
<el-form-item prop="captcha">
|
||||
<div class="captcha">
|
||||
<el-input
|
||||
class="text"
|
||||
v-model="model.captcha"
|
||||
prefix-icon="Picture"
|
||||
placeholder="请输入验证码"
|
||||
></el-input>
|
||||
<img :src="captchaSrc" @click="refreshCaptcha" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button
|
||||
:loading="loading"
|
||||
@ -60,16 +75,21 @@ import {
|
||||
computed,
|
||||
watch,
|
||||
} from 'vue'
|
||||
import { Login } from '@/api/login'
|
||||
|
||||
import { Login, GetValidateCode } from '@/api/login'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import ChangeLang from '@/layout/components/Topbar/ChangeLang.vue'
|
||||
import useLang from '@/i18n/useLang'
|
||||
import { useApp } from '@/pinia/modules/app'
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: { ChangeLang },
|
||||
name: 'login',
|
||||
setup() {
|
||||
onMounted(() => {
|
||||
state.refreshCaptcha()
|
||||
})
|
||||
const { proxy: ctx } = getCurrentInstance() // 可以把ctx当成vue2中的this
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@ -100,9 +120,19 @@ export default defineComponent({
|
||||
],
|
||||
})
|
||||
const state = reactive({
|
||||
refreshCaptcha: () => {
|
||||
// 请求后端验证码接口,api
|
||||
GetValidateCode().then(response => {
|
||||
state.captchaSrc = response.data.codeValue
|
||||
state.model.codeKey = response.data.codeKey
|
||||
})
|
||||
},
|
||||
captchaSrc: '',
|
||||
model: {
|
||||
userName: 'admin',
|
||||
password: '123456',
|
||||
password: '111111',
|
||||
captcha: '',
|
||||
codeKey: '',
|
||||
},
|
||||
rules: getRules(),
|
||||
loading: false,
|
||||
@ -152,6 +182,20 @@ export default defineComponent({
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 验证码输入框样式 start
|
||||
.captcha {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.captcha img {
|
||||
cursor: pointer;
|
||||
margin-left: 20px;
|
||||
}
|
||||
// 验证码输入框样式 end
|
||||
|
||||
.login {
|
||||
transition: transform 1s;
|
||||
transform: scale(1);
|
||||
|
||||
155
src/views/system/sysMenu.vue
Normal file
155
src/views/system/sysMenu.vue
Normal file
@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="tools-div">
|
||||
<el-button type="success" size="small" @click="addShow()">
|
||||
添 加
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:data="list"
|
||||
style="width: 100%; margin-bottom: 20px"
|
||||
row-key="id"
|
||||
border
|
||||
default-expand-all
|
||||
>
|
||||
<el-table-column prop="title" label="菜单标题" />
|
||||
<el-table-column prop="component" label="路由名称" />
|
||||
<el-table-column prop="sortValue" label="排序" />
|
||||
<el-table-column prop="status" label="状态" #default="scope">
|
||||
{{ scope.row.status == 1 ? '正常' : '停用' }}
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" />
|
||||
<el-table-column label="操作" align="center" width="280" #default="scope">
|
||||
<el-button type="success" size="small" @click="addShow(scope.row)">
|
||||
添加下级节点
|
||||
</el-button>
|
||||
<el-button type="primary" size="small" @click="editShow(scope.row)">
|
||||
修改
|
||||
</el-button>
|
||||
<el-button type="danger" size="small">删除</el-button>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="30%">
|
||||
<el-form label-width="120px">
|
||||
<el-form-item label="菜单标题">
|
||||
<el-input v-model="sysMenu.title" />
|
||||
</el-form-item>
|
||||
<el-form-item label="路由名称">
|
||||
<el-input v-model="sysMenu.component" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序">
|
||||
<el-input v-model="sysMenu.sortValue" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="sysMenu.status">
|
||||
<el-radio :label="1">正常</el-radio>
|
||||
<el-radio :label="0">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="saveOrUpdate">提交</el-button>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { FindNodes, UpdateSysMenu, SaveSysMenu } from '@/api/sysMenu.js'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
// 加载数据
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
// 定义表格数据模型
|
||||
const list = ref([])
|
||||
|
||||
//页面表单数据
|
||||
const defaultForm = {
|
||||
id: '',
|
||||
parentId: 0,
|
||||
title: '',
|
||||
url: '',
|
||||
component: '',
|
||||
icon: '',
|
||||
sortValue: 1,
|
||||
status: 1,
|
||||
}
|
||||
// 表单响应式数据模型对象
|
||||
const sysMenu = ref(defaultForm)
|
||||
|
||||
// 对话框属性
|
||||
const dialogTitle = ref('添加顶级菜单')
|
||||
const dialogVisible = ref(false)
|
||||
|
||||
// 提交添加或添加下一级或修改
|
||||
const saveOrUpdate = async () => {
|
||||
let code = 0
|
||||
let message = ''
|
||||
if (sysMenu.value.id) {
|
||||
const response = await UpdateSysMenu(sysMenu.value)
|
||||
code = response.code
|
||||
message = response.message
|
||||
} else {
|
||||
const response = await SaveSysMenu(sysMenu.value)
|
||||
code = response.code
|
||||
message = response.message
|
||||
}
|
||||
|
||||
if (code == 200) {
|
||||
ElMessage.success('操作成功')
|
||||
dialogVisible.value = false
|
||||
fetchData()
|
||||
} else {
|
||||
ElMessage.error(message)
|
||||
}
|
||||
}
|
||||
|
||||
// 打开对话框
|
||||
const addShow = row => {
|
||||
if (row) {
|
||||
// 添加下一级
|
||||
dialogTitle.value = '添加' + row.title + '的下一级菜单'
|
||||
sysMenu.value.parentId = row.id
|
||||
} else {
|
||||
// 添加顶级菜单
|
||||
dialogTitle.value = '添加顶级菜单'
|
||||
sysMenu.value = { ...defaultForm }
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function editShow(row) {
|
||||
sysMenu.value = { ...row }
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
//列表
|
||||
const fetchData = async () => {
|
||||
const { code, data, message } = await FindNodes()
|
||||
list.value = data
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-div {
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
}
|
||||
.tools-div {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
267
src/views/system/sysRole.vue
Normal file
267
src/views/system/sysRole.vue
Normal file
@ -0,0 +1,267 @@
|
||||
<!--
|
||||
* @Descripttion:
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @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>
|
||||
<div class="search-div">
|
||||
<!-- 搜索表单 -->
|
||||
<el-form label-width="70px" size="small">
|
||||
<el-form-item label="角色名称">
|
||||
<el-input
|
||||
style="width: 100%"
|
||||
placeholder="角色名称"
|
||||
v-model="queryDto.roleName"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-row style="display: flex">
|
||||
<el-button type="primary" size="small">搜索</el-button>
|
||||
<el-button size="small">重置</el-button>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<!-- 添加按钮 -->
|
||||
<div class="tools-div">
|
||||
<el-button type="success" size="small" @click="addShow()">
|
||||
添 加
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!--- 角色表格数据 -->
|
||||
<el-table :data="list" style="width: 100%">
|
||||
<el-table-column prop="roleName" label="角色名称" width="180" />
|
||||
<el-table-column prop="roleCode" label="角色code" width="180" />
|
||||
<el-table-column prop="createTime" label="创建时间" />
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
width="280"
|
||||
#default="scope"
|
||||
>
|
||||
<el-button type="primary" size="small" @click="editShow(scope.row)">
|
||||
修改
|
||||
</el-button>
|
||||
<el-button type="danger" size="small">删除</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
size="small"
|
||||
@click="showAssignMenu(scope.row)"
|
||||
>
|
||||
分配菜单
|
||||
</el-button>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!--分页条-->
|
||||
<el-pagination
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="total"
|
||||
/>
|
||||
|
||||
<!-- 页面表单 -->
|
||||
<el-dialog v-model="dialogVisible" title="添加或修改角色" width="30%">
|
||||
<el-form label-width="120px">
|
||||
<el-form-item label="角色名称">
|
||||
<el-input v-model="sysRole.roleName" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色Code">
|
||||
<el-input v-model="sysRole.roleCode" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit">提交</el-button>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 分配菜单的对话框 -->
|
||||
<el-dialog v-model="dialogMenuVisible" title="分配菜单" width="40%">
|
||||
<el-form label-width="80px">
|
||||
<el-tree
|
||||
:data="sysMenuTreeList"
|
||||
show-checkbox
|
||||
default-expand-all
|
||||
node-key="id"
|
||||
:props="defaultProps"
|
||||
:check-on-click-node="true"
|
||||
ref="tree"
|
||||
/>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="doAssign()">提交</el-button>
|
||||
<el-button @click="dialogMenuVisible = false">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import {
|
||||
GetSysRoleListByPage,
|
||||
SaveSysRole,
|
||||
UpdateSysRole,
|
||||
GetSysRoleMenuIds,
|
||||
DoAssignMenuIdToSysRole,
|
||||
} from '@/api/sysRole'
|
||||
import { ElMessage } from 'element-plus'
|
||||
onMounted(() => {
|
||||
// 加载初始化list数据
|
||||
fetchData()
|
||||
})
|
||||
|
||||
// 分页条总记录数
|
||||
let total = ref(0)
|
||||
|
||||
// 定义表格数据模型
|
||||
let list = ref([])
|
||||
|
||||
// 表单参数
|
||||
const queryDto = ref({
|
||||
roleName: '',
|
||||
})
|
||||
|
||||
// 分页参数
|
||||
const pageParams = ref({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
})
|
||||
|
||||
// 对话框
|
||||
const dialogVisible = ref(false)
|
||||
const sysRole = ref({
|
||||
roleName: '',
|
||||
roleCode: '',
|
||||
})
|
||||
|
||||
// 分配对话框
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'title',
|
||||
}
|
||||
const dialogMenuVisible = ref(false)
|
||||
const sysMenuTreeList = ref([])
|
||||
const tree = ref({})
|
||||
const roleId = ref(0)
|
||||
|
||||
// 提交分配菜单
|
||||
const doAssign = () => {
|
||||
// 全选菜单ids节点
|
||||
let checkedNodes = tree.value.getCheckedNodes()
|
||||
// 半选菜单ids节点
|
||||
let halfCheckedNodes = tree.value.getHalfCheckedNodes()
|
||||
|
||||
// 全选菜单ids
|
||||
let checkedIds = checkedNodes.map(checkedNode => {
|
||||
return {
|
||||
id: checkedNode.id,
|
||||
isHalf: 0,
|
||||
}
|
||||
})
|
||||
|
||||
// 半选菜单ids
|
||||
let halfCheckedIds = halfCheckedNodes.map(halfCheckedNode => {
|
||||
return {
|
||||
id: halfCheckedNode.id,
|
||||
isHalf: 1,
|
||||
}
|
||||
})
|
||||
// 合并
|
||||
let ids = [...checkedIds, ...halfCheckedIds]
|
||||
|
||||
// 提交参数
|
||||
let assginMenuDto = {
|
||||
roleId: roleId.value,
|
||||
menuIdList: ids,
|
||||
}
|
||||
|
||||
DoAssignMenuIdToSysRole(assginMenuDto).then(response => {
|
||||
if (response.code == 200) {
|
||||
ElMessage.success('分配菜单成功')
|
||||
dialogMenuVisible.value = false
|
||||
} else {
|
||||
ElMessage.error('分配菜单失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 打开分配对话框
|
||||
const showAssignMenu = async row => {
|
||||
roleId.value = row.id
|
||||
dialogMenuVisible.value = true
|
||||
// 加载对话框数据,菜单树形数据,被选中的菜单
|
||||
const response = await GetSysRoleMenuIds(row.id)
|
||||
sysMenuTreeList.value = response.data.sysMenuList // 所有菜单
|
||||
tree.value.setCheckedKeys(response.data.roleMenuIds) // 设置已经被选中的菜单id,数据回显
|
||||
}
|
||||
|
||||
// 打开对话框
|
||||
function addShow() {
|
||||
sysRole.value = { ...null }
|
||||
dialogVisible.value = true
|
||||
}
|
||||
function editShow(row) {
|
||||
sysRole.value = { ...row }
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 添加或修改
|
||||
const submit = async () => {
|
||||
let code = 0
|
||||
if (sysRole.value.id) {
|
||||
let response = await UpdateSysRole(sysRole.value)
|
||||
code = response.code
|
||||
} else {
|
||||
let response = await SaveSysRole(sysRole.value)
|
||||
code = response.code
|
||||
}
|
||||
if (code == 200) {
|
||||
// 操作成功后关闭对话框
|
||||
ElMessage.success('操作成功')
|
||||
dialogVisible.value = false
|
||||
fetchData()
|
||||
} else {
|
||||
ElMessage.error('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 查询
|
||||
function fetchData() {
|
||||
// alert("查询数据表格")
|
||||
GetSysRoleListByPage(
|
||||
queryDto.value,
|
||||
pageParams.value.page,
|
||||
pageParams.value.limit
|
||||
).then(respoonse => {
|
||||
list.value = respoonse.data.list
|
||||
total.value = respoonse.data.total
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-div {
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.tools-div {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
343
src/views/system/sysUser.vue
Normal file
343
src/views/system/sysUser.vue
Normal file
@ -0,0 +1,343 @@
|
||||
<!--
|
||||
* @Descripttion:
|
||||
* @version:
|
||||
* @Date: 2021-04-20 11:06:21
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @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>
|
||||
<!---搜索表单-->
|
||||
<div class="search-div">
|
||||
<el-form label-width="70px" size="small">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="关键字">
|
||||
<el-input
|
||||
v-model="queryDto.keyword"
|
||||
style="width: 100%"
|
||||
placeholder="用户名、姓名、手机号码"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="创建时间">
|
||||
<el-date-picker
|
||||
v-model="createTimes"
|
||||
type="daterange"
|
||||
range-separator="To"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row style="display: flex">
|
||||
<el-button type="primary" size="small" @click="searchSysUser">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button size="small" @click="resetData">重置</el-button>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!--添加按钮-->
|
||||
<div class="tools-div">
|
||||
<el-button type="success" size="small" @click="addShow">添 加</el-button>
|
||||
</div>
|
||||
|
||||
<!---数据表格-->
|
||||
<el-table :data="list" style="width: 100%">
|
||||
<el-table-column prop="username" label="用户名" />
|
||||
<el-table-column prop="name" label="姓名" />
|
||||
<el-table-column prop="phone" label="手机" />
|
||||
<el-table-column prop="avatar" label="头像" #default="scope">
|
||||
<img :src="scope.row.avatar" width="50" />
|
||||
</el-table-column>
|
||||
<el-table-column prop="description" label="描述" />
|
||||
<el-table-column prop="status" label="状态" #default="scope">
|
||||
{{ scope.row.status == 1 ? '正常' : '停用' }}
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" />
|
||||
<el-table-column label="操作" align="center" width="280" #default="scope">
|
||||
<el-button type="primary" size="small" @click="editShow(scope.row)">
|
||||
修改
|
||||
</el-button>
|
||||
<el-button type="danger" size="small">删除</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
size="small"
|
||||
@click="showAssignRole(scope.row)"
|
||||
>
|
||||
分配角色
|
||||
</el-button>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
v-model:current-page="pageParams.page"
|
||||
v-model:page-size="pageParams.limit"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="total"
|
||||
/>
|
||||
|
||||
<el-dialog v-model="dialogVisible" title="添加或修改" width="40%">
|
||||
<el-form label-width="120px">
|
||||
<el-form-item label="用户名">
|
||||
<el-input v-model="sysUser.username" />
|
||||
</el-form-item>
|
||||
<el-form-item label="密码">
|
||||
<el-input type="password" show-password v-model="sysUser.password" />
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名">
|
||||
<el-input v-model="sysUser.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机">
|
||||
<el-input v-model="sysUser.phone" />
|
||||
</el-form-item>
|
||||
<el-form-item label="头像">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="http://localhost:8501/admin/system/fileUpload"
|
||||
:show-file-list="false"
|
||||
:on-success="handleAvatarSuccess"
|
||||
:headers="headers"
|
||||
>
|
||||
<img v-if="sysUser.avatar" :src="sysUser.avatar" class="avatar" />
|
||||
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述">
|
||||
<el-input v-model="sysUser.description" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit">提交</el-button>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="dialogRoleVisible" title="分配角色" width="40%">
|
||||
<el-form label-width="80px">
|
||||
<el-form-item label="用户名">
|
||||
<el-input disabled :value="sysUser.userName"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="角色列表">
|
||||
<el-checkbox-group v-model="userRoleIds">
|
||||
<el-checkbox
|
||||
v-for="role in allRoles"
|
||||
:key="role.id"
|
||||
:label="role.id"
|
||||
>
|
||||
{{ role.roleName }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="doAssign()">提交</el-button>
|
||||
<el-button @click="dialogRoleVisible = false">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import {
|
||||
GetSysUserListByPage,
|
||||
UpdateSysUser,
|
||||
SaveSysUser,
|
||||
DoAssignRoleToUser,
|
||||
} from '@/api/sysUser'
|
||||
GetAllRoleList
|
||||
import { GetAllRoleList } from '@/api/sysRole'
|
||||
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useApp } from '@/pinia/modules/app'
|
||||
const headers = {
|
||||
token: useApp().authorization.token,
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
// 搜索参数
|
||||
const queryDto = ref({
|
||||
keyword: '',
|
||||
createTimeBegin: '',
|
||||
createTimeEnd: '',
|
||||
})
|
||||
const createTimes = ref([])
|
||||
|
||||
// 分页参数
|
||||
const pageParams = ref({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
})
|
||||
|
||||
// 表单对象
|
||||
const sysUser = ref({
|
||||
userName: '',
|
||||
name: '',
|
||||
phone: '',
|
||||
password: '',
|
||||
description: '',
|
||||
avatar: '',
|
||||
})
|
||||
const dialogVisible = ref(false)
|
||||
const dialogRoleVisible = ref(false)
|
||||
|
||||
// 角色列表和用户角色id
|
||||
const userRoleIds = ref([])
|
||||
const allRoles = ref([])
|
||||
const userId = ref(0)
|
||||
|
||||
// 提交分配
|
||||
const doAssign = async () => {
|
||||
let assginRoleDto = {
|
||||
userId: userId.value, // 打开对话框时候给当前对话框的user赋值
|
||||
roleIdList: userRoleIds.value,
|
||||
}
|
||||
|
||||
// 调用后端提交分配
|
||||
let code = 0
|
||||
const response = await DoAssignRoleToUser(assginRoleDto)
|
||||
code = response.code
|
||||
|
||||
if (code == 200) {
|
||||
// 操作成功后关闭对话框
|
||||
ElMessage.success('操作成功')
|
||||
dialogRoleVisible.value = false
|
||||
fetchData()
|
||||
} else {
|
||||
ElMessage.error('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 分配窗口
|
||||
const showAssignRole = row => {
|
||||
userId.value = row.id
|
||||
sysUser.value.userName = row.username
|
||||
dialogRoleVisible.value = true
|
||||
// 查询后端角色列表和用户角色id
|
||||
GetAllRoleList(row.id).then(response => {
|
||||
allRoles.value = response.data.allRoles
|
||||
userRoleIds.value = response.data.userRoleIds
|
||||
})
|
||||
}
|
||||
|
||||
// 头像图片
|
||||
const handleAvatarSuccess = (response, uploadFile) => {
|
||||
sysUser.value.avatar = response.data
|
||||
}
|
||||
|
||||
const addShow = () => {
|
||||
sysUser.value = { ...null }
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const editShow = row => {
|
||||
sysUser.value = { ...row }
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 提交保存或修改
|
||||
const submit = async () => {
|
||||
let code = 0
|
||||
if (sysUser.value.id) {
|
||||
let response = await UpdateSysUser(sysUser.value)
|
||||
code = response.code
|
||||
} else {
|
||||
let response = await SaveSysUser(sysUser.value)
|
||||
code = response.code
|
||||
}
|
||||
if (code == 200) {
|
||||
// 操作成功后关闭对话框
|
||||
ElMessage.success('操作成功')
|
||||
dialogVisible.value = false
|
||||
fetchData()
|
||||
} else {
|
||||
ElMessage.error('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 查询
|
||||
const fetchData = () => {
|
||||
// 处理时间参数
|
||||
if (createTimes.value.length > 0) {
|
||||
queryDto.value.createTimeBegin = createTimes.value[0]
|
||||
queryDto.value.createTimeEnd = createTimes.value[1]
|
||||
}
|
||||
GetSysUserListByPage(
|
||||
pageParams.value.page,
|
||||
pageParams.value.limit,
|
||||
queryDto.value
|
||||
).then(res => {
|
||||
list.value = res.data.list
|
||||
total.value = res.data.total
|
||||
})
|
||||
}
|
||||
|
||||
// 表格数据模型
|
||||
const list = ref([])
|
||||
|
||||
// 分页条数据模型
|
||||
const total = ref(0)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-div {
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
}
|
||||
.tools-div {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
<style scoped>
|
||||
.avatar-uploader .avatar {
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.avatar-uploader .el-upload {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@ -77,10 +77,30 @@ export default env => {
|
||||
open: true,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://dev.api.xxx.com', // 后端接口的域名
|
||||
target: 'http://localhost:8501', // 后端接口的域名
|
||||
changeOrigin: true,
|
||||
rewrite: path => path.replace(/^\/api/, ''),
|
||||
|
||||
},
|
||||
'/admin': {
|
||||
target: 'http://localhost:8501', // 后端接口的域名
|
||||
changeOrigin: true,
|
||||
|
||||
},
|
||||
// 热处理模块
|
||||
'/heat-api': {
|
||||
target: 'http://192.168.0.207:8000',
|
||||
changeOrigin: true,
|
||||
rewrite: path => path.replace(/^\/heat-api/, '') // 去掉前缀,保留原始路径
|
||||
},
|
||||
|
||||
// 强度计算模块
|
||||
'/strength-api': {
|
||||
target: 'http://localhost:8501',
|
||||
changeOrigin: true,
|
||||
rewrite: path => path.replace(/^\/strength-api/, '')
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
},
|
||||
esbuild: false,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user