From e8eda23ee6fe0273619d826ba768ce975a0d8ccf Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期五, 12 十二月 2025 14:47:33 +0800
Subject: [PATCH] 1.公司-添加商机管理页面

---
 src/views/salesManagement/opportunityManagement/index.vue                 |  487 +++++++++++++++++++++++++++++++++++++++--------
 src/api/salesManagement/opportunityManagement.js                          |   10 
 src/views/salesManagement/opportunityManagement/fileList.vue              |   43 ++++
 vite.config.js                                                            |    2 
 src/views/productionManagement/productionReporting/components/formDia.vue |    3 
 5 files changed, 454 insertions(+), 91 deletions(-)

diff --git a/src/api/salesManagement/opportunityManagement.js b/src/api/salesManagement/opportunityManagement.js
index b989150..31f64c3 100644
--- a/src/api/salesManagement/opportunityManagement.js
+++ b/src/api/salesManagement/opportunityManagement.js
@@ -27,6 +27,14 @@
     data: data,
   });
 }
+// 娣诲姞鍟嗘満
+export function addDescription(data) {
+  return request({
+    url: "/businessOpportunity/addDescription",
+    method: "post",
+    data: data,
+  });
+}
 
 // 鍒犻櫎鍟嗘満
 export function delOpportunity(ids) {
@@ -35,4 +43,4 @@
     method: "delete",
     data: ids,
   });
-}
\ No newline at end of file
+}
diff --git a/src/views/productionManagement/productionReporting/components/formDia.vue b/src/views/productionManagement/productionReporting/components/formDia.vue
index 89f6c76..e5adbc2 100644
--- a/src/views/productionManagement/productionReporting/components/formDia.vue
+++ b/src/views/productionManagement/productionReporting/components/formDia.vue
@@ -42,6 +42,9 @@
 								v-model="form.schedulingUserId"
 								placeholder="閫夋嫨浜哄憳"
 								style="width: 100%;"
+                filterable
+                default-first-option
+                :reserve-keyword="false"
 							>
 								<el-option
 									v-for="user in userList"
diff --git a/src/views/salesManagement/opportunityManagement/fileList.vue b/src/views/salesManagement/opportunityManagement/fileList.vue
new file mode 100644
index 0000000..025218d
--- /dev/null
+++ b/src/views/salesManagement/opportunityManagement/fileList.vue
@@ -0,0 +1,43 @@
+<template>
+  <el-dialog v-model="dialogVisible" title="闄勪欢" width="40%" :before-close="handleClose">
+    <el-table :data="tableData" border height="40vh" stripe>
+      <el-table-column label="闄勪欢鍚嶇О" prop="name" min-width="400" show-overflow-tooltip />
+      <el-table-column fixed="right" label="鎿嶄綔" width="100" align="center">
+        <template #default="scope">
+          <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">涓嬭浇</el-button>
+          <el-button link type="primary" size="small" @click="lookFile(scope.row)">棰勮</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </el-dialog>
+  <filePreview ref="filePreviewRef" />
+</template>
+
+<script setup>
+import { ref, getCurrentInstance } from 'vue'
+import filePreview from '@/components/filePreview/index.vue'
+
+const dialogVisible = ref(false)
+const tableData = ref([])
+const { proxy } = getCurrentInstance();
+const filePreviewRef = ref()
+const handleClose = () => {
+  dialogVisible.value = false
+}
+const open = (list) => {
+  dialogVisible.value = true
+  tableData.value = list
+}
+const downLoadFile = (row) => {
+  proxy.$download.name(row.url);
+
+}
+const lookFile = (row) => {
+  filePreviewRef.value.open(row.url)
+}
+defineExpose({
+  open
+})
+</script>
+
+<style></style>
\ No newline at end of file
diff --git a/src/views/salesManagement/opportunityManagement/index.vue b/src/views/salesManagement/opportunityManagement/index.vue
index 170830b..23cd728 100644
--- a/src/views/salesManagement/opportunityManagement/index.vue
+++ b/src/views/salesManagement/opportunityManagement/index.vue
@@ -51,16 +51,16 @@
           </template>
         </el-table-column>
         <el-table-column label="鐪佷唤" prop="province" show-overflow-tooltip width="120" />
-        <el-table-column label="瀹㈡埛鍚嶇О" prop="customerName" show-overflow-tooltip width="230" />
-        <el-table-column label="鍟嗘満鏉ユ簮" prop="businessSource" show-overflow-tooltip width="150" />
-        <el-table-column label="瀹㈡埛鎻忚堪" prop="description" show-overflow-tooltip min-width="200" />
-        <el-table-column label="褰曞叆浜�" prop="entryPerson" show-overflow-tooltip width="120" />
-        <el-table-column label="鏇存柊鏃ユ湡" prop="updateTime" width="120">
+        <el-table-column label="瀹㈡埛鍚嶇О" prop="customerName" show-overflow-tooltip />
+        <el-table-column label="鍟嗘満鏉ユ簮" prop="businessSource" show-overflow-tooltip />
+        <!-- <el-table-column label="瀹㈡埛鎻忚堪" prop="description" show-overflow-tooltip min-width="200" /> -->
+        <el-table-column label="褰曞叆浜�" prop="entryPerson" show-overflow-tooltip />
+        <el-table-column label="鏇存柊鏃ユ湡" prop="updateTime">
           <template #default="{ row }">
             {{ formatDate(row.updateTime) }}
           </template>
         </el-table-column>
-        <el-table-column label="鎿嶄綔" fixed="right" width="130" align="center">
+        <el-table-column label="鎿嶄綔" fixed="right" width="220" align="center">
           <template #default="{ row }">
             <el-button
               link
@@ -74,9 +74,25 @@
               link
               type="primary"
               size="small"
+              @click="handleAddOperation(row)"
+            >
+              娣诲姞鎻忚堪
+            </el-button>
+            <el-button
+              link
+              type="primary"
+              size="small"
               @click="handleDetail(row)"
             >
               璇︽儏
+            </el-button>
+            <el-button
+              link
+              type="primary"
+              size="small"
+              @click="handleAttachment(row)"
+            >
+              闄勪欢
             </el-button>
           </template>
         </el-table-column>
@@ -96,7 +112,7 @@
     <!-- 鏂板/缂栬緫瀵硅瘽妗� -->
     <el-dialog
       v-model="dialogFormVisible"
-      :title="operationType === 'add' ? '鏂板缓鍟嗘満' : operationType === 'edit' ? '缂栬緫鍟嗘満' : '鍟嗘満璇︽儏'"
+      :title="operationType === 'add' ? '鏂板缓鍟嗘満' : operationType === 'edit' ? '缂栬緫鍟嗘満' : operationType === 'addOperation' ? '娣诲姞鍟嗘満' : '鍟嗘満璇︽儏'"
       width="600px"
       @close="closeDialog"
     >
@@ -119,7 +135,7 @@
         </el-form-item>
         
         <el-form-item label="鐪佷唤" prop="province">
-          <el-select v-model="form.province" filterable placeholder="璇烽�夋嫨鐪佷唤" style="width: 100%" :disabled="operationType === 'detail'">
+          <el-select v-model="form.province" filterable placeholder="璇烽�夋嫨鐪佷唤" style="width: 100%" :disabled="operationType === 'detail' || operationType === 'addOperation'">
             <el-option
               v-for="item in provinceOptions"
               :key="item.value"
@@ -130,7 +146,7 @@
         </el-form-item>
         
         <el-form-item label="瀹㈡埛鍚嶇О" prop="customerName">
-          <el-select v-model="form.customerName" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'detail'">
+          <el-select v-model="form.customerName" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'detail' || operationType === 'addOperation'">
             <el-option v-for="item in customerOption" :key="item.customerName" :label="item.customerName" :value="item.customerName">
               {{
                 item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
@@ -140,10 +156,10 @@
         </el-form-item>
         
         <el-form-item label="鍟嗘満鏉ユ簮" prop="businessSource">
-          <el-input v-model="form.businessSource" placeholder="璇疯緭鍏ュ晢鏈烘潵婧�" :disabled="operationType === 'detail'" />
+          <el-input v-model="form.businessSource" placeholder="璇疯緭鍏ュ晢鏈烘潵婧�" :disabled="operationType === 'detail' || operationType === 'addOperation'" />
         </el-form-item>
         
-        <el-form-item label="瀹㈡埛鎻忚堪" prop="description">
+        <el-form-item label="瀹㈡埛鎻忚堪" prop="description" v-if="operationType !== 'detail'">
           <el-input
             v-model="form.description"
             type="textarea"
@@ -151,26 +167,80 @@
             placeholder="璇疯緭鍏ュ鎴锋弿杩�"
             maxlength="500"
             show-word-limit
-            :disabled="operationType === 'detail'"
           />
         </el-form-item>
         
         <el-row :gutter="20">
           <el-col :span="12">
             <el-form-item label="褰曞叆浜�" prop="entryPerson">
-              <el-select v-model="form.entryPerson" placeholder="璇烽�夋嫨" clearable @change="changs" :disabled="operationType === 'detail'">
+              <el-select v-model="form.entryPerson" placeholder="璇烽�夋嫨" clearable @change="changs" :disabled="operationType === 'detail' || operationType === 'addOperation'">
                 <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName" />
               </el-select>
             </el-form-item>
           </el-col>
-          <el-col :span="12">
-            <el-form-item label="褰曞叆鏃ユ湡" prop="entryDateStart">
-              <el-date-picker style="width: 100%" v-model="form.entryDateStart" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
-                type="date" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'detail'" />
+          <el-col :span="12"> 
+             <el-form-item label="褰曞叆鏃ユ湡" prop="entryDate"> 
+               <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" 
+                 type="date" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'detail' || operationType === 'addOperation'" /> 
+             </el-form-item> 
+           </el-col>
+        </el-row>
+        
+        <!-- 闄勪欢涓婁紶锛堥潪璇︽儏妯″紡涓嬫樉绀猴級 -->
+        <el-row :gutter="30" v-if="operationType !== 'detail'">
+          <el-col :span="24">
+            <el-form-item label="闄勪欢鏉愭枡锛�">
+              <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
+                :headers="upload.headers" :data="upload.data" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
+                :on-success="handleUploadSuccess" :on-remove="handleRemove">
+                <el-button type="primary">涓婁紶</el-button>
+                <template #tip>
+                  <div class="el-upload__tip">
+                    鏂囦欢鏍煎紡鏀寔
+                    doc锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z
+                  </div>
+                </template>
+              </el-upload>
             </el-form-item>
           </el-col>
         </el-row>
       </el-form>
+      
+      <!-- 鍙樻洿璁板綍鏃堕棿绾匡紙浠呭湪璇︽儏妯″紡涓嬫樉绀猴級 -->
+      <div v-if="operationType === 'detail'" class="change-history-section">
+        <el-divider content-position="left">鍙樻洿璁板綍</el-divider>
+        <el-timeline>
+          <el-timeline-item
+            v-for="record in changeHistory"
+            :key="record.id"
+            :timestamp="record.timestamp"
+            :type="record.type === 'current' ? 'primary' : record.type === 'update' ? 'success' : 'info'"
+            :hollow="record.type === 'current'"
+            placement="top"
+          >
+            <el-card shadow="hover" class="timeline-card">
+              <template #header>
+                <div class="card-header">
+                  <span class="action-type">{{ record.action }}</span>
+                  <span class="operator">鎿嶄綔浜猴細{{ record.operator }}</span>
+                </div>
+              </template>
+              <div class="change-content">
+                <div class="status-change" v-if="record.status">
+                  <span class="label">鐘舵�侊細</span>
+                  <el-tag :type="record.type === 'current' ? 'primary' : 'info'" size="small">
+                    {{ getStatusLabel(record.status) }}
+                  </el-tag>
+                </div>
+                <div class="description-change" v-if="record.description">
+                  <span class="label">瀹㈡埛鎻忚堪锛�</span>
+                  <span class="description-text">{{ record.description }}</span>
+                </div>
+              </div>
+            </el-card>
+          </el-timeline-item>
+        </el-timeline>
+      </div>
       
       <template #footer>
         <div class="dialog-footer">
@@ -181,6 +251,9 @@
         </div>
       </template>
     </el-dialog>
+
+    <!-- 闄勪欢鍒楄〃瀵硅瘽妗� -->
+    <FileList ref="fileListRef" />
   </div>
 </template>
 
@@ -191,14 +264,17 @@
 import pagination from '@/components/PIMTable/Pagination.vue'
 import useUserStore from '@/store/modules/user'
 import dayjs from 'dayjs'
+import { getToken } from '@/utils/auth'
 import { 
   opportunityListPage, 
   addOpportunity, 
   updateOpportunity, 
-  delOpportunity
+  delOpportunity,
+  addDescription
 } from '@/api/salesManagement/opportunityManagement.js'
 import { userListNoPage } from '@/api/system/user.js'
-import { customerList } from '@/api/salesManagement/salesLedger.js'
+import {customerList, getSalesLedgerWithProducts} from '@/api/salesManagement/salesLedger.js'
+import FileList from './fileList.vue'
 
 const { proxy } = getCurrentInstance()
 const userStore = useUserStore()
@@ -231,14 +307,39 @@
 const formRef = ref()
 const form = reactive({
   id: undefined,
-  status: 'new',
+  status: undefined,
   province: '',
   customerName: '',
   businessSource: '',
   description: '',
   entryPerson: userStore.nickName,
-  entryDateStart: dayjs().format('YYYY-MM-DD')
+  entryDate: dayjs().format('YYYY-MM-DD')
 })
+
+// 鍙樻洿璁板綍鏁版嵁锛堟ā鎷熸暟鎹級
+const changeHistory = ref([])
+
+// 鏂囦欢鍒楄〃
+const fileList = ref([])
+
+// FileList缁勪欢寮曠敤
+const fileListRef = ref(null)
+
+// 涓婁紶閰嶇疆
+const upload = reactive({
+  // 涓婁紶鐨勫湴鍧�
+  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
+  // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+  headers: { Authorization: "Bearer " + getToken() },
+  // 涓婁紶鍙傛暟
+  data: { type: 9 }
+})
+
+// 鑾峰彇鐘舵�佹爣绛�
+const getStatusLabel = (statusValue) => {
+  const status = statusOptions.find(item => item.value === statusValue)
+  return status ? status.label : statusValue
+}
 
 // 琛ㄥ崟楠岃瘉瑙勫垯
 const rules = reactive({
@@ -251,80 +352,80 @@
   entryPerson: [
     { required: true, message: '璇烽�夋嫨褰曞叆浜�', trigger: 'change' }
   ],
-  entryDateStart: [
+  entryDate: [
     { required: true, message: '璇烽�夋嫨褰曞叆鏃ユ湡', trigger: 'change' }
   ]
 })
 
 // 鐘舵�侀�夐」
 const statusOptions = [
-  { value: 'new', label: '鏂板缓' },
-  { value: 'tracking', label: '椤圭洰璺熻釜' },
-  { value: 'contract', label: '鍚堝悓绛剧害' },
-  { value: 'delivery', label: '椤圭洰浜や粯' },
-  { value: 'acceptance', label: '椤圭洰楠屾敹' }
+  { value: '鏂板缓', label: '鏂板缓' },
+  { value: '椤圭洰璺熻釜', label: '椤圭洰璺熻釜' },
+  { value: '鍚堝悓绛剧害', label: '鍚堝悓绛剧害' },
+  { value: '椤圭洰浜や粯', label: '椤圭洰浜や粯' },
+  { value: '椤圭洰楠屾敹', label: '椤圭洰楠屾敹' }
 ]
 
 // 鐪佷唤閫夐」锛堢ず渚嬶級
 const provinceOptions = [
-  { value: 'beijing', label: '鍖椾含甯�' },
-  { value: 'tianjin', label: '澶╂触甯�' },
-  { value: 'hebei', label: '娌冲寳鐪�' },
-  { value: 'shanxi', label: '灞辫タ鐪�' },
-  { value: 'neimenggu', label: '鍐呰挋鍙よ嚜娌诲尯' },
-  { value: 'liaoning', label: '杈藉畞鐪�' },
-  { value: 'jilin', label: '鍚夋灄鐪�' },
-  { value: 'heilongjiang', label: '榛戦緳姹熺渷' },
-  { value: 'shanghai', label: '涓婃捣甯�' },
-  { value: 'jiangsu', label: '姹熻嫃鐪�' },
-  { value: 'zhejiang', label: '娴欐睙鐪�' },
-  { value: 'anhui', label: '瀹夊窘鐪�' },
-  { value: 'fujian', label: '绂忓缓鐪�' },
-  { value: 'jiangxi', label: '姹熻タ鐪�' },
-  { value: 'shandong', label: '灞变笢鐪�' },
-  { value: 'henan', label: '娌冲崡鐪�' },
-  { value: 'hubei', label: '婀栧寳鐪�' },
-  { value: 'hunan', label: '婀栧崡鐪�' },
-  { value: 'guangdong', label: '骞夸笢鐪�' },
-  { value: 'guangxi', label: '骞胯タ澹棌鑷不鍖�' },
-  { value: 'hainan', label: '娴峰崡鐪�' },
-  { value: 'chongqing', label: '閲嶅簡甯�' },
-  { value: 'sichuan', label: '鍥涘窛鐪�' },
-  { value: 'guizhou', label: '璐靛窞鐪�' },
-  { value: 'yunnan', label: '浜戝崡鐪�' },
-  { value: 'xizang', label: '瑗胯棌鑷不鍖�' },
-  { value: 'shaanxi', label: '闄曡タ鐪�' },
-  { value: 'gansu', label: '鐢樿們鐪�' },
-  { value: 'qinghai', label: '闈掓捣鐪�' },
-  { value: 'ningxia', label: '瀹佸鍥炴棌鑷不鍖�' },
-  { value: 'xinjiang', label: '鏂扮枂缁村惥灏旇嚜娌诲尯' },
-  { value: 'taiwan', label: '鍙版咕鐪�' },
-  { value: 'xianggang', label: '棣欐腐鐗瑰埆琛屾斂鍖�' },
-  { value: 'aomen', label: '婢抽棬鐗瑰埆琛屾斂鍖�' }
+  { value: '鍖椾含甯�', label: '鍖椾含甯�' },
+  { value: '澶╂触甯�', label: '澶╂触甯�' },
+  { value: '娌冲寳鐪�', label: '娌冲寳鐪�' },
+  { value: '灞辫タ鐪�', label: '灞辫タ鐪�' },
+  { value: '鍐呰挋鍙よ嚜娌诲尯', label: '鍐呰挋鍙よ嚜娌诲尯' },
+  { value: '杈藉畞鐪�', label: '杈藉畞鐪�' },
+  { value: '鍚夋灄鐪�', label: '鍚夋灄鐪�' },
+  { value: '榛戦緳姹熺渷', label: '榛戦緳姹熺渷' },
+  { value: '涓婃捣甯�', label: '涓婃捣甯�' },
+  { value: '姹熻嫃鐪�', label: '姹熻嫃鐪�' },
+  { value: '娴欐睙鐪�', label: '娴欐睙鐪�' },
+  { value: '瀹夊窘鐪�', label: '瀹夊窘鐪�' },
+  { value: '绂忓缓鐪�', label: '绂忓缓鐪�' },
+  { value: '姹熻タ鐪�', label: '姹熻タ鐪�' },
+  { value: '灞变笢鐪�', label: '灞变笢鐪�' },
+  { value: '娌冲崡鐪�', label: '娌冲崡鐪�' },
+  { value: '婀栧寳鐪�', label: '婀栧寳鐪�' },
+  { value: '婀栧崡鐪�', label: '婀栧崡鐪�' },
+  { value: '骞夸笢鐪�', label: '骞夸笢鐪�' },
+  { value: '骞胯タ澹棌鑷不鍖�', label: '骞胯タ澹棌鑷不鍖�' },
+  { value: '娴峰崡鐪�', label: '娴峰崡鐪�' },
+  { value: '閲嶅簡甯�', label: '閲嶅簡甯�' },
+  { value: '鍥涘窛鐪�', label: '鍥涘窛鐪�' },
+  { value: '璐靛窞鐪�', label: '璐靛窞鐪�' },
+  { value: '浜戝崡鐪�', label: '浜戝崡鐪�' },
+  { value: '瑗胯棌鑷不鍖�', label: '瑗胯棌鑷不鍖�' },
+  { value: '闄曡タ鐪�', label: '闄曡タ鐪�' },
+  { value: '鐢樿們鐪�', label: '鐢樿們鐪�' },
+  { value: '闈掓捣鐪�', label: '闈掓捣鐪�' },
+  { value: '瀹佸鍥炴棌鑷不鍖�', label: '瀹佸鍥炴棌鑷不鍖�' },
+  { value: '鏂扮枂缁村惥灏旇嚜娌诲尯', label: '鏂扮枂缁村惥灏旇嚜娌诲尯' },
+  { value: '鍙版咕鐪�', label: '鍙版咕鐪�' },
+  { value: '棣欐腐鐗瑰埆琛屾斂鍖�', label: '棣欐腐鐗瑰埆琛屾斂鍖�' },
+  { value: '婢抽棬鐗瑰埆琛屾斂鍖�', label: '婢抽棬鐗瑰埆琛屾斂鍖�' }
 ]
 
-// 鑾峰彇鐘舵�佹爣绛剧被鍨�
-const getStatusTagType = (status) => {
-  const typeMap = {
-    'new': 'info',
-    'tracking': 'primary',
-    'contract': 'warning',
-    'delivery': 'success',
-    'acceptance': 'success'
-  }
-  return typeMap[status] || 'info'
-}
+// 鑾峰彇鐘舵�佹爣绛剧被鍨� 
+const getStatusTagType = (status) => { 
+  const typeMap = { 
+    '鏂板缓': 'info', 
+    '椤圭洰璺熻釜': 'primary', 
+    '鍚堝悓绛剧害': 'warning', 
+    '椤圭洰浜や粯': 'success', 
+    '椤圭洰楠屾敹': 'success' 
+  } 
+  return typeMap[status] || 'info' 
+} 
 
-// 鑾峰彇鐘舵�佹枃鏈�
-const getStatusText = (status) => {
-  const textMap = {
-    'new': '鏂板缓',
-    'tracking': '椤圭洰璺熻釜',
-    'contract': '鍚堝悓绛剧害',
-    'delivery': '椤圭洰浜や粯',
-    'acceptance': '椤圭洰楠屾敹'
-  }
-  return textMap[status] || '鏈煡'
+// 鑾峰彇鐘舵�佹枃鏈� 
+const getStatusText = (status) => { 
+  const textMap = { 
+    '鏂板缓': '鏂板缓', 
+    '椤圭洰璺熻釜': '椤圭洰璺熻釜', 
+    '鍚堝悓绛剧害': '鍚堝悓绛剧害', 
+    '椤圭洰浜や粯': '椤圭洰浜や粯', 
+    '椤圭洰楠屾敹': '椤圭洰楠屾敹' 
+  } 
+  return textMap[status] || '鏈煡' 
 }
 
 // 鏍煎紡鍖栨棩鏈�
@@ -419,6 +520,28 @@
   dialogFormVisible.value = true
 }
 
+// 娣诲姞鎿嶄綔
+const handleAddOperation = async (row) => {
+  operationType.value = 'addOperation'
+  
+  // 鍔犺浇鐢ㄦ埛鍒楄〃鍜屽鎴峰垪琛�
+  let userLists = await userListNoPage()
+  userList.value = userLists.data
+  customerList().then((res) => {
+    customerOption.value = res
+  })
+  
+  // 浣跨敤褰撳墠琛屾暟鎹綔涓哄熀纭�锛屼絾鍙兘淇敼鐘舵�佸拰瀹㈡埛鎻忚堪
+  Object.assign(form, row, {
+    // 淇濈暀鍘熷鍟嗘満ID锛岀敤浜庡叧鑱旀搷浣滆褰�
+    status: row.status,
+    description: '', // 娓呯┖瀹㈡埛鎻忚堪锛屽厑璁搁噸鏂板~鍐�
+    entryPerson: userStore.nickName, // 璁剧疆褰曞叆浜轰负褰撳墠璐﹀彿
+    entryDate: dayjs().format('YYYY-MM-DD') // 璁剧疆褰曞叆鏃堕棿涓哄綋澶�
+  })
+  dialogFormVisible.value = true
+}
+
 // 鏌ョ湅璇︽儏
 const handleDetail = async (row) => {
   operationType.value = 'detail'
@@ -434,7 +557,32 @@
   Object.assign(form, row, {
     entryDateStart: row.updateTime || row.entryDateStart
   })
+  
+  // 鐢熸垚妯℃嫙鍙樻洿璁板綍
+  generateChangeHistory(row)
   dialogFormVisible.value = true
+}
+
+// 鐢熸垚鍙樻洿璁板綍
+const generateChangeHistory = (row) => {
+  // 浣跨敤businessDescription鏁扮粍鏁版嵁鐢熸垚鍙樻洿璁板綍
+  const history = []
+  
+  if (row.businessDescription && Array.isArray(row.businessDescription)) {
+    row.businessDescription.forEach((item, index) => {
+      history.push({
+        id: item.id || index,
+        timestamp: item.entryDate || item.updateTime || item.createTime,
+        operator: item.entryPerson || '绯荤粺',
+        status: item.status,
+        description: item.description,
+        type: index === 0 ? 'current' : 'info',
+        action: index === 0 ? '褰撳墠鐘舵��' : '鍘嗗彶璁板綍'
+      })
+    })
+  }
+  
+  changeHistory.value = history
 }
 
 // 缂栬緫鍟嗘満
@@ -448,9 +596,10 @@
     customerOption.value = res
   })
   
-  // 浣跨敤updateTime浣滀负褰曞叆鏃堕棿鍙嶆樉
+  // 浣跨敤褰撳墠璐﹀彿鍜屽綋澶╂棩鏈熶綔涓洪粯璁ゅ��
   Object.assign(form, row, {
-    entryDateStart: row.updateTime || row.entryDateStart
+    entryPerson: userStore.nickName, // 璁剧疆褰曞叆浜轰负褰撳墠璐﹀彿
+    entryDate: dayjs().format('YYYY-MM-DD') // 璁剧疆褰曞叆鏃堕棿涓哄綋澶�
   })
   dialogFormVisible.value = true
 }
@@ -464,11 +613,50 @@
 const submitForm = () => {
   formRef.value.validate(valid => {
     if (valid) {
-      const api = operationType.value === 'add' ? addOpportunity : updateOpportunity
+      // 鏀堕泦闄勪欢鏂囦欢鐨勪复鏃禝D
+      let tempFileIds = []
+      if (fileList.value !== null && fileList.value.length > 0) {
+        tempFileIds = fileList.value.map(item => item.tempId)
+      }
       
-      api(form).then(res => {
+      let api
+      let successMessage
+      let submitData
+      
+      if (operationType.value === 'add') {
+        api = addOpportunity
+        successMessage = '鏂板缓鎴愬姛'
+        submitData = {
+          ...form,
+          tempFileIds: tempFileIds,
+          type: 9  // 鍟嗘満绠$悊鐨勭被鍨嬫爣璇�
+        }
+      } else if (operationType.value === 'addOperation') {
+        api = addDescription
+        successMessage = '娣诲姞鎿嶄綔鎴愬姛'
+        // 娣诲姞鎿嶄綔鏃朵紶閫掔姸鎬併�佹弿杩般�佸綍鍏ヤ汉銆佸綍鍏ユ棩鏈熴�侀檮浠跺拰鍟嗘満ID
+        submitData = {
+          status: form.status,
+          description: form.description,
+          entryPerson: form.entryPerson,
+          entryDate: form.entryDate,
+          tempFileIds: tempFileIds,
+          type: 9,  // 鍟嗘満绠$悊鐨勭被鍨嬫爣璇�
+          businessOpportunityId: form.id  // 浼犻�掑晢鏈篒D
+        }
+      } else {
+        api = updateOpportunity
+        successMessage = '淇敼鎴愬姛'
+        submitData = {
+          ...form,
+          tempFileIds: tempFileIds,
+          type: 9  // 鍟嗘満绠$悊鐨勭被鍨嬫爣璇�
+        }
+      }
+      
+      api(submitData).then(res => {
         if (res.code === 200) {
-          proxy.$modal.msgSuccess(operationType.value === 'add' ? '鏂板缓鎴愬姛' : '淇敼鎴愬姛')
+          proxy.$modal.msgSuccess(successMessage)
           closeDialog()
           getList()
         } else {
@@ -515,13 +703,13 @@
 const resetForm = () => {
   Object.assign(form, {
     id: undefined,
-    status: 'new',
+    status: '鏂板缓',
     province: '',
     customerName: '',
     businessSource: '',
     description: '',
     entryPerson: userStore.nickName,
-    entryDateStart: dayjs().format('YYYY-MM-DD')
+    entryDate: dayjs().format('YYYY-MM-DD')
   })
   
   if (formRef.value) {
@@ -533,6 +721,46 @@
 const closeDialog = () => {
   dialogFormVisible.value = false
   resetForm()
+}
+
+// 涓婁紶鍓嶆牎妫�
+function handleBeforeUpload(file) {
+  // 鏍℃鏂囦欢澶у皬
+  // if (file.size > 1024 * 1024 * 10) {
+  //   proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB!");
+  //   return false;
+  // }
+  proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
+  return true;
+}
+
+// 涓婁紶澶辫触
+function handleUploadError(err) {
+  proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
+  proxy.$modal.closeLoading();
+}
+
+// 涓婁紶鎴愬姛鍥炶皟
+function handleUploadSuccess(res, file, uploadFiles) {
+  proxy.$modal.closeLoading();
+  if (res.code === 200) {
+    file.tempId = res.data.tempId;
+    proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
+  } else {
+    proxy.$modal.msgError(res.msg);
+    proxy.$refs.fileUpload.handleRemove(file);
+  }
+}
+
+// 绉婚櫎鏂囦欢
+function handleRemove(file) {
+  // 杩欓噷鍙互娣诲姞鍒犻櫎鏂囦欢鐨勯�昏緫
+  proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+}
+
+// 鏌ョ湅闄勪欢
+function handleAttachment(row) {
+	fileListRef.value.open(row.businessCommonFiles)
 }
 
 onMounted(() => {
@@ -571,4 +799,85 @@
     }
   }
 }
+
+/* 鍙樻洿璁板綍鏃堕棿绾挎牱寮� */
+.change-history-section {
+  margin-top: 20px;
+  
+  .el-divider {
+    margin: 20px 0;
+  }
+  
+  .timeline-card {
+    margin: 8px 0;
+    border-radius: 8px;
+    
+    .card-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 8px 0;
+      
+      .action-type {
+        font-weight: 600;
+        color: #333;
+      }
+      
+      .operator {
+        font-size: 12px;
+        color: #666;
+      }
+    }
+    
+    .change-content {
+      .status-change, .description-change {
+        margin-bottom: 8px;
+        
+        .label {
+          font-weight: 500;
+          color: #666;
+          margin-right: 8px;
+        }
+        
+        .description-text {
+          color: #333;
+          line-height: 1.5;
+        }
+      }
+    }
+  }
+  
+  /* 鏃堕棿绾挎牱寮忎紭鍖� */
+  :deep(.el-timeline) {
+    padding-left: 0;
+    
+    .el-timeline-item {
+      .el-timeline-item__node {
+        background-color: #409eff;
+        
+        &.el-timeline-item__node--primary {
+          background-color: #409eff;
+        }
+        
+        &.el-timeline-item__node--success {
+          background-color: #67c23a;
+        }
+        
+        &.el-timeline-item__node--info {
+          background-color: #909399;
+        }
+        
+        &.el-timeline-item__node--hollow {
+          background-color: transparent;
+          border-color: #409eff;
+        }
+      }
+      
+      .el-timeline-item__timestamp {
+        color: #666;
+        font-size: 12px;
+      }
+    }
+  }
+}
 </style>
\ No newline at end of file
diff --git a/vite.config.js b/vite.config.js
index 3b36495..d686a0f 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -8,7 +8,7 @@
   const { VITE_APP_ENV } = env;
   const baseUrl =
     VITE_APP_ENV == "development"
-      ? "http://114.132.189.42:7003" // 寮�鍙戠幆澧冨悗绔帴鍙�
+      ? "http://192.168.1.147:7003" // 寮�鍙戠幆澧冨悗绔帴鍙�
       : "http://114.132.189.42:7003"; // 鐢熶骇鐜鍚庣鎺ュ彛
 
   return {

--
Gitblit v1.9.3