<template>
|
<div>
|
<div v-if="currentView === 'list'">
|
<!-- 顶部操作栏 -->
|
<div class="header-container">
|
<div class="search-container">
|
<el-input
|
style="width: 200px;"
|
v-model="TreeDataPool.searchInput"
|
placeholder="搜索"
|
clearable
|
@input="querySearchAsync('camera')"
|
>
|
<i class="el-icon-search el-input__icon" style="color: #dcdfe6" slot="prefix" @click="searchAreaData"></i>
|
</el-input>
|
</div>
|
<div class="button-container">
|
<el-button type="primary" @click="openDialog('create')">
|
<i class="el-icon-plus"></i> 创建知识库
|
</el-button>
|
</div>
|
</div>
|
|
<!-- 通用弹窗组件 -->
|
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="600px" custom-class="custom-dialog"
|
@open="handleDialogOpen" @closed="handleDialogClose">
|
<el-form label-width="100px" :disabled="isViewMode" ref="form" :rules="formRules" :model="currentFile">
|
<el-form-item label="名称" prop="title">
|
<el-input v-model="currentFile.title" placeholder="请输入知识库名称" />
|
</el-form-item>
|
|
<el-form-item label="描述" prop="content">
|
<el-input type="textarea" :rows="6" v-model="currentFile.content" resize="none">
|
</el-input>
|
</el-form-item>
|
</el-form>
|
|
<span slot="footer" class="dialog-footer">
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
<el-button type="primary" @click="handleConfirm" v-if="!isViewMode">
|
确 定
|
</el-button>
|
</span>
|
</el-dialog>
|
|
<div class="file-manager-container">
|
<div class="file-grid" v-if="files.length > 0">
|
<div v-for="(file, index) in files" :key="file.checkId" class="file-item">
|
<!-- 修改这里:修复点击事件 -->
|
<div class="card-container" @click="handleCardClick(file, $event)">
|
<!-- 卡片头部 -->
|
<div class="card-top">
|
<div class="card-top-left">
|
<div class="card-icon"><i class="el-icon-folder"></i></div>
|
<div class="card-name">{{ file.title }}</div>
|
</div>
|
|
<!-- 修改后的下拉菜单 - 鼠标移入触发 -->
|
<div class="card-settings" @mouseenter="showDropdown(index)" @mouseleave="hideDropdown(index)">
|
<i class="el-icon-more"></i>
|
<div class="dropdown-menu" v-show="activeDropdown === index">
|
<ul>
|
<li @click.stop="openDialog('edit', file)">
|
<i class="el-icon-edit"></i> 编辑
|
</li>
|
<li @click.stop="handleView(file)">
|
<i class="el-icon-view"></i> 查看
|
</li>
|
<li @click.stop="handleDelete(file)">
|
<i class="el-icon-delete"></i> 删除
|
</li>
|
</ul>
|
</div>
|
</div>
|
</div>
|
|
<!-- 卡片标签区域 -->
|
<div class="card-label"></div>
|
|
<!-- 卡片描述区域 -->
|
<div class="card-des"></div>
|
|
<!-- 卡片底部信息 -->
|
<div class="card-bottom">
|
<div class="bottom-left">
|
<div>{{ file.documentCount || 0 }} 文档</div>
|
<div>{{ file.updateTime || '无更新' }}</div>
|
</div>
|
<div class="bottom-right"></div>
|
</div>
|
</div>
|
</div>
|
</div>
|
|
<!-- 新增空状态显示 -->
|
<el-empty v-else description="暂无数据"></el-empty>
|
</div>
|
<div class="footer-container">
|
<el-pagination @current-change="handleCurrentChange" :page-size="16" layout="prev, pager, next"
|
:total="pagination.total">
|
</el-pagination>
|
</div>
|
</div>
|
<!-- 知识库详情视图 -->
|
<KnowLedgeFilesView v-if="currentView === 'detail'" :knowledge-id="currentKnowledgeId" @back="handleBackToList" />
|
</div>
|
</template>
|
|
<script>
|
import FileAPI from '@/api/KnowledgeView.ts'
|
import KnowLedgeFilesView from './KnowLedgeFilesView.vue' // 引入组件
|
|
export default {
|
components: {
|
KnowLedgeFilesView
|
},
|
data() {
|
return {
|
currentView: 'list', // 'list' 或 'detail'
|
currentKnowledgeId: null,
|
formRules: {},
|
pagination: {
|
page: 1,
|
pageSize: 16,
|
total: 0,
|
totalPage: 1
|
},
|
dialogVisible: false,
|
dialogType: 'create',
|
currentFile: this.initFile(),
|
form: {
|
fileName: '',
|
checkContent: '',
|
rangeValue: 0
|
},
|
files: [],
|
userName: 'user', // 用户名,实际应用中应从用户信息获取
|
TreeDataPool: {
|
searchInput: ''
|
},
|
activeDropdown: -1 // 当前显示下拉菜单的卡片索引
|
}
|
},
|
mounted() {
|
this.fetchFiles()
|
},
|
computed: {
|
dialogTitle() {
|
return {
|
create: '创建知识库',
|
edit: '编辑知识库',
|
view: '知识库详情'
|
}[this.dialogType]
|
},
|
isViewMode() {
|
return this.dialogType === 'view'
|
}
|
},
|
methods: {
|
// 处理卡片点击事件
|
handleCardClick(file, event) {
|
// 检查点击是否发生在设置图标上
|
const isSettingsClick = event.target.closest('.card-settings');
|
|
if (!isSettingsClick) {
|
this.handleView(file);
|
}
|
},
|
|
// 显示下拉菜单
|
showDropdown(index) {
|
this.activeDropdown = index;
|
},
|
|
// 隐藏下拉菜单
|
hideDropdown(index) {
|
if (this.activeDropdown === index) {
|
this.activeDropdown = -1;
|
}
|
},
|
|
handleDialogOpen() {
|
this.$nextTick(() => {
|
this.$refs.form.clearValidate()
|
})
|
},
|
handleDialogClose() {
|
this.$refs.form.resetFields()
|
this.formRules = {}
|
},
|
async fetchFiles() {
|
try {
|
const res = await FileAPI.getKnowledges({
|
page: this.pagination.page,
|
pageSize: this.pagination.pageSize
|
})
|
const totalPage = res.data.pagination.totalPage
|
const currentPage = this.pagination.page > totalPage
|
? totalPage
|
: res.data.pagination.page
|
this.pagination = {
|
...this.pagination,
|
page: currentPage,
|
total: res.data.pagination.total,
|
totalPage: totalPage
|
}
|
|
// 添加模拟数据字段
|
this.files = res.data.list.map(file => {
|
return {
|
...file,
|
documentCount: Math.floor(Math.random() * 100), // 模拟文档数量
|
updateTime: this.formatDate(new Date()) // 模拟更新时间
|
}
|
})
|
|
this.pagination.total = res.data.pagination.total
|
this.pagination.totalPage = res.data.pagination.totalPage
|
} catch (error) {
|
this.$message.error('获取列表失败')
|
console.error('API Error:', error)
|
}
|
},
|
formatDate(date) {
|
// 格式化日期为 YYYY-MM-DD HH:mm:ss
|
const year = date.getFullYear()
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
const day = String(date.getDate()).padStart(2, '0')
|
const hours = String(date.getHours()).padStart(2, '0')
|
const minutes = String(date.getMinutes()).padStart(2, '0')
|
const seconds = String(date.getSeconds()).padStart(2, '0')
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
},
|
initFile() {
|
return {
|
id: null,
|
title: '',
|
content: ''
|
}
|
},
|
openDialog(type, file = null) {
|
if (type != 'view') {
|
this.formRules = {
|
title: [
|
{ required: true, message: '文件名称不能为空', trigger: 'blur' }
|
],
|
content: [
|
{ required: true, message: '检测内容不能为空', trigger: 'blur' }
|
]
|
}
|
}
|
this.dialogType = type
|
this.currentFile = file ? { ...file } : this.initFile()
|
this.dialogVisible = true
|
},
|
handleConfirm() {
|
this.$refs.form.validate(valid => {
|
if (valid) {
|
const operation = {
|
create: this.createFile,
|
edit: this.updateFile
|
}[this.dialogType]
|
|
operation && operation(this.currentFile)
|
this.dialogVisible = false
|
}
|
})
|
},
|
async createFile(file) {
|
try {
|
await FileAPI.createKnowledge({
|
title: file.title,
|
content: file.content
|
})
|
this.$message.success('添加成功')
|
this.fetchFiles()
|
} catch (error) {
|
this.$message.error('添加失败')
|
console.error('API Error:', error)
|
}
|
},
|
async updateFile(file) {
|
try {
|
await FileAPI.updateKnowledge({
|
id: file.id,
|
title: file.title,
|
content: file.content
|
})
|
this.$message.success('编辑成功')
|
this.fetchFiles()
|
} catch (error) {
|
this.$message.error('编辑失败')
|
console.error('API Error:', error)
|
}
|
},
|
handleCurrentChange(page) {
|
this.pagination.page = page
|
this.fetchFiles()
|
},
|
async handleDelete(file) {
|
this.$confirm('即将删除该知识库及其所有文件,所有内容将无法找回, 是否继续?', '提示', {
|
confirmButtonText: '确定',
|
cancelButtonText: '取消',
|
type: 'warning'
|
}).then(async () => {
|
try {
|
await FileAPI.deleteKnowledgee({
|
id: file.id
|
}).then((rsp) => {
|
if (rsp && rsp.status === 200) {
|
// 删除成功后自动修正页码
|
if (this.files.length === 1 && this.pagination.page > 1) {
|
this.pagination.page -= 1
|
}
|
this.fetchFiles()
|
|
this.$message({
|
type: "success",
|
message: "删除成功"
|
})
|
} else {
|
this.$message({
|
type: "error",
|
message: rsp.msg
|
})
|
}
|
})
|
} catch (error) {
|
this.$message.error('删除失败')
|
console.error('API Error:', error)
|
}
|
}).catch(() => {
|
// this.$message({
|
// type: 'info',
|
// message: '已取消删除'
|
// });
|
});
|
},
|
// 处理查看知识库详情
|
handleView(file) {
|
this.currentKnowledgeId = file.id
|
this.currentView = 'detail'
|
},
|
// 返回列表方法
|
handleBackToList() {
|
this.currentView = 'list'
|
},
|
// 搜索方法(模拟)
|
querySearchAsync(type) {
|
console.log('Searching for:', this.TreeDataPool.searchInput)
|
},
|
searchAreaData() {
|
console.log('Search button clicked')
|
}
|
}
|
}
|
</script>
|
<style lang="scss" scoped>
|
/* 顶部操作栏样式 */
|
.header-container {
|
display: flex;
|
justify-content: flex-end;
|
margin: 20px;
|
}
|
|
.search-container {
|
flex: 1;
|
max-width: 300px;
|
}
|
|
.button-container {
|
margin-left: 20px;
|
}
|
|
/* 文件网格布局 */
|
.file-grid {
|
display: grid;
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
gap: 20px;
|
padding: 0 20px;
|
}
|
|
/* 卡片容器样式 */
|
.card-container {
|
margin: 5px;
|
background: #ffffff;
|
border-radius: 8px;
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
padding: 20px;
|
height: 220px;
|
display: flex;
|
flex-direction: column;
|
transition: all 0.3s ease;
|
position: relative;
|
cursor: pointer; /* 添加指针样式表明可点击 */
|
|
// &:hover {
|
// transform: translateY(-5px);
|
// box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
// }
|
}
|
|
/* 卡片顶部区域 */
|
.card-top {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 15px;
|
}
|
|
.card-top-left {
|
display: flex;
|
align-items: center;
|
}
|
|
.card-icon {
|
width: 44px;
|
height: 44px;
|
// background-color: #e6f7ff;
|
border-radius: 6px;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
margin-right: 12px;
|
|
i {
|
font-size: 20px;
|
color: #1890ff;
|
}
|
}
|
|
.card-name {
|
font-size: 16px;
|
font-weight: 600;
|
color: #333;
|
}
|
|
/* 设置图标 */
|
.card-settings {
|
cursor: pointer;
|
padding: 5px;
|
border-radius: 4px;
|
transition: background 0.2s;
|
position: relative;
|
z-index: 2; /* 确保下拉菜单在卡片上方 */
|
|
&:hover {
|
background: #f5f7fa;
|
}
|
|
i {
|
font-size: 20px;
|
color: #666;
|
}
|
}
|
|
/* 卡片标签 */
|
.card-label {
|
font-size: 14px;
|
// color: #1890ff;
|
// background-color: #e6f7ff;
|
padding: 4px 10px;
|
border-radius: 4px;
|
display: inline-flex;
|
align-items: center;
|
margin-bottom: 15px;
|
width: fit-content;
|
}
|
|
/* 卡片描述 */
|
.card-des {
|
flex: 1;
|
font-size: 14px;
|
color: #666;
|
line-height: 1.5;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
display: -webkit-box;
|
-webkit-line-clamp: 3;
|
-webkit-box-orient: vertical;
|
margin-bottom: 15px;
|
}
|
|
/* 卡片底部 */
|
.card-bottom {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-top: auto;
|
padding-top: 15px;
|
border-top: 1px solid #eee;
|
color: #999;
|
font-size: 13px;
|
}
|
|
.bottom-left {
|
display: flex;
|
gap: 15px;
|
}
|
|
/* 下拉菜单样式 */
|
.dropdown-menu {
|
position: absolute;
|
top: 100%;
|
right: 0;
|
z-index: 1000;
|
min-width: 100px;
|
background: #fff;
|
border-radius: 4px;
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
|
padding: 5px 0;
|
|
ul {
|
list-style: none;
|
padding: 0;
|
margin: 0;
|
|
li {
|
padding: 8px 16px;
|
font-size: 14px;
|
color: #333;
|
cursor: pointer;
|
display: flex;
|
align-items: center;
|
|
i {
|
margin-right: 8px;
|
font-size: 14px;
|
}
|
|
&:hover {
|
background-color: #f5f7fa;
|
color: #409EFF;
|
}
|
}
|
}
|
}
|
|
.footer-container {
|
margin-top: 30px;
|
padding: 0 20px;
|
text-align: center;
|
}
|
|
/* 弹窗样式 */
|
:deep(.custom-dialog) {
|
border-radius: 8px;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
.el-dialog__header {
|
border-bottom: 1px solid #ebeef5;
|
padding: 16px 20px;
|
|
.el-dialog__title {
|
font-size: 16px;
|
color: #303133;
|
}
|
}
|
|
.el-dialog__body {
|
padding: 20px 30px;
|
}
|
|
.dialog-input {
|
width: 80%;
|
|
.el-input__inner {
|
border-color: #dcdfe6;
|
}
|
}
|
|
.dialog-textarea {
|
.el-textarea__inner {
|
font-family: 'Microsoft YaHei', sans-serif;
|
line-height: 1.6;
|
color: #606266;
|
border-color: #dcdfe6;
|
}
|
}
|
|
.dialog-slider {
|
width: 70%;
|
margin-right: 20px;
|
flex: 1; // 滑块自适应宽度
|
}
|
|
.score-value {
|
color: rgb(4, 8, 12);
|
font-weight: bold;
|
margin-right: 15px;
|
}
|
|
.dialog-footer {
|
.el-button {
|
padding: 10px 20px;
|
|
&--primary {
|
background: #409EFF;
|
border-color: #409EFF;
|
}
|
}
|
}
|
}
|
|
.content-pre {
|
white-space: pre-wrap;
|
word-break: break-word;
|
margin: 0;
|
font-size: 13px;
|
line-height: 1.6;
|
}
|
</style>
|