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 | 883 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 883 insertions(+), 0 deletions(-)
diff --git a/src/views/salesManagement/opportunityManagement/index.vue b/src/views/salesManagement/opportunityManagement/index.vue
new file mode 100644
index 0000000..23cd728
--- /dev/null
+++ b/src/views/salesManagement/opportunityManagement/index.vue
@@ -0,0 +1,883 @@
+<template>
+ <div class="app-container">
+ <!-- 鎼滅储鍖哄煙 -->
+ <div class="search_form">
+ <el-form :model="searchForm" :inline="true" label-width="auto">
+ <el-form-item label="瀹㈡埛鍚嶇О">
+ <el-input
+ v-model="searchForm.customerName"
+ placeholder="璇疯緭鍏ュ鎴峰悕绉�"
+ clearable
+ prefix-icon="Search"
+ style="width: 200px"
+ @change="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="褰曞叆鏃ユ湡锛�">
+ <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
+ placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+ <el-button @click="resetQuery">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+ <div class="actions">
+ <el-button type="primary" @click="handleAdd">鏂板缓</el-button>
+ <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+ </div>
+ </div>
+ <!-- 琛ㄦ牸鍖哄煙 -->
+ <div class="table_list">
+ <el-table
+ :data="tableData"
+ border
+ v-loading="tableLoading"
+ @selection-change="handleSelectionChange"
+ :row-key="(row) => row.id"
+ height="calc(100vh - 18.5em)"
+ stripe
+ >
+ <el-table-column align="center" type="selection" width="55" />
+ <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+ <el-table-column label="鐘舵��" prop="status" width="120">
+ <template #default="{ row }">
+ <el-tag
+ :type="getStatusTagType(row.status)"
+ effect="light"
+ >
+ {{ getStatusText(row.status) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鐪佷唤" prop="province" show-overflow-tooltip 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="220" align="center">
+ <template #default="{ row }">
+ <el-button
+ link
+ type="primary"
+ size="small"
+ @click="handleEdit(row)"
+ >
+ 缂栬緫
+ </el-button>
+ <el-button
+ 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>
+ </el-table>
+
+ <!-- 鍒嗛〉缁勪欢 -->
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange"
+ />
+ </div>
+
+ <!-- 鏂板/缂栬緫瀵硅瘽妗� -->
+ <el-dialog
+ v-model="dialogFormVisible"
+ :title="operationType === 'add' ? '鏂板缓鍟嗘満' : operationType === 'edit' ? '缂栬緫鍟嗘満' : operationType === 'addOperation' ? '娣诲姞鍟嗘満' : '鍟嗘満璇︽儏'"
+ width="600px"
+ @close="closeDialog"
+ >
+ <el-form
+ :model="form"
+ :rules="rules"
+ ref="formRef"
+ label-width="100px"
+ label-position="left"
+ >
+ <el-form-item label="鐘舵��" prop="status">
+ <el-select v-model="form.status" placeholder="璇烽�夋嫨鐘舵��" style="width: 100%" :disabled="operationType === 'detail'">
+ <el-option
+ v-for="item in statusOptions"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+ </el-form-item>
+
+ <el-form-item label="鐪佷唤" prop="province">
+ <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"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+ </el-form-item>
+
+ <el-form-item label="瀹㈡埛鍚嶇О" prop="customerName">
+ <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
+ }}
+ </el-option>
+ </el-select>
+ </el-form-item>
+
+ <el-form-item label="鍟嗘満鏉ユ簮" prop="businessSource">
+ <el-input v-model="form.businessSource" placeholder="璇疯緭鍏ュ晢鏈烘潵婧�" :disabled="operationType === 'detail' || operationType === 'addOperation'" />
+ </el-form-item>
+
+ <el-form-item label="瀹㈡埛鎻忚堪" prop="description" v-if="operationType !== 'detail'">
+ <el-input
+ v-model="form.description"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ュ鎴锋弿杩�"
+ maxlength="500"
+ show-word-limit
+ />
+ </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' || 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="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">
+ <el-button @click="closeDialog">鍙栨秷</el-button>
+ <el-button type="primary" @click="submitForm" v-if="operationType !== 'detail'">
+ 纭畾
+ </el-button>
+ </div>
+ </template>
+ </el-dialog>
+
+ <!-- 闄勪欢鍒楄〃瀵硅瘽妗� -->
+ <FileList ref="fileListRef" />
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
+import { Search } from '@element-plus/icons-vue'
+import { ElMessageBox, ElMessage } from 'element-plus'
+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,
+ addDescription
+} from '@/api/salesManagement/opportunityManagement.js'
+import { userListNoPage } from '@/api/system/user.js'
+import {customerList, getSalesLedgerWithProducts} from '@/api/salesManagement/salesLedger.js'
+import FileList from './fileList.vue'
+
+const { proxy } = getCurrentInstance()
+const userStore = useUserStore()
+
+// 琛ㄦ牸鏁版嵁
+const tableData = ref([])
+const selectedRows = ref([])
+const tableLoading = ref(false)
+const userList = ref([])
+const customerOption = ref([])
+
+// 鍒嗛〉閰嶇疆
+const page = reactive({
+ current: 1,
+ size: 100,
+})
+const total = ref(0)
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+ customerName: '',
+ entryDate: [],
+ entryDateStart: '',
+ entryDateEnd: ''
+})
+
+// 瀵硅瘽妗嗙浉鍏�
+const dialogFormVisible = ref(false)
+const operationType = ref('') // add, detail
+const formRef = ref()
+const form = reactive({
+ id: undefined,
+ status: undefined,
+ province: '',
+ customerName: '',
+ businessSource: '',
+ description: '',
+ entryPerson: userStore.nickName,
+ 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({
+ customerName: [
+ { required: true, message: '璇烽�夋嫨瀹㈡埛', trigger: 'change' }
+ ],
+ status: [
+ { required: true, message: '璇烽�夋嫨鐘舵��', trigger: 'change' }
+ ],
+ entryPerson: [
+ { required: true, message: '璇烽�夋嫨褰曞叆浜�', trigger: 'change' }
+ ],
+ entryDate: [
+ { required: true, message: '璇烽�夋嫨褰曞叆鏃ユ湡', trigger: 'change' }
+ ]
+})
+
+// 鐘舵�侀�夐」
+const statusOptions = [
+ { value: '鏂板缓', label: '鏂板缓' },
+ { value: '椤圭洰璺熻釜', label: '椤圭洰璺熻釜' },
+ { value: '鍚堝悓绛剧害', label: '鍚堝悓绛剧害' },
+ { value: '椤圭洰浜や粯', label: '椤圭洰浜や粯' },
+ { value: '椤圭洰楠屾敹', label: '椤圭洰楠屾敹' }
+]
+
+// 鐪佷唤閫夐」锛堢ず渚嬶級
+const provinceOptions = [
+ { 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 = {
+ '鏂板缓': 'info',
+ '椤圭洰璺熻釜': 'primary',
+ '鍚堝悓绛剧害': 'warning',
+ '椤圭洰浜や粯': 'success',
+ '椤圭洰楠屾敹': 'success'
+ }
+ return typeMap[status] || 'info'
+}
+
+// 鑾峰彇鐘舵�佹枃鏈�
+const getStatusText = (status) => {
+ const textMap = {
+ '鏂板缓': '鏂板缓',
+ '椤圭洰璺熻釜': '椤圭洰璺熻釜',
+ '鍚堝悓绛剧害': '鍚堝悓绛剧害',
+ '椤圭洰浜や粯': '椤圭洰浜や粯',
+ '椤圭洰楠屾敹': '椤圭洰楠屾敹'
+ }
+ return textMap[status] || '鏈煡'
+}
+
+// 鏍煎紡鍖栨棩鏈�
+const formatDate = (date) => {
+ if (!date) return ''
+ return dayjs(date).format('YYYY-MM-DD')
+}
+
+// 鏌ヨ鍒楄〃
+const handleQuery = () => {
+ page.current = 1
+ getList()
+}
+
+// 閲嶇疆鏌ヨ
+const resetQuery = () => {
+ Object.assign(searchForm, {
+ customerName: '',
+ entryDate: [],
+ entryDateStart: '',
+ entryDateEnd: ''
+ })
+ handleQuery()
+}
+
+// 鏃ユ湡鑼冨洿鍙樺寲
+const changeDaterange = (val) => {
+ if (val && val.length === 2) {
+ searchForm.entryDateStart = val[0]
+ searchForm.entryDateEnd = val[1]
+ } else {
+ searchForm.entryDateStart = ''
+ searchForm.entryDateEnd = ''
+ }
+ handleQuery()
+}
+
+// 鑾峰彇鍒楄〃鏁版嵁
+const getList = () => {
+ tableLoading.value = true
+
+ // 鍒涘缓鏌ヨ鍙傛暟锛屾帓闄ntryDate瀛楁锛屽彧浣跨敤entryDateStart鍜宔ntryDateEnd
+ const { entryDate, ...queryParams } = searchForm
+ const params = {
+ ...queryParams,
+ ...page
+ }
+
+ // 鍒犻櫎绌哄�煎弬鏁�
+ Object.keys(params).forEach(key => {
+ if (params[key] === '' || params[key] === null || params[key] === undefined) {
+ delete params[key]
+ }
+ })
+
+ opportunityListPage(params).then(res => {
+ tableData.value = res.data.records || []
+ total.value = res.data.total || 0
+ }).catch(err => {
+ console.error('鑾峰彇鍟嗘満鍒楄〃澶辫触:', err)
+ tableData.value = []
+ total.value = 0
+ }).finally(() => {
+ tableLoading.value = false
+ })
+}
+
+// 鍒嗛〉鍙樺寲
+const paginationChange = (pagination) => {
+ page.current = pagination.page
+ page.size = pagination.limit
+ getList()
+}
+
+// 閫夋嫨鍙樺寲
+const handleSelectionChange = (selection) => {
+ selectedRows.value = selection
+}
+
+// 鏂板缓鍟嗘満
+const handleAdd = async () => {
+ operationType.value = 'add'
+ resetForm()
+
+ // 鍔犺浇鐢ㄦ埛鍒楄〃鍜屽鎴峰垪琛�
+ let userLists = await userListNoPage()
+ userList.value = userLists.data
+ customerList().then((res) => {
+ customerOption.value = res
+ })
+
+ 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'
+
+ // 鍔犺浇鐢ㄦ埛鍒楄〃鍜屽鎴峰垪琛�
+ let userLists = await userListNoPage()
+ userList.value = userLists.data
+ customerList().then((res) => {
+ customerOption.value = res
+ })
+
+ // 浣跨敤updateTime浣滀负褰曞叆鏃堕棿鍙嶆樉
+ 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
+}
+
+// 缂栬緫鍟嗘満
+const handleEdit = async (row) => {
+ operationType.value = 'edit'
+
+ // 鍔犺浇鐢ㄦ埛鍒楄〃鍜屽鎴峰垪琛�
+ let userLists = await userListNoPage()
+ userList.value = userLists.data
+ customerList().then((res) => {
+ customerOption.value = res
+ })
+
+ // 浣跨敤褰撳墠璐﹀彿鍜屽綋澶╂棩鏈熶綔涓洪粯璁ゅ��
+ Object.assign(form, row, {
+ entryPerson: userStore.nickName, // 璁剧疆褰曞叆浜轰负褰撳墠璐﹀彿
+ entryDate: dayjs().format('YYYY-MM-DD') // 璁剧疆褰曞叆鏃堕棿涓哄綋澶�
+ })
+ dialogFormVisible.value = true
+}
+
+// 褰曞叆浜哄彉鍖栧鐞�
+const changs = (value) => {
+ // 鍙互鏍规嵁闇�瑕佹坊鍔犲鐞嗛�昏緫
+}
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = () => {
+ formRef.value.validate(valid => {
+ if (valid) {
+ // 鏀堕泦闄勪欢鏂囦欢鐨勪复鏃禝D
+ let tempFileIds = []
+ if (fileList.value !== null && fileList.value.length > 0) {
+ tempFileIds = fileList.value.map(item => item.tempId)
+ }
+
+ 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(successMessage)
+ closeDialog()
+ getList()
+ } else {
+ proxy.$modal.msgError(res.msg || '鎿嶄綔澶辫触')
+ }
+ }).catch(err => {
+ proxy.$modal.msgError('鎿嶄綔澶辫触')
+ })
+ }
+ })
+}
+
+// 鍒犻櫎鍟嗘満
+const handleDelete = () => {
+ if (selectedRows.value.length === 0) {
+ proxy.$modal.msgWarning('璇烽�夋嫨瑕佸垹闄ょ殑鍟嗘満')
+ return
+ }
+
+ ElMessageBox.confirm('纭畾鍒犻櫎閫変腑鐨勫晢鏈哄悧锛�', '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }).then(() => {
+ const ids = selectedRows.value.map(item => item.id)
+ delOpportunity(ids).then(res => {
+ if (res.code === 200) {
+ proxy.$modal.msgSuccess('鍒犻櫎鎴愬姛')
+ getList()
+ } else {
+ proxy.$modal.msgError(res.msg || '鍒犻櫎澶辫触')
+ }
+ }).catch(err => {
+ proxy.$modal.msgError('鍒犻櫎澶辫触')
+ })
+ }).catch(() => {
+ // 鐢ㄦ埛鍙栨秷鍒犻櫎
+ })
+}
+
+
+
+// 閲嶇疆琛ㄥ崟
+const resetForm = () => {
+ Object.assign(form, {
+ id: undefined,
+ status: '鏂板缓',
+ province: '',
+ customerName: '',
+ businessSource: '',
+ description: '',
+ entryPerson: userStore.nickName,
+ entryDate: dayjs().format('YYYY-MM-DD')
+ })
+
+ if (formRef.value) {
+ formRef.value.clearValidate()
+ }
+}
+
+// 鍏抽棴瀵硅瘽妗�
+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(() => {
+ getList()
+})
+</script>
+
+<style scoped lang="scss">
+.app-container {
+ padding: 20px;
+}
+.search_form {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+}
+
+.table_list {
+ margin-top: unset;
+}
+
+.dialog-footer {
+ text-align: right;
+}
+
+:deep(.el-form-item__label) {
+ font-weight: 500;
+}
+
+:deep(.el-table) {
+ .el-table__header-wrapper {
+ th {
+ background-color: #f0f2f5;
+ color: #333;
+ font-weight: 600;
+ }
+ }
+}
+
+/* 鍙樻洿璁板綍鏃堕棿绾挎牱寮� */
+.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
--
Gitblit v1.9.3