删除无用代码
This commit is contained in:
@@ -1,42 +0,0 @@
|
|||||||
<template>
|
|
||||||
<AdminLayout title="智能体管理" subtitle="智能体管理">
|
|
||||||
<div class="ai-management">
|
|
||||||
<h1 class="page-title">智能体管理</h1>
|
|
||||||
|
|
||||||
<el-tabs v-model="activeTab">
|
|
||||||
<el-tab-pane label="基础配置" name="config">
|
|
||||||
<AIConfig />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="知识库管理" name="knowledge">
|
|
||||||
<KnowledgeManagement />
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</div>
|
|
||||||
</AdminLayout>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import { ElTabs, ElTabPane } from 'element-plus';
|
|
||||||
import AIConfig from './components/AIConfig.vue';
|
|
||||||
import KnowledgeManagement from './components/KnowledgeManagement.vue';
|
|
||||||
import { AdminLayout } from '@/views/admin';
|
|
||||||
defineOptions({
|
|
||||||
name: 'AIManagementView'
|
|
||||||
});
|
|
||||||
const activeTab = ref('config');
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.ai-management {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-title {
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #141F38;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="ai-assistant-page">
|
|
||||||
<!-- 悬浮球入口(可以通过props控制显示/隐藏) -->
|
|
||||||
<div class="floating-button" @click="togglePanel" v-if="!isPanelVisible">
|
|
||||||
<img src="@/assets/imgs/ai-icon.svg" alt="AI助手" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- AI助手面板 -->
|
|
||||||
<transition name="slide">
|
|
||||||
<div class="assistant-panel" v-if="isPanelVisible">
|
|
||||||
<div class="panel-header">
|
|
||||||
<h2>AI思政助手</h2>
|
|
||||||
<div class="header-actions">
|
|
||||||
<el-button size="small" @click="handleFileUpload">📎 上传文件</el-button>
|
|
||||||
<el-button size="small" @click="showHistory">📜 历史记录</el-button>
|
|
||||||
<el-button size="small" @click="togglePanel">✕</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel-tabs">
|
|
||||||
<div
|
|
||||||
class="panel-tab"
|
|
||||||
v-for="tab in tabs"
|
|
||||||
:key="tab.key"
|
|
||||||
:class="{ active: activeTab === tab.key }"
|
|
||||||
@click="activeTab = tab.key"
|
|
||||||
>
|
|
||||||
{{ tab.label }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel-content">
|
|
||||||
<component :is="currentComponent" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
|
|
||||||
<!-- 历史对话记录弹窗 -->
|
|
||||||
<el-dialog v-model="historyVisible" title="历史对话记录" width="600px">
|
|
||||||
<DialogHistory @load-conversation="loadConversation" />
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- 文件解读与记录弹窗 -->
|
|
||||||
<el-dialog v-model="fileDialogVisible" title="文件解读与记录" width="800px">
|
|
||||||
<FileInterpretation />
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, computed } from 'vue';
|
|
||||||
import { ElButton, ElDialog } from 'element-plus';
|
|
||||||
import ChatInterface from './components/ChatInterface.vue';
|
|
||||||
import KnowledgeBase from './components/KnowledgeBase.vue';
|
|
||||||
import DialogHistory from './components/DialogHistory.vue';
|
|
||||||
import FileInterpretation from './components/FileInterpretation.vue';
|
|
||||||
|
|
||||||
const isPanelVisible = ref(false);
|
|
||||||
const activeTab = ref('chat');
|
|
||||||
const historyVisible = ref(false);
|
|
||||||
const fileDialogVisible = ref(false);
|
|
||||||
|
|
||||||
const tabs = [
|
|
||||||
{ key: 'chat', label: '对话' },
|
|
||||||
{ key: 'knowledge', label: '知识库' }
|
|
||||||
];
|
|
||||||
|
|
||||||
const componentMap: Record<string, any> = {
|
|
||||||
'chat': ChatInterface,
|
|
||||||
'knowledge': KnowledgeBase
|
|
||||||
};
|
|
||||||
|
|
||||||
const currentComponent = computed(() => componentMap[activeTab.value]);
|
|
||||||
|
|
||||||
function togglePanel() {
|
|
||||||
isPanelVisible.value = !isPanelVisible.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showHistory() {
|
|
||||||
historyVisible.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFileUpload() {
|
|
||||||
fileDialogVisible.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadConversation(conversation: any) {
|
|
||||||
// TODO: 加载历史对话
|
|
||||||
historyVisible.value = false;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.ai-assistant-page {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.floating-button {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 40px;
|
|
||||||
right: 40px;
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
background: linear-gradient(135deg, #C62828, #E53935);
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 4px 12px rgba(198, 40, 40, 0.4);
|
|
||||||
transition: all 0.3s;
|
|
||||||
z-index: 1000;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: scale(1.1);
|
|
||||||
box-shadow: 0 6px 16px rgba(198, 40, 40, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.assistant-panel {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 20px;
|
|
||||||
right: 20px;
|
|
||||||
width: 450px;
|
|
||||||
height: 650px;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
z-index: 1000;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-header {
|
|
||||||
padding: 20px;
|
|
||||||
border-bottom: 1px solid #e0e0e0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
background: linear-gradient(135deg, #C62828, #E53935);
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-tabs {
|
|
||||||
display: flex;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 0 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-tab {
|
|
||||||
padding: 12px 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
position: relative;
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #C62828;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: #C62828;
|
|
||||||
font-weight: 600;
|
|
||||||
background: white;
|
|
||||||
border-radius: 8px 8px 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-content {
|
|
||||||
flex: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-enter-active,
|
|
||||||
.slide-leave-active {
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-enter-from,
|
|
||||||
.slide-leave-to {
|
|
||||||
transform: translateY(100%);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
@@ -1,250 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="chat-interface">
|
|
||||||
<!-- 消息列表 -->
|
|
||||||
<div class="messages-container" ref="messagesContainer">
|
|
||||||
<div
|
|
||||||
class="message"
|
|
||||||
v-for="message in messages"
|
|
||||||
:key="message.id"
|
|
||||||
:class="message.role"
|
|
||||||
>
|
|
||||||
<div class="message-avatar">
|
|
||||||
<img :src="getAvatar(message.role)" :alt="message.role" />
|
|
||||||
</div>
|
|
||||||
<div class="message-content">
|
|
||||||
<div class="message-text" v-html="message.content"></div>
|
|
||||||
<div class="message-time">{{ message.timestamp }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 加载中动画 -->
|
|
||||||
<div class="message assistant" v-if="isLoading">
|
|
||||||
<div class="message-avatar">
|
|
||||||
<img src="@/assets/imgs/ai-avatar.svg" alt="AI" />
|
|
||||||
</div>
|
|
||||||
<div class="message-content">
|
|
||||||
<div class="typing-indicator">
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 输入框 -->
|
|
||||||
<div class="input-container">
|
|
||||||
<el-input
|
|
||||||
v-model="inputMessage"
|
|
||||||
type="textarea"
|
|
||||||
:rows="3"
|
|
||||||
placeholder="输入您的问题..."
|
|
||||||
@keydown.enter.prevent="handleSend"
|
|
||||||
:disabled="isLoading"
|
|
||||||
/>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
@click="handleSend"
|
|
||||||
:loading="isLoading"
|
|
||||||
:disabled="!inputMessage.trim()"
|
|
||||||
>
|
|
||||||
发送
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, nextTick, onMounted } from 'vue';
|
|
||||||
import { ElInput, ElButton } from 'element-plus';
|
|
||||||
|
|
||||||
const messagesContainer = ref<HTMLElement | null>(null);
|
|
||||||
const inputMessage = ref('');
|
|
||||||
const isLoading = ref(false);
|
|
||||||
|
|
||||||
const messages = ref<any[]>([
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
role: 'assistant',
|
|
||||||
content: '您好!我是AI思政助手,请问有什么可以帮助您的吗?',
|
|
||||||
timestamp: new Date().toLocaleTimeString()
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
// TODO: 加载历史消息
|
|
||||||
});
|
|
||||||
|
|
||||||
async function handleSend() {
|
|
||||||
if (!inputMessage.value.trim() || isLoading.value) return;
|
|
||||||
|
|
||||||
const userMessage = {
|
|
||||||
id: Date.now(),
|
|
||||||
role: 'user',
|
|
||||||
content: inputMessage.value,
|
|
||||||
timestamp: new Date().toLocaleTimeString()
|
|
||||||
};
|
|
||||||
|
|
||||||
messages.value.push(userMessage);
|
|
||||||
const question = inputMessage.value;
|
|
||||||
inputMessage.value = '';
|
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
scrollToBottom();
|
|
||||||
|
|
||||||
// 模拟AI回复
|
|
||||||
isLoading.value = true;
|
|
||||||
|
|
||||||
// TODO: 调用AI API
|
|
||||||
setTimeout(() => {
|
|
||||||
const aiMessage = {
|
|
||||||
id: Date.now(),
|
|
||||||
role: 'assistant',
|
|
||||||
content: `关于"${question}",我来为您解答...`,
|
|
||||||
timestamp: new Date().toLocaleTimeString()
|
|
||||||
};
|
|
||||||
|
|
||||||
messages.value.push(aiMessage);
|
|
||||||
isLoading.value = false;
|
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
scrollToBottom();
|
|
||||||
});
|
|
||||||
}, 1500);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAvatar(role: string) {
|
|
||||||
return role === 'user'
|
|
||||||
? '@/assets/imgs/user-avatar.svg'
|
|
||||||
: '@/assets/imgs/ai-avatar.svg';
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollToBottom() {
|
|
||||||
if (messagesContainer.value) {
|
|
||||||
messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.chat-interface {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messages-container {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 20px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
|
|
||||||
&.user {
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
|
|
||||||
.message-content {
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-text {
|
|
||||||
background: #C62828;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.assistant {
|
|
||||||
.message-text {
|
|
||||||
background: #f5f5f5;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-avatar {
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
border-radius: 50%;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-content {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-text {
|
|
||||||
padding: 12px 16px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.6;
|
|
||||||
max-width: 80%;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-time {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #999;
|
|
||||||
padding: 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.typing-indicator {
|
|
||||||
display: flex;
|
|
||||||
gap: 4px;
|
|
||||||
padding: 12px 16px;
|
|
||||||
background: #f5f5f5;
|
|
||||||
border-radius: 12px;
|
|
||||||
|
|
||||||
span {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
background: #999;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: typing 1.4s infinite;
|
|
||||||
|
|
||||||
&:nth-child(2) {
|
|
||||||
animation-delay: 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(3) {
|
|
||||||
animation-delay: 0.4s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes typing {
|
|
||||||
0%, 60%, 100% {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
30% {
|
|
||||||
transform: translateY(-10px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-container {
|
|
||||||
padding: 16px;
|
|
||||||
border-top: 1px solid #e0e0e0;
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
|
|
||||||
:deep(.el-textarea) {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="dialog-history">
|
|
||||||
<div class="history-list">
|
|
||||||
<div
|
|
||||||
class="history-item"
|
|
||||||
v-for="conversation in conversations"
|
|
||||||
:key="conversation.id"
|
|
||||||
@click="$emit('load-conversation', conversation)"
|
|
||||||
>
|
|
||||||
<div class="item-header">
|
|
||||||
<h4>{{ conversation.title }}</h4>
|
|
||||||
<span class="item-date">{{ conversation.date }}</span>
|
|
||||||
</div>
|
|
||||||
<p class="item-preview">{{ conversation.preview }}</p>
|
|
||||||
<div class="item-footer">
|
|
||||||
<span class="item-count">{{ conversation.messageCount }} 条消息</span>
|
|
||||||
<el-button size="small" type="danger" @click.stop="deleteConversation(conversation)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, onMounted } from 'vue';
|
|
||||||
import { ElButton, ElMessage } from 'element-plus';
|
|
||||||
|
|
||||||
const conversations = ref<any[]>([]);
|
|
||||||
|
|
||||||
defineEmits(['load-conversation']);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
// TODO: 加载历史对话列表
|
|
||||||
});
|
|
||||||
|
|
||||||
function deleteConversation(conversation: any) {
|
|
||||||
// TODO: 删除对话
|
|
||||||
ElMessage.success('已删除对话');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.dialog-history {
|
|
||||||
max-height: 500px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-item {
|
|
||||||
padding: 16px;
|
|
||||||
border: 1px solid #e0e0e0;
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: #C62828;
|
|
||||||
background: #fff5f5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #141F38;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-date {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-preview {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding-top: 12px;
|
|
||||||
border-top: 1px solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-count {
|
|
||||||
font-size: 13px;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="file-interpretation">
|
|
||||||
<el-tabs v-model="activeTab">
|
|
||||||
<el-tab-pane label="文件上传" name="upload">
|
|
||||||
<div class="upload-section">
|
|
||||||
<el-upload
|
|
||||||
drag
|
|
||||||
action="#"
|
|
||||||
:before-upload="beforeUpload"
|
|
||||||
:on-success="handleUploadSuccess"
|
|
||||||
multiple
|
|
||||||
>
|
|
||||||
<div class="upload-icon">📁</div>
|
|
||||||
<div class="upload-text">
|
|
||||||
<p>点击或拖拽文件到此处上传</p>
|
|
||||||
<p class="upload-hint">支持 PDF、Word、TXT 格式</p>
|
|
||||||
</div>
|
|
||||||
</el-upload>
|
|
||||||
|
|
||||||
<!-- 已上传文件列表 -->
|
|
||||||
<div class="uploaded-files" v-if="uploadedFiles.length">
|
|
||||||
<h4>已上传文件</h4>
|
|
||||||
<div class="file-list">
|
|
||||||
<div class="file-item" v-for="file in uploadedFiles" :key="file.id">
|
|
||||||
<div class="file-icon">📄</div>
|
|
||||||
<div class="file-info">
|
|
||||||
<h5>{{ file.name }}</h5>
|
|
||||||
<p>{{ file.size }} · {{ file.uploadDate }}</p>
|
|
||||||
</div>
|
|
||||||
<el-button size="small" @click="analyzeFile(file)">解读</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="历史文件" name="history">
|
|
||||||
<div class="history-files">
|
|
||||||
<div class="file-item" v-for="file in historyFiles" :key="file.id">
|
|
||||||
<div class="file-icon">📄</div>
|
|
||||||
<div class="file-info">
|
|
||||||
<h5>{{ file.name }}</h5>
|
|
||||||
<p>上传时间:{{ file.uploadDate }}</p>
|
|
||||||
<p class="file-summary">{{ file.summary }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="file-actions">
|
|
||||||
<el-button size="small" @click="viewAnalysis(file)">查看解读</el-button>
|
|
||||||
<el-button size="small" type="danger" @click="deleteFile(file)">删除</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, onMounted } from 'vue';
|
|
||||||
import { ElTabs, ElTabPane, ElUpload, ElButton, ElMessage } from 'element-plus';
|
|
||||||
|
|
||||||
const activeTab = ref('upload');
|
|
||||||
const uploadedFiles = ref<any[]>([]);
|
|
||||||
const historyFiles = ref<any[]>([]);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
// TODO: 加载历史文件
|
|
||||||
});
|
|
||||||
|
|
||||||
function beforeUpload(file: File) {
|
|
||||||
const allowedTypes = ['application/pdf', 'application/msword', 'text/plain',
|
|
||||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
|
|
||||||
|
|
||||||
if (!allowedTypes.includes(file.type)) {
|
|
||||||
ElMessage.error('只支持 PDF、Word、TXT 格式');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file.size > 10 * 1024 * 1024) {
|
|
||||||
ElMessage.error('文件大小不能超过 10MB');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleUploadSuccess(response: any, file: any) {
|
|
||||||
uploadedFiles.value.push({
|
|
||||||
id: Date.now(),
|
|
||||||
name: file.name,
|
|
||||||
size: formatFileSize(file.size),
|
|
||||||
uploadDate: new Date().toLocaleString()
|
|
||||||
});
|
|
||||||
ElMessage.success('文件上传成功');
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatFileSize(bytes: number): string {
|
|
||||||
if (bytes < 1024) return bytes + ' B';
|
|
||||||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB';
|
|
||||||
return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
|
|
||||||
}
|
|
||||||
|
|
||||||
function analyzeFile(file: any) {
|
|
||||||
// TODO: 调用文件解读API
|
|
||||||
ElMessage.info('正在解读文件...');
|
|
||||||
}
|
|
||||||
|
|
||||||
function viewAnalysis(file: any) {
|
|
||||||
// TODO: 查看文件解读结果
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteFile(file: any) {
|
|
||||||
// TODO: 删除文件
|
|
||||||
ElMessage.success('文件已删除');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.file-interpretation {
|
|
||||||
min-height: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-section {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-icon {
|
|
||||||
font-size: 64px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-text {
|
|
||||||
p {
|
|
||||||
margin: 8px 0;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-hint {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uploaded-files {
|
|
||||||
margin-top: 32px;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #141F38;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list,
|
|
||||||
.history-files {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 16px;
|
|
||||||
padding: 16px;
|
|
||||||
background: #f9f9f9;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-icon {
|
|
||||||
font-size: 32px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-info {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #141F38;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 13px;
|
|
||||||
color: #666;
|
|
||||||
margin: 2px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-summary {
|
|
||||||
margin-top: 8px;
|
|
||||||
padding-top: 8px;
|
|
||||||
border-top: 1px solid #e0e0e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="knowledge-base">
|
|
||||||
<div class="knowledge-header">
|
|
||||||
<el-input
|
|
||||||
v-model="searchKeyword"
|
|
||||||
placeholder="搜索知识库..."
|
|
||||||
prefix-icon="Search"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="knowledge-list">
|
|
||||||
<div
|
|
||||||
class="knowledge-item"
|
|
||||||
v-for="item in filteredKnowledge"
|
|
||||||
:key="item.id"
|
|
||||||
@click="viewKnowledge(item)"
|
|
||||||
>
|
|
||||||
<div class="item-icon">{{ item.icon }}</div>
|
|
||||||
<div class="item-info">
|
|
||||||
<h4>{{ item.title }}</h4>
|
|
||||||
<p>{{ item.description }}</p>
|
|
||||||
<div class="item-meta">
|
|
||||||
<span class="item-category">{{ item.category }}</span>
|
|
||||||
<span class="item-date">{{ item.updateDate }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, computed, onMounted } from 'vue';
|
|
||||||
import { ElInput } from 'element-plus';
|
|
||||||
|
|
||||||
const searchKeyword = ref('');
|
|
||||||
const knowledgeList = ref<any[]>([]);
|
|
||||||
|
|
||||||
const filteredKnowledge = computed(() => {
|
|
||||||
if (!searchKeyword.value) return knowledgeList.value;
|
|
||||||
return knowledgeList.value.filter(item =>
|
|
||||||
item.title.includes(searchKeyword.value) ||
|
|
||||||
item.description.includes(searchKeyword.value)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
// TODO: 加载知识库数据
|
|
||||||
knowledgeList.value = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
icon: '📚',
|
|
||||||
title: '党的二十大精神',
|
|
||||||
description: '深入学习党的二十大精神要点',
|
|
||||||
category: '党史学习',
|
|
||||||
updateDate: '2024-01-15'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
icon: '🎯',
|
|
||||||
title: '社会主义核心价值观',
|
|
||||||
description: '践行社会主义核心价值观',
|
|
||||||
category: '理论学习',
|
|
||||||
updateDate: '2024-01-10'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
function viewKnowledge(item: any) {
|
|
||||||
// TODO: 查看知识详情
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.knowledge-base {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.knowledge-header {
|
|
||||||
padding: 16px;
|
|
||||||
border-bottom: 1px solid #e0e0e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.knowledge-list {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 16px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.knowledge-item {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
padding: 16px;
|
|
||||||
background: #f9f9f9;
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #ffe6e6;
|
|
||||||
transform: translateX(4px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-icon {
|
|
||||||
font-size: 32px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-info {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #141F38;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 13px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-meta {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-category {
|
|
||||||
color: #C62828;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-date {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user