From 96aaeb495edfbc4a41dce4b7f7a416a8204a5455 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期二, 28 四月 2026 11:04:38 +0800
Subject: [PATCH] Merge branch 'dev_NEW_pro' of http://114.132.189.42:9002/r/product-inventory-management into dev_NEW_pro

---
 src/views/fileManagement/document/index.vue |    8 
 src/views/fileManagement/return/index.vue   |   33 
 src/views/fileManagement/borrow/index.vue   |   33 
 src/layout/index.vue                        |  264 +++++-----
 src/components/AIChatSidebar/index.vue      | 1206 ++++++++++++++++++++++++++++++++++++++++++++++
 vite.config.js                              |    2 
 6 files changed, 1,387 insertions(+), 159 deletions(-)

diff --git a/src/components/AIChatSidebar/index.vue b/src/components/AIChatSidebar/index.vue
new file mode 100644
index 0000000..d14978c
--- /dev/null
+++ b/src/components/AIChatSidebar/index.vue
@@ -0,0 +1,1206 @@
+<template>
+  <div class="ai-chat-sidebar-wrapper">
+    <!-- 鎮诞鍥炬爣 -->
+    <div class="ai-chat-trigger" @click="toggleSidebar" v-show="!visible">
+      <el-tooltip content="AI 鍔╂墜" placement="left">
+        <div class="trigger-icon">
+          <el-icon :size="30" color="#fff"><Cpu /></el-icon>
+        </div>
+      </el-tooltip>
+    </div>
+
+    <!-- 渚ц竟鏍忓璇濇 -->
+    <el-drawer
+      v-model="visible"
+      :size="drawerSize"
+      direction="rtl"
+      :with-header="true"
+      class="ai-chat-drawer"
+      :modal="false"
+      :show-close="true"
+      :append-to-body="false"
+      @close="handleClose"
+    >
+      <template #header>
+        <div class="drawer-header">
+          <div class="header-left">
+            <el-icon :size="20" class="header-icon"><Cpu /></el-icon>
+            <span class="title">AI 鏅鸿兘鍔╂墜</span>
+          </div>
+          <div class="header-actions">
+            <el-tooltip content="浼氳瘽鍘嗗彶" placement="bottom">
+              <el-button link @click="toggleHistory">
+                <el-icon :size="18"><Timer /></el-icon>
+              </el-button>
+            </el-tooltip>
+            <el-tooltip content="寮�鍚柊浼氳瘽" placement="bottom">
+              <el-button link @click="newChat">
+                <el-icon :size="18"><Plus /></el-icon>
+              </el-button>
+            </el-tooltip>
+          </div>
+        </div>
+      </template>
+      
+      <div class="chat-container">
+        <!-- 鍘嗗彶浼氳瘽鍒楄〃 -->
+        <div v-if="showHistory" class="history-panel">
+          <div class="history-header">
+            <span>鏈�杩戜細璇�</span>
+            <el-button link type="primary" @click="showHistory = false">杩斿洖瀵硅瘽</el-button>
+          </div>
+          <el-skeleton :loading="loadingSessions" animated>
+            <template #template>
+              <div v-for="i in 5" :key="i" style="padding: 10px">
+                <el-skeleton-item variant="p" style="width: 80%" />
+              </div>
+            </template>
+            <div class="session-list">
+              <div 
+                v-for="session in sessions" 
+                :key="session.memoryId"
+                :class="['session-item', { active: uuid === session.memoryId }]"
+                @click="selectSession(session)"
+              >
+                <el-icon><ChatDotSquare /></el-icon>
+                <span class="session-name" :title="session.lastMessage || '鏂颁細璇�'">
+                  {{ session.lastMessage || '鏂颁細璇�' }}
+                </span>
+                <el-button 
+                  link 
+                  type="danger" 
+                  class="delete-btn"
+                  @click.stop="handleDeleteSession(session.memoryId)"
+                >
+                  <el-icon><Delete /></el-icon>
+                </el-button>
+              </div>
+              <el-empty v-if="sessions.length === 0" description="鏆傛棤鍘嗗彶浼氳瘽" />
+            </div>
+          </el-skeleton>
+        </div>
+
+        <div v-else class="chat-main">
+          <div class="message-list" ref="messageListRef">
+          <div
+            v-for="(message, index) in messages"
+            :key="index"
+            :class="['message-item', message.isUser ? 'user-message' : 'bot-message']"
+          >
+            <div class="avatar">
+              <el-icon v-if="message.isUser"><User /></el-icon>
+              <el-icon v-else><Cpu /></el-icon>
+            </div>
+            <div class="message-content">
+              <!-- 鏂囨湰鍐呭 -->
+              <div class="text-box" v-html="message.htmlContent"></div>
+
+              <!-- 鍥捐〃鍐呭 -->
+              <div v-if="message.chartOptions && message.chartRenderReady" class="charts-wrapper">
+                <div
+                  v-for="(option, key) in message.chartOptions"
+                  :key="key"
+                  class="chart-item"
+                  :id="`ai-chart-${index}-${key}`"
+                ></div>
+              </div>
+
+              <!-- 琛ㄦ牸鍐呭 -->
+              <div v-if="message.type === 'todo_list' && message.tableData" class="table-wrapper">
+                <el-table :data="message.tableData.items" border stripe size="small" style="width: 100%">
+                  <el-table-column
+                    v-for="col in message.tableData.columns"
+                    :key="col"
+                    :prop="col"
+                    :label="columnLabelMap[col] || col"
+                    min-width="100"
+                    show-overflow-tooltip
+                  />
+                </el-table>
+              </div>
+
+              <!-- 鎵撳瓧涓姩鐢� -->
+              <div v-if="message.isTyping" class="typing-indicator">
+                <span class="dot"></span>
+                <span class="dot"></span>
+                <span class="dot"></span>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <div class="input-area">
+          <div class="input-actions">
+            <el-button link type="primary" size="small" @click="newChat">
+              <el-icon><Plus /></el-icon>鏂颁細璇�
+            </el-button>
+            <el-button v-if="isSending" link type="danger" size="small" @click="stopGeneration">
+              <el-icon><VideoPause /></el-icon>鍋滄鐢熸垚
+            </el-button>
+            <el-upload
+              class="file-upload-trigger"
+              action="#"
+              :auto-upload="false"
+              :show-file-list="false"
+              :on-change="handleFileChange"
+              :disabled="isSending"
+            >
+              <el-button link type="primary" size="small" :disabled="isSending">
+                <el-icon><Upload /></el-icon>鍒嗘瀽鏂囦欢
+              </el-button>
+            </el-upload>
+          </div>
+          <div class="input-box">
+            <div v-if="selectedFile" class="selected-file-tag">
+              <el-icon><Document /></el-icon>
+              <span class="file-name">{{ selectedFile.name }}</span>
+              <el-icon class="remove-file" @click="removeSelectedFile"><Close /></el-icon>
+            </div>
+            <el-input
+              v-model="inputMessage"
+              type="textarea"
+              :rows="selectedFile ? 2 : 3"
+              placeholder="璇疯緭鍏ユ偍鐨勯棶棰�... (Enter 鍙戦��, Shift+Enter 鎹㈣)"
+              resize="none"
+              @keydown.enter.exact.prevent="sendMessage"
+            />
+            <el-button 
+              type="primary" 
+              class="send-btn"
+              :disabled="isSending || (!inputMessage.trim() && !selectedFile)"
+              @click="sendMessage"
+            >
+              鍙戦��
+            </el-button>
+          </div>
+        </div>
+        </div>
+      </div>
+    </el-drawer>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted, nextTick, watch, computed } from 'vue'
+import request from '@/utils/request'
+import * as echarts from 'echarts'
+import { Cpu, User, Plus, Loading, Timer, Delete, ChatDotSquare, VideoPause, Upload, Document, Close } from '@element-plus/icons-vue'
+import { ElMessage } from 'element-plus'
+
+const visible = ref(false)
+const windowWidth = ref(window.innerWidth)
+const drawerSize = computed(() => {
+  if (windowWidth.value < 768) return '100%'
+  if (windowWidth.value < 1200) return '500px'
+  return '600px'
+})
+const messageListRef = ref(null)
+const isSending = ref(false)
+const currentAbortController = ref(null)
+const inputMessage = ref('')
+const selectedFile = ref(null)
+const messages = ref([])
+const uuid = ref('')
+const chartInstances = ref({})
+const resizeHandlers = ref([])
+const outputState = ref({})
+
+// 鍘嗗彶浼氳瘽鐩稿叧
+const showHistory = ref(false)
+const sessions = ref([])
+const loadingSessions = ref(false)
+
+const toggleHistory = () => {
+  showHistory.value = !showHistory.value
+  if (showHistory.value) {
+    loadSessions()
+  }
+}
+
+const loadSessions = async () => {
+  loadingSessions.value = true
+  try {
+    const res = await request.get('/xiaozhi/history/sessions')
+    if (res.code === 200) {
+      sessions.value = res.data || []
+    }
+  } catch (err) {
+    console.error('Failed to load sessions', err)
+  } finally {
+    loadingSessions.value = false
+  }
+}
+
+const selectSession = async (session) => {
+  showHistory.value = false
+  uuid.value = session.memoryId
+  localStorage.setItem('ai_chat_uuid', uuid.value)
+  
+  // 鍔犺浇浼氳瘽娑堟伅
+  try {
+    const res = await request.get(`/xiaozhi/history/messages/${uuid.value}`)
+    if (res.code === 200) {
+      disposeCharts()
+      messages.value = []
+      const historyMsgs = res.data || []
+      
+      // 閲嶆柊鏋勯�犳秷鎭垪琛ㄥ苟瑙f瀽
+      historyMsgs.forEach((msg, idx) => {
+        const isUser = msg.role === 'user'
+        const botMsgIndex = messages.value.length
+        
+        const messageObj = {
+          isUser,
+          content: msg.content,
+          htmlContent: '',
+          isTyping: false,
+          chartOptions: null,
+          chartRenderReady: false,
+          type: '',
+          tableData: null
+        }
+        
+        messages.value.push(messageObj)
+        
+        if (!isUser) {
+          outputState.value[botMsgIndex] = {
+            isPaused: false,
+            jsonBlockStartPos: -1,
+            jsBlockStartPos: -1,
+            blockEndPos: -1,
+            hasRenderedChart: false
+          }
+          
+          // 瑙f瀽鍘嗗彶娑堟伅涓殑 JSON
+          const jsonRegex = /\{"success":\s*true,[\s\S]*\}/
+          const jsonMatch = msg.content.match(jsonRegex)
+          if (jsonMatch) {
+            try {
+              const parsedData = JSON.parse(jsonMatch[0])
+              if (parsedData.success) {
+                messageObj.type = parsedData.type || ''
+                if (messageObj.type === 'todo_list' && parsedData.data) {
+                  messageObj.tableData = parsedData.data
+                }
+                if (parsedData.charts && Object.keys(parsedData.charts).length > 0) {
+                  messageObj.chartOptions = parsedData.charts
+                  messageObj.chartRenderReady = true
+                  renderCharts(botMsgIndex, messageObj.chartOptions)
+                }
+              }
+            } catch (err) {}
+          }
+          
+          updateOutputState(msg.content, botMsgIndex)
+          messageObj.htmlContent = convertStreamOutput(msg.content, botMsgIndex)
+        } else {
+          messageObj.htmlContent = convertTextToHtml(msg.content)
+        }
+      })
+      scrollToBottom()
+    }
+  } catch (err) {
+    console.error('Failed to load messages', err)
+  }
+}
+
+const handleDeleteSession = async (memoryId) => {
+  try {
+    const res = await request.delete(`/xiaozhi/history/${memoryId}`)
+    if (res.code === 200) {
+      loadSessions()
+      if (uuid.value === memoryId) {
+        newChat()
+      }
+    }
+  } catch (err) {
+    console.error('Failed to delete session', err)
+  }
+}
+
+const columnLabelMap = {
+  approveId: '瀹℃壒缂栧彿',
+  approveType: '瀹℃壒绫诲瀷',
+  approveUserName: '瀹℃壒浜�',
+  approveUserCurrentName: '褰撳墠澶勭悊浜�',
+  approveReason: '瀹℃壒鍘熷洜',
+  approveStatus: '瀹℃壒鐘舵��',
+  createTime: '鍒涘缓鏃堕棿'
+}
+
+onMounted(() => {
+  initUUID()
+  // 鍒濆娆㈣繋
+  if (messages.value.length === 0) {
+    hello()
+  }
+  window.addEventListener('resize', handleWindowResize)
+})
+
+onUnmounted(() => {
+  disposeCharts()
+  window.removeEventListener('resize', handleWindowResize)
+})
+
+const handleWindowResize = () => {
+  windowWidth.value = window.innerWidth
+}
+
+const toggleSidebar = () => {
+  visible.value = !visible.value
+  if (visible.value) {
+    scrollToBottom()
+  }
+}
+
+const handleClose = () => {
+  visible.value = false
+}
+
+const initUUID = () => {
+  let storedUUID = localStorage.getItem('ai_chat_uuid')
+  if (!storedUUID) {
+    storedUUID = Math.random().toString(36).substring(2, 10) + Date.now().toString(36).substring(4)
+    localStorage.setItem('ai_chat_uuid', storedUUID)
+  }
+  uuid.value = storedUUID
+}
+
+const hello = () => {
+  sendRequest('浣犲ソ')
+}
+
+const newChat = () => {
+  disposeCharts()
+  messages.value = []
+  outputState.value = {}
+  localStorage.removeItem('ai_chat_uuid')
+  initUUID()
+  hello()
+}
+
+const disposeCharts = () => {
+  Object.values(chartInstances.value).forEach(chart => chart.dispose())
+  resizeHandlers.value.forEach(handler => window.removeEventListener('resize', handler))
+  chartInstances.value = {}
+  resizeHandlers.value = []
+}
+
+const scrollToBottom = () => {
+  nextTick(() => {
+    if (messageListRef.value) {
+      messageListRef.value.scrollTop = messageListRef.value.scrollHeight
+    }
+  })
+}
+
+const handleFileChange = (file) => {
+  if (!file) return
+  const rawFile = file.raw
+  if (rawFile) {
+    // 闄愬埗鏂囦欢澶у皬锛屼緥濡� 10MB
+    const isLt10M = rawFile.size / 1024 / 1024 < 10
+    if (!isLt10M) {
+      ElMessage.error('鏂囦欢澶у皬涓嶈兘瓒呰繃 10MB!')
+      return
+    }
+    selectedFile.value = rawFile
+  }
+}
+
+const removeSelectedFile = () => {
+  selectedFile.value = null
+}
+
+const analyzeFile = async (file, message = '') => {
+  if (isSending.value) return
+  isSending.value = true
+  currentAbortController.value = new AbortController()
+  
+  const userMsg = message ? `${message}\n[涓婁紶鏂囦欢鍒嗘瀽] ${file.name}` : `[涓婁紶鏂囦欢鍒嗘瀽] ${file.name}`
+  messages.value.push({
+    isUser: true,
+    content: userMsg,
+    htmlContent: convertTextToHtml(userMsg),
+    isTyping: false
+  })
+
+  const botMsgIndex = messages.value.length
+  messages.value.push({
+    isUser: false,
+    content: '',
+    htmlContent: '',
+    isTyping: true,
+    chartOptions: null,
+    chartRenderReady: false,
+    type: '',
+    tableData: null
+  })
+
+  outputState.value[botMsgIndex] = {
+    isPaused: false,
+    jsonBlockStartPos: -1,
+    jsBlockStartPos: -1,
+    blockEndPos: -1,
+    hasRenderedChart: false
+  }
+
+  scrollToBottom()
+
+  const formData = new FormData()
+  formData.append('file', file)
+  formData.append('memoryId', uuid.value)
+  if (message.trim()) {
+    formData.append('message', message.trim())
+  }
+
+  request.post('/xiaozhi/analyze-file', formData, {
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    },
+    signal: currentAbortController.value.signal,
+    onDownloadProgress: (e) => {
+      const fullText = e.target ? e.target.responseText : (e.event ? e.event.target.responseText : '')
+      if (!fullText) return
+      
+      const currentMsg = messages.value[botMsgIndex]
+      if (!currentMsg) return
+      
+      currentMsg.content = fullText
+
+      // 瑙f瀽 JSON 鏁版嵁锛堥拡瀵瑰祵鍏ュ紡 JSON锛�
+      const jsonRegex = /\{"success":\s*true,[\s\S]*\}/
+      const jsonMatch = fullText.match(jsonRegex)
+
+      if (jsonMatch) {
+        try {
+          const parsedData = JSON.parse(jsonMatch[0])
+          if (parsedData.success) {
+            currentMsg.type = parsedData.type || ''
+            if (currentMsg.type === 'todo_list' && parsedData.data) {
+              currentMsg.tableData = parsedData.data
+            }
+            if (parsedData.charts && Object.keys(parsedData.charts).length > 0) {
+              currentMsg.chartOptions = parsedData.charts
+              currentMsg.chartRenderReady = true
+              if (!outputState.value[botMsgIndex].hasRenderedChart) {
+                renderCharts(botMsgIndex, currentMsg.chartOptions)
+                outputState.value[botMsgIndex].hasRenderedChart = true
+              }
+            }
+          }
+        } catch (err) {}
+      }
+
+      updateOutputState(fullText, botMsgIndex)
+      currentMsg.htmlContent = convertStreamOutput(fullText, botMsgIndex)
+      scrollToBottom()
+    }
+  }).then(() => {
+    const currentMsg = messages.value[botMsgIndex]
+    currentMsg.isTyping = false
+    isSending.value = false
+    currentAbortController.value = null
+    
+    // 鏈�缁堣В鏋愮‘淇濆浘琛ㄦ覆鏌�
+    if (currentMsg.chartOptions && !outputState.value[botMsgIndex].hasRenderedChart) {
+      renderCharts(botMsgIndex, currentMsg.chartOptions)
+      outputState.value[botMsgIndex].hasRenderedChart = true
+    }
+  }).catch(err => {
+    if (err.name === 'CanceledError' || err.name === 'AbortError') {
+      console.log('Analysis aborted by user')
+      return
+    }
+    console.error('File analysis error:', err)
+    const errorMsg = '鎶辨瓑锛屾枃浠跺垎鏋愯繃绋嬩腑閬囧埌浜嗕竴鐐归棶棰橈紝璇风◢鍚庡啀璇曘��'
+    if (messages.value[botMsgIndex]) {
+      messages.value[botMsgIndex].content = errorMsg
+      messages.value[botMsgIndex].htmlContent = convertTextToHtml(errorMsg)
+      messages.value[botMsgIndex].isTyping = false
+    }
+    isSending.value = false
+    currentAbortController.value = null
+  })
+}
+
+const sendMessage = () => {
+  const msg = inputMessage.value?.trim() || ''
+  if ((msg || selectedFile.value) && !isSending.value) {
+    if (selectedFile.value) {
+      analyzeFile(selectedFile.value, msg)
+      selectedFile.value = null
+    } else {
+      sendRequest(msg)
+    }
+    inputMessage.value = ''
+  }
+}
+
+const stopGeneration = () => {
+  if (currentAbortController.value) {
+    currentAbortController.value.abort()
+    currentAbortController.value = null
+    isSending.value = false
+    
+    // 灏嗘渶鍚庝竴鏉℃秷鎭爣璁颁负闈炴墦瀛楃姸鎬�
+    const lastMsg = messages.value[messages.value.length - 1]
+    if (lastMsg && !lastMsg.isUser) {
+      lastMsg.isTyping = false
+    }
+  }
+}
+
+const sendRequest = (message) => {
+  isSending.value = true
+  currentAbortController.value = new AbortController()
+
+  // 鐢ㄦ埛娑堟伅
+  if (messages.value.length > 0) {
+    messages.value.push({
+      isUser: true,
+      content: message,
+      htmlContent: convertTextToHtml(message),
+      isTyping: false
+    })
+  }
+
+  // 鏈哄櫒浜哄崰浣�
+  const botMsgIndex = messages.value.length
+  const botMsg = {
+    isUser: false,
+    content: '',
+    htmlContent: '',
+    isTyping: true,
+    chartOptions: null,
+    chartRenderReady: false,
+    type: '',
+    tableData: null
+  }
+  messages.value.push(botMsg)
+  
+  outputState.value[botMsgIndex] = {
+    isPaused: false,
+    jsonBlockStartPos: -1,
+    jsBlockStartPos: -1,
+    blockEndPos: -1,
+    hasRenderedChart: false
+  }
+  
+  scrollToBottom()
+
+  request.post('/xiaozhi/chat', 
+    { memoryId: uuid.value, message },
+    {
+      signal: currentAbortController.value.signal,
+      onDownloadProgress: (e) => {
+        // 鍏煎涓嶅悓鐗堟湰鐨� axios 鑾峰彇鍝嶅簲鏂囨湰鐨勬柟寮�
+        const fullText = e.target ? e.target.responseText : (e.event ? e.event.target.responseText : '')
+        if (!fullText) return
+        
+        const currentMsg = messages.value[botMsgIndex]
+        if (!currentMsg) return
+        
+        currentMsg.content = fullText
+
+        // 瑙f瀽 JSON 鏁版嵁锛堥拡瀵瑰祵鍏ュ紡 JSON锛�
+        const jsonRegex = /\{"success":\s*true,[\s\S]*\}/
+        const jsonMatch = fullText.match(jsonRegex)
+
+        if (jsonMatch) {
+          try {
+            const parsedData = JSON.parse(jsonMatch[0])
+            if (parsedData.success) {
+              currentMsg.type = parsedData.type || ''
+              if (currentMsg.type === 'todo_list' && parsedData.data) {
+                currentMsg.tableData = parsedData.data
+              }
+              if (parsedData.charts && Object.keys(parsedData.charts).length > 0) {
+                currentMsg.chartOptions = parsedData.charts
+                currentMsg.chartRenderReady = true
+                if (!outputState.value[botMsgIndex].hasRenderedChart) {
+                  renderCharts(botMsgIndex, currentMsg.chartOptions)
+                  outputState.value[botMsgIndex].hasRenderedChart = true
+                }
+              }
+            }
+          } catch (err) {}
+        }
+
+        updateOutputState(fullText, botMsgIndex)
+        currentMsg.htmlContent = convertStreamOutput(fullText, botMsgIndex)
+        scrollToBottom()
+      }
+    }
+  ).then(() => {
+    const currentMsg = messages.value[botMsgIndex]
+    currentMsg.isTyping = false
+    isSending.value = false
+    currentAbortController.value = null
+    
+    // 鏈�缁堣В鏋�
+    const fullText = currentMsg.content
+    const jsonRegex = /\{"success":\s*true,[\s\S]*\}/
+    const jsonMatch = fullText.match(jsonRegex)
+    if (jsonMatch) {
+      try {
+        const parsedData = JSON.parse(jsonMatch[0])
+        if (parsedData.success) {
+          currentMsg.type = parsedData.type || ''
+          if (currentMsg.type === 'todo_list' && parsedData.data) {
+            currentMsg.tableData = parsedData.data
+          }
+          if (parsedData.charts && Object.keys(parsedData.charts).length > 0) {
+            currentMsg.chartOptions = parsedData.charts
+            currentMsg.chartRenderReady = true
+            if (!outputState.value[botMsgIndex].hasRenderedChart) {
+              renderCharts(botMsgIndex, currentMsg.chartOptions)
+              outputState.value[botMsgIndex].hasRenderedChart = true
+            }
+          }
+          currentMsg.htmlContent = convertStreamOutput(fullText, botMsgIndex)
+        }
+      } catch (err) {}
+    }
+    
+    // 鍏滃簳娓叉煋
+    if (currentMsg.chartOptions && !outputState.value[botMsgIndex].hasRenderedChart) {
+      renderCharts(botMsgIndex, currentMsg.chartOptions)
+    }
+  }).catch(err => {
+    if (err.name === 'CanceledError' || err.name === 'AbortError') {
+      console.log('Request aborted by user')
+      return
+    }
+    console.error('AI Chat Error:', err)
+    const errorMsg = '鎶辨瓑锛屾垜鐜板湪閬囧埌浜嗕竴鐐归棶棰橈紝璇风◢鍚庡啀璇曘��'
+    if (messages.value[botMsgIndex]) {
+      messages.value[botMsgIndex].content = errorMsg
+      messages.value[botMsgIndex].htmlContent = convertTextToHtml(errorMsg)
+      messages.value[botMsgIndex].isTyping = false
+    }
+    isSending.value = false
+    currentAbortController.value = null
+  })
+}
+
+const updateOutputState = (text, msgIndex) => {
+  const state = outputState.value[msgIndex]
+  if (state.jsonBlockStartPos === -1) {
+    const pos = text.indexOf('```json')
+    if (pos !== -1) { state.jsonBlockStartPos = pos; state.isPaused = true }
+  }
+  if (state.jsBlockStartPos === -1) {
+    const pos = text.indexOf('```javascript') !== -1 ? text.indexOf('```javascript') : text.indexOf('```js')
+    if (pos !== -1) { state.jsBlockStartPos = pos; state.isPaused = true }
+  }
+  if ((state.jsonBlockStartPos !== -1 || state.jsBlockStartPos !== -1) && state.blockEndPos === -1) {
+    const startCheck = state.jsonBlockStartPos !== -1 ? state.jsonBlockStartPos + 7 : state.jsBlockStartPos + (text.includes('javascript') ? 13 : 5)
+    const endPos = text.indexOf('```', startCheck)
+    if (endPos !== -1) { state.blockEndPos = endPos + 3; state.isPaused = false }
+  }
+}
+
+const convertTextToHtml = (text) => {
+  if (!text) return ''
+  return text
+    .replace(/&/g, '&amp;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;')
+    .replace(/\n/g, '<br>')
+}
+
+const convertStreamOutput = (output, msgIndex) => {
+  if (!output) return ''
+  const state = outputState.value[msgIndex]
+  let display = output
+
+  const jsonRegex = /\{"success":\s*true,[\s\S]*\}/
+  const jsonMatch = output.match(jsonRegex)
+  if (jsonMatch) {
+    try {
+      const parsed = JSON.parse(jsonMatch[0])
+      display = output.replace(jsonMatch[0], '').trim()
+      if (!display && parsed.description) display = parsed.description
+    } catch (e) {
+      const start = output.search(/\{"success":\s*true/)
+      display = output.substring(0, start) + '... (姝e湪鐢熸垚鏁版嵁鍥捐〃)'
+    }
+  }
+
+  if (state.jsonBlockStartPos !== -1 && state.blockEndPos === -1) {
+    display = display.substring(0, state.jsonBlockStartPos)
+  } else if (state.jsBlockStartPos !== -1 && state.blockEndPos === -1) {
+    display = display.substring(0, state.jsBlockStartPos)
+  }
+
+  display = display.replace(/```(javascript|js)([\s\S]*?)```/g, '<pre class="code-block js-code">$2</pre>')
+  display = display.replace(/```([\s\S]*?)```/g, '<pre class="code-block">$1</pre>')
+
+  return convertTextToHtml(display)
+}
+
+const renderCharts = (msgIndex, chartOptions) => {
+  nextTick(() => {
+    Object.keys(chartOptions).forEach(key => {
+      const id = `ai-chart-${msgIndex}-${key}`
+      const tryInit = (count = 0) => {
+        const dom = document.getElementById(id)
+        if (dom) {
+          if (chartInstances.value[id]) chartInstances.value[id].dispose()
+          const chart = echarts.init(dom)
+          chartInstances.value[id] = chart
+          chart.setOption(chartOptions[key])
+          const handler = () => chart.resize()
+          resizeHandlers.value.push(handler)
+          window.addEventListener('resize', handler)
+        } else if (count < 10) {
+          setTimeout(() => tryInit(count + 1), 200)
+        }
+      }
+      tryInit()
+    })
+  })
+}
+
+watch(messages, () => scrollToBottom(), { deep: true })
+</script>
+
+<style scoped lang="scss">
+.ai-chat-sidebar-wrapper {
+  position: fixed;
+  inset: 0;
+  z-index: 2000;
+  pointer-events: none;
+
+  :deep(.el-drawer__container) {
+    pointer-events: none;
+  }
+
+  :deep(.el-drawer) {
+    pointer-events: auto;
+  }
+}
+
+.ai-chat-trigger {
+  pointer-events: auto;
+  position: fixed;
+  right: 20px;
+  bottom: 100px;
+  width: 60px;
+  height: 60px;
+  background: linear-gradient(135deg, #409eff 0%, #007aff 100%);
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  box-shadow: 0 4px 12px rgba(0, 122, 255, 0.4);
+  transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+  z-index: 2001;
+
+  &:hover {
+    transform: scale(1.1) translateY(-5px);
+    box-shadow: 0 8px 20px rgba(0, 122, 255, 0.5);
+  }
+
+  .trigger-icon {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+}
+
+.ai-chat-drawer {
+  :deep(.el-drawer__body) {
+    padding: 0;
+    overflow: hidden;
+  }
+  :deep(.el-drawer__header) {
+    margin-bottom: 0;
+    padding: 12px 16px;
+    background: #fff;
+    border-bottom: 1px solid #ebeef5;
+    color: #303133;
+  }
+}
+
+.drawer-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  width: 100%;
+  padding-right: 32px;
+
+  .header-left {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    
+    .header-icon {
+      color: #409eff;
+    }
+    
+    .title {
+      font-size: 16px;
+      font-weight: 600;
+      color: #303133;
+    }
+  }
+
+  .header-actions {
+    display: flex;
+    gap: 8px;
+  }
+}
+
+.chat-container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  background-color: #f5f7fa;
+  position: relative;
+}
+
+.history-panel {
+  position: absolute;
+  inset: 0;
+  background: #fff;
+  z-index: 10;
+  display: flex;
+  flex-direction: column;
+  
+  .history-header {
+    padding: 16px;
+    border-bottom: 1px solid #ebeef5;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    font-weight: 600;
+    font-size: 14px;
+  }
+
+  .session-list {
+    flex: 1;
+    overflow-y: auto;
+    padding: 8px;
+
+    .session-item {
+      display: flex;
+      align-items: center;
+      padding: 12px;
+      margin-bottom: 4px;
+      border-radius: 8px;
+      cursor: pointer;
+      transition: all 0.2s;
+      gap: 10px;
+      position: relative;
+      border: 1px solid transparent;
+
+      &:hover {
+        background-color: #f5f7fa;
+        .delete-btn {
+          opacity: 1;
+        }
+      }
+
+      &.active {
+        background-color: #ecf5ff;
+        border-color: #d9ecff;
+        color: #409eff;
+      }
+
+      .el-icon {
+        font-size: 16px;
+        flex-shrink: 0;
+      }
+
+      .session-name {
+        flex: 1;
+        font-size: 13px;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+
+      .delete-btn {
+        opacity: 0;
+        transition: opacity 0.2s;
+        padding: 4px;
+        &:hover {
+          color: #f56c6c;
+        }
+      }
+    }
+  }
+}
+
+.chat-main {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  flex: 1;
+  overflow: hidden;
+}
+
+.message-list {
+  flex: 1;
+  overflow-y: auto;
+  padding: 20px;
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+
+  &::-webkit-scrollbar {
+    width: 6px;
+  }
+  &::-webkit-scrollbar-thumb {
+    background: #dcdfe6;
+    border-radius: 3px;
+  }
+}
+
+.message-item {
+  display: flex;
+  gap: 12px;
+  width: 100%;
+
+  .avatar {
+    width: 36px;
+    height: 36px;
+    border-radius: 8px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-shrink: 0;
+    font-size: 20px;
+  }
+
+  .message-content {
+    flex: 1;
+    overflow-x: hidden; // 淇敼涓� hidden锛屽唴閮ㄥ鍣ㄥ鐞嗘粴鍔�
+    display: flex;
+    flex-direction: column;
+    max-width: calc(100% - 48px); // 鍑忓幓澶村儚鍜岄棿璺�
+    
+    .text-box {
+      padding: 12px 16px;
+      border-radius: 12px;
+      font-size: 14px;
+      line-height: 1.6;
+      word-break: break-word;
+      max-width: 100%;
+      width: fit-content;
+      overflow-x: auto;
+      &::-webkit-scrollbar {
+        height: 6px;
+      }
+      &::-webkit-scrollbar-thumb {
+        background: #dcdfe6;
+        border-radius: 3px;
+      }
+    }
+  }
+
+  &.bot-message {
+    .message-content {
+      align-items: flex-start;
+    }
+    .avatar {
+      background-color: #409eff;
+      color: #fff;
+    }
+    .text-box {
+      background-color: #fff;
+      color: #303133;
+      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+    }
+  }
+
+  &.user-message {
+    flex-direction: row-reverse;
+    .message-content {
+      align-items: flex-end;
+    }
+    .avatar {
+      background-color: #95d475;
+      color: #fff;
+    }
+    .text-box {
+      background-color: #409eff;
+      color: #fff;
+    }
+  }
+}
+
+.charts-wrapper {
+  margin-top: 10px;
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+  overflow-x: auto;
+  width: 100%;
+  padding-bottom: 8px;
+  &::-webkit-scrollbar {
+    height: 6px;
+  }
+  &::-webkit-scrollbar-thumb {
+    background: #dcdfe6;
+    border-radius: 3px;
+  }
+}
+
+.chart-item {
+  width: 100%;
+  min-width: 300px;
+  height: 300px;
+  background: #fff;
+  border-radius: 8px;
+  padding: 10px;
+}
+
+.table-wrapper {
+  margin-top: 10px;
+  background: #fff;
+  border-radius: 8px;
+  overflow: hidden;
+  overflow-x: auto;
+  width: 100%;
+  &::-webkit-scrollbar {
+    height: 6px;
+  }
+  &::-webkit-scrollbar-thumb {
+    background: #dcdfe6;
+    border-radius: 3px;
+  }
+  .el-table {
+    min-width: 300px;
+  }
+}
+
+.input-area {
+  padding: 16px;
+  background-color: #fff;
+  border-top: 1px solid #dcdfe6;
+
+  .input-actions {
+  display: flex;
+  gap: 12px;
+  margin-bottom: 8px;
+  align-items: center;
+
+  .file-upload-trigger {
+    display: inline-flex;
+    align-items: center;
+  }
+}
+
+  .input-box {
+    padding: 12px;
+    position: relative;
+    background: #fff;
+    border: 1px solid #dcdfe6;
+    border-radius: 8px;
+    margin: 0 16px 16px;
+    transition: border-color 0.2s;
+
+    &:focus-within {
+      border-color: #409eff;
+    }
+
+    .selected-file-tag {
+      display: flex;
+      align-items: center;
+      background: #f0f7ff;
+      border: 1px solid #d9ecff;
+      border-radius: 4px;
+      padding: 4px 8px;
+      margin-bottom: 8px;
+      gap: 6px;
+      width: fit-content;
+      max-width: 100%;
+
+      .el-icon {
+        color: #409eff;
+        font-size: 14px;
+      }
+
+      .file-name {
+        font-size: 12px;
+        color: #606266;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+
+      .remove-file {
+        cursor: pointer;
+        color: #909399;
+        transition: color 0.2s;
+        &:hover {
+          color: #f56c6c;
+        }
+      }
+    }
+
+    :deep(.el-textarea__inner) {
+      padding: 0;
+      border: none;
+      box-shadow: none;
+      background: transparent;
+      font-family: inherit;
+      font-size: 14px;
+      line-height: 1.5;
+      color: #303133;
+      &::placeholder {
+        color: #c0c4cc;
+      }
+    }
+
+    .send-btn {
+      position: absolute;
+      right: 12px;
+      bottom: 12px;
+      padding: 8px 16px;
+    }
+  }
+}
+
+.typing-indicator {
+  display: flex;
+  gap: 4px;
+  padding: 8px 12px;
+  background: #fff;
+  border-radius: 12px;
+  width: fit-content;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+  margin-top: 4px;
+  .dot {
+    width: 6px;
+    height: 6px;
+    background-color: #909399;
+    border-radius: 50%;
+    animation: typing 1.4s infinite ease-in-out;
+    &:nth-child(2) { animation-delay: 0.2s; }
+    &:nth-child(3) { animation-delay: 0.4s; }
+  }
+}
+
+@keyframes typing {
+  0%, 80%, 100% { transform: scale(0); }
+  40% { transform: scale(1); }
+}
+
+.code-block {
+  background: #2d2d2d;
+  color: #ccc;
+  padding: 12px;
+  border-radius: 6px;
+  font-family: monospace;
+  margin: 8px 0;
+  overflow-x: auto;
+  &.js-code {
+    color: #f08d49;
+  }
+}
+</style>
diff --git a/src/layout/index.vue b/src/layout/index.vue
index d3580d0..a1bb724 100644
--- a/src/layout/index.vue
+++ b/src/layout/index.vue
@@ -1,131 +1,133 @@
-<template>
-  <div :class="classObj"
-       class="app-wrapper"
-       :style="{ '--current-color': theme }">
-    <div v-if="device === 'mobile' && sidebar.opened"
-         class="drawer-bg"
-         @click="handleClickOutside" />
-    <sidebar v-if="!sidebar.hide"
-             class="sidebar-container" />
-    <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }"
-         class="main-container">
-      <div :class="{ 'fixed-header': fixedHeader }">
-        <navbar @setLayout="setLayout" />
-        <tags-view v-if="needTagsView" />
-      </div>
-      <app-main />
-      <settings ref="settingRef" />
-    </div>
-  </div>
-</template>
-
-<script setup>
-  import { useWindowSize } from "@vueuse/core";
-  import Sidebar from "./components/Sidebar/index.vue";
-  import { AppMain, Navbar, Settings, TagsView } from "./components";
-  import defaultSettings from "@/settings";
-
-  import useAppStore from "@/store/modules/app";
-  import useSettingsStore from "@/store/modules/settings";
-
-  const settingsStore = useSettingsStore();
-  const theme = computed(() => settingsStore.theme);
-  const sideTheme = computed(() => settingsStore.sideTheme);
-  const sidebar = computed(() => useAppStore().sidebar);
-  const device = computed(() => useAppStore().device);
-  const needTagsView = computed(() => settingsStore.tagsView);
-  const fixedHeader = computed(() => settingsStore.fixedHeader);
-
-  const classObj = computed(() => ({
-    hideSidebar: !sidebar.value.opened,
-    openSidebar: sidebar.value.opened,
-    withoutAnimation: sidebar.value.withoutAnimation,
-    mobile: device.value === "mobile",
-  }));
-
-  const { width, height } = useWindowSize();
-  const WIDTH = 992; // refer to Bootstrap's responsive design
-
-  watch(
-    () => device.value,
-    () => {
-      if (device.value === "mobile" && sidebar.value.opened) {
-        useAppStore().closeSideBar({ withoutAnimation: false });
-      }
-    }
-  );
-
-  watchEffect(() => {
-    if (width.value - 1 < WIDTH) {
-      useAppStore().toggleDevice("mobile");
-      useAppStore().closeSideBar({ withoutAnimation: true });
-    } else {
-      useAppStore().toggleDevice("desktop");
-    }
-  });
-
-  function handleClickOutside() {
-    useAppStore().closeSideBar({ withoutAnimation: false });
-  }
-
-  const settingRef = ref(null);
-  function setLayout() {
-    settingRef.value.openSetting();
-  }
-</script>
-
-<style lang="scss" scoped>
-  @import "@/assets/styles/mixin.scss";
-  @import "@/assets/styles/variables.module.scss";
-
-  .app-wrapper {
-    @include clearfix;
-    position: relative;
-    height: 100%;
-    width: 100%;
-    background: radial-gradient(
-        circle at top,
-        rgba(223, 232, 226, 0.95),
-        transparent 32%
-      ),
-      linear-gradient(180deg, #f7faf8 0%, var(--app-bg) 100%);
-
-    &.mobile.openSidebar {
-      position: fixed;
-      top: 0;
-    }
-  }
-
-  .drawer-bg {
-    background: #000;
-    opacity: 0.3;
-    width: 100%;
-    top: 0;
-    height: 100%;
-    position: absolute;
-    z-index: 999;
-  }
-
-  .fixed-header {
-    position: fixed;
-    top: 0px;
-    padding-top: 12px;
-    right: 16px;
-    z-index: 9;
-    width: calc(100% - #{$base-sidebar-width} - 32px);
-    transition: width 0.28s, right 0.28s;
-    padding-bottom: 8px;
-    background-color: #f3f6f4;
-  }
-  .hideSidebar .fixed-header {
-    width: calc(100% - 100px);
-  }
-
-  .sidebarHide .fixed-header {
-    width: calc(100% - 32px);
-  }
-
-  .mobile .fixed-header {
-    width: 100%;
-  }
-</style>
+<template>
+  <div :class="classObj"
+       class="app-wrapper"
+       :style="{ '--current-color': theme }">
+    <div v-if="device === 'mobile' && sidebar.opened"
+         class="drawer-bg"
+         @click="handleClickOutside" />
+    <sidebar v-if="!sidebar.hide"
+             class="sidebar-container" />
+    <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }"
+         class="main-container">
+      <div :class="{ 'fixed-header': fixedHeader }">
+        <navbar @setLayout="setLayout" />
+        <tags-view v-if="needTagsView" />
+      </div>
+      <app-main />
+      <settings ref="settingRef" />
+    </div>
+    <AIChatSidebar />
+  </div>
+</template>
+
+<script setup>
+  import { useWindowSize } from "@vueuse/core";
+  import Sidebar from "./components/Sidebar/index.vue";
+  import { AppMain, Navbar, Settings, TagsView } from "./components";
+  import AIChatSidebar from "@/components/AIChatSidebar/index.vue";
+  import defaultSettings from "@/settings";
+
+  import useAppStore from "@/store/modules/app";
+  import useSettingsStore from "@/store/modules/settings";
+
+  const settingsStore = useSettingsStore();
+  const theme = computed(() => settingsStore.theme);
+  const sideTheme = computed(() => settingsStore.sideTheme);
+  const sidebar = computed(() => useAppStore().sidebar);
+  const device = computed(() => useAppStore().device);
+  const needTagsView = computed(() => settingsStore.tagsView);
+  const fixedHeader = computed(() => settingsStore.fixedHeader);
+
+  const classObj = computed(() => ({
+    hideSidebar: !sidebar.value.opened,
+    openSidebar: sidebar.value.opened,
+    withoutAnimation: sidebar.value.withoutAnimation,
+    mobile: device.value === "mobile",
+  }));
+
+  const { width, height } = useWindowSize();
+  const WIDTH = 992; // refer to Bootstrap's responsive design
+
+  watch(
+    () => device.value,
+    () => {
+      if (device.value === "mobile" && sidebar.value.opened) {
+        useAppStore().closeSideBar({ withoutAnimation: false });
+      }
+    }
+  );
+
+  watchEffect(() => {
+    if (width.value - 1 < WIDTH) {
+      useAppStore().toggleDevice("mobile");
+      useAppStore().closeSideBar({ withoutAnimation: true });
+    } else {
+      useAppStore().toggleDevice("desktop");
+    }
+  });
+
+  function handleClickOutside() {
+    useAppStore().closeSideBar({ withoutAnimation: false });
+  }
+
+  const settingRef = ref(null);
+  function setLayout() {
+    settingRef.value.openSetting();
+  }
+</script>
+
+<style lang="scss" scoped>
+  @import "@/assets/styles/mixin.scss";
+  @import "@/assets/styles/variables.module.scss";
+
+  .app-wrapper {
+    @include clearfix;
+    position: relative;
+    height: 100%;
+    width: 100%;
+    background: radial-gradient(
+        circle at top,
+        rgba(223, 232, 226, 0.95),
+        transparent 32%
+      ),
+      linear-gradient(180deg, #f7faf8 0%, var(--app-bg) 100%);
+
+    &.mobile.openSidebar {
+      position: fixed;
+      top: 0;
+    }
+  }
+
+  .drawer-bg {
+    background: #000;
+    opacity: 0.3;
+    width: 100%;
+    top: 0;
+    height: 100%;
+    position: absolute;
+    z-index: 999;
+  }
+
+  .fixed-header {
+    position: fixed;
+    top: 0px;
+    padding-top: 12px;
+    right: 16px;
+    z-index: 9;
+    width: calc(100% - #{$base-sidebar-width} - 32px);
+    transition: width 0.28s, right 0.28s;
+    padding-bottom: 8px;
+    background-color: #f3f6f4;
+  }
+  .hideSidebar .fixed-header {
+    width: calc(100% - 100px);
+  }
+
+  .sidebarHide .fixed-header {
+    width: calc(100% - 32px);
+  }
+
+  .mobile .fixed-header {
+    width: 100%;
+  }
+</style>
diff --git a/src/views/fileManagement/borrow/index.vue b/src/views/fileManagement/borrow/index.vue
index 8983665..c7d8e0d 100644
--- a/src/views/fileManagement/borrow/index.vue
+++ b/src/views/fileManagement/borrow/index.vue
@@ -100,16 +100,14 @@
            </el-col>
            <el-col :span="12">
              <el-form-item label="鍊熼槄涔︾睄锛�" prop="documentationId">
-               <!-- <el-select v-model="borrowForm.documentationId" placeholder="璇烽�夋嫨鍊熼槄涔︾睄" style="width: 100%" @change="handleScanContent">
-                 <el-option 
-                   v-for="item in documentList" 
-                   :key="item.id" 
-                   :label="item.docName || item.name" 
-                   :value="item.id"
-                 />
-               </el-select> -->
                <div style="display: flex; gap: 10px;">
-                <el-select v-model="borrowForm.documentationId" placeholder="璇烽�夋嫨鍊熼槄涔︾睄" style="flex: 1;width: 100px;" @change="handleSelectChange">
+                <el-select
+                  v-if="borrowOperationType !== 'edit'"
+                  v-model="borrowForm.documentationId"
+                  placeholder="璇烽�夋嫨鍊熼槄涔︾睄"
+                  style="flex: 1;width: 100px;"
+                  @change="handleSelectChange"
+                >
                   <el-option 
                     v-for="item in documentList" 
                     :key="item.id" 
@@ -118,6 +116,13 @@
                   />
                 </el-select>
                 <el-input
+                  v-else
+                  v-model="currentEditDocName"
+                  style="flex: 1;width: 100px;"
+                  disabled
+                />
+                <el-input
+                  v-if="borrowOperationType !== 'edit'"
                   v-model="scanContent"
                   placeholder="鎵爜杈撳叆"
                   style="width: 100px;"
@@ -205,6 +210,7 @@
 const selectedRows = ref([]);
 const documentList = ref([]); // 鏂囨。鍒楄〃锛岀敤浜庡�熼槄涔︾睄閫夋嫨
 const scanContent = ref() // 鎵爜鍐呭
+const currentEditDocName = ref(''); // 缂栬緫鏃跺瓨鍌ㄧ殑鏂囨。鍚嶇О
 // 鍒嗛〉鐩稿叧
 const pagination = reactive({
   currentPage: 1,
@@ -282,6 +288,7 @@
       {
         name: "缂栬緫",
         type: "text",
+        disabled: (row) => row.borrowStatus === '褰掕繕',
         clickFun: (row) => {
           openBorrowDia('edit', row)
         },
@@ -428,13 +435,16 @@
   if (type === "edit") {
     // 缂栬緫妯″紡锛屽姞杞界幇鏈夋暟鎹�
     Object.assign(borrowForm, data);
+    // 瀛樺偍鏂囨。鍚嶇О鐢ㄤ簬鏄剧ず
+    currentEditDocName.value = data.docName || '';
   } else {
     // 鏂板妯″紡锛屾竻绌鸿〃鍗�
     Object.keys(borrowForm).forEach(key => {
       borrowForm[key] = "";
     });
-         // 璁剧疆榛樿鐘舵��
-     borrowForm.borrowStatus = "鍊熼槄";
+    currentEditDocName.value = ''; // 娓呯┖缂栬緫鏃剁殑鏂囨。鍚嶇О
+    // 璁剧疆榛樿鐘舵��
+    borrowForm.borrowStatus = "鍊熼槄";
     // 璁剧疆褰撳墠鏃ユ湡涓哄�熼槄鏃ユ湡
     borrowForm.borrowDate = new Date().toISOString().split('T')[0];
   }
@@ -445,6 +455,7 @@
   proxy.$refs.borrowFormRef.resetFields();
   borrowDia.value = false;
   scanContent.value = ''; // 娓呯┖鎵爜鍐呭
+  currentEditDocName.value = ''; // 娓呯┖缂栬緫鏃剁殑鏂囨。鍚嶇О
 };
 
 // 鎻愪氦鍊熼槄琛ㄥ崟
diff --git a/src/views/fileManagement/document/index.vue b/src/views/fileManagement/document/index.vue
index c31b044..f4eac2d 100644
--- a/src/views/fileManagement/document/index.vue
+++ b/src/views/fileManagement/document/index.vue
@@ -862,12 +862,14 @@
       documentForm[key] = "";
     });
     documentForm.attachments = []; // 鏂板妯″紡涓嬩篃娓呯┖闄勪欢
-    // 璁剧疆榛樿鍊� - 浣跨敤瀛楀吀鏁版嵁鐨勭涓�涓�夐」浣滀负榛樿鍊�
+    // 璁剧疆榛樿鍊� - 鏂囨。鐘舵�侀粯璁よ缃负"姝e父"
     if (document_status.value && document_status.value.length > 0) {
-      documentForm.docStatus = document_status.value[0].value;
+      const normalStatus = document_status.value.find(item => item.label === '姝e父');
+      documentForm.docStatus = normalStatus ? normalStatus.value : document_status.value[0].value;
     }
     if (document_urgency.value && document_urgency.value.length > 0) {
-      documentForm.urgencyLevel = document_urgency.value[0].value;
+      const normalUrgency = document_urgency.value.find(item => item.label === '鏅��');
+      documentForm.urgencyLevel = normalUrgency ? normalUrgency.value : document_urgency.value[0].value;
     }
   }
 };
diff --git a/src/views/fileManagement/return/index.vue b/src/views/fileManagement/return/index.vue
index bc86c56..097ab29 100644
--- a/src/views/fileManagement/return/index.vue
+++ b/src/views/fileManagement/return/index.vue
@@ -103,16 +103,14 @@
                  <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="鏂囨。锛�" prop="borrowId">
-               <!-- <el-select v-model="returnForm.borrowId" placeholder="璇烽�夋嫨鏂囨。" style="flex: 1;" @change="handleDocumentChange">
-                 <el-option 
-                   v-for="item in documentList" 
-                   :key="item.id" 
-                   :label="item.docName || item.name" 
-                   :value="item.id"
-                 />
-               </el-select> -->
                <div style="display: flex; gap: 10px;">
-                <el-select v-model="returnForm.borrowId" placeholder="璇烽�夋嫨鏂囨。" style="width: 120px;" @change="handleDocumentChange">
+                <el-select 
+                  v-if="returnOperationType !== 'edit'"
+                  v-model="returnForm.borrowId" 
+                  placeholder="璇烽�夋嫨鏂囨。" 
+                  style="width: 120px;" 
+                  @change="handleDocumentChange"
+                >
                   <el-option 
                     v-for="item in documentList" 
                     :key="item.id" 
@@ -121,6 +119,13 @@
                   />
                 </el-select>
                 <el-input
+                  v-else
+                  v-model="currentEditDocName"
+                  style="width: 120px;"
+                  disabled
+                />
+                <el-input
+                  v-if="returnOperationType !== 'edit'"
                   v-model="scanContent"
                   placeholder="鎵爜杈撳叆"
                   style="flex: 1;"
@@ -215,6 +220,7 @@
 const documentList = ref([]); // 鏂囨。鍒楄〃
 const borrowInfoList = ref([]); // 鍊熼槄淇℃伅鍒楄〃
 const scanContent = ref(); // 鎵爜鍐呭
+const currentEditDocName = ref(''); // 缂栬緫鏃跺瓨鍌ㄧ殑鏂囨。鍚嶇О
 
 // 鍒嗛〉鐩稿叧
 const pagination = reactive({
@@ -286,6 +292,7 @@
       {
         name: "缂栬緫",
         type: "text",
+        disabled: (row) => row.borrowStatus === '褰掕繕',
         clickFun: (row) => {
           openReturnDia('edit', row)
         },
@@ -396,15 +403,14 @@
   if (type === "edit") {
     // 缂栬緫妯″紡锛屽姞杞界幇鏈夋暟鎹�
     Object.assign(returnForm, data);
-    // 缂栬緫妯″紡涓嬶紝鏂囨。閫夋嫨鍚庤嚜鍔ㄥ~鍏呭�熼槄浜哄拰搴斿綊杩樻棩鏈�
-    if (returnForm.borrowId) {
-      handleDocumentChange(returnForm.borrowId);
-    }
+    // 瀛樺偍鏂囨。鍚嶇О鐢ㄤ簬鏄剧ず
+    currentEditDocName.value = data.docName || '';
   } else {
     // 鏂板妯″紡锛屾竻绌鸿〃鍗�
     Object.keys(returnForm).forEach(key => {
       returnForm[key] = "";
     });
+    currentEditDocName.value = ''; // 娓呯┖缂栬緫鏃剁殑鏂囨。鍚嶇О
     // 璁剧疆榛樿鐘舵��
     returnForm.borrowStatus = "褰掕繕";
     // 璁剧疆褰撳墠鏃ユ湡涓哄綊杩樻棩鏈�
@@ -418,6 +424,7 @@
   returnDia.value = false;
   scanContent.value = ''; // 娓呯┖鎵爜鍐呭
   borrowInfoList.value = []; // 娓呯┖鍊熼槄淇℃伅鍒楄〃
+  currentEditDocName.value = ''; // 娓呯┖缂栬緫鏃剁殑鏂囨。鍚嶇О
 };
 
 // 鎻愪氦褰掕繕琛ㄥ崟
diff --git a/vite.config.js b/vite.config.js
index dc687a8..ac18ec5 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -8,7 +8,7 @@
   const { VITE_APP_ENV } = env;
   const baseUrl =
       env.VITE_APP_ENV === "development"
-          ? "http://1.15.17.182:9003"
+          ? "http://localhost:7005"
           : env.VITE_BASE_API;
   const javaUrl =
       env.VITE_APP_ENV === "development"

--
Gitblit v1.9.3