| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">客æ·åç§°ï¼</span> |
| | | <el-input v-model="searchForm.customerName" |
| | | style="width: 240px;margin-right: 10px" |
| | | placeholder="请è¾å
¥" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <span class="search_title">客æ·åç±»ï¼</span> |
| | | <el-select v-model="searchForm.customerType" |
| | | placeholder="è¯·éæ©" |
| | | style="width: 240px" |
| | | clearable |
| | | @change="handleQuery"> |
| | | <el-option label="é¶å®å®¢æ·" |
| | | value="é¶å®å®¢æ·" /> |
| | | <el-option label="è¿éå客æ·" |
| | | value="è¿éå客æ·" /> |
| | | </el-select> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">æç´¢</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">æ°å¢å®¢æ·</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="info" |
| | | plain |
| | | icon="Upload" |
| | | @click="handleImport">导å
¥</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination"></PIMTable> |
| | | </div> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢å®¢æ·ä¿¡æ¯' : 'ç¼è¾å®¢æ·ä¿¡æ¯'" |
| | | width="70%" |
| | | @close="closeDia"> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·åç§°ï¼" |
| | | prop="customerName"> |
| | | <el-input v-model="form.customerName" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="纳ç¨äººè¯å«å·ï¼" |
| | | prop="taxpayerIdentificationNumber"> |
| | | <el-input v-model="form.taxpayerIdentificationNumber" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¬å¸å°åï¼" |
| | | prop="companyAddress"> |
| | | <el-input v-model="form.companyAddress" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¬å¸çµè¯ï¼" |
| | | prop="companyPhone"> |
| | | <el-input v-model="form.companyPhone" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¶è¡åºæ¬æ·ï¼" |
| | | prop="basicBankAccount"> |
| | | <el-input v-model="form.basicBankAccount" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¶è¡è´¦å·ï¼" |
| | | prop="bankAccount"> |
| | | <el-input v-model="form.bankAccount" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="弿·è¡å·ï¼" |
| | | prop="bankCode"> |
| | | <el-input v-model="form.bankCode" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·åç±»ï¼" |
| | | prop="customerType"> |
| | | <el-select v-model="form.customerType" |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option label="é¶å®å®¢æ·" |
| | | value="é¶å®å®¢æ·" /> |
| | | <el-option label="è¿éå客æ·" |
| | | value="è¿éå客æ·" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30" |
| | | v-for="(contact, index) in formYYs.contactList" |
| | | :key="index"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è系人ï¼" |
| | | prop="contactPerson"> |
| | | <el-input v-model="contact.contactPerson" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èç³»çµè¯ï¼" |
| | | prop="contactPhone"> |
| | | <div style="display: flex; align-items: center;width: 100%;"> |
| | | <el-input v-model="contact.contactPhone" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | <el-button @click="removeContact(index)" |
| | | type="danger" |
| | | circle |
| | | style="margin-left: 5px;"> |
| | | <el-icon> |
| | | <Close /> |
| | | </el-icon> |
| | | </el-button> |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-button @click="addNewContact" |
| | | style="margin-bottom: 10px;">+ æ°å¢è系人</el-button> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç»´æ¤äººï¼" |
| | | prop="maintainer"> |
| | | <el-select v-model="form.maintainer" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | disabled> |
| | | <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="maintenanceTime"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.maintenanceTime" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <el-dialog v-model="assignDialogVisible" |
| | | title="åé
客æ·" |
| | | width="500px" |
| | | @close="closeAssignDialog"> |
| | | <el-form :model="assignForm" |
| | | :rules="assignRules" |
| | | ref="assignFormRef" |
| | | label-width="100px"> |
| | | <el-form-item label="客æ·åç§°"> |
| | | <el-input v-model="assignForm.customerName" |
| | | disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="åé
人å" |
| | | prop="maintainer"> |
| | | <el-select v-model="assignForm.maintainer" |
| | | placeholder="è¯·éæ©åé
人å" |
| | | style="width: 100%" |
| | | filterable> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId || item.nickName" |
| | | :label="item.nickName" |
| | | :value="item.nickName" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitAssignForm">确认</el-button> |
| | | <el-button @click="closeAssignDialog">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- ç¨æ·å¯¼å
¥å¯¹è¯æ¡ --> |
| | | <el-dialog :title="upload.title" |
| | | v-model="upload.open" |
| | | width="400px" |
| | | append-to-body> |
| | | <el-upload ref="uploadRef" |
| | | :limit="1" |
| | | accept=".xlsx, .xls" |
| | | :headers="upload.headers" |
| | | :action="upload.url + '?updateSupport=' + upload.updateSupport" |
| | | :disabled="upload.isUploading" |
| | | :before-upload="upload.beforeUpload" |
| | | :on-progress="upload.onProgress" |
| | | :on-success="upload.onSuccess" |
| | | :on-error="upload.onError" |
| | | :on-change="upload.onChange" |
| | | :auto-upload="false" |
| | | drag> |
| | | <el-icon class="el-icon--upload"><upload-filled /></el-icon> |
| | | <div class="el-upload__text">å°æä»¶æå°æ¤å¤ï¼æ<em>ç¹å»ä¸ä¼ </em></div> |
| | | <template #tip> |
| | | <div class="el-upload__tip text-center"> |
| | | <span>ä»
å
许导å
¥xlsãxlsxæ ¼å¼æä»¶ã</span> |
| | | <el-link type="primary" |
| | | :underline="false" |
| | | style="font-size: 12px; vertical-align: baseline" |
| | | @click="importTemplate">ä¸è½½æ¨¡æ¿</el-link> |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitFileForm">ç¡® å®</el-button> |
| | | <el-button @click="upload.open = false">å æ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- å访æéå¯¹è¯æ¡ --> |
| | | <el-dialog title="å访æé" |
| | | v-model="reminderDialogVisible" |
| | | width="500px" |
| | | @close="closeReminderDialog"> |
| | | <el-form :model="reminderForm" |
| | | label-width="100px" |
| | | :rules="reminderRules" |
| | | ref="reminderFormRef"> |
| | | <el-form-item label="客æ·åç§°ï¼"> |
| | | <el-input v-model="reminderForm.customerName" |
| | | disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="æéå¼å
³ï¼"> |
| | | <el-switch v-model="reminderForm.reminderSwitch" /> |
| | | </el-form-item> |
| | | <el-form-item label="æéå
容ï¼" |
| | | prop="reminderContent"> |
| | | <el-input v-model="reminderForm.reminderContent" |
| | | type="textarea" |
| | | :maxlength="100" |
| | | show-word-limit |
| | | placeholder="请è¾å
¥æéå
容" /> |
| | | </el-form-item> |
| | | <el-form-item label="æéæ¶é´ï¼" |
| | | prop="reminderTime"> |
| | | <el-date-picker v-model="reminderForm.reminderTime" |
| | | type="datetime" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | format="YYYY-MM-DD HH:mm:ss" |
| | | placeholder="è¯·éæ©æéæ¶é´" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitReminderForm">确认</el-button> |
| | | <el-button @click="closeReminderDialog">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- æ·»å /ä¿®æ¹æ´½è°è¿åº¦å¯¹è¯æ¡ --> |
| | | <el-dialog :title="negotiationForm.editIndex !== undefined ? 'ä¿®æ¹è¿åº¦' : 'æ·»å è¿åº¦'" |
| | | v-model="negotiationDialogVisible" |
| | | width="600px" |
| | | @close="closeNegotiationDialog"> |
| | | <el-form :model="negotiationForm" |
| | | label-width="100px" |
| | | :rules="negotiationRules" |
| | | ref="negotiationFormRef"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è·è¿æ¹å¼ï¼" |
| | | prop="followUpMethod"> |
| | | <el-select v-model="negotiationForm.followUpMethod" |
| | | placeholder="è¯·éæ©" |
| | | style="width: 100%"> |
| | | <el-option label="çµè¯" |
| | | value="çµè¯" /> |
| | | <el-option label="é®ä»¶" |
| | | value="é®ä»¶" /> |
| | | <el-option label="ä¸é¨" |
| | | value="ä¸é¨" /> |
| | | <el-option label="微信" |
| | | value="微信" /> |
| | | <el-option label="å
¶ä»" |
| | | value="å
¶ä»" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è·è¿ç¨åº¦ï¼" |
| | | prop="followUpLevel"> |
| | | <el-select v-model="negotiationForm.followUpLevel" |
| | | placeholder="è¯·éæ©" |
| | | style="width: 100%"> |
| | | <el-option label="æ½å¨å®¢æ·" |
| | | value="æ½å¨å®¢æ·" /> |
| | | <el-option label="忬¡æè®¿" |
| | | value="忬¡æè®¿" /> |
| | | <el-option label="夿¬¡æè®¿" |
| | | value="夿¬¡æè®¿" /> |
| | | <el-option label="æå客æ·" |
| | | value="æå客æ·" /> |
| | | <el-option label="å·²ç¾çº¦å®¢æ·" |
| | | value="å·²ç¾çº¦å®¢æ·" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è·è¿æ¶é´ï¼" |
| | | prop="followUpTime"> |
| | | <el-date-picker v-model="negotiationForm.followUpTime" |
| | | type="datetime" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | format="YYYY-MM-DD HH:mm:ss" |
| | | placeholder="è¯·éæ©" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è·è¿äººï¼"> |
| | | <el-input v-model="negotiationForm.followerUserName" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="å
容ï¼" |
| | | prop="content"> |
| | | <el-input v-model="negotiationForm.content" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请è¾å
¥" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitNegotiationForm">确认</el-button> |
| | | <el-button @click="closeNegotiationDialog">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- 客æ·è¯¦æ
å¯¹è¯æ¡ --> |
| | | <el-dialog title="客æ·è¯¦æ
" |
| | | v-model="detailDialogVisible" |
| | | width="1000px" |
| | | @close="closeDetailDialog"> |
| | | <!-- 客æ·åºæ¬ä¿¡æ¯ --> |
| | | <div class="detail-section"> |
| | | <h3 class="section-title">客æ·åºæ¬ä¿¡æ¯</h3> |
| | | <div class="info-display"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">客æ·åç§°ï¼</span> |
| | | <span class="info-value">{{ detailForm.customerName }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">客æ·åç±»ï¼</span> |
| | | <span class="info-value">{{ detailForm.customerType }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">纳ç¨äººè¯å«å·ï¼</span> |
| | | <span class="info-value">{{ detailForm.taxpayerIdentificationNumber }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">å
¬å¸çµè¯ï¼</span> |
| | | <span class="info-value">{{ detailForm.companyPhone }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">å
¬å¸å°åï¼</span> |
| | | <span class="info-value">{{ detailForm.companyAddress }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">é¶è¡åºæ¬æ·ï¼</span> |
| | | <span class="info-value">{{ detailForm.basicBankAccount }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">é¶è¡è´¦å·ï¼</span> |
| | | <span class="info-value">{{ detailForm.bankAccount }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">弿·è¡å·ï¼</span> |
| | | <span class="info-value">{{ detailForm.bankCode }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">è系人ï¼</span> |
| | | <span class="info-value">{{ detailForm.contactPerson }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">èç³»çµè¯ï¼</span> |
| | | <span class="info-value">{{ detailForm.contactPhone }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">ç»´æ¤äººï¼</span> |
| | | <span class="info-value">{{ detailForm.maintainer }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">ç»´æ¤æ¶é´ï¼</span> |
| | | <span class="info-value">{{ detailForm.maintenanceTime }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </div> |
| | | <!-- æ´½è°è¿åº¦è®°å½ --> |
| | | <div class="detail-section"> |
| | | <div class="section-header"> |
| | | <h3 class="section-title">æ´½è°è¿åº¦è®°å½</h3> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="openNegotiationDialog(detailForm)"> |
| | | æ·»å è¿åº¦ |
| | | </el-button> |
| | | </div> |
| | | <el-table :data="negotiationRecords" |
| | | border |
| | | style="width: 100%"> |
| | | <el-table-column prop="followUpTime" |
| | | label="è·è¿æ¶é´" |
| | | width="160" /> |
| | | <el-table-column prop="followUpMethod" |
| | | label="è·è¿æ¹å¼" |
| | | width="100" /> |
| | | <el-table-column prop="followUpLevel" |
| | | label="è·è¿ç¨åº¦" /> |
| | | <el-table-column prop="followerUserName" |
| | | label="è·è¿äºº" |
| | | width="100" /> |
| | | <el-table-column prop="content" |
| | | label="å
容" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="éä»¶" |
| | | width="100" |
| | | align="center"> |
| | | <template #default="{ row }"> |
| | | <el-button type="info" |
| | | link |
| | | @click="openAttachmentDialog(row)"> |
| | | <el-icon> |
| | | <Paperclip /> |
| | | </el-icon> |
| | | éä»¶ |
| | | <!-- {{ row.fileList && row.fileList.length > 0 ? row.fileList.length : 'ä¸ä¼ ' }} --> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" |
| | | width="150" |
| | | align="center"> |
| | | <template #default="{ row, $index }"> |
| | | <el-button type="primary" |
| | | link |
| | | @click="editNegotiationRecord(row, $index)"> |
| | | ä¿®æ¹ |
| | | </el-button> |
| | | <el-button type="danger" |
| | | link |
| | | @click="deleteNegotiationRecord(row, $index)"> |
| | | å é¤ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <div v-if="negotiationRecords.length === 0" |
| | | class="no-records"> |
| | | ææ æ´½è°è¿åº¦è®°å½ |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeDetailDialog">å
³é</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- éä»¶ä¸ä¼ å¼¹çª --> |
| | | <el-dialog title="é件管ç" |
| | | v-model="attachmentDialogVisible" |
| | | width="600px" |
| | | @close="closeAttachmentDialog"> |
| | | <div class="attachment-section"> |
| | | <div class="upload-area"> |
| | | <el-upload ref="attachmentUploadRef" |
| | | :action="getAttachmentUploadUrl()" |
| | | :headers="attachmentUploadHeaders" |
| | | :file-list="currentAttachmentList" |
| | | :on-success="handleAttachmentSuccess" |
| | | :on-error="handleAttachmentError" |
| | | :on-remove="handleAttachmentRemove" |
| | | :before-upload="beforeAttachmentUpload" |
| | | multiple |
| | | :limit="10" |
| | | name="files"> |
| | | <el-button type="primary"> |
| | | <el-icon> |
| | | <Upload /> |
| | | </el-icon> |
| | | ä¸ä¼ éä»¶ |
| | | </el-button> |
| | | <template #tip> |
| | | <div class="el-upload__tip"> |
| | | æ¯æä¸ä¼ å¾çãææ¡£çæä»¶ï¼å个æä»¶ä¸è¶
è¿50MB |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | </div> |
| | | <div v-if="currentAttachmentList.length > 0" |
| | | class="attachment-list"> |
| | | <h4>å·²ä¸ä¼ éä»¶ï¼</h4> |
| | | <el-table :data="currentAttachmentList" |
| | | border |
| | | size="small"> |
| | | <el-table-column prop="name" |
| | | label="æä»¶å" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="size" |
| | | label="大å°" |
| | | width="100"> |
| | | <template #default="{ row }"> |
| | | {{ formatFileSize(row.size) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" |
| | | width="120" |
| | | align="center"> |
| | | <template #default="{ row, $index }"> |
| | | <el-button type="primary" |
| | | link |
| | | @click="downloadAttachment(row)"> |
| | | ä¸è½½ |
| | | </el-button> |
| | | <el-button type="danger" |
| | | link |
| | | @click="deleteAttachment(row, $index)"> |
| | | å é¤ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | <div v-else |
| | | class="no-attachment"> |
| | | ææ éä»¶ |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeAttachmentDialog">å
³é</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, ref, reactive, getCurrentInstance, toRefs } from "vue"; |
| | | import { Search, Paperclip, Upload } from "@element-plus/icons-vue"; |
| | | import { |
| | | addCustomer, |
| | | delCustomer, |
| | | getCustomer, |
| | | listCustomer, |
| | | updateCustomer, |
| | | addCustomerFollow, |
| | | updateCustomerFollow, |
| | | delCustomerFollow, |
| | | addReturnVisit, |
| | | getReturnVisit, |
| | | } from "@/api/basicData/customerFile.js"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | 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 assignDialogVisible = ref(false); |
| | | const assignFormRef = ref(); |
| | | const assignForm = reactive({ |
| | | id: undefined, |
| | | customerName: "", |
| | | maintainer: "", |
| | | }); |
| | | const assignRules = { |
| | | maintainer: [{ required: true, message: "è¯·éæ©åé
人å", trigger: "change" }], |
| | | }; |
| | | |
| | | // å访æéç¸å
³ |
| | | const reminderDialogVisible = ref(false); |
| | | const reminderFormRef = ref(); |
| | | const currentCustomerId = ref(); |
| | | 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 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 attachmentUploadRef = ref(); |
| | | const currentAttachmentList = ref([]); |
| | | const currentFollowRecord = ref({}); |
| | | const attachmentUploadHeaders = { Authorization: "Bearer " + getToken() }; |
| | | |
| | | // 卿æå»ºä¸ä¼ URL |
| | | const getAttachmentUploadUrl = () => { |
| | | const baseUrl = |
| | | import.meta.env.VITE_APP_BASE_API + "/basic/customer-follow/upload"; |
| | | return currentFollowRecord.value.id |
| | | ? `${baseUrl}/${currentFollowRecord.value.id}` |
| | | : baseUrl; |
| | | }; |
| | | |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "客æ·åç±»", |
| | | prop: "customerType", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "客æ·åç§°", |
| | | prop: "customerName", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "纳ç¨äººè¯å«ç ", |
| | | prop: "taxpayerIdentificationNumber", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "å°ååèç³»æ¹å¼", |
| | | prop: "addressPhone", |
| | | width: 250, |
| | | }, |
| | | { |
| | | label: "è系人", |
| | | prop: "contactPerson", |
| | | }, |
| | | { |
| | | label: "èç³»çµè¯", |
| | | prop: "contactPhone", |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "è·è¿è¿åº¦", |
| | | prop: "followUpLevel", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "è·è¿æ¶é´", |
| | | prop: "followUpTime", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "é¶è¡åºæ¬æ·", |
| | | prop: "basicBankAccount", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "é¶è¡è´¦å·", |
| | | prop: "bankAccount", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "弿·è¡å·", |
| | | prop: "bankCode", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "ç»´æ¤äºº", |
| | | prop: "maintainer", |
| | | }, |
| | | { |
| | | label: "ç»´æ¤æ¶é´", |
| | | prop: "maintenanceTime", |
| | | width: 100, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 320, |
| | | operation: [ |
| | | { |
| | | name: "åé
", |
| | | type: "text", |
| | | showHide: row => !row.maintainer, |
| | | clickFun: row => { |
| | | openAssignDialog(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "åæ¶", |
| | | type: "text", |
| | | showHide: row => !!row.maintainer, |
| | | clickFun: row => { |
| | | recycleCustomer(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "详æ
", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openDetailDialog(row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const userList = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const total = ref(0); |
| | | |
| | | // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® |
| | | const operationType = ref(""); |
| | | const dialogFormVisible = ref(false); |
| | | 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" }], |
| | | // contactPerson: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | // contactPhone: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | maintainer: [{ required: false, message: "è¯·éæ©", trigger: "change" }], |
| | | maintenanceTime: [ |
| | | { required: false, message: "è¯·éæ©", trigger: "change" }, |
| | | ], |
| | | 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", |
| | | // æä»¶ä¸ä¼ åçåè° |
| | | beforeUpload: file => { |
| | | console.log("æä»¶å³å°ä¸ä¼ ", file); |
| | | // å¯ä»¥å¨æ¤å¤åæä»¶ç±»åæå¤§å°æ ¡éª |
| | | const isValid = |
| | | file.type === |
| | | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" || |
| | | file.name.endsWith(".xlsx") || |
| | | file.name.endsWith(".xls"); |
| | | if (!isValid) { |
| | | proxy.$modal.msgError("åªè½ä¸ä¼ Excel æä»¶"); |
| | | } |
| | | return isValid; |
| | | }, |
| | | // æä»¶ç¶ææ¹åæ¶çåè° |
| | | onChange: (file, fileList) => { |
| | | console.log("æä»¶ç¶ææ¹å", file, fileList); |
| | | }, |
| | | // æä»¶ä¸ä¼ æåæ¶çåè° |
| | | onSuccess: (response, file, fileList) => { |
| | | console.log("ä¸ä¼ æå", response, file, fileList); |
| | | upload.isUploading = false; |
| | | if (response.code === 200) { |
| | | proxy.$modal.msgSuccess("æä»¶ä¸ä¼ æå"); |
| | | upload.open = false; |
| | | proxy.$refs["uploadRef"].clearFiles(); |
| | | getList(); |
| | | } else if (response.code === 500) { |
| | | proxy.$modal.msgError(response.msg); |
| | | } else { |
| | | proxy.$modal.msgWarning(response.msg); |
| | | } |
| | | }, |
| | | // æä»¶ä¸ä¼ 失败æ¶çåè° |
| | | onError: (error, file, fileList) => { |
| | | console.error("ä¸ä¼ 失败", error, file, fileList); |
| | | upload.isUploading = false; |
| | | proxy.$modal.msgError("æä»¶ä¸ä¼ 失败"); |
| | | }, |
| | | // æä»¶ä¸ä¼ è¿åº¦åè° |
| | | onProgress: (event, file, fileList) => { |
| | | console.log("ä¸ä¼ ä¸...", event.percent); |
| | | }, |
| | | }); |
| | | const { searchForm, form, rules } = toRefs(data); |
| | | const addNewContact = () => { |
| | | formYYs.value.contactList.push({ |
| | | contactPerson: "", |
| | | contactPhone: "", |
| | | }); |
| | | }; |
| | | |
| | | const removeContact = index => { |
| | | if (formYYs.value.contactList.length > 1) { |
| | | formYYs.value.contactList.splice(index, 1); |
| | | } |
| | | }; |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | listCustomer({ ...searchForm.value, ...page }).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.records; |
| | | page.total = res.total; |
| | | }); |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | /** æäº¤ä¸ä¼ æä»¶ */ |
| | | function submitFileForm() { |
| | | upload.isUploading = true; |
| | | proxy.$refs["uploadRef"].submit(); |
| | | } |
| | | /** 导å
¥æé®æä½ */ |
| | | function handleImport() { |
| | | upload.title = "客æ·å¯¼å
¥"; |
| | | upload.open = true; |
| | | } |
| | | /** ä¸è½½æ¨¡æ¿ */ |
| | | function importTemplate() { |
| | | proxy.download("/basic/customer/downloadTemplate", {}, "客æ·å¯¼å
¥æ¨¡æ¿.xlsx"); |
| | | } |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | operationType.value = type; |
| | | form.value = {}; |
| | | form.value.maintainer = userStore.nickName; |
| | | formYYs.value.contactList = [ |
| | | { |
| | | contactPerson: "", |
| | | contactPhone: "", |
| | | }, |
| | | ]; |
| | | form.value.maintenanceTime = getCurrentDate(); |
| | | userListNoPage().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | if (type === "edit") { |
| | | getCustomer(row.id).then(res => { |
| | | form.value = { ...res.data }; |
| | | formYYs.value.contactList = res.data.contactPerson |
| | | .split(",") |
| | | .map((item, index) => { |
| | | return { |
| | | contactPerson: item, |
| | | contactPhone: res.data.contactPhone.split(",")[index], |
| | | }; |
| | | }); |
| | | }); |
| | | } |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | if (operationType.value === "edit") { |
| | | submitEdit(); |
| | | } else { |
| | | submitAdd(); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | // æäº¤æ°å¢ |
| | | const submitAdd = () => { |
| | | if (formYYs.value.contactList.length < 1) { |
| | | return proxy.$modal.msgWarning("请è³å°æ·»å ä¸ä¸ªè系人"); |
| | | } |
| | | 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 => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | 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 => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | }; |
| | | const ensureUserList = () => { |
| | | if (userList.value.length) { |
| | | return Promise.resolve(); |
| | | } |
| | | return userListNoPage().then(res => { |
| | | userList.value = res.data || []; |
| | | }); |
| | | }; |
| | | const openAssignDialog = row => { |
| | | assignForm.id = row.id; |
| | | assignForm.customerName = row.customerName; |
| | | assignForm.maintainer = row.maintainer || ""; |
| | | ensureUserList().then(() => { |
| | | assignDialogVisible.value = true; |
| | | }); |
| | | }; |
| | | const closeAssignDialog = () => { |
| | | proxy.resetForm("assignFormRef"); |
| | | assignForm.id = undefined; |
| | | assignForm.customerName = ""; |
| | | assignForm.maintainer = ""; |
| | | assignDialogVisible.value = false; |
| | | }; |
| | | const submitAssignForm = () => { |
| | | proxy.$refs.assignFormRef.validate(valid => { |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | updateCustomer({ |
| | | id: assignForm.id, |
| | | maintainer: assignForm.maintainer, |
| | | maintenanceTime: getCurrentDate(), |
| | | }).then(() => { |
| | | proxy.$modal.msgSuccess("åé
æå"); |
| | | closeAssignDialog(); |
| | | getList(); |
| | | }); |
| | | }); |
| | | }; |
| | | const recycleCustomer = row => { |
| | | ElMessageBox.confirm("ç¡®è®¤åæ¶å®¢æ·â" + row.customerName + "âåï¼", "åæ¶æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | return updateCustomer({ |
| | | id: row.id, |
| | | maintainer: "", |
| | | maintenanceTime: "", |
| | | }); |
| | | }) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("åæ¶æå"); |
| | | getList(); |
| | | }) |
| | | .catch(error => { |
| | | if (error === "cancel" || error === "close") { |
| | | proxy.$modal.msg("已忶"); |
| | | } |
| | | }); |
| | | }; |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/basic/customer/export", {}, "å®¢æ·æ¡£æ¡.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | // æ£æ¥æ¯å¦æä»äººç»´æ¤çæ°æ® |
| | | const unauthorizedData = selectedRows.value.filter( |
| | | item => item.maintainer !== userStore.nickName |
| | | ); |
| | | if (unauthorizedData.length > 0) { |
| | | proxy.$modal.msgWarning("ä¸å¯å é¤ä»äººç»´æ¤çæ°æ®"); |
| | | return; |
| | | } |
| | | ids = selectedRows.value.map(item => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "å é¤æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | delCustomer(ids) |
| | | .then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | // æå¼å访æéå¼¹çª |
| | | const openReminderDialog = row => { |
| | | currentCustomerId.value = row.id; |
| | | reminderForm.customerName = row.customerName; |
| | | reminderForm.reminderSwitch = false; |
| | | reminderForm.reminderContent = ""; |
| | | reminderForm.reminderTime = ""; |
| | | |
| | | // å°è¯è·åå·²æçå访æé |
| | | 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 = () => { |
| | | proxy.resetForm("reminderFormRef"); |
| | | reminderDialogVisible.value = false; |
| | | }; |
| | | const submitvalue = ref({}); |
| | | |
| | | // æäº¤å访æé |
| | | const submitReminderForm = () => { |
| | | console.log("æäº¤å访æéæ°æ®:", userStore.id, userStore); |
| | | proxy.$refs.reminderFormRef.validate(valid => { |
| | | if (valid) { |
| | | if (reminderForm.id) { |
| | | submitvalue.value = { |
| | | id: reminderForm.id, |
| | | customerId: currentCustomerId.value, |
| | | isEnabled: reminderForm.reminderSwitch ? 1 : 0, |
| | | content: reminderForm.reminderContent, |
| | | reminderTime: reminderForm.reminderTime, |
| | | remindUserId: userStore.id, |
| | | }; |
| | | } else { |
| | | submitvalue.value = { |
| | | customerId: currentCustomerId.value, |
| | | isEnabled: reminderForm.reminderSwitch ? 1 : 0, |
| | | content: reminderForm.reminderContent, |
| | | reminderTime: reminderForm.reminderTime, |
| | | remindUserId: userStore.id, |
| | | }; |
| | | } |
| | | |
| | | console.log("æäº¤å访æéæ°æ®:", submitvalue.value); |
| | | |
| | | // è°ç¨æ¥å£ |
| | | addReturnVisit(submitvalue.value) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | proxy.$modal.msgSuccess("å访æé设置æå"); |
| | | closeReminderDialog(); |
| | | } else { |
| | | proxy.$modal.msgError(res.msg || "设置失败"); |
| | | } |
| | | }) |
| | | .catch(error => { |
| | | console.error("设置å访æé失败:", error); |
| | | proxy.$modal.msgError("设置失败"); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // æå¼æ´½è°è¿åº¦å¼¹çª |
| | | const openNegotiationDialog = row => { |
| | | negotiationForm.customerName = row.customerName; |
| | | negotiationForm.customerId = row.id; |
| | | negotiationForm.followUpMethod = ""; |
| | | negotiationForm.followUpLevel = ""; |
| | | negotiationForm.followUpTime = ""; |
| | | negotiationForm.followerUserName = userStore.nickName; // é»è®¤å½åç»å½äºº |
| | | negotiationForm.content = ""; |
| | | // { |
| | | // "customerId": 152, |
| | | // "followUpMethod": "çµè¯æ²é", |
| | | // "followUpLevel": "没ææå", |
| | | // "followUpTime": "2026-03-04T15:30:00", |
| | | // "followerUserName": "管çåè´¦å·", |
| | | // "content": "111" |
| | | // } |
| | | negotiationDialogVisible.value = true; |
| | | }; |
| | | |
| | | // å
³éæ´½è°è¿åº¦å¼¹çª |
| | | const closeNegotiationDialog = () => { |
| | | proxy.resetForm("negotiationFormRef"); |
| | | // æ¸
é¤ç¼è¾ç¶æ |
| | | delete negotiationForm.editIndex; |
| | | delete negotiationForm.id; |
| | | negotiationDialogVisible.value = false; |
| | | }; |
| | | |
| | | // æäº¤æ´½è°è¿åº¦ |
| | | const submitNegotiationForm = () => { |
| | | proxy.$refs.negotiationFormRef.validate(valid => { |
| | | if (valid) { |
| | | // å¤ææ¯æ°å¢è¿æ¯ä¿®æ¹ |
| | | const isEdit = negotiationForm.editIndex !== undefined; |
| | | |
| | | if (isEdit) { |
| | | // ä¿®æ¹æä½ |
| | | console.log("ä¿®æ¹æ´½è°è¿åº¦æ°æ®:", negotiationForm); |
| | | // è¿éå¯ä»¥è°ç¨æ´æ°æ¥å£ |
| | | // å®é
项ç®ä¸éè¦æ ¹æ®å端æ¥å£è¿è¡è°æ´ |
| | | // 示ä¾ï¼updateCustomerFollow(negotiationForm).then(res => { |
| | | // // æ´æ°æ¬å°æ°æ® |
| | | // const index = negotiationForm.editIndex; |
| | | // negotiationRecords.value[index] = { |
| | | // followUpTime: negotiationForm.followUpTime, |
| | | // followUpMethod: negotiationForm.followUpMethod, |
| | | // followUpLevel: negotiationForm.followUpLevel, |
| | | // followerUserName: negotiationForm.followerUserName, |
| | | // content: negotiationForm.content, |
| | | // id: negotiationForm.id, |
| | | // }; |
| | | // proxy.$modal.msgSuccess("ä¿®æ¹æå"); |
| | | // closeNegotiationDialog(); |
| | | // }); |
| | | updateCustomerFollow(negotiationForm).then(res => { |
| | | // æ´æ°æ¬å°æ°æ® |
| | | getCustomer(negotiationForm.customerId).then(res => { |
| | | // æ´æ°æ¬å°æ°æ® |
| | | negotiationRecords.value = res.data.followUpList || []; |
| | | }); |
| | | }); |
| | | proxy.$modal.msgSuccess("ä¿®æ¹æå"); |
| | | closeNegotiationDialog(); |
| | | } else { |
| | | // æ°å¢æä½ |
| | | console.log("æäº¤æ´½è°è¿åº¦æ°æ®:", negotiationForm); |
| | | 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); |
| | | |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeNegotiationDialog(); |
| | | getList(); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // æå¼è¯¦æ
å¼¹çª |
| | | const openDetailDialog = row => { |
| | | // è°ç¨getCustomeræ¥å£è·å客æ·è¯¦æ
|
| | | 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, // è®°å½IDç¨äºæ´æ° |
| | | editIndex: index, // è®°å½ç´¢å¼ç¨äºæ¬å°æ´æ° |
| | | }); |
| | | negotiationDialogVisible.value = true; |
| | | }; |
| | | |
| | | // å 餿´½è°è®°å½ |
| | | const deleteNegotiationRecord = (row, index) => { |
| | | ElMessageBox.confirm("ç¡®å®è¦å é¤è¿æ¡æ´½è°è®°å½åï¼", "å é¤æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | // è¿éå¯ä»¥è°ç¨å 餿¥å£ |
| | | // å®é
项ç®ä¸éè¦æ ¹æ®å端æ¥å£è¿è¡è°æ´ |
| | | // 示ä¾ï¼deleteCustomerFollow(row.id).then(() => { |
| | | // negotiationRecords.value.splice(index, 1); |
| | | // proxy.$modal.msgSuccess("å 餿å"); |
| | | // }); |
| | | delCustomerFollow(row.id).then(() => { |
| | | // å 餿ååæ´æ°æ¬å°æ°æ® |
| | | getCustomer(row.customerId).then(res => { |
| | | // æ´æ°æ¬å°æ°æ® |
| | | negotiationRecords.value = res.data.followUpList || []; |
| | | }); |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | }); |
| | | // æ¬å°å é¤ï¼æ¨¡æï¼ |
| | | negotiationRecords.value.splice(index, 1); |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶å é¤"); |
| | | }); |
| | | }; |
| | | |
| | | // æå¼éä»¶å¼¹çª |
| | | const openAttachmentDialog = row => { |
| | | currentFollowRecord.value = row; |
| | | // 转æ¢ä¸ºç¬¦åElement Plus fileListæ ¼å¼çæ°ç» |
| | | 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 handleAttachmentSuccess = (response, file, fileList) => { |
| | | if (response.code === 200) { |
| | | proxy.$modal.msgSuccess("ä¸ä¼ æå"); |
| | | // æ´æ°å½åè®°å½çéä»¶å表 |
| | | currentAttachmentList.value = fileList.map(item => ({ |
| | | name: item.name, |
| | | size: item.size, |
| | | url: item.response?.data?.url || item.url, |
| | | id: item.response?.data?.id, |
| | | uid: item.uid, |
| | | status: "success", |
| | | })); |
| | | // æ´æ°åè®°å½ä¸çfilesåæ®µ |
| | | if (currentFollowRecord.value) { |
| | | currentFollowRecord.value.files = [...currentAttachmentList.value]; |
| | | } |
| | | } else { |
| | | proxy.$modal.msgError(response.msg || "ä¸ä¼ 失败"); |
| | | } |
| | | }; |
| | | |
| | | // éä»¶ä¸ä¼ 失败 |
| | | const handleAttachmentError = (error, file, fileList) => { |
| | | console.error("ä¸ä¼ 失败:", error); |
| | | proxy.$modal.msgError("ä¸ä¼ 失败"); |
| | | }; |
| | | |
| | | // éä»¶ç§»é¤ |
| | | const handleAttachmentRemove = (file, fileList) => { |
| | | currentAttachmentList.value = fileList; |
| | | // æ´æ°åè®°å½ä¸çfilesåæ®µ |
| | | if (currentFollowRecord.value) { |
| | | currentFollowRecord.value.files = [...fileList]; |
| | | } |
| | | }; |
| | | |
| | | // éä»¶ä¸ä¼ åæ ¡éª |
| | | const beforeAttachmentUpload = file => { |
| | | const maxSize = 50 * 1024 * 1024; // 50MB |
| | | if (file.size > maxSize) { |
| | | proxy.$modal.msgError("æä»¶å¤§å°ä¸è½è¶
è¿50MB"); |
| | | return false; |
| | | } |
| | | return true; |
| | | }; |
| | | |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | | const formatFileSize = size => { |
| | | 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(row.url, {}, row.name); |
| | | proxy.$download.name(row.url); |
| | | } else { |
| | | proxy.$modal.msgError("ä¸è½½é¾æ¥ä¸åå¨"); |
| | | } |
| | | }; |
| | | |
| | | // å é¤éä»¶ |
| | | const deleteAttachment = (row, index) => { |
| | | ElMessageBox.confirm("ç¡®å®è¦å é¤è¿ä¸ªéä»¶åï¼", "å é¤æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | // è°ç¨å端æ¥å£å é¤éä»¶ |
| | | const deleteUrl = |
| | | import.meta.env.VITE_APP_BASE_API + |
| | | "/basic/customer-follow/file/" + |
| | | row.id; |
| | | fetch(deleteUrl, { |
| | | method: "DELETE", |
| | | headers: { |
| | | Authorization: "Bearer " + getToken(), |
| | | "Content-Type": "application/json", |
| | | }, |
| | | }) |
| | | .then(response => response.json()) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | // å 餿ååæ´æ°æ¬å°æä»¶å表 |
| | | currentAttachmentList.value.splice(index, 1); |
| | | // æ´æ°åè®°å½ä¸çfilesåæ®µ |
| | | if (currentFollowRecord.value) { |
| | | currentFollowRecord.value.files = [ |
| | | ...currentAttachmentList.value, |
| | | ]; |
| | | } |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | } else { |
| | | proxy.$modal.msgError(res.msg || "å é¤å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(error => { |
| | | console.error("å é¤é件失败:", error); |
| | | proxy.$modal.msgError("å é¤å¤±è´¥"); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶å é¤"); |
| | | }); |
| | | }; |
| | | |
| | | // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD |
| | | function getCurrentDate() { |
| | | const today = new Date(); |
| | | const year = today.getFullYear(); |
| | | const month = String(today.getMonth() + 1).padStart(2, "0"); // æä»½ä»0å¼å§ |
| | | const day = String(today.getDate()).padStart(2, "0"); |
| | | return `${year}-${month}-${day}`; |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .detail-section { |
| | | margin-bottom: 20px; |
| | | padding: 15px; |
| | | background-color: #f9f9f9; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .section-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .section-title { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | margin: 0 0 15px 0; |
| | | color: #333; |
| | | } |
| | | |
| | | .info-display { |
| | | background-color: #fff; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .info-item { |
| | | margin-bottom: 12px; |
| | | display: flex; |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | .info-label { |
| | | width: 120px; |
| | | font-weight: 500; |
| | | color: #606266; |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .info-value { |
| | | flex: 1; |
| | | color: #303133; |
| | | word-break: break-word; |
| | | } |
| | | |
| | | .no-records { |
| | | text-align: center; |
| | | padding: 30px; |
| | | color: #999; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .attachment-section { |
| | | .upload-area { |
| | | margin-bottom: 20px; |
| | | padding: 20px; |
| | | background-color: #f9f9f9; |
| | | border-radius: 4px; |
| | | border: 1px dashed #d9d9d9; |
| | | |
| | | .el-upload__tip { |
| | | margin-top: 10px; |
| | | color: #909399; |
| | | } |
| | | } |
| | | |
| | | .attachment-list { |
| | | h4 { |
| | | margin: 0 0 10px 0; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | } |
| | | } |
| | | |
| | | .no-attachment { |
| | | text-align: center; |
| | | padding: 40px; |
| | | color: #909399; |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | </style> |