From e170706bd4f620170ef60c13a06a59a81156a069 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 30 三月 2026 11:25:06 +0800
Subject: [PATCH] 金鹰黄金app 1.客户修改
---
src/api/basicData/customerFile.js | 41 +
src/pages/sales/customerFile/index.vue | 1544 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1,585 insertions(+), 0 deletions(-)
diff --git a/src/api/basicData/customerFile.js b/src/api/basicData/customerFile.js
index c52b76e..409161a 100644
--- a/src/api/basicData/customerFile.js
+++ b/src/api/basicData/customerFile.js
@@ -50,3 +50,44 @@
})
}
+
+// 鏂板瀹㈡埛璺熻繘
+export function addCustomerFollow(data) {
+ return request({
+ url: '/basic/customer-follow/add',
+ method: 'post',
+ data: data
+ })
+}
+
+// 淇敼瀹㈡埛璺熻繘
+export function updateCustomerFollow(data) {
+ return request({
+ url: '/basic/customer-follow/edit',
+ method: 'put',
+ data: data,
+ })
+}
+// 鍒犻櫎瀹㈡埛璺熻繘
+export function delCustomerFollow(id) {
+ return request({
+ url: '/basic/customer-follow/'+id,
+ method: 'delete',
+ })
+}
+
+// 鍥炶鎻愰啋-鏂板/鏇存柊
+export function addReturnVisit(data) {
+ return request({
+ url: '/basic/customer-follow/return-visit',
+ method: 'post',
+ data: data
+ })
+}
+// 鑾峰彇鍥炶鎻愰啋璇︽儏
+export function getReturnVisit(id) {
+ return request({
+ url: '/basic/customer-follow/return-visit/' + id,
+ method: 'get'
+ })
+}
\ No newline at end of file
diff --git a/src/pages/sales/customerFile/index.vue b/src/pages/sales/customerFile/index.vue
new file mode 100644
index 0000000..04017b5
--- /dev/null
+++ b/src/pages/sales/customerFile/index.vue
@@ -0,0 +1,1544 @@
+<template>
+ <view class="app-container">
+ <!-- 鎼滅储鍖哄煙 -->
+ <view class="search-section">
+ <view class="search-row">
+ <up-input
+ v-model="searchForm.customerName"
+ placeholder="璇疯緭鍏ュ鎴峰悕绉�"
+ prefixIcon="search"
+ :customStyle="{ marginBottom: '10rpx' }"
+ @confirm="handleQuery"
+ />
+ </view>
+ <view class="search-row">
+ <up-input
+ v-model="searchForm.customerType"
+ placeholder="璇烽�夋嫨瀹㈡埛鍒嗙被"
+ suffixIcon="arrow-down"
+ readonly
+ @click="showTypePicker = true"
+ />
+ </view>
+ <view class="search-actions">
+ <up-button type="primary" size="small" @click="handleQuery">鎼滅储</up-button>
+ <up-button size="small" @click="resetSearch">閲嶇疆</up-button>
+ </view>
+ </view>
+
+ <!-- 鎿嶄綔鎸夐挳 -->
+ <view class="action-bar">
+ <up-button type="primary" size="small" icon="plus" @click="openForm('add')">鏂板</up-button>
+ <up-button size="small" icon="download" @click="handleOut">瀵煎嚭</up-button>
+ <up-button type="info" size="small" icon="upload" @click="handleImport">瀵煎叆</up-button>
+ <up-button type="error" size="small" icon="trash" @click="handleDelete">鍒犻櫎</up-button>
+ </view>
+
+ <!-- 瀹㈡埛鍒楄〃 -->
+ <scroll-view class="customer-list" scroll-y @scrolltolower="loadMore">
+ <view
+ v-for="item in tableData"
+ :key="item.id"
+ class="customer-card"
+ @click="openDetailDialog(item)"
+ >
+ <view class="card-header">
+ <text class="customer-name">{{ item.customerName }}</text>
+ <up-tag :text="item.customerType" type="primary" size="mini" />
+ </view>
+ <view class="card-body">
+ <view class="info-row">
+ <text class="label">鑱旂郴浜猴細</text>
+ <text class="value">{{ item.contactPerson || '-' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="label">鑱旂郴鐢佃瘽锛�</text>
+ <text class="value">{{ item.contactPhone || '-' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="label">璺熻繘杩涘害锛�</text>
+ <text class="value">{{ item.followUpLevel || '-' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="label">璺熻繘鏃堕棿锛�</text>
+ <text class="value">{{ item.followUpTime || '-' }}</text>
+ </view>
+ <view class="info-row">
+ <text class="label">缁存姢浜猴細</text>
+ <text class="value">{{ item.maintainer || '-' }}</text>
+ </view>
+ </view>
+ <view class="card-footer">
+ <up-button type="primary" text="缂栬緫" size="mini" @click.stop="openForm('edit', item)" />
+ <up-button type="warning" text="娲借皥杩涘害" size="mini" @click.stop="openNegotiationDialog(item)" />
+ <up-button type="success" text="鍥炶鎻愰啋" size="mini" @click.stop="openReminderDialog(item)" />
+ </view>
+ </view>
+ <up-loadmore :status="loadStatus" />
+ </scroll-view>
+
+ <!-- 瀹㈡埛鍒嗙被閫夋嫨鍣� -->
+ <up-picker
+ :show="showTypePicker"
+ :columns="[customerTypeOptions]"
+ @confirm="onTypeConfirm"
+ @cancel="showTypePicker = false"
+ />
+
+ <!-- 鏂板/缂栬緫瀹㈡埛寮圭獥 -->
+ <up-popup :show="dialogFormVisible" mode="bottom" :round="10" @close="closeDia">
+ <view class="popup-header">
+ <text class="popup-title">{{ operationType === 'add' ? '鏂板瀹㈡埛淇℃伅' : '缂栬緫瀹㈡埛淇℃伅' }}</text>
+ <up-icon name="close" size="20" @click="closeDia" />
+ </view>
+ <scroll-view class="popup-body" scroll-y>
+ <up-form :model="form" :rules="rules" ref="formRef" labelPosition="top">
+ <up-form-item label="瀹㈡埛鍚嶇О" prop="customerName" required>
+ <up-input v-model="form.customerName" placeholder="璇疯緭鍏�" />
+ </up-form-item>
+ <up-form-item label="绾崇◣浜鸿瘑鍒彿" prop="taxpayerIdentificationNumber" required>
+ <up-input v-model="form.taxpayerIdentificationNumber" placeholder="璇疯緭鍏�" />
+ </up-form-item>
+ <up-form-item label="鍏徃鍦板潃" prop="companyAddress" required>
+ <up-input v-model="form.companyAddress" placeholder="璇疯緭鍏�" />
+ </up-form-item>
+ <up-form-item label="鍏徃鐢佃瘽" prop="companyPhone" required>
+ <up-input v-model="form.companyPhone" placeholder="璇疯緭鍏�" />
+ </up-form-item>
+ <up-form-item label="閾惰鍩烘湰鎴�" prop="basicBankAccount" required>
+ <up-input v-model="form.basicBankAccount" placeholder="璇疯緭鍏�" />
+ </up-form-item>
+ <up-form-item label="閾惰璐﹀彿" prop="bankAccount" required>
+ <up-input v-model="form.bankAccount" placeholder="璇疯緭鍏�" />
+ </up-form-item>
+ <up-form-item label="寮�鎴疯鍙�" prop="bankCode" required>
+ <up-input v-model="form.bankCode" placeholder="璇疯緭鍏�" />
+ </up-form-item>
+ <up-form-item label="瀹㈡埛鍒嗙被" prop="customerType" required>
+ <up-input
+ v-model="form.customerType"
+ placeholder="璇烽�夋嫨"
+ suffixIcon="arrow-down"
+ readonly
+ @click="showFormTypePicker = true"
+ />
+ </up-form-item>
+ <view v-for="(contact, index) in formYYs.contactList" :key="index" class="contact-section">
+ <up-form-item :label="`鑱旂郴浜�${index + 1}`">
+ <up-input v-model="contact.contactPerson" placeholder="璇疯緭鍏�" />
+ </up-form-item>
+ <up-form-item :label="`鑱旂郴鐢佃瘽${index + 1}`">
+ <view class="contact-phone-row">
+ <up-input v-model="contact.contactPhone" placeholder="璇疯緭鍏�" />
+ <up-button type="error" size="mini" icon="minus" @click="removeContact(index)" />
+ </view>
+ </up-form-item>
+ </view>
+ <up-button type="primary" plain size="small" icon="plus" @click="addNewContact" style="margin-bottom: 20rpx;">
+ 鏂板鑱旂郴浜�
+ </up-button>
+ <up-form-item label="缁存姢浜�" prop="maintainer">
+ <up-input v-model="form.maintainer" disabled />
+ </up-form-item>
+ <up-form-item label="缁存姢鏃堕棿" prop="maintenanceTime">
+ <up-input
+ v-model="form.maintenanceTime"
+ placeholder="璇烽�夋嫨"
+ suffixIcon="calendar"
+ readonly
+ @click="showDatePicker = true"
+ />
+ </up-form-item>
+ </up-form>
+ </scroll-view>
+ <view class="popup-footer">
+ <up-button type="primary" @click="submitForm">纭</up-button>
+ <up-button @click="closeDia">鍙栨秷</up-button>
+ </view>
+ </up-popup>
+
+ <!-- 瀹㈡埛鍒嗙被閫夋嫨鍣紙琛ㄥ崟鍐咃級 -->
+ <up-picker
+ :show="showFormTypePicker"
+ :columns="[customerTypeOptions]"
+ @confirm="onFormTypeConfirm"
+ @cancel="showFormTypePicker = false"
+ />
+
+ <!-- 鏃ユ湡閫夋嫨鍣� -->
+ <up-datetime-picker
+ :show="showDatePicker"
+ v-model="datePickerValue"
+ mode="date"
+ @confirm="onDateConfirm"
+ @cancel="showDatePicker = false"
+ />
+
+ <!-- 瀵煎叆寮圭獥 -->
+ <up-popup :show="upload.open" mode="center" :round="10" @close="upload.open = false">
+ <view class="popup-content" style="width: 600rpx; padding: 30rpx;">
+ <text class="popup-title">{{ upload.title }}</text>
+ <view class="upload-area" @click="chooseFile">
+ <up-icon name="upload" size="48" color="#909399" />
+ <text class="upload-text">鐐瑰嚮閫夋嫨鏂囦欢</text>
+ <text class="upload-tip">浠呮敮鎸� xls銆亁lsx 鏍煎紡</text>
+ </view>
+ <text v-if="upload.fileName" class="file-name">宸查�夋嫨: {{ upload.fileName }}</text>
+ <view class="popup-footer">
+ <up-button type="primary" size="small" @click="submitFileForm">纭畾</up-button>
+ <up-button size="small" @click="upload.open = false">鍙栨秷</up-button>
+ </view>
+ <up-button type="primary" text="涓嬭浇妯℃澘" size="mini" plain @click="importTemplate" style="margin-top: 20rpx;" />
+ </view>
+ </up-popup>
+
+ <!-- 鍥炶鎻愰啋寮圭獥 -->
+ <up-popup :show="reminderDialogVisible" mode="bottom" :round="10" @close="closeReminderDialog">
+ <view class="popup-header">
+ <text class="popup-title">鍥炶鎻愰啋</text>
+ <up-icon name="close" size="20" @click="closeReminderDialog" />
+ </view>
+ <view class="popup-body">
+ <up-form :model="reminderForm" :rules="reminderRules" ref="reminderFormRef" labelPosition="top">
+ <up-form-item label="瀹㈡埛鍚嶇О">
+ <up-input v-model="reminderForm.customerName" disabled />
+ </up-form-item>
+ <up-form-item label="鎻愰啋寮�鍏�">
+ <up-switch v-model="reminderForm.reminderSwitch" />
+ </up-form-item>
+ <up-form-item label="鎻愰啋鍐呭" prop="reminderContent">
+ <up-textarea v-model="reminderForm.reminderContent" placeholder="璇疯緭鍏ユ彁閱掑唴瀹�" maxlength="100" count />
+ </up-form-item>
+ <up-form-item label="鎻愰啋鏃堕棿" prop="reminderTime">
+ <up-input
+ v-model="reminderForm.reminderTime"
+ placeholder="璇烽�夋嫨鎻愰啋鏃堕棿"
+ suffixIcon="clock"
+ readonly
+ @click="showReminderTimePicker = true"
+ />
+ </up-form-item>
+ </up-form>
+ </view>
+ <view class="popup-footer">
+ <up-button type="primary" @click="submitReminderForm">纭</up-button>
+ <up-button @click="closeReminderDialog">鍙栨秷</up-button>
+ </view>
+ </up-popup>
+
+ <!-- 鎻愰啋鏃堕棿閫夋嫨鍣� -->
+ <up-datetime-picker
+ :show="showReminderTimePicker"
+ v-model="reminderTimeValue"
+ mode="datetime"
+ @confirm="onReminderTimeConfirm"
+ @cancel="showReminderTimePicker = false"
+ />
+
+ <!-- 娲借皥杩涘害寮圭獥 -->
+ <up-popup :show="negotiationDialogVisible" mode="bottom" :round="10" @close="closeNegotiationDialog">
+ <view class="popup-header">
+ <text class="popup-title">{{ negotiationForm.editIndex !== undefined ? '淇敼杩涘害' : '娣诲姞杩涘害' }}</text>
+ <up-icon name="close" size="20" @click="closeNegotiationDialog" />
+ </view>
+ <view class="popup-body">
+ <up-form :model="negotiationForm" :rules="negotiationRules" ref="negotiationFormRef" labelPosition="top">
+ <up-form-item label="璺熻繘鏂瑰紡" prop="followUpMethod" required>
+ <up-input
+ v-model="negotiationForm.followUpMethod"
+ placeholder="璇烽�夋嫨"
+ suffixIcon="arrow-down"
+ readonly
+ @click="showFollowMethodPicker = true"
+ />
+ </up-form-item>
+ <up-form-item label="璺熻繘绋嬪害" prop="followUpLevel" required>
+ <up-input
+ v-model="negotiationForm.followUpLevel"
+ placeholder="璇烽�夋嫨"
+ suffixIcon="arrow-down"
+ readonly
+ @click="showFollowLevelPicker = true"
+ />
+ </up-form-item>
+ <up-form-item label="璺熻繘鏃堕棿" prop="followUpTime" required>
+ <up-input
+ v-model="negotiationForm.followUpTime"
+ placeholder="璇烽�夋嫨"
+ suffixIcon="clock"
+ readonly
+ @click="showFollowTimePicker = true"
+ />
+ </up-form-item>
+ <up-form-item label="璺熻繘浜�">
+ <up-input v-model="negotiationForm.followerUserName" disabled />
+ </up-form-item>
+ <up-form-item label="鍐呭" prop="content" required>
+ <up-textarea v-model="negotiationForm.content" placeholder="璇疯緭鍏�" :rows="4" />
+ </up-form-item>
+ </up-form>
+ </view>
+ <view class="popup-footer">
+ <up-button type="primary" @click="submitNegotiationForm">纭</up-button>
+ <up-button @click="closeNegotiationDialog">鍙栨秷</up-button>
+ </view>
+ </up-popup>
+
+ <!-- 璺熻繘鏂瑰紡閫夋嫨鍣� -->
+ <up-picker
+ :show="showFollowMethodPicker"
+ :columns="[followMethodOptions]"
+ @confirm="onFollowMethodConfirm"
+ @cancel="showFollowMethodPicker = false"
+ />
+
+ <!-- 璺熻繘绋嬪害閫夋嫨鍣� -->
+ <up-picker
+ :show="showFollowLevelPicker"
+ :columns="[followLevelOptions]"
+ @confirm="onFollowLevelConfirm"
+ @cancel="showFollowLevelPicker = false"
+ />
+
+ <!-- 璺熻繘鏃堕棿閫夋嫨鍣� -->
+ <up-datetime-picker
+ :show="showFollowTimePicker"
+ v-model="followTimeValue"
+ mode="datetime"
+ @confirm="onFollowTimeConfirm"
+ @cancel="showFollowTimePicker = false"
+ />
+
+ <!-- 瀹㈡埛璇︽儏寮圭獥 -->
+ <up-popup :show="detailDialogVisible" mode="bottom" :round="10" @close="closeDetailDialog">
+ <view class="popup-header">
+ <text class="popup-title">瀹㈡埛璇︽儏</text>
+ <up-icon name="close" size="20" @click="closeDetailDialog" />
+ </view>
+ <scroll-view class="popup-body" scroll-y style="max-height: 70vh;">
+ <!-- 瀹㈡埛鍩烘湰淇℃伅 -->
+ <view class="detail-section">
+ <view class="section-header">
+ <text class="section-title">瀹㈡埛鍩烘湰淇℃伅</text>
+ </view>
+ <view class="info-list">
+ <view class="info-item">
+ <text class="info-label">瀹㈡埛鍚嶇О锛�</text>
+ <text class="info-value">{{ detailForm.customerName }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">瀹㈡埛鍒嗙被锛�</text>
+ <text class="info-value">{{ detailForm.customerType }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">绾崇◣浜鸿瘑鍒彿锛�</text>
+ <text class="info-value">{{ detailForm.taxpayerIdentificationNumber }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鍏徃鐢佃瘽锛�</text>
+ <text class="info-value">{{ detailForm.companyPhone }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鍏徃鍦板潃锛�</text>
+ <text class="info-value">{{ detailForm.companyAddress }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">閾惰鍩烘湰鎴凤細</text>
+ <text class="info-value">{{ detailForm.basicBankAccount }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">閾惰璐﹀彿锛�</text>
+ <text class="info-value">{{ detailForm.bankAccount }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">寮�鎴疯鍙凤細</text>
+ <text class="info-value">{{ detailForm.bankCode }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鑱旂郴浜猴細</text>
+ <text class="info-value">{{ detailForm.contactPerson }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鑱旂郴鐢佃瘽锛�</text>
+ <text class="info-value">{{ detailForm.contactPhone }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">缁存姢浜猴細</text>
+ <text class="info-value">{{ detailForm.maintainer }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">缁存姢鏃堕棿锛�</text>
+ <text class="info-value">{{ detailForm.maintenanceTime }}</text>
+ </view>
+ </view>
+ </view>
+
+ <!-- 娲借皥杩涘害璁板綍 -->
+ <view class="detail-section">
+ <view class="section-header">
+ <text class="section-title">娲借皥杩涘害璁板綍</text>
+ <up-button type="primary" size="mini" @click="openNegotiationDialog(detailForm)">娣诲姞杩涘害</up-button>
+ </view>
+ <view v-if="negotiationRecords.length === 0" class="empty-text">鏆傛棤璁板綍</view>
+ <view v-for="(record, index) in negotiationRecords" :key="index" class="record-card">
+ <view class="record-header">
+ <text class="record-time">{{ record.followUpTime }}</text>
+ <up-tag :text="record.followUpMethod" type="info" size="mini" />
+ </view>
+ <view class="record-body">
+ <text class="record-level">{{ record.followUpLevel }}</text>
+ <text class="record-content">{{ record.content }}</text>
+ </view>
+ <view class="record-footer">
+ <text class="record-user">璺熻繘浜猴細{{ record.followerUserName }}</text>
+ <view class="record-actions">
+ <up-button type="primary" text="闄勪欢" size="mini" @click="openAttachmentDialog(record)" />
+ <up-button type="warning" text="缂栬緫" size="mini" @click="editNegotiationRecord(record, index)" />
+ <up-button type="error" text="鍒犻櫎" size="mini" @click="deleteNegotiationRecord(record, index)" />
+ </view>
+ </view>
+ </view>
+ </view>
+ </scroll-view>
+ </up-popup>
+
+ <!-- 闄勪欢寮圭獥 -->
+ <up-popup :show="attachmentDialogVisible" mode="bottom" :round="10" @close="closeAttachmentDialog">
+ <view class="popup-header">
+ <text class="popup-title">闄勪欢绠$悊</text>
+ <up-icon name="close" size="20" @click="closeAttachmentDialog" />
+ </view>
+ <scroll-view class="popup-body" scroll-y style="max-height: 60vh;">
+ <view v-if="currentAttachmentList.length === 0" class="empty-text">鏆傛棤闄勪欢</view>
+ <view v-for="(file, index) in currentAttachmentList" :key="index" class="file-item">
+ <view class="file-info">
+ <up-icon name="file-text" size="24" />
+ <view class="file-detail">
+ <text class="file-name">{{ file.name }}</text>
+ <text class="file-size">{{ formatFileSize(file.size) }}</text>
+ </view>
+ </view>
+ <view class="file-actions">
+ <up-button type="primary" size="mini" text="涓嬭浇" @click="downloadAttachment(file)" />
+ <up-button type="error" size="mini" text="鍒犻櫎" @click="deleteAttachment(file, index)" />
+ </view>
+ </view>
+ </scroll-view>
+ <view class="popup-footer">
+ <up-button type="primary" @click="chooseAttachment">涓婁紶闄勪欢</up-button>
+ <up-button @click="closeAttachmentDialog">鍏抽棴</up-button>
+ </view>
+ </up-popup>
+ </view>
+</template>
+
+<script setup>
+import { onMounted, ref, reactive, getCurrentInstance, toRefs } from 'vue';
+import {
+ addCustomer,
+ delCustomer,
+ getCustomer,
+ listCustomer,
+ updateCustomer,
+ addCustomerFollow,
+ updateCustomerFollow,
+ delCustomerFollow,
+ addReturnVisit,
+ getReturnVisit,
+} from '@/api/basicData/customerFile.js';
+import { userListNoPage } from '@/api/system/user.js';
+import useUserStore from '@/store/modules/user';
+import { getToken } from '@/utils/auth.js';
+
+const { proxy } = getCurrentInstance();
+const userStore = useUserStore();
+
+// 鎼滅储鐩稿叧
+const showTypePicker = ref(false);
+const customerTypeOptions = ['闆跺敭瀹㈡埛', '杩涢攢鍟嗗鎴�'];
+
+// 琛ㄥ崟鐩稿叧
+const showFormTypePicker = ref(false);
+const showDatePicker = ref(false);
+const datePickerValue = ref(Date.now());
+
+// 鍥炶鎻愰啋鐩稿叧
+const reminderDialogVisible = ref(false);
+const reminderFormRef = ref();
+const currentCustomerId = ref();
+const showReminderTimePicker = ref(false);
+const reminderTimeValue = ref(Date.now());
+const reminderForm = reactive({
+ customerName: '',
+ reminderSwitch: false,
+ reminderContent: '',
+ reminderTime: '',
+});
+const reminderRules = {
+ reminderContent: [
+ { required: true, message: '璇疯緭鍏ユ彁閱掑唴瀹�', trigger: 'blur' },
+ ],
+ reminderTime: [
+ { required: true, message: '璇烽�夋嫨鎻愰啋鏃堕棿', trigger: 'change' },
+ ],
+};
+
+// 娲借皥杩涘害鐩稿叧
+const negotiationDialogVisible = ref(false);
+const negotiationFormRef = ref();
+const showFollowMethodPicker = ref(false);
+const showFollowLevelPicker = ref(false);
+const showFollowTimePicker = ref(false);
+const followTimeValue = ref(Date.now());
+const followMethodOptions = ['鐢佃瘽', '閭欢', '涓婇棬', '寰俊', '鍏朵粬'];
+const followLevelOptions = ['娼滃湪瀹㈡埛', '鍒濇鎷滆', '澶氭鎷滆', '鎰忓悜瀹㈡埛', '宸茬绾﹀鎴�'];
+const negotiationForm = reactive({
+ customerName: '',
+ customerId: '',
+ followUpMethod: '',
+ followUpLevel: '',
+ followUpTime: '',
+ followerUserName: '',
+ content: '',
+});
+const negotiationRules = {
+ followUpMethod: [
+ { required: true, message: '璇烽�夋嫨璺熻繘鏂瑰紡', trigger: 'change' },
+ ],
+ followUpLevel: [
+ { required: true, message: '璇烽�夋嫨璺熻繘绋嬪害', trigger: 'change' },
+ ],
+ followUpTime: [
+ { required: true, message: '璇烽�夋嫨璺熻繘鏃堕棿', trigger: 'change' },
+ ],
+ content: [{ required: true, message: '璇疯緭鍏ュ唴瀹�', trigger: 'blur' }],
+};
+
+// 璇︽儏鐩稿叧
+const detailDialogVisible = ref(false);
+const detailForm = reactive({
+ customerName: '',
+ customerType: '',
+ taxpayerIdentificationNumber: '',
+ companyPhone: '',
+ companyAddress: '',
+ basicBankAccount: '',
+ bankAccount: '',
+ bankCode: '',
+ contactPerson: '',
+ contactPhone: '',
+ maintainer: '',
+ maintenanceTime: '',
+});
+const negotiationRecords = ref([]);
+
+// 闄勪欢鐩稿叧
+const attachmentDialogVisible = ref(false);
+const currentAttachmentList = ref([]);
+const currentFollowRecord = ref({});
+
+// 鍒楄〃鐩稿叧
+const tableData = ref([]);
+const selectedRows = ref([]);
+const userList = ref([]);
+const tableLoading = ref(false);
+const loadStatus = ref('loadmore');
+const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+});
+
+// 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
+const operationType = ref('');
+const dialogFormVisible = ref(false);
+const formRef = ref();
+const formYYs = ref({
+ contactList: [
+ {
+ contactPerson: '',
+ contactPhone: '',
+ },
+ ],
+});
+const data = reactive({
+ searchForm: {
+ customerName: '',
+ customerType: '',
+ },
+ form: {
+ customerName: '',
+ taxpayerIdentificationNumber: '',
+ companyAddress: '',
+ companyPhone: '',
+ contactPerson: '',
+ contactPhone: '',
+ maintainer: '',
+ maintenanceTime: '',
+ basicBankAccount: '',
+ bankAccount: '',
+ bankCode: '',
+ customerType: '',
+ },
+ rules: {
+ customerName: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+ taxpayerIdentificationNumber: [
+ { required: true, message: '璇疯緭鍏�', trigger: 'blur' },
+ ],
+ companyAddress: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+ companyPhone: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+ basicBankAccount: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+ bankAccount: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+ bankCode: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+ customerType: [{ required: true, message: '璇烽�夋嫨', trigger: 'change' }],
+ },
+});
+const upload = reactive({
+ open: false,
+ title: '',
+ isUploading: false,
+ headers: { Authorization: 'Bearer ' + getToken() },
+ url: import.meta.env.VITE_APP_BASE_API + '/basic/customer/importData',
+ fileName: '',
+ filePath: '',
+});
+
+const { searchForm, form, rules } = toRefs(data);
+
+// 閫夋嫨瀹㈡埛鍒嗙被锛堟悳绱級
+const onTypeConfirm = (e) => {
+ searchForm.value.customerType = e.value[0];
+ showTypePicker.value = false;
+ handleQuery();
+};
+
+// 閫夋嫨瀹㈡埛鍒嗙被锛堣〃鍗曪級
+const onFormTypeConfirm = (e) => {
+ form.value.customerType = e.value[0];
+ showFormTypePicker.value = false;
+};
+
+// 閫夋嫨鏃ユ湡
+const onDateConfirm = (e) => {
+ const date = new Date(e.value);
+ form.value.maintenanceTime = formatDate(date);
+ showDatePicker.value = false;
+};
+
+// 閫夋嫨鎻愰啋鏃堕棿
+const onReminderTimeConfirm = (e) => {
+ const date = new Date(e.value);
+ reminderForm.reminderTime = formatDateTime(date);
+ showReminderTimePicker.value = false;
+};
+
+// 閫夋嫨璺熻繘鏂瑰紡
+const onFollowMethodConfirm = (e) => {
+ negotiationForm.followUpMethod = e.value[0];
+ showFollowMethodPicker.value = false;
+};
+
+// 閫夋嫨璺熻繘绋嬪害
+const onFollowLevelConfirm = (e) => {
+ negotiationForm.followUpLevel = e.value[0];
+ showFollowLevelPicker.value = false;
+};
+
+// 閫夋嫨璺熻繘鏃堕棿
+const onFollowTimeConfirm = (e) => {
+ const date = new Date(e.value);
+ negotiationForm.followUpTime = formatDateTime(date);
+ showFollowTimePicker.value = false;
+};
+
+// 鏍煎紡鍖栨棩鏈�
+const formatDate = (date) => {
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, '0');
+ const day = String(date.getDate()).padStart(2, '0');
+ return `${year}-${month}-${day}`;
+};
+
+// 鏍煎紡鍖栨棩鏈熸椂闂�
+const formatDateTime = (date) => {
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, '0');
+ const day = String(date.getDate()).padStart(2, '0');
+ const hours = String(date.getHours()).padStart(2, '0');
+ const minutes = String(date.getMinutes()).padStart(2, '0');
+ const seconds = String(date.getSeconds()).padStart(2, '0');
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+};
+
+// 閲嶇疆鎼滅储
+const resetSearch = () => {
+ searchForm.value.customerName = '';
+ searchForm.value.customerType = '';
+ handleQuery();
+};
+
+// 鏂板鑱旂郴浜�
+const addNewContact = () => {
+ formYYs.value.contactList.push({
+ contactPerson: '',
+ contactPhone: '',
+ });
+};
+
+// 鍒犻櫎鑱旂郴浜�
+const removeContact = (index) => {
+ if (formYYs.value.contactList.length > 1) {
+ formYYs.value.contactList.splice(index, 1);
+ } else {
+ uni.showToast({ title: '鑷冲皯淇濈暀涓�涓仈绯讳汉', icon: 'none' });
+ }
+};
+
+// 鏌ヨ鍒楄〃
+const handleQuery = () => {
+ page.current = 1;
+ getList();
+};
+
+// 鍔犺浇鏇村
+const loadMore = () => {
+ if (loadStatus.value === 'nomore') return;
+ page.current++;
+ getList(true);
+};
+
+const getList = (isLoadMore = false) => {
+ tableLoading.value = true;
+ loadStatus.value = 'loading';
+ listCustomer({ ...searchForm.value, ...page })
+ .then((res) => {
+ tableLoading.value = false;
+ if (isLoadMore) {
+ tableData.value = [...tableData.value, ...res.records];
+ } else {
+ tableData.value = res.records;
+ }
+ page.total = res.total;
+ loadStatus.value = tableData.value.length >= res.total ? 'nomore' : 'loadmore';
+ })
+ .catch(() => {
+ tableLoading.value = false;
+ loadStatus.value = 'loadmore';
+ });
+};
+
+// 鎻愪氦涓婁紶鏂囦欢
+const chooseFile = () => {
+ uni.chooseFile({
+ count: 1,
+ extension: ['.xls', '.xlsx'],
+ success: (res) => {
+ const tempFile = res.tempFiles[0];
+ upload.fileName = tempFile.name;
+ upload.filePath = tempFile.path;
+ },
+ });
+};
+
+const submitFileForm = () => {
+ if (!upload.filePath) {
+ uni.showToast({ title: '璇烽�夋嫨鏂囦欢', icon: 'none' });
+ return;
+ }
+ upload.isUploading = true;
+ uni.uploadFile({
+ url: upload.url,
+ filePath: upload.filePath,
+ name: 'file',
+ header: upload.headers,
+ success: (res) => {
+ upload.isUploading = false;
+ const data = JSON.parse(res.data);
+ if (data.code === 200) {
+ uni.showToast({ title: '涓婁紶鎴愬姛', icon: 'success' });
+ upload.open = false;
+ getList();
+ } else {
+ uni.showToast({ title: data.msg || '涓婁紶澶辫触', icon: 'none' });
+ }
+ },
+ fail: () => {
+ upload.isUploading = false;
+ uni.showToast({ title: '涓婁紶澶辫触', icon: 'none' });
+ },
+ });
+};
+
+// 瀵煎叆鎸夐挳鎿嶄綔
+const handleImport = () => {
+ upload.title = '瀹㈡埛瀵煎叆';
+ upload.open = true;
+ upload.fileName = '';
+ upload.filePath = '';
+};
+
+// 涓嬭浇妯℃澘
+const importTemplate = () => {
+ proxy.download('/basic/customer/downloadTemplate', {}, '瀹㈡埛瀵煎叆妯℃澘.xlsx');
+};
+
+// 鎵撳紑寮规
+const openForm = (type, row) => {
+ operationType.value = type;
+ form.value = {
+ customerName: '',
+ taxpayerIdentificationNumber: '',
+ companyAddress: '',
+ companyPhone: '',
+ contactPerson: '',
+ contactPhone: '',
+ maintainer: userStore.nickName,
+ maintenanceTime: getCurrentDate(),
+ basicBankAccount: '',
+ bankAccount: '',
+ bankCode: '',
+ customerType: '',
+ };
+ formYYs.value.contactList = [
+ {
+ contactPerson: '',
+ contactPhone: '',
+ },
+ ];
+ userListNoPage().then((res) => {
+ userList.value = res.data;
+ });
+ if (type === 'edit' && row) {
+ getCustomer(row.id).then((res) => {
+ form.value = { ...res.data };
+ formYYs.value.contactList = res.data.contactPerson
+ ? res.data.contactPerson.split(',').map((item, index) => {
+ return {
+ contactPerson: item,
+ contactPhone: res.data.contactPhone
+ ? res.data.contactPhone.split(',')[index] || ''
+ : '',
+ };
+ })
+ : [{ contactPerson: '', contactPhone: '' }];
+ });
+ }
+ dialogFormVisible.value = true;
+};
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = () => {
+ formRef.value.validate().then(() => {
+ if (operationType.value === 'edit') {
+ submitEdit();
+ } else {
+ submitAdd();
+ }
+ }).catch(() => {});
+};
+
+// 鎻愪氦鏂板
+const submitAdd = () => {
+ if (formYYs.value.contactList.length < 1) {
+ uni.showToast({ title: '璇疯嚦灏戞坊鍔犱竴涓仈绯讳汉', icon: 'none' });
+ return;
+ }
+ form.value.contactPerson = formYYs.value.contactList
+ .map((item) => item.contactPerson)
+ .join(',');
+ form.value.contactPhone = formYYs.value.contactList
+ .map((item) => item.contactPhone)
+ .join(',');
+ addCustomer(form.value).then((res) => {
+ uni.showToast({ title: '鎻愪氦鎴愬姛', icon: 'success' });
+ closeDia();
+ getList();
+ });
+};
+
+// 鎻愪氦淇敼
+const submitEdit = () => {
+ form.value.contactPerson = formYYs.value.contactList
+ .map((item) => item.contactPerson)
+ .join(',');
+ form.value.contactPhone = formYYs.value.contactList
+ .map((item) => item.contactPhone)
+ .join(',');
+ updateCustomer(form.value).then((res) => {
+ uni.showToast({ title: '鎻愪氦鎴愬姛', icon: 'success' });
+ closeDia();
+ getList();
+ });
+};
+
+// 鍏抽棴寮规
+const closeDia = () => {
+ formRef.value?.resetFields?.();
+ dialogFormVisible.value = false;
+};
+
+// 瀵煎嚭
+const handleOut = () => {
+ uni.showModal({
+ title: '瀵煎嚭',
+ content: '閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�',
+ success: (res) => {
+ if (res.confirm) {
+ proxy.download('/basic/customer/export', {}, '瀹㈡埛妗f.xlsx');
+ }
+ },
+ });
+};
+
+// 鍒犻櫎
+const handleDelete = () => {
+ if (selectedRows.value.length === 0) {
+ uni.showToast({ title: '璇烽�夋嫨鏁版嵁', icon: 'none' });
+ return;
+ }
+ const unauthorizedData = selectedRows.value.filter(
+ (item) => item.maintainer !== userStore.nickName
+ );
+ if (unauthorizedData.length > 0) {
+ uni.showToast({ title: '涓嶅彲鍒犻櫎浠栦汉缁存姢鐨勬暟鎹�', icon: 'none' });
+ return;
+ }
+ const ids = selectedRows.value.map((item) => item.id);
+ uni.showModal({
+ title: '鍒犻櫎鎻愮ず',
+ content: '閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�',
+ success: (res) => {
+ if (res.confirm) {
+ tableLoading.value = true;
+ delCustomer(ids)
+ .then(() => {
+ uni.showToast({ title: '鍒犻櫎鎴愬姛', icon: 'success' });
+ getList();
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+ }
+ },
+ });
+};
+
+// 鎵撳紑鍥炶鎻愰啋寮圭獥
+const openReminderDialog = (row) => {
+ currentCustomerId.value = row.id;
+ reminderForm.customerName = row.customerName;
+ reminderForm.reminderSwitch = false;
+ reminderForm.reminderContent = '';
+ reminderForm.reminderTime = '';
+ delete reminderForm.id;
+
+ getReturnVisit(row.id)
+ .then((res) => {
+ if (res.code === 200 && res.data) {
+ reminderForm.reminderSwitch = res.data.isEnabled === 1;
+ reminderForm.reminderContent = res.data.content;
+ reminderForm.reminderTime = res.data.reminderTime;
+ reminderForm.id = res.data.id;
+ }
+ })
+ .catch((error) => {
+ console.error('鑾峰彇鍥炶鎻愰啋澶辫触:', error);
+ });
+
+ reminderDialogVisible.value = true;
+};
+
+// 鍏抽棴鍥炶鎻愰啋寮圭獥
+const closeReminderDialog = () => {
+ reminderFormRef.value?.resetFields?.();
+ reminderDialogVisible.value = false;
+};
+
+// 鎻愪氦鍥炶鎻愰啋
+const submitReminderForm = () => {
+ reminderFormRef.value.validate().then(() => {
+ const submitvalue = reminderForm.id
+ ? {
+ id: reminderForm.id,
+ customerId: currentCustomerId.value,
+ isEnabled: reminderForm.reminderSwitch ? 1 : 0,
+ content: reminderForm.reminderContent,
+ reminderTime: reminderForm.reminderTime,
+ remindUserId: userStore.id,
+ }
+ : {
+ customerId: currentCustomerId.value,
+ isEnabled: reminderForm.reminderSwitch ? 1 : 0,
+ content: reminderForm.reminderContent,
+ reminderTime: reminderForm.reminderTime,
+ remindUserId: userStore.id,
+ };
+
+ addReturnVisit(submitvalue)
+ .then((res) => {
+ if (res.code === 200) {
+ uni.showToast({ title: '鍥炶鎻愰啋璁剧疆鎴愬姛', icon: 'success' });
+ closeReminderDialog();
+ } else {
+ uni.showToast({ title: res.msg || '璁剧疆澶辫触', icon: 'none' });
+ }
+ })
+ .catch((error) => {
+ console.error('璁剧疆鍥炶鎻愰啋澶辫触:', error);
+ uni.showToast({ title: '璁剧疆澶辫触', icon: 'none' });
+ });
+ }).catch(() => {});
+};
+
+// 鎵撳紑娲借皥杩涘害寮圭獥
+const openNegotiationDialog = (row) => {
+ negotiationForm.customerName = row.customerName;
+ negotiationForm.customerId = row.id;
+ negotiationForm.followUpMethod = '';
+ negotiationForm.followUpLevel = '';
+ negotiationForm.followUpTime = '';
+ negotiationForm.followerUserName = userStore.nickName;
+ negotiationForm.content = '';
+ delete negotiationForm.editIndex;
+ delete negotiationForm.id;
+ negotiationDialogVisible.value = true;
+};
+
+// 鍏抽棴娲借皥杩涘害寮圭獥
+const closeNegotiationDialog = () => {
+ negotiationFormRef.value?.resetFields?.();
+ delete negotiationForm.editIndex;
+ delete negotiationForm.id;
+ negotiationDialogVisible.value = false;
+};
+
+// 鎻愪氦娲借皥杩涘害
+const submitNegotiationForm = () => {
+ negotiationFormRef.value.validate().then(() => {
+ const isEdit = negotiationForm.editIndex !== undefined;
+
+ if (isEdit) {
+ updateCustomerFollow(negotiationForm).then((res) => {
+ getCustomer(negotiationForm.customerId).then((res) => {
+ negotiationRecords.value = res.data.followUpList || [];
+ });
+ uni.showToast({ title: '淇敼鎴愬姛', icon: 'success' });
+ closeNegotiationDialog();
+ });
+ } else {
+ addCustomerFollow(negotiationForm).then((res) => {
+ const newRecord = {
+ followUpTime: negotiationForm.followUpTime,
+ followUpMethod: negotiationForm.followUpMethod,
+ followUpLevel: negotiationForm.followUpLevel,
+ followerUserName: negotiationForm.followerUserName,
+ content: negotiationForm.content,
+ };
+ negotiationRecords.value.unshift(newRecord);
+ uni.showToast({ title: '鎻愪氦鎴愬姛', icon: 'success' });
+ closeNegotiationDialog();
+ getList();
+ });
+ }
+ }).catch(() => {});
+};
+
+// 鎵撳紑璇︽儏寮圭獥
+const openDetailDialog = (row) => {
+ getCustomer(row.id).then((res) => {
+ Object.assign(detailForm, res.data);
+ negotiationRecords.value = res.data.followUpList || [];
+ detailDialogVisible.value = true;
+ });
+};
+
+// 鍏抽棴璇︽儏寮圭獥
+const closeDetailDialog = () => {
+ detailDialogVisible.value = false;
+};
+
+// 淇敼娲借皥璁板綍
+const editNegotiationRecord = (row, index) => {
+ Object.assign(negotiationForm, {
+ customerName: row.customerName,
+ customerId: row.customerId,
+ followUpMethod: row.followUpMethod,
+ followUpLevel: row.followUpLevel,
+ followUpTime: row.followUpTime,
+ followerUserName: row.followerUserName,
+ content: row.content,
+ id: row.id,
+ editIndex: index,
+ });
+ negotiationDialogVisible.value = true;
+};
+
+// 鍒犻櫎娲借皥璁板綍
+const deleteNegotiationRecord = (row, index) => {
+ uni.showModal({
+ title: '鍒犻櫎鎻愮ず',
+ content: '纭畾瑕佸垹闄よ繖鏉℃唇璋堣褰曞悧锛�',
+ success: (res) => {
+ if (res.confirm) {
+ delCustomerFollow(row.id).then(() => {
+ getCustomer(row.customerId).then((res) => {
+ negotiationRecords.value = res.data.followUpList || [];
+ });
+ uni.showToast({ title: '鍒犻櫎鎴愬姛', icon: 'success' });
+ });
+ negotiationRecords.value.splice(index, 1);
+ }
+ },
+ });
+};
+
+// 鎵撳紑闄勪欢寮圭獥
+const openAttachmentDialog = (row) => {
+ currentFollowRecord.value = row;
+ currentAttachmentList.value = (row.fileList || []).map((file, index) => ({
+ name: file.fileName,
+ url: file.fileUrl,
+ size: file.fileSize,
+ id: file.id,
+ uid: file.id || index,
+ status: 'success',
+ }));
+ attachmentDialogVisible.value = true;
+};
+
+// 鍏抽棴闄勪欢寮圭獥
+const closeAttachmentDialog = () => {
+ attachmentDialogVisible.value = false;
+ currentFollowRecord.value = {};
+ currentAttachmentList.value = [];
+};
+
+// 閫夋嫨闄勪欢
+const chooseAttachment = () => {
+ uni.chooseFile({
+ count: 1,
+ success: (res) => {
+ const tempFile = res.tempFiles[0];
+ uploadAttachment(tempFile);
+ },
+ });
+};
+
+// 涓婁紶闄勪欢
+const uploadAttachment = (file) => {
+ const maxSize = 50 * 1024 * 1024;
+ if (file.size > maxSize) {
+ uni.showToast({ title: '鏂囦欢澶у皬涓嶈兘瓒呰繃50MB', icon: 'none' });
+ return;
+ }
+ const uploadUrl = currentFollowRecord.value.id
+ ? import.meta.env.VITE_APP_BASE_API +
+ '/basic/customer-follow/upload/' +
+ currentFollowRecord.value.id
+ : import.meta.env.VITE_APP_BASE_API + '/basic/customer-follow/upload';
+
+ uni.uploadFile({
+ url: uploadUrl,
+ filePath: file.path,
+ name: 'file',
+ header: { Authorization: 'Bearer ' + getToken() },
+ success: (res) => {
+ const data = JSON.parse(res.data);
+ if (data.code === 200) {
+ uni.showToast({ title: '涓婁紶鎴愬姛', icon: 'success' });
+ currentAttachmentList.value.push({
+ name: file.name,
+ size: file.size,
+ url: data.data?.url || '',
+ id: data.data?.id,
+ uid: Date.now(),
+ status: 'success',
+ });
+ } else {
+ uni.showToast({ title: data.msg || '涓婁紶澶辫触', icon: 'none' });
+ }
+ },
+ fail: () => {
+ uni.showToast({ title: '涓婁紶澶辫触', icon: 'none' });
+ },
+ });
+};
+
+// 鏍煎紡鍖栨枃浠跺ぇ灏�
+const formatFileSize = (size) => {
+ if (!size) return '0 B';
+ if (size < 1024) {
+ return size + ' B';
+ } else if (size < 1024 * 1024) {
+ return (size / 1024).toFixed(2) + ' KB';
+ } else {
+ return (size / (1024 * 1024)).toFixed(2) + ' MB';
+ }
+};
+
+// 涓嬭浇闄勪欢
+const downloadAttachment = (row) => {
+ if (row.url) {
+ proxy.$download.name(row.url);
+ } else {
+ uni.showToast({ title: '涓嬭浇閾炬帴涓嶅瓨鍦�', icon: 'none' });
+ }
+};
+
+// 鍒犻櫎闄勪欢
+const deleteAttachment = (row, index) => {
+ uni.showModal({
+ title: '鍒犻櫎鎻愮ず',
+ content: '纭畾瑕佸垹闄よ繖涓檮浠跺悧锛�',
+ success: (res) => {
+ if (res.confirm) {
+ const deleteUrl =
+ import.meta.env.VITE_APP_BASE_API +
+ '/basic/customer-follow/file/' +
+ row.id;
+ uni.request({
+ url: deleteUrl,
+ method: 'DELETE',
+ header: {
+ Authorization: 'Bearer ' + getToken(),
+ 'Content-Type': 'application/json',
+ },
+ success: (res) => {
+ if (res.data.code === 200) {
+ currentAttachmentList.value.splice(index, 1);
+ uni.showToast({ title: '鍒犻櫎鎴愬姛', icon: 'success' });
+ } else {
+ uni.showToast({ title: res.data.msg || '鍒犻櫎澶辫触', icon: 'none' });
+ }
+ },
+ fail: () => {
+ uni.showToast({ title: '鍒犻櫎澶辫触', icon: 'none' });
+ },
+ });
+ }
+ },
+ });
+};
+
+// 鑾峰彇褰撳墠鏃ユ湡
+function getCurrentDate() {
+ const today = new Date();
+ const year = today.getFullYear();
+ const month = String(today.getMonth() + 1).padStart(2, '0');
+ const day = String(today.getDate()).padStart(2, '0');
+ return `${year}-${month}-${day}`;
+}
+
+onMounted(() => {
+ getList();
+});
+</script>
+
+<style scoped lang="scss">
+.app-container {
+ min-height: 100vh;
+ background-color: #f5f5f5;
+ padding: 20rpx;
+}
+
+.search-section {
+ background-color: #fff;
+ padding: 20rpx;
+ border-radius: 12rpx;
+ margin-bottom: 20rpx;
+}
+
+.search-row {
+ margin-bottom: 16rpx;
+}
+
+.search-actions {
+ display: flex;
+ gap: 20rpx;
+ justify-content: flex-end;
+}
+
+.action-bar {
+ display: flex;
+ gap: 16rpx;
+ margin-bottom: 20rpx;
+ flex-wrap: wrap;
+}
+
+.customer-list {
+ height: calc(100vh - 300rpx);
+}
+
+.customer-card {
+ background-color: #fff;
+ border-radius: 12rpx;
+ padding: 24rpx;
+ margin-bottom: 20rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+}
+
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20rpx;
+ padding-bottom: 16rpx;
+ border-bottom: 1rpx solid #f0f0f0;
+}
+
+.customer-name {
+ font-size: 32rpx;
+ font-weight: bold;
+ color: #333;
+}
+
+.card-body {
+ margin-bottom: 20rpx;
+}
+
+.info-row {
+ display: flex;
+ margin-bottom: 12rpx;
+}
+
+.label {
+ color: #666;
+ font-size: 26rpx;
+ width: 140rpx;
+ flex-shrink: 0;
+}
+
+.value {
+ color: #333;
+ font-size: 26rpx;
+ flex: 1;
+}
+
+.card-footer {
+ display: flex;
+ gap: 16rpx;
+ justify-content: flex-end;
+ padding-top: 16rpx;
+ border-top: 1rpx solid #f0f0f0;
+}
+
+.popup-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 30rpx;
+ border-bottom: 1rpx solid #eee;
+}
+
+.popup-title {
+ font-size: 32rpx;
+ font-weight: bold;
+ color: #333;
+}
+
+.popup-body {
+ padding: 30rpx;
+ max-height: 60vh;
+}
+
+.popup-footer {
+ display: flex;
+ gap: 20rpx;
+ padding: 20rpx 30rpx;
+ border-top: 1rpx solid #eee;
+}
+
+.contact-section {
+ background-color: #f9f9f9;
+ padding: 20rpx;
+ border-radius: 8rpx;
+ margin-bottom: 20rpx;
+}
+
+.contact-phone-row {
+ display: flex;
+ gap: 16rpx;
+ align-items: center;
+}
+
+.upload-area {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 60rpx;
+ border: 2rpx dashed #dcdcdc;
+ border-radius: 8rpx;
+ margin: 30rpx 0;
+}
+
+.upload-text {
+ font-size: 28rpx;
+ color: #666;
+ margin-top: 16rpx;
+}
+
+.upload-tip {
+ font-size: 24rpx;
+ color: #999;
+ margin-top: 8rpx;
+}
+
+.file-name {
+ font-size: 26rpx;
+ color: #333;
+ margin-bottom: 20rpx;
+ word-break: break-all;
+}
+
+.detail-section {
+ margin-bottom: 30rpx;
+}
+
+.section-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20rpx;
+}
+
+.section-title {
+ font-size: 30rpx;
+ font-weight: bold;
+ color: #333;
+}
+
+.info-list {
+ background-color: #f9f9f9;
+ padding: 20rpx;
+ border-radius: 8rpx;
+}
+
+.info-item {
+ display: flex;
+ padding: 12rpx 0;
+ border-bottom: 1rpx solid #eee;
+}
+
+.info-item:last-child {
+ border-bottom: none;
+}
+
+.info-label {
+ color: #666;
+ font-size: 26rpx;
+ width: 200rpx;
+ flex-shrink: 0;
+}
+
+.info-value {
+ color: #333;
+ font-size: 26rpx;
+ flex: 1;
+ word-break: break-all;
+}
+
+.record-card {
+ background-color: #f9f9f9;
+ padding: 20rpx;
+ border-radius: 8rpx;
+ margin-bottom: 16rpx;
+}
+
+.record-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12rpx;
+}
+
+.record-time {
+ font-size: 26rpx;
+ color: #666;
+}
+
+.record-body {
+ margin-bottom: 12rpx;
+}
+
+.record-level {
+ font-size: 28rpx;
+ font-weight: bold;
+ color: #333;
+ margin-bottom: 8rpx;
+ display: block;
+}
+
+.record-content {
+ font-size: 26rpx;
+ color: #666;
+ line-height: 1.5;
+}
+
+.record-footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-top: 12rpx;
+ border-top: 1rpx solid #eee;
+}
+
+.record-user {
+ font-size: 24rpx;
+ color: #999;
+}
+
+.record-actions {
+ display: flex;
+ gap: 12rpx;
+}
+
+.file-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20rpx;
+ background-color: #f9f9f9;
+ border-radius: 8rpx;
+ margin-bottom: 16rpx;
+}
+
+.file-info {
+ display: flex;
+ align-items: center;
+ flex: 1;
+ overflow: hidden;
+}
+
+.file-detail {
+ margin-left: 16rpx;
+ flex: 1;
+ overflow: hidden;
+}
+
+.file-name {
+ font-size: 26rpx;
+ color: #333;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.file-size {
+ font-size: 24rpx;
+ color: #999;
+ margin-top: 4rpx;
+}
+
+.file-actions {
+ display: flex;
+ gap: 12rpx;
+}
+
+.empty-text {
+ text-align: center;
+ padding: 40rpx;
+ color: #999;
+ font-size: 28rpx;
+}
+</style>
--
Gitblit v1.9.3