feat(layout): 登录登出及菜单权限处理

- 重构了侧边栏菜单的渲染逻辑,支持多级菜单
-优化了菜单项的样式和布局,提高了可读性和美观性- 增加了菜单项的点击事件处理,实现了菜单的动态展开和收缩
-调整了菜单项的路径处理方式,支持动态生成菜单路径- 优化了菜单项的激活状态管理,提高了用户体验
This commit is contained in:
du.meimei
2025-04-24 15:32:31 +08:00
parent f6154f7147
commit b0c0446f52
19 changed files with 533 additions and 204 deletions

View File

@@ -4,7 +4,7 @@ import getUrl from '@/assets/js/utils/get-url'
// 登录
export function login(data) {
return request({
url: getUrl('/user/login', 0),
url: getUrl('/sysUserEx/baseLogin'),
method: 'post',
data
})
@@ -13,17 +13,24 @@ export function login(data) {
// 获取个人信息
export function getInfo(token) {
return request({
url: getUrl('/user/info', 0),
method: 'get',
params: { token }
url: getUrl('/sysUserEx/getUserInfo'),
method: 'get'
// params: { token }
})
}
// 获取菜单
export function getRouters(token) {
return request({
url: getUrl('/sysUserEx/getLoginUserMenu'),
method: 'get'
// params: { token }
})
}
// 登出
export function logout() {
return request({
url: getUrl('/user/logout', 0),
method: 'post'
url: getUrl('/sysUserEx/logout'),
method: 'get'
})
}
// 测试示例

View File

@@ -4,28 +4,55 @@ import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/assets/js/utils/auth' // get token from cookie
import getPageTitle from '@/assets/js/utils/get-page-title'
import { Message } from 'element-ui'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/authentication', '/home', '/404'] // no redirect whitelist
router.beforeEach(async (to, from, next) => {
sessionStorage.setItem('token', 'MockToken')
document.title = getPageTitle(to.meta.title)
const hasToken = getToken()
if (hasToken) {
next()
} else {
if (to.path.indexOf('/home') !== -1) {
next(`/authentication?redirect=${to.path}`)
const whiteList = ['/login', '/authentication', '/404'] // no redirect whitelist
console.log('token:' + getToken())
router.beforeEach((to, from, next) => {
NProgress.start()
if (getToken()) {
console.log(getToken())
/* has token*/
if (to.path === '/login') {
next({ path: '/login' })
NProgress.done()
} else {
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
next()
NProgress.done()
}
// 判断当前用户是否已拉取完user_info信息
store
.dispatch('user/getInfo')
.then(() => {
console.log(to)
store.dispatch('app/GenerateRoutes').then(accessRoutes => {
// // 根据roles权限生成可访问的路由表
// router.addRoutes(accessRoutes) // 动态添加可访问路由表
// // getArea();//初始化省市区划配置信息
// next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
next()
})
})
.catch(err => {
store.dispatch('user/logout').then(() => {
Message.error(err)
next({ path: '/ogin' })
})
})
}
} else {
console.log('没有token')
console.log(to.path)
console.log(to.fullPath)
// 没有token
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next()
} else {
next(`/login`) // 否则全部重定向到登录页
NProgress.done()
}
}
})
router.afterEach(() => {
// finish progress bar
NProgress.done()

View File

@@ -29,7 +29,7 @@ service.interceptors.request.use(
if (store.getters.token) {
// config.headers['sid'] = getToken()
config.headers['sysType'] = '3'
config.headers['auth'] = getToken()
config.headers['sid'] = getToken()
}
if (loading) {
endLoading()

View File

@@ -342,7 +342,7 @@ h3 {
#app .main-container {
min-height: 100%;
transition: margin-left 0.28s;
margin-left: 210px;
/*margin-left: 210px;*/
position: relative;
}

View File

@@ -8,7 +8,7 @@
.sidebar-container {
transition: width 0.28s;
width: $sideBarWidth !important;
//width: $sideBarWidth !important;
background-color: #f0f4fa;
border: none;
padding: 10px;

View File

@@ -1,13 +1,23 @@
<template>
<el-dialog :visible.sync="visible" :title="title" :append-to-body="appendToBody" :destroy-on-close="destroyOnClose"
:width="width" :before-close="handleBeforeClose">
<el-dialog
:visible.sync="visible"
:title="title"
:append-to-body="appendToBody"
:destroy-on-close="destroyOnClose"
:width="width"
:before-close="handleBeforeClose"
>
<div class="render-dialog-body">
<slot name="default"></slot>
</div>
<div slot="footer">
<slot name="footer">
<el-button size="medium" @click="cancel">{{ cancelButtonText }}</el-button>
<el-button size="medium" type="primary" @click="confirm">{{ confirmButtonText }}</el-button>
<el-button size="medium" @click="cancel">{{
cancelButtonText
}}</el-button>
<el-button size="medium" type="primary" @click="confirm">{{
confirmButtonText
}}</el-button>
</slot>
</div>
</el-dialog>
@@ -33,7 +43,7 @@ export default {
},
beforeClose: {
type: Function,
default: () => { }
default: () => {}
},
visible: {
type: Boolean,
@@ -68,8 +78,8 @@ export default {
this.beforeClose()
}
},
created() { },
mounted() { },
created() {},
mounted() {},
computed: {}
}
</script>

View File

@@ -21,9 +21,17 @@ export default {
</script>
<template>
<iframe id="iframe" frameborder="0"
:src="`${iframeSrc}/pdfjs-dist/web/viewer.html?file=${encodeURIComponent(getPdfUrl({ documentId: id }))}`"
class="miner-u" style="width: 100%; height: 800px;"></iframe>
<iframe
id="iframe"
frameborder="0"
:src="
`${iframeSrc}/pdfjs-dist/web/viewer.html?file=${encodeURIComponent(
getPdfUrl({ documentId: id })
)}`
"
class="miner-u"
style="width: 100%; height: 800px;"
></iframe>
</template>
<style lang="scss" scoped></style>
<style lang="scss" scoped></style>

View File

@@ -1,4 +1,5 @@
import Cookies from 'js-cookie'
import { getRouters } from '@/api/app/user'
const state = {
sidebar: {
@@ -44,6 +45,27 @@ const actions = {
},
setSidebarList({ commit }, sidebarList) {
commit('SET_SIDEBAR_LIST', sidebarList)
},
// 生成路由
GenerateRoutes({ commit }) {
return new Promise(resolve => {
// 向后端请求路由数据
getRouters().then(res => {
// const sdata = JSON.parse(JSON.stringify(res.data))
// const rdata = JSON.parse(JSON.stringify(res.data))
// const sidebarRoutes = filterAsyncRouter(sdata)
// const rewriteRoutes = filterAsyncRouter(rdata, false, true)
// const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
// rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
// router.addRoutes(asyncRoutes)
// commit('SET_ROUTES', rewriteRoutes)
// commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
// commit('SET_DEFAULT_ROUTES', sidebarRoutes)
// commit('SET_TOPBAR_ROUTES', sidebarRoutes)
commit('SET_SIDEBAR_LIST', res.content.content)
resolve()
})
})
}
}

View File

@@ -1,5 +1,6 @@
import { login, logout, getInfo } from '@/api/app/user'
import { login, logout, getInfo, getRouters } from '@/api/app/user'
import { getToken, setToken, removeToken } from '@/assets/js/utils/auth'
import router from '@/router'
const state = {
token: getToken(),
@@ -31,12 +32,13 @@ const actions = {
},
// user login
login({ commit }, userInfo) {
const { username, password } = userInfo
const { userName, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password })
login({ userName: userName.trim(), password: password })
.then(res => {
commit('SET_TOKEN', res.token)
setToken(res.token)
setToken(res.content.content)
commit('SET_TOKEN', res.content.content)
// getInfo()
resolve()
})
.catch(error => {
@@ -53,9 +55,10 @@ const actions = {
if (!res) {
reject('Verification failed, please Login again.')
}
console.log(res)
commit('SET_NAME', res.name)
commit('SET_AVATAR', res.avatar)
commit('SET_NAME', res.content.content.userName)
// commit('SET_AVATAR', res.avatar)
resolve(res)
})
.catch(error => {

View File

@@ -7,13 +7,17 @@
<logo class="logo" v-if="showLogo" :collapse="isCollapse" />
<div
v-for="route in menuList"
:key="route.path"
:key="route.id"
class="primary-menu-item"
:class="{ active: activeParent === route.path }"
:class="{ active: activeParent === route.url }"
@click="selectParentMenu(route)"
>
<i v-if="route.meta && route.meta.icon" class="iconfont fs22" :class="route.meta.icon"></i>
<span class="menu-title">{{ route.meta ? route.meta.title : '' }}</span>
<i
v-if="route.meta && route.meta.icon"
class="iconfont fs22"
:class="route.meta.icon"
></i>
<span class="menu-title">{{ route.menuName }}</span>
</div>
</div>
<div>
@@ -22,22 +26,28 @@
</div>
<!-- Right side - Submenu -->
<div class="submenu-container" v-if="activeParent && currentSubmenu.length">
<div class="submenu-container" v-if="currentSubmenu.length">
<!-- <div class="submenu-header">-->
<!-- <span>{{ currentParentTitle }}</span>-->
<!-- </div>-->
<el-scrollbar wrap-class="scrollbar-wrapper">
<div class="submenu-list">
<pre style="font-size: 12px; padding: 10px; display: none;">{{ JSON.stringify(currentSubmenu, null, 2) }}</pre>
<pre style="font-size: 12px; padding: 10px; display: none;">{{
JSON.stringify(currentSubmenu, null, 2)
}}</pre>
<div
v-for="subItem in currentSubmenu"
:key="subItem.path"
:key="subItem.id"
class="submenu-item"
:class="{ active: activeMenu === subItem.path }"
@click="navigateTo(subItem.path)"
:class="{ active: activeMenu === subItem.url }"
@click="navigateTo(subItem.url)"
>
<i v-if="subItem.meta && subItem.meta.icon" class="iconfont mr10" :class="subItem.meta.icon" />
<span>{{ subItem.meta ? subItem.meta.title : subItem.name || 'Unnamed Item' }}</span>
<i
v-if="subItem.meta && subItem.meta.icon"
class="iconfont mr10"
:class="subItem.meta.icon"
/>
<span>{{ subItem.menuName }}</span>
</div>
</div>
</el-scrollbar>
@@ -47,6 +57,7 @@
</template>
<script>
import Cookies from 'js-cookie'
import { mapGetters, mapActions } from 'vuex'
import Logo from './Logo'
import variables from '@/assets/sass/variables.scss'
@@ -79,14 +90,14 @@ export default {
},
currentSubmenu() {
if (!this.activeParent) return []
const parent = this.menuList.find(item => item.path === this.activeParent)
const parent = this.menuList.find(item => item.url === this.activeParent)
console.log('Parent found:', parent)
console.log('Children:', parent ? parent.children : 'No children')
return parent ? parent.children || [] : []
},
currentParentTitle() {
if (!this.activeParent) return ''
const parent = this.menuList.find(item => item.path === this.activeParent)
const parent = this.menuList.find(item => item.url === this.activeParent)
return parent && parent.meta ? parent.meta.title : ''
}
},
@@ -103,9 +114,9 @@ export default {
formatList(menu, state, parentPath = null) {
menu.map(item => {
if (parentPath) {
item.path = '/' + parentPath + '/' + item.url
item.url = '/' + parentPath + '/' + item.url
} else {
item.path = item.url
item.url = item.url
}
if (item.otherInfo1 == 0) {
item.name = item.menuName
@@ -115,7 +126,7 @@ export default {
icon: item.img
}
if (item.children != null) {
this.formatList(item.children, undefined, item.path)
this.formatList(item.children, undefined, item.url)
} else {
item.children = []
}
@@ -126,20 +137,21 @@ export default {
return menu
},
selectParentMenu(route) {
this.activeParent = route.path
this.activeParent = route.url
console.log(Cookies.get('sidebarStatus'))
// this.$store.dispatch('app/toggleSideBar')
// If this parent has children, don't navigate
if (route.children && route.children.length > 0) {
// Optionally navigate to the first child
// this.navigateTo(route.children[0].path)
// this.navigateTo(route.children[0].url)
} else {
// If no children, navigate to the parent route
this.navigateTo(route.path)
this.navigateTo(route.url)
}
},
navigateTo(path) {
if (path && path !== this.$route.path) {
this.$router.push(path)
navigateTo(url) {
if (url && url !== this.$route.path) {
this.$router.push(url)
}
},
findParentForCurrentRoute() {
@@ -148,17 +160,21 @@ export default {
// Try to find the parent menu that contains the current route
for (const parent of this.menuList) {
// Check if current route is the parent itself
if (parent.path === currentPath) {
this.activeParent = parent.path
if (parent.url === currentPath) {
this.activeParent = parent.url
return
}
// Check if current route is one of the children
if (parent.children && parent.children.length) {
const childMatch = parent.children.find(child => child.path === currentPath || currentPath.startsWith(child.path + '/'))
const childMatch = parent.children.find(
child =>
child.url === currentPath ||
currentPath.startsWith(child.url + '/')
)
if (childMatch) {
this.activeParent = parent.path
this.activeParent = parent.url
return
}
}
@@ -166,7 +182,7 @@ export default {
// If no match found, default to first menu item if available
if (this.menuList.length > 0 && !this.activeParent) {
this.activeParent = this.menuList[0].path
this.activeParent = this.menuList[0].url
}
}
},
@@ -174,14 +190,15 @@ export default {
if (sessionStorage.token !== 'MockToken') {
// 获取路由数据
let data = this.$store.state.app.sidebarList
console.log('sidebarList')
console.log(data)
// 通过检查路由结构来确定顶级菜单
const topLevelRoutes = data.filter(route => {
// 如果路由有 meta 和 children则认为它是顶级菜单
return route.meta && route.path && !route.hidden
return route.meta && route.children && !route.hidden
})
this.menuList = topLevelRoutes
this.menuList = data
} else {
// 从路由配置中获取顶级路由
const topLevelRoutes = this.routes.filter(route => {
@@ -281,6 +298,7 @@ export default {
flex-direction: column;
overflow: hidden;
padding: 23px 10px;
width: 160px;
}
.submenu-header {

View File

@@ -1,6 +1,6 @@
<template>
<div :class="{ 'has-logo': showLogo }">
<logo v-if="showLogo" :collapse="isCollapse" />
<logo v-if="showLogo" />
<!--<div class="www el-scrollbar" style="float: left;color:red">-->
<!--<div v-for="route in this.$router.options" class="box">-->
<!--<el-button>{{ route.name }}</el-button>-->

View File

@@ -1,7 +1,11 @@
<template>
<div class="navbar">
<div class="right-menu" v-if="$store.state.settings.sidebarLogo">
<el-dropdown class="avatar-container" trigger="click" placement="top-start">
<el-dropdown
class="avatar-container"
trigger="click"
placement="top-start"
>
<div class="avatar-wrapper">
<el-avatar :size="size" :src="circleUrl" class="user-avatar" />
<!-- <i class="el-icon-caret-bottom" />-->
@@ -44,8 +48,14 @@ export default {
this.$store.dispatch('app/toggleSideBar')
},
async logout() {
await this.$store.dispatch('user/logout')
this.$router.push(`/login?redirect=${this.$route.fullPath}`)
this.$confirm('确定要退出吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
await this.$store.dispatch('user/logout')
this.$router.push(`/login?redirect=${this.$route.fullPath}`)
})
}
}
}

View File

@@ -4,7 +4,7 @@
<sidebar class="sidebar-container" />
<div class="main-container">
<div :class="{ 'fixed-header': fixedHeader }">
<navbar />
<!-- <navbar />-->
</div>
<app-main />
</div>

View File

@@ -2,14 +2,14 @@
<div class="login-container">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left">
<div class="title-container">
<h3 class="title">用户账号</h3>
<h3 class="title">欢迎登录</h3>
</div>
<el-form-item prop="username">
<el-form-item prop="userName">
<span class="svg-container">
<svg-icon icon-class="user" />
</span>
<el-input ref="username" v-model="loginForm.username" placeholder="Username" name="username" type="text" tabindex="1" auto-complete="on" />
<el-input ref="userName" v-model="loginForm.userName" placeholder="userName" name="userName" type="text" tabindex="1" auto-complete="on" />
</el-form-item>
<el-form-item prop="password">
@@ -33,23 +33,18 @@
</el-form-item>
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">登录</el-button>
<div class="tips">
<span style="margin-right:20px;">username: any</span>
<span>password: any</span>
</div>
</el-form>
</div>
</template>
<script>
import { validUsername } from '@/assets/js/utils/validate'
import { validuserName } from '@/assets/js/utils/validate'
// import { indexUser } from '@/api/app/user'
export default {
name: 'Login',
data() {
const validateUsername = (rule, value, callback) => {
if (!validUsername(value)) {
const validateuserName = (rule, value, callback) => {
if (!validuserName(value)) {
callback(new Error('Please enter the correct user name'))
} else {
callback()
@@ -64,11 +59,11 @@ export default {
}
return {
loginForm: {
username: 'admin',
password: '111111'
userName: '',
password: ''
},
loginRules: {
username: [{ required: true, trigger: 'blur', validator: validateUsername }],
userName: [{ required: true, trigger: 'blur' }],
password: [{ required: true, trigger: 'blur', validator: validatePassword }]
},
loading: false,
@@ -107,15 +102,15 @@ export default {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
// this.$store.dispatch('user/login', this.loginForm)
localStorage.setItem('token', 'MockToken')
// .then(() => {
this.$router.push({ path: '/' })
this.loading = false
// })
// .catch(() => {
// this.loading = false
// })
this.$store
.dispatch('user/login', this.loginForm)
.then(() => {
this.$router.push({ path: '/home' })
this.loading = false
})
.catch(() => {
this.loading = false
})
} else {
return false
}

View File

@@ -1,12 +1,17 @@
<!-- src/views/knowledge/detail/components/DocumentDrawer.vue -->
<template>
<el-drawer :visible.sync="visible" size="80%" @close="$emit('update:visible', false)">
<el-drawer
:visible.sync="visible"
size="80%"
@close="$emit('update:visible', false)"
>
<!-- drawer title -->
<template #title>
<div class="flex align-items-c">
<div class="el-icon-document"></div>
<div class="ml10">{{ descriptions.dataset.knowledgeName }}</div>
<el-tag type="info" class="ml10" size="mini"> {{ descriptions.dataset.segmentedMode | filterSegmentedMode }}
<el-tag type="info" class="ml10" size="mini">
{{ descriptions.dataset.segmentedMode | filterSegmentedMode }}
</el-tag>
</div>
</template>
@@ -15,7 +20,8 @@
<el-card :shadow="'none'">
<el-descriptions size="small" :column="4">
<el-descriptions-item label="文件名称">
<i class="el-icon-document"></i>{{ descriptions.dataset.knowledgeName }}
<i class="el-icon-document"></i
>{{ descriptions.dataset.knowledgeName }}
</el-descriptions-item>
<el-descriptions-item label="分段模式">
{{ descriptions.dataset.segmentedMode | filterSegmentedMode }}
@@ -120,13 +126,25 @@
<div class="segment-content-container">
<div class="segment-header mb15">
<div class="segment-summary">
<span>{{ descriptions.data ? descriptions.data.length : 0 }}个分段</span>
<span
>{{
descriptions.data ? descriptions.data.length : 0
}}个分段</span
>
</div>
</div>
<text-model v-if="descriptions.doc_form === 'text_model'" :descriptions="descriptions"
:documentDetail="documentDetail" :activeSegment="activeSegment" />
<q-a-model v-else-if="descriptions.doc_form === 'qa_model'" :descriptions="descriptions"
:documentDetail="documentDetail" :activeSegment="activeSegment" />
<text-model
v-if="descriptions.doc_form === 'text_model'"
:descriptions="descriptions"
:documentDetail="documentDetail"
:activeSegment="activeSegment"
/>
<q-a-model
v-else-if="descriptions.doc_form === 'qa_model'"
:descriptions="descriptions"
:documentDetail="documentDetail"
:activeSegment="activeSegment"
/>
</div>
</el-card>
<!-- 文件展示 -->
@@ -140,7 +158,10 @@
<script>
import TextModel from './TextModel.vue'
import QAModel from './QAModel.vue'
import { documentSourceOptions, segmentedModeOptionsMap } from '@/assets/js/utils/utilOptions'
import {
documentSourceOptions,
segmentedModeOptionsMap
} from '@/assets/js/utils/utilOptions'
import RenderFile from '@/components/RenderFile/Index.vue'
export default {
@@ -212,14 +233,18 @@ export default {
* @return { string } 预处理状态
*/
filterUseMineru(val) {
let item = segmentedModeOptionsMap.find(item => item.value === String(val))
let item = segmentedModeOptionsMap.find(
item => item.value === String(val)
)
return item ? item.label : '否'
}
},
methods: {
// 获取文档来源标签
getDocumentSourceLabel(sourceValue) {
const source = this.documentSourceOptions.find(item => item.value === String(sourceValue))
const source = this.documentSourceOptions.find(
item => item.value === String(sourceValue)
)
return source ? source.label : '未知来源'
}
}

View File

@@ -2,19 +2,35 @@
<div class="segment-split-view">
<div class="segment-list">
<el-checkbox-group v-model="selectedSegments">
<div class="segment-list-item" v-for="(segment, index) in descriptions.data" :key="index"
:class="{ active: activeSegment === index }">
<div
class="segment-list-item"
v-for="(segment, index) in descriptions.data"
:key="index"
:class="{ active: activeSegment === index }"
>
<el-checkbox class="mr pt15" :label="index">
<span class="el-icon-s-unfold"></span>
<span class="segment-number">分段 - {{ (index + 1).toString().padStart(2, '0') }}</span>
<span v-if="segment.word_count > 0"> · {{ segment.word_count }}个字符 ·</span>
<span class="segment-number"
>分段 - {{ (index + 1).toString().padStart(2, '0') }}</span
>
<span v-if="segment.word_count > 0">
· {{ segment.word_count }}个字符 ·</span
>
<span> {{ 0 }} 次召回次数</span>
</el-checkbox>
<div @click="handleSegmentClick(index)">
<p>{{ segment.content.slice(0, 20) + '.....' }}</p>
<div class="segment-keywords flex" v-if="segment.keywords && segment.keywords.length">
<p v-for="(item, index) in segment.keywords" :key="index" class="mr10">#{{ item }}</p>
<div
class="segment-keywords flex"
v-if="segment.keywords && segment.keywords.length"
>
<p
v-for="(item, index) in segment.keywords"
:key="index"
class="mr10"
>
#{{ item }}
</p>
</div>
</div>
</div>
@@ -22,23 +38,47 @@
</div>
<!-- 弹窗 -->
<r-dialog title="分段详情" :visible.sync="dialogVisible" width="50%" append-to-body :before-close="handleClose">
<div v-if="activeSegment !== null && descriptions.data && descriptions.data.length > 0">
<r-dialog
title="分段详情"
:visible.sync="dialogVisible"
width="50%"
append-to-body
:before-close="handleClose"
>
<div
v-if="
activeSegment !== null &&
descriptions.data &&
descriptions.data.length > 0
"
>
<div class="segment-content">
{{ descriptions.data[activeSegment].content }}
<div class="flex align-items-c mt20"
v-if="descriptions.data[activeSegment].keywords && descriptions.data[activeSegment].keywords.length"
style="flex-wrap: wrap">
<div
class="flex align-items-c mt20"
v-if="
descriptions.data[activeSegment].keywords &&
descriptions.data[activeSegment].keywords.length
"
style="flex-wrap: wrap"
>
<span>关键词 </span>
<el-tag v-for="(item, index) in descriptions.data[activeSegment].keywords" :key="index"
class="mr10 ellipsis" size="mini" type="primary">
<el-tag
v-for="(item, index) in descriptions.data[activeSegment].keywords"
:key="index"
class="mr10 ellipsis"
size="mini"
type="primary"
>
{{ item }}
</el-tag>
</div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="medium"> </el-button>
<el-button @click="dialogVisible = false" size="medium"
>关 闭</el-button
>
</span>
</r-dialog>
</div>

View File

@@ -4,47 +4,104 @@
<img :src="knowledgePng_1" class="header-icon" />
<div class="ml20" style="flex:1">
<div class="flex align-items-c">
<div class="mr20 header" v-if="!editKnowledge">{{ knowledgeName }}</div>
<el-input class="mr20 w400" size="small" v-else v-model="copyKnowledgeName">{{ knowledgeName }}</el-input>
<el-icon class="fs16 el-icon-edit-outline cursor-pointer" @click.native="editKnowledgeName"
v-if="!editKnowledge" />
<div v-else>
<el-button type="primary" size="medium" class="render-button" @click="saveKnowledgeName">保存</el-button>
<el-button size="medium" class="render-button" @click="cancelKnowledgeName">取消</el-button>
<div class="mr20 header" v-if="!editKnowledge">
{{ knowledgeName }}
</div>
<span class="segment-content">{{ segmentedMode | filterSegmentedMode }}</span>
<el-input
class="mr20 w400"
size="small"
v-else
v-model="copyKnowledgeName"
>{{ knowledgeName }}</el-input
>
<el-icon
class="fs16 el-icon-edit-outline cursor-pointer"
@click.native="editKnowledgeName"
v-if="!editKnowledge"
/>
<div v-else>
<el-button
type="primary"
size="medium"
class="render-button"
@click="saveKnowledgeName"
>保存</el-button
>
<el-button
size="medium"
class="render-button"
@click="cancelKnowledgeName"
>取消</el-button
>
</div>
<span class="segment-content">{{
segmentedMode | filterSegmentedMode
}}</span>
</div>
<p class="mt10 fs14" style="line-height: 20px">描述{{ knowledgeDesc }}</p>
<p class="mt10 fs14" style="line-height: 20px">
描述{{ knowledgeDesc }}
</p>
<!-- <p class="mt10 fs14" style="line-height: 20px">分段模式{{ segmentedMode | filterSegmentedMode }}</p>-->
</div>
<div>
<!-- <el-button type="primary" size="medium" class="normal-button" @click="jumpEditKnowledge">修改知识库</el-button>-->
<el-button type="primary" size="medium" icon="el-icon-edit-outline" class="primary-button"
@click="jumpEditKnowledge">修改知识库</el-button>
<el-button type="primary" size="medium" icon="el-icon-plus" class="primary-button"
@click="jumpAddKnowledge">上传知识</el-button>
<el-button
type="primary"
size="medium"
icon="el-icon-edit-outline"
class="primary-button"
@click="jumpEditKnowledge"
>修改知识库</el-button
>
<el-button
type="primary"
size="medium"
icon="el-icon-plus"
class="primary-button"
@click="jumpAddKnowledge"
>上传知识</el-button
>
</div>
</div>
<div class="mt20 card-body">
<el-empty v-if="!hasList">
<div class="mt20">
<el-button type="primary" size="medium" class="fs14" @click="jumpAddKnowledge">立即添加</el-button>
<el-button
type="primary"
size="medium"
class="fs14"
@click="jumpAddKnowledge"
>立即添加</el-button
>
</div>
</el-empty>
<div class="table-container" v-else>
<div class="flex align-items-c justify-content-b">
<el-form :model="form" label-width="100px" label-position="top" inline>
<el-form
:model="form"
label-width="100px"
label-position="top"
inline
>
<el-form-item label="知识文件名称" prop="fileName">
<el-input v-model="form.knowledgeNameLike" size="medium" placeholder="请输入知识文件名称"
@keydown.enter.native="search"></el-input>
<el-input
v-model="form.knowledgeNameLike"
size="medium"
placeholder="请输入知识文件名称"
@keydown.enter.native="search"
></el-input>
</el-form-item>
<el-form-item label="知识文件来源" prop="documentSource">
<el-select v-model="form.documentSource" size="medium">
<el-option label="全部" value=""></el-option>
<el-option v-for="item in documentSourceOptions" :label="item.label" :value="item.value"
:key="item.value"></el-option>
<el-option
v-for="item in documentSourceOptions"
:label="item.label"
:value="item.value"
:key="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="上传用户" prop="createdUserLike">
@@ -53,32 +110,73 @@
</el-select>
</el-form-item>
<el-form-item label="关键字">
<el-input v-model="form.name" placeholder="请输入关键字/敏感词" disabled size="medium"></el-input>
<el-input
v-model="form.name"
placeholder="请输入关键字/敏感词"
disabled
size="medium"
></el-input>
</el-form-item>
<el-form-item label="上传时间" prop="times">
<el-date-picker size="medium" style="width:100%" v-model="form.times" value-format="yyyy-MM-dd"
start-placeholder="开始时间" end-placeholder="结束时间" type="daterange"></el-date-picker>
<el-date-picker
size="medium"
style="width:100%"
v-model="form.times"
value-format="yyyy-MM-dd"
start-placeholder="开始时间"
end-placeholder="结束时间"
type="daterange"
></el-date-picker>
</el-form-item>
</el-form>
<div class="mt15 flex align-items-c justify-content-b">
<el-button size="medium" type="primary" @click="search">查询</el-button>
<el-button size="medium" type="primary" @click="search"
>查询</el-button
>
<el-button size="medium" @click="reset">重置</el-button>
</div>
</div>
<r-table :columns="columns" :data="list" :deletion="false" :total="total" @page-change="pageChange"
@current-change="currentChange" :current-page="page" :page-size="pageSize"></r-table>
<r-table
:columns="columns"
:data="list"
:deletion="false"
:total="total"
@page-change="pageChange"
@current-change="currentChange"
:current-page="page"
:page-size="pageSize"
></r-table>
</div>
</div>
<document-drawer :visible.sync="drawer" :descriptions="descriptions" :document-detail="documentDetail"
:active-segment="activeSegment" @update:visible="val => (drawer = val)" />
<document-drawer
:visible.sync="drawer"
:descriptions="descriptions"
:document-detail="documentDetail"
:active-segment="activeSegment"
@update:visible="val => (drawer = val)"
/>
<knowledgeForm :visible.sync="drawerForm" :datasetId="$route.query.datasetId" @update:visible="getKnowledgeDetail">
<knowledgeForm
:visible.sync="drawerForm"
:datasetId="$route.query.datasetId"
@update:visible="getKnowledgeDetail"
>
</knowledgeForm>
</div>
</template>
<script>
import { datasetDocumentEx, datasetQueryDelete, datasetQuerySegments, datasetsExPages, datasetUpdate, getDatasetById } from '@/api/generatedApi/index'
import { documentSourceOptions, segmentedModeOptionsMap } from '@/assets/js/utils/utilOptions'
import {
datasetDocumentEx,
datasetQueryDelete,
datasetQuerySegments,
datasetsExPages,
datasetUpdate,
getDatasetById
} from '@/api/generatedApi/index'
import {
documentSourceOptions,
segmentedModeOptionsMap
} from '@/assets/js/utils/utilOptions'
import DocumentDrawer from './components/documentDetail/DocumentDrawer.vue'
import knowledgeForm from '@/views/knowledge/detail/components/knowledgeForm.vue'
import knowledgePng_1 from '@/assets/images/konwledge/konwledge-1.png'
@@ -211,8 +309,12 @@ export default {
datasetId,
page: this.page,
pageSize: this.pageSize,
startCreatedDate: this.form.times[0] ? this.form.times[0] + ' 00:00:00' : '',
endCreatedDate: this.form.times[1] ? this.form.times[1] + ' 23:59:59' : ''
startCreatedDate: this.form.times[0]
? this.form.times[0] + ' 00:00:00'
: '',
endCreatedDate: this.form.times[1]
? this.form.times[1] + ' 23:59:59'
: ''
}).then(res => {
this.list = res.content.content.list
this.total = res.content.content.total
@@ -242,7 +344,7 @@ export default {
segmentedMode: this.segmentedMode
},
...row,
...res.content.content,
...res.content.content
}
this.drawer = true
@@ -274,7 +376,10 @@ export default {
try {
// 尝试解析JSON字符串
if (typeof rule === 'string' && (rule.startsWith('{') || rule.startsWith('['))) {
if (
typeof rule === 'string' &&
(rule.startsWith('{') || rule.startsWith('['))
) {
const parsedRule = JSON.parse(rule)
return JSON.stringify(parsedRule, null, 2)
}
@@ -316,11 +421,13 @@ export default {
}
},
filterUseMineru(val) {
let item = segmentedModeOptionsMap.find(item => item.value === String(val))
let item = segmentedModeOptionsMap.find(
item => item.value === String(val)
)
return item ? item.label : '否'
}
},
created() { },
created() {},
async mounted() {
this.getKnowledgeDetail()
// 获取知识库文件列表
@@ -353,7 +460,9 @@ export default {
prop: 'documentSource',
width: '200px',
render: (h, params) => {
let text = documentSourceOptions.find(item => item.value === String(params.row.documentSource)).label
let text = documentSourceOptions.find(
item => item.value === String(params.row.documentSource)
).label
return h('div', [h('span', text)])
}
},
@@ -368,7 +477,10 @@ export default {
key: '上传用户',
prop: 'createdUser',
render: (h, params) => {
return h('div', !params.row.createdUser ? '-' : params.row.createdUser)
return h(
'div',
!params.row.createdUser ? '-' : params.row.createdUser
)
}
},
{
@@ -382,33 +494,32 @@ export default {
isRedraw: true,
render: (h, params) => {
return h('div', [
h('el-button', {
class: 'floatSpan',
props: {
type: 'danger',
size: 'mini',
icon: 'el-icon-delete',
title: '删除'
},
on: {
click: () => this.deleteKnowledge(params.row)
}
}),
h(
'el-button',
{
class: 'floatSpan',
class: 'normal-button',
props: {
type: 'danger',
type: 'primary',
size: 'mini',
icon: 'el-icon-delete',
title: '删除'
disabled: true,
icon: 'el-icon-edit-outline',
title: '编辑'
},
on: {
click: () => this.deleteKnowledge(params.row)
}
on: {}
},
),
h('el-button', {
class: 'normal-button',
props: {
type: 'primary',
size: 'mini',
disabled: true,
icon: 'el-icon-edit-outline',
title: '编辑'
},
on: {}
}, "编辑"
'编辑'
),
h(
'el-button',

View File

@@ -39,11 +39,20 @@ export default {
},
computed: {
documentStatus() {
if (this.form.documentStatus === 0 || this.form.documentStatus === '上传中') {
if (
this.form.documentStatus === 0 ||
this.form.documentStatus === '上传中'
) {
return '上传中'
} else if (this.form.documentStatus === 1 || this.form.documentStatus === '成功') {
} else if (
this.form.documentStatus === 1 ||
this.form.documentStatus === '成功'
) {
return '成功'
} else if (this.form.documentStatus === -1 || this.form.documentStatus === '失败') {
} else if (
this.form.documentStatus === -1 ||
this.form.documentStatus === '失败'
) {
return '失败'
}
return this.form.documentStatus
@@ -95,22 +104,38 @@ export default {
</div>
<el-form ref="form" :model="newForm" label-width="120px">
<el-form-item label="知识库">
<el-input v-model="newForm.datasetName" placeholder="" disabled></el-input>
<el-input
v-model="newForm.datasetName"
placeholder=""
disabled
></el-input>
</el-form-item>
<el-form-item label="知识文件名称">
<el-input v-model="newForm.fileName" placeholder="" disabled></el-input>
<el-input
v-model="newForm.fileName"
placeholder=""
disabled
></el-input>
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="上传用户">
<el-input v-model="newForm.userName" placeholder="" disabled></el-input>
<el-input
v-model="newForm.userName"
placeholder=""
disabled
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="上传时间">
<el-input v-model="newForm.createdDate" placeholder="" disabled></el-input>
<el-input
v-model="newForm.createdDate"
placeholder=""
disabled
></el-input>
</el-form-item>
</el-col>
</el-row>
@@ -118,12 +143,20 @@ export default {
<el-row>
<el-col :span="12">
<el-form-item label="任务号">
<el-input v-model="newForm.id" placeholder="" disabled></el-input>
<el-input
v-model="newForm.id"
placeholder=""
disabled
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="分段模式">
<el-input v-model="newForm.segmentedMode" placeholder="null" disabled></el-input>
<el-input
v-model="newForm.segmentedMode"
placeholder="null"
disabled
></el-input>
</el-form-item>
</el-col>
</el-row>
@@ -131,18 +164,32 @@ export default {
<el-row>
<el-col :span="12">
<el-form-item label="高级模式">
<el-input v-model="useMineru" placeholder="" disabled></el-input>
<el-input
v-model="useMineru"
placeholder=""
disabled
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="任务状态">
<el-input v-model="documentStatus" placeholder="" disabled></el-input>
<el-input
v-model="documentStatus"
placeholder=""
disabled
></el-input>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="失败原因" v-if="newForm.documentStatus === -1">
<el-input v-model="newForm.errMessage" placeholder=""></el-input>
<el-form-item
label="失败原因"
v-if="newForm.documentStatus === -1"
>
<el-input
v-model="newForm.errMessage"
placeholder=""
></el-input>
</el-form-item>
</el-form>
</el-card>
@@ -153,8 +200,14 @@ export default {
<span>知识内容</span>
</div>
<div class="knowledge-content" v-if="descriptions">
<text-model v-if="descriptions.doc_form === 'text_model'" :descriptions="descriptions" />
<q-a-model v-else-if="descriptions.doc_form === 'qa_model'" :descriptions="descriptions" />
<text-model
v-if="descriptions.doc_form === 'text_model'"
:descriptions="descriptions"
/>
<q-a-model
v-else-if="descriptions.doc_form === 'qa_model'"
:descriptions="descriptions"
/>
</div>
<span v-else>暂无知识内容</span>
</el-card>