|
|
|
|
@@ -11,49 +11,72 @@
|
|
|
|
|
<!-- 筛选区域 -->
|
|
|
|
|
<el-card class="filter-card">
|
|
|
|
|
<div class="ticket-filters">
|
|
|
|
|
<el-date-picker v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" style="width: 280px;" />
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="dateRange"
|
|
|
|
|
type="daterange"
|
|
|
|
|
range-separator="至"
|
|
|
|
|
start-placeholder="开始日期"
|
|
|
|
|
end-placeholder="结束日期"
|
|
|
|
|
style="width: 280px;"
|
|
|
|
|
@change="handleSearch"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<div class="filter-right">
|
|
|
|
|
<el-select v-model="operationFilter" placeholder="操作类型" clearable style="width: 140px;">
|
|
|
|
|
<el-select v-model="filter.action" placeholder="操作类型" clearable style="width: 140px;" @change="handleSearch">
|
|
|
|
|
<el-option label="上传" value="upload" />
|
|
|
|
|
<el-option label="下载" value="download" />
|
|
|
|
|
<el-option label="删除" value="delete" />
|
|
|
|
|
<el-option label="更新" value="update" />
|
|
|
|
|
</el-select>
|
|
|
|
|
<el-select v-model="kbTypeFilter" placeholder="知识库类型" clearable style="width: 140px;">
|
|
|
|
|
<el-option label="外部知识库" value="external" />
|
|
|
|
|
<el-option label="内部知识库" value="internal" />
|
|
|
|
|
<el-select v-model="filter.knowledgeId" placeholder="知识库" clearable style="width: 180px;" @change="handleSearch">
|
|
|
|
|
<el-option
|
|
|
|
|
v-for="kb in knowledgeList"
|
|
|
|
|
:key="kb.knowledgeId"
|
|
|
|
|
:label="kb.title"
|
|
|
|
|
:value="kb.knowledgeId"
|
|
|
|
|
/>
|
|
|
|
|
</el-select>
|
|
|
|
|
<el-input v-model="searchKeyword" placeholder="搜索文件名/操作人" style="width: 200px;" :prefix-icon="Search" clearable />
|
|
|
|
|
<el-input
|
|
|
|
|
v-model="filter.fileName"
|
|
|
|
|
placeholder="搜索文件名"
|
|
|
|
|
style="width: 200px;"
|
|
|
|
|
:prefix-icon="Search"
|
|
|
|
|
clearable
|
|
|
|
|
@clear="handleSearch"
|
|
|
|
|
@keyup.enter="handleSearch"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
<!-- 日志列表 -->
|
|
|
|
|
<el-card>
|
|
|
|
|
<el-table :data="filteredLogs" style="width: 100%">
|
|
|
|
|
<el-table-column prop="logId" label="日志ID" width="120">
|
|
|
|
|
<el-card v-loading="loading">
|
|
|
|
|
<el-table :data="logs" style="width: 100%">
|
|
|
|
|
<el-table-column prop="logId" label="日志ID" width="180">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<span style="color: #409eff; font-weight: 500;">{{ row.logId }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="fileName" label="文件名" min-width="200" />
|
|
|
|
|
<el-table-column prop="operation" label="操作类型" width="100">
|
|
|
|
|
<el-table-column prop="fileName" label="文件名" min-width="200" show-overflow-tooltip />
|
|
|
|
|
<el-table-column prop="action" label="操作类型" width="100">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-tag :type="getOperationType(row.operation)" size="small">
|
|
|
|
|
{{ row.operationName }}
|
|
|
|
|
<el-tag :type="getOperationType(row.action)" size="small">
|
|
|
|
|
{{ getOperationName(row.action) }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="kbType" label="知识库" width="100">
|
|
|
|
|
<el-table-column prop="version" label="版本" width="80">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<span>{{ row.kbType === 'external' ? '外部' : '内部' }}</span>
|
|
|
|
|
<span>v{{ row.version || 1 }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="operator" label="操作人" width="100" />
|
|
|
|
|
<el-table-column prop="operationTime" label="操作时间" width="160" />
|
|
|
|
|
<el-table-column prop="fileSize" label="文件大小" width="100" />
|
|
|
|
|
<el-table-column label="操作" width="120" fixed="right">
|
|
|
|
|
<el-table-column prop="creatorName" label="操作人" width="120" />
|
|
|
|
|
<el-table-column prop="createTime" label="操作时间" width="180">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
{{ formatTime(row.createTime) }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="操作" width="100" fixed="right">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-button type="primary" link size="small" @click="viewDetail(row)">详情</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
@@ -61,7 +84,15 @@
|
|
|
|
|
</el-table>
|
|
|
|
|
|
|
|
|
|
<div class="table-pagination">
|
|
|
|
|
<el-pagination v-model:current-page="currentPage" :page-size="10" :total="logs.length" layout="total, prev, pager, next" />
|
|
|
|
|
<el-pagination
|
|
|
|
|
v-model:current-page="pagination.page"
|
|
|
|
|
v-model:page-size="pagination.pageSize"
|
|
|
|
|
:page-sizes="[10, 20, 50]"
|
|
|
|
|
:total="pagination.total"
|
|
|
|
|
layout="total, sizes, prev, pager, next"
|
|
|
|
|
@size-change="loadLogs"
|
|
|
|
|
@current-change="loadLogs"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</el-card>
|
|
|
|
|
</div>
|
|
|
|
|
@@ -76,19 +107,22 @@
|
|
|
|
|
<span>{{ selectedLog.fileName }}</span>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="操作类型">
|
|
|
|
|
<el-tag :type="getOperationType(selectedLog.operation)">{{ selectedLog.operationName }}</el-tag>
|
|
|
|
|
<el-tag :type="getOperationType(selectedLog.action)">{{ getOperationName(selectedLog.action) }}</el-tag>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="知识库">
|
|
|
|
|
<span>{{ selectedLog.kbType === 'external' ? '外部知识库' : '内部知识库' }}</span>
|
|
|
|
|
<el-form-item label="文件版本">
|
|
|
|
|
<span>v{{ selectedLog.version || 1 }}</span>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="操作人">
|
|
|
|
|
<span>{{ selectedLog.operator }}</span>
|
|
|
|
|
<span>{{ selectedLog.creatorName }}</span>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="操作时间">
|
|
|
|
|
<span>{{ selectedLog.operationTime }}</span>
|
|
|
|
|
<span>{{ formatTime(selectedLog.createTime) }}</span>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="文件大小">
|
|
|
|
|
<span>{{ selectedLog.fileSize }}</span>
|
|
|
|
|
<el-form-item label="知识库ID">
|
|
|
|
|
<span>{{ selectedLog.knowledgeId }}</span>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="文件ID">
|
|
|
|
|
<span>{{ selectedLog.fileId }}</span>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
@@ -96,63 +130,140 @@
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref, computed } from 'vue'
|
|
|
|
|
import { ref, reactive, onMounted } from 'vue'
|
|
|
|
|
import AdminLayout from '@/views/admin/AdminLayout.vue'
|
|
|
|
|
import { Download, Search } from 'lucide-vue-next'
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
import { aiKnowledgeAPI } from 'shared/api/ai'
|
|
|
|
|
import type { TbKnowledgeFileLog, TbKnowledge } from 'shared/types'
|
|
|
|
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
const dateRange = ref<[Date, Date] | null>(null)
|
|
|
|
|
const operationFilter = ref('')
|
|
|
|
|
const kbTypeFilter = ref('')
|
|
|
|
|
const searchKeyword = ref('')
|
|
|
|
|
const currentPage = ref(1)
|
|
|
|
|
const showDetailDialog = ref(false)
|
|
|
|
|
const selectedLog = ref<any>(null)
|
|
|
|
|
const selectedLog = ref<TbKnowledgeFileLog | null>(null)
|
|
|
|
|
|
|
|
|
|
const logs = ref([
|
|
|
|
|
{ logId: 'LOG001', fileName: 'TH-500GF操作手册.pdf', operation: 'upload', operationName: '上传', kbType: 'external', operator: '张三', operationTime: '2024-12-10 14:30', fileSize: '2.5MB' },
|
|
|
|
|
{ logId: 'LOG002', fileName: 'TH-300D故障排查指南.pdf', operation: 'upload', operationName: '上传', kbType: 'external', operator: '李四', operationTime: '2024-12-09 10:15', fileSize: '1.8MB' },
|
|
|
|
|
{ logId: 'LOG003', fileName: '内部技术规范v2.0.pdf', operation: 'update', operationName: '更新', kbType: 'internal', operator: '赵六', operationTime: '2024-12-11 16:20', fileSize: '3.2MB' },
|
|
|
|
|
{ logId: 'LOG004', fileName: '售后服务流程.pdf', operation: 'download', operationName: '下载', kbType: 'internal', operator: '孙七', operationTime: '2024-12-10 11:00', fileSize: '1.1MB' },
|
|
|
|
|
{ logId: 'LOG005', fileName: '员工培训手册.pdf', operation: 'delete', operationName: '删除', kbType: 'internal', operator: '周八', operationTime: '2024-12-09 15:30', fileSize: '2.0MB' }
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
const filteredLogs = computed(() => {
|
|
|
|
|
let result = logs.value
|
|
|
|
|
if (operationFilter.value) {
|
|
|
|
|
result = result.filter(l => l.operation === operationFilter.value)
|
|
|
|
|
}
|
|
|
|
|
if (kbTypeFilter.value) {
|
|
|
|
|
result = result.filter(l => l.kbType === kbTypeFilter.value)
|
|
|
|
|
}
|
|
|
|
|
if (searchKeyword.value) {
|
|
|
|
|
const keyword = searchKeyword.value.toLowerCase()
|
|
|
|
|
result = result.filter(l =>
|
|
|
|
|
l.fileName.toLowerCase().includes(keyword) ||
|
|
|
|
|
l.operator.toLowerCase().includes(keyword)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
return result.slice((currentPage.value - 1) * 10, currentPage.value * 10)
|
|
|
|
|
// 筛选条件
|
|
|
|
|
const filter = reactive<TbKnowledgeFileLog>({
|
|
|
|
|
action: '',
|
|
|
|
|
knowledgeId: '',
|
|
|
|
|
fileName: '',
|
|
|
|
|
service: 'workcase'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const getOperationType = (operation: string) => {
|
|
|
|
|
const map: Record<string, string> = {
|
|
|
|
|
upload: 'success',
|
|
|
|
|
download: 'info',
|
|
|
|
|
delete: 'danger',
|
|
|
|
|
update: 'warning'
|
|
|
|
|
}
|
|
|
|
|
return map[operation] || 'info'
|
|
|
|
|
// 分页
|
|
|
|
|
const pagination = reactive({
|
|
|
|
|
page: 1,
|
|
|
|
|
pageSize: 10,
|
|
|
|
|
total: 0
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 日志列表
|
|
|
|
|
const logs = ref<TbKnowledgeFileLog[]>([])
|
|
|
|
|
|
|
|
|
|
// 知识库列表(用于筛选)
|
|
|
|
|
const knowledgeList = ref<TbKnowledge[]>([])
|
|
|
|
|
|
|
|
|
|
// 操作类型映射
|
|
|
|
|
const operationTypeMap: Record<string, string> = {
|
|
|
|
|
upload: 'success',
|
|
|
|
|
download: 'info',
|
|
|
|
|
delete: 'danger',
|
|
|
|
|
update: 'warning'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const viewDetail = (row: any) => {
|
|
|
|
|
const operationNameMap: Record<string, string> = {
|
|
|
|
|
upload: '上传',
|
|
|
|
|
download: '下载',
|
|
|
|
|
delete: '删除',
|
|
|
|
|
update: '更新'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getOperationType = (action: string) => operationTypeMap[action] || 'info'
|
|
|
|
|
const getOperationName = (action: string) => operationNameMap[action] || action
|
|
|
|
|
|
|
|
|
|
// 格式化时间
|
|
|
|
|
const formatTime = (time?: string | number): string => {
|
|
|
|
|
if (!time) return '-'
|
|
|
|
|
const date = new Date(time)
|
|
|
|
|
return date.toLocaleString('zh-CN', {
|
|
|
|
|
year: 'numeric',
|
|
|
|
|
month: '2-digit',
|
|
|
|
|
day: '2-digit',
|
|
|
|
|
hour: '2-digit',
|
|
|
|
|
minute: '2-digit',
|
|
|
|
|
second: '2-digit'
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 加载知识库列表
|
|
|
|
|
const loadKnowledgeList = async () => {
|
|
|
|
|
try {
|
|
|
|
|
const result = await aiKnowledgeAPI.listKnowledges({})
|
|
|
|
|
if (result.success && result.dataList) {
|
|
|
|
|
knowledgeList.value = result.dataList
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('加载知识库列表失败:', error)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 加载日志列表
|
|
|
|
|
const loadLogs = async () => {
|
|
|
|
|
loading.value = true
|
|
|
|
|
try {
|
|
|
|
|
// 构建查询条件
|
|
|
|
|
const queryFilter: TbKnowledgeFileLog = { ...filter }
|
|
|
|
|
console.log(queryFilter)
|
|
|
|
|
// 处理日期范围
|
|
|
|
|
if (dateRange.value && dateRange.value[0] && dateRange.value[1]) {
|
|
|
|
|
queryFilter.createTimeStart = dateRange.value[0].toISOString()
|
|
|
|
|
queryFilter.createTimeEnd = dateRange.value[1].toISOString()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await aiKnowledgeAPI.getFileLogPage({
|
|
|
|
|
filter: queryFilter,
|
|
|
|
|
pageParam: {
|
|
|
|
|
page: pagination.page,
|
|
|
|
|
pageSize: pagination.pageSize,
|
|
|
|
|
total: 0
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
logs.value = result.dataList || []
|
|
|
|
|
pagination.total = result.pageParam?.total || 0
|
|
|
|
|
} else {
|
|
|
|
|
ElMessage.error(result.message || '加载日志失败')
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('加载日志失败:', error)
|
|
|
|
|
ElMessage.error('加载日志失败')
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 搜索
|
|
|
|
|
const handleSearch = () => {
|
|
|
|
|
pagination.page = 1
|
|
|
|
|
loadLogs()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查看详情
|
|
|
|
|
const viewDetail = (row: TbKnowledgeFileLog) => {
|
|
|
|
|
selectedLog.value = row
|
|
|
|
|
showDetailDialog.value = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 导出日志
|
|
|
|
|
const exportLogs = () => {
|
|
|
|
|
ElMessage.success('日志导出成功')
|
|
|
|
|
ElMessage.success('日志导出功能开发中')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
loadKnowledgeList()
|
|
|
|
|
loadLogs()
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
@@ -163,4 +274,4 @@ const exportLogs = () => {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 16px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
</style>
|
|
|
|
|
|