Pārlūkot izejas kodu

收单表单的新增和编辑

chenyidong 3 mēneši atpakaļ
vecāks
revīzija
ada862fb74

+ 14 - 6
components/ld-select/ld-select.vue

@@ -45,12 +45,14 @@
45 45
 		watch: {
46 46
 			value(val) {
47 47
 				if (val) {
48
-					const data = this.list.find((v) => v.dictValue == val);
48
+					// 使用valueKey而不是硬编码的dictValue,提高通用性
49
+					const data = this.list.find((v) => this.getValueKeyValue(v) == val);
49 50
 					if(data){
50
-						const {
51
-							dictLabel
52
-						} = data;
51
+						const dictLabel = this.getLabelKeyValue(data);
53 52
 						this.$emit("update:name", dictLabel);
53
+					} else {
54
+						// 如果找不到对应项,也发送原值
55
+						this.$emit("update:name", val);
54 56
 					}
55 57
 				} else {
56 58
 					this.$emit("update:name", undefined)
@@ -153,14 +155,20 @@
153 155
 							})
154 156
 							chooseAttr.push(choose)
155 157
 						})
156
-						let values = chooseAttr.map(temp => this.getLabelKeyValue(temp)).join(',')
158
+						let values = chooseAttr.map(temp => {
159
+							if (temp) {
160
+								return this.getLabelKeyValue(temp)
161
+							}
162
+							return temp
163
+						}).filter(temp => temp !== undefined && temp !== null).join(',')
157 164
 						return values
158 165
 					} else {
159 166
 						let choose = this.list.find(temp => {
160 167
 							let val_val = this.getValueKeyValue(temp)
161 168
 							return val === val_val
162 169
 						})
163
-						return this.getLabelKeyValue(choose)
170
+						// 如果找不到对应项,返回原值
171
+						return choose ? this.getLabelKeyValue(choose) : val
164 172
 					}
165 173
 				} else {
166 174
 					return ""

+ 9 - 0
pages.json

@@ -214,6 +214,15 @@
214 214
 				"enablePullDownRefresh": true,
215 215
 				"navigationStyle": "custom"
216 216
 			}
217
+		},
218
+		{
219
+			"path": "pages/receiptForm/index",
220
+			"style": {
221
+				"navigationBarTitleText": "",
222
+				"enablePullDownRefresh": false,
223
+				"navigationBarBackgroundColor": "#108cff",
224
+				"navigationStyle": "custom"
225
+			}
217 226
 		}
218 227
 	],
219 228
 	"globalStyle": {

+ 130 - 35
pages/orderDetail/page/detail.vue

@@ -50,7 +50,7 @@
50 50
         </view>
51 51
         <view class="bottom"> 采用战术 </view>
52 52
       </view>
53
-      <view class="top_info_item">
53
+      <view class="top_info_item status-item">
54 54
         <view class="top">
55 55
           {{ crmFollowStatusFormat(receiptDetail.status) }}
56 56
         </view>
@@ -73,6 +73,48 @@
73 73
       </view>
74 74
     </view>
75 75
 
76
+        <!-- 跟进状态 -->
77
+    <view class="order_action_wrap">
78
+      <view class="action_title">状态:</view>
79
+      <view class="action_buttons" v-if="['1','2'].includes(receiptDetail.status)">
80
+        <u-button 
81
+          v-if="receiptDetail.status === '1'"
82
+          type="success"
83
+          size="small"
84
+          text="接单"
85
+          @click="handleOrderForm"
86
+          customStyle="margin-right: 20rpx;"
87
+        ></u-button>
88
+        <u-button 
89
+          v-if="receiptDetail.status === '2'"
90
+          type="primary"
91
+          size="small"
92
+          text="收单"
93
+          @click="handleReceiptForm"
94
+          customStyle="margin-right: 20rpx;"
95
+        ></u-button>
96
+        <u-button 
97
+          v-if="receiptDetail.status === '2'"
98
+          type="warning"
99
+          size="small"
100
+          text="未收"
101
+          @click="handleDenialForm"
102
+          customStyle="margin-right: 20rpx;"
103
+        ></u-button>
104
+        <u-button 
105
+          v-if="receiptDetail.status === '1'"
106
+          type="error"
107
+          size="small"
108
+          text="撤销"
109
+          @click="handleDelete"
110
+          customStyle="margin-right: 20rpx;"
111
+        ></u-button>
112
+      </view>
113
+      <view v-else class="last_status">
114
+         该订单{{ crmFollowStatusFormat(receiptDetail.status) }}不可再操作
115
+      </view>
116
+    </view>
117
+
76 118
     <view class="clue_tag_wrap">
77 119
       <view class="clue_tag_add_btn" @click="handleAddClueTag">
78 120
         + 添加标签
@@ -166,15 +208,15 @@
166 208
           type="5"
167 209
         ></clue-follow>
168 210
       </template>
169
-      <!-- <template #receiptInfo>
211
+      <template #receiptInfo>
170 212
         <receipt-form-list
171
-          :sendFormId="receiptDetail.id"
172
-          :clueId="receiptDetail.clueId"
213
+          :sendFormId="orderId"
214
+          :clueId="clueId"
173 215
           :receiptDetail="receiptDetail"
174 216
           ref="receiptFormList"
175 217
         ></receipt-form-list>
176 218
       </template>
177
-      <template #commissionInfo>
219
+      <!-- <template #commissionInfo>
178 220
         <commission-form-list
179 221
           :sendFormId="receiptDetail.id"
180 222
           :clueId="receiptDetail.clueId"
@@ -196,28 +238,9 @@
196 238
         @click="handleCallPhone"
197 239
       ></u-tabbar-item>
198 240
       <u-tabbar-item
199
-        v-if="receiptDetail.status === '1'"
200
-        text="接单"
201
-        icon="../../static/orderDetail/jd.png"
202
-        @click="handleOrderForm"
203
-      ></u-tabbar-item>
204
-      <u-tabbar-item
205
-        v-if="receiptDetail.status === '2'"
206
-        text="收单"
241
+        text="添加收单"
207 242
         icon="../../static/orderDetail/sd.png"
208
-        @click="handleReceiptForm"
209
-      ></u-tabbar-item>
210
-      <u-tabbar-item
211
-        v-if="receiptDetail.status === '2'"
212
-        text="未收"
213
-        icon="../../static/orderDetail/ws.png"
214
-        @click="handleDenialForm"
215
-      ></u-tabbar-item>
216
-      <u-tabbar-item
217
-        v-if="receiptDetail.status === '1'"
218
-        text="撤销"
219
-        icon="../../static/orderDetail/cx.png"
220
-        @click="handleDelete"
243
+        @click="handleAddReceiptForm"
221 244
       ></u-tabbar-item>
222 245
       <u-tabbar-item
223 246
         text="添加跟进"
@@ -249,13 +272,13 @@ import { cloneDeep } from "lodash";
249 272
 import { selectDictLabel } from "@/utils/util";
250 273
 import uploadFile from "../tabs/uploadFile/index.vue";
251 274
 import clueFollow from "../tabs/followRecord/index.vue";
252
-// import receiptFormList from "../tabs/receiptFormList/receiptFormList.vue";
275
+import receiptFormList from "../tabs/receiptFormList/receiptFormList.vue";
253 276
 // import commissionFormList from "../tabs/commissionFormList/commissionFormList.vue";
254 277
 export default {
255 278
   components: {
256 279
     uploadFile,
257 280
     clueFollow,
258
-    // receiptFormList,
281
+    receiptFormList,
259 282
     // commissionFormList,
260 283
   },
261 284
   props: {
@@ -284,6 +307,10 @@ export default {
284 307
       crmFormStateDict: [],
285 308
       crmHandelStatusDict: [],
286 309
       tabs: [
310
+         {
311
+          label: "收单信息",
312
+          slot: "receiptInfo",
313
+        },
287 314
         {
288 315
           label: "聊天附件",
289 316
           slot: "chatFile",
@@ -312,10 +339,7 @@ export default {
312 339
           label: "跟进记录",
313 340
           slot: "followRecord",
314 341
         },
315
-        // {
316
-        //   label: "收单信息",
317
-        //   slot: "receiptInfo",
318
-        // },
342
+       
319 343
         // {
320 344
         //   label: "收单分成",
321 345
         //   slot: "commissionInfo",
@@ -367,10 +391,10 @@ export default {
367 391
       return text ? text : "-";
368 392
     },
369 393
     crmFormCategoryFormat(v) {
370
-      return selectDictLabel(this.crmFormCategoryDict, v);
394
+      return v ?  selectDictLabel(this.crmFormCategoryDict, v) : "-";
371 395
     },
372 396
     crmFormTacticFormat(v) {
373
-      return selectDictLabel(this.crmFormTacticDict, v);
397
+      return v ? selectDictLabel(this.crmFormTacticDict, v) : "-";
374 398
     },
375 399
     crmFollowStatusFormat(v) {
376 400
       return selectDictLabel(this.crmHandelStatusDict, v);
@@ -403,6 +427,12 @@ export default {
403 427
         url: `/pages/addFollow/index?orderId=${this.orderId}`,
404 428
       });
405 429
     },
430
+    // 跳转到收单表单页面
431
+    handleAddReceiptForm() {
432
+      uni.navigateTo({
433
+        url: `/pages/receiptForm/index?orderId=${this.orderId}&clueId=${this.clueId}`,
434
+      });
435
+    },
406 436
     handleUploadRecord() {
407 437
       uni.navigateTo({
408 438
         url: `/pages/uploadRecord/index?clueId=${this.orderId}`,
@@ -522,7 +552,7 @@ export default {
522 552
       this.$getDicts("crm_form_state").then((res) => {
523 553
         this.crmFormStateDict = res;
524 554
       });
525
-      this.$getDicts("crm_follow_status").then((res) => {
555
+      this.$getDicts("crm_form_status").then((res) => {
526 556
         this.crmHandelStatusDict = res;
527 557
       });
528 558
       uni.$u.api
@@ -589,6 +619,66 @@ export default {
589 619
   }
590 620
 }
591 621
 
622
+.order_action_wrap {
623
+  padding: 10px;
624
+  margin: 15px 0;
625
+  background: #fff;
626
+  display: flex;
627
+  align-items: center;
628
+
629
+  .action_title {
630
+    font-size: 16px;
631
+    flex : 0 0 50px;
632
+  }
633
+  .last_status{
634
+    color: #c0c0c7;
635
+    font-size: 14px;
636
+  }
637
+
638
+  .action_buttons {
639
+    display: grid;
640
+    grid-template-columns: repeat(2, 1fr);
641
+    gap: 10px;
642
+    
643
+    ::v-deep .u-button {
644
+      height: 30px !important;
645
+      font-size: 12px !important;
646
+      border-radius: 8px !important;
647
+      border: 1px solid #e2e8f0 !important;
648
+      background: #ffffff !important;
649
+      color: #2d3748 !important;
650
+      font-weight: 500 !important;
651
+      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) !important;
652
+      transition: all 0.3s ease !important;
653
+
654
+      &.u-button--success {
655
+        background: #ffd025 !important;
656
+        color: white !important;
657
+        border-color: #ffd025 !important;
658
+      }
659
+      
660
+      // 特别样式化不同按钮类型
661
+      &.u-button--primary {
662
+        background: #35dbd9 !important;
663
+        color: white !important;
664
+        border-color: #35dbd9 !important;
665
+      }
666
+      
667
+      &.u-button--warning {
668
+        background: #ba9fb0 !important;
669
+        color: white !important;
670
+        border-color: #ba9fb0 !important;
671
+      }
672
+      
673
+      &.u-button--error {
674
+        background: #e53e3e !important;
675
+        color: white !important;
676
+        border-color: #e53e3e !important;
677
+      }
678
+    }
679
+  }
680
+}
681
+
592 682
 .clueDetail_tabber {
593 683
   ::v-deep .u-tabbar__content {
594 684
     background: #108cff;
@@ -643,3 +733,8 @@ export default {
643 733
 </style>
644 734
 
645 735
 
736
+
737
+
738
+
739
+
740
+

+ 0 - 454
pages/orderDetail/tabs/receiptFormList/receiptForm/index.vue

@@ -1,454 +0,0 @@
1
-<template>
2
-	<u-popup :show="visible" :mode="dialogMode" :width="dialogWidth" border-radius="16" @close="handleDialogClose">
3
-		<view class="receipt-form">
4
-			<view class="dialog_header">{{ dialogTitle }}</view>
5
-			<view class="dialog_body">
6
-				<view v-if="loading" class="loading_wrap">
7
-					<u-loading-icon text="加载中" textSize="18"></u-loading-icon>
8
-				</view>
9
-				<u-form :model="form" ref="receiptFormRef" label-width="100rpx" :label-style="labelStyle">
10
-					<!-- 第一行:最后修改时间、客服 -->
11
-					<u-row gutter="20">
12
-						<u-col span="12">
13
-							<u-form-item label="最后修改时间">
14
-								<u-input v-model="form.updateTime" disabled placeholder="最后修改时间" />
15
-							</u-form-item>
16
-						</u-col>
17
-						<u-col span="12">
18
-							<u-form-item label="客服" prop="customerServiceName">
19
-								<u-input v-model="form.customerServiceName" :disabled="!isEdit" placeholder="请输入客服姓名" />
20
-							</u-form-item>
21
-						</u-col>
22
-					</u-row>
23
-
24
-					<!-- 第二行:物品、类别 -->
25
-					<u-row gutter="20">
26
-						<u-col span="12">
27
-							<u-form-item label="物品" prop="item" :required="true">
28
-								<u-input v-model="form.item" :disabled="!isEdit" placeholder="请输入物品名称" />
29
-							</u-form-item>
30
-						</u-col>
31
-						<u-col span="12">
32
-							<u-form-item label="类别" prop="category" :required="true">
33
-								<ld-select 
34
-									:load="true"
35
-									dict-type="crm_form_category" 
36
-									v-model="form.category" 
37
-									:select-attributes="{
38
-										clearable: true,
39
-										disabled: !isEdit,
40
-										placeholder: '请选择类别'
41
-									}"
42
-								></ld-select>
43
-							</u-form-item>
44
-						</u-col>
45
-					</u-row>
46
-
47
-					<!-- 第三行:品牌、是否需要查码 -->
48
-					<u-row gutter="20">
49
-						<u-col span="12">
50
-							<u-form-item label="品牌" prop="brand">
51
-								<u-input v-model="form.brand" :disabled="!isEdit" placeholder="请输入品牌" />
52
-							</u-form-item>
53
-						</u-col>
54
-						<u-col span="12">
55
-							<u-form-item label="是否需要查码" prop="needCheckCode">
56
-								<u-select v-model="form.needCheckCode" :disabled="!isEdit" placeholder="请选择是否需要查码"
57
-									:list="needCheckCodeOptions"></u-select>
58
-							</u-form-item>
59
-						</u-col>
60
-					</u-row>
61
-
62
-					<!-- 第四行:编码、查码费 -->
63
-					<u-row gutter="20">
64
-						<u-col span="12">
65
-							<u-form-item label="编码" prop="code">
66
-								<u-input v-model="form.code" :disabled="form.needCheckCode !== 1 || !isEdit" placeholder="请输入编码" />
67
-							</u-form-item>
68
-						</u-col>
69
-						<u-col span="12">
70
-							<u-form-item label="查码费" prop="checkCodeFee">
71
-								<u-input v-model.number="form.checkCodeFee" :disabled="form.needCheckCode !== 1 || !isEdit" placeholder="请输入查码费" />
72
-							</u-form-item>
73
-						</u-col>
74
-					</u-row>
75
-
76
-					<!-- 第五行:表款、好处费、运费 -->
77
-					<u-row gutter="20">
78
-						<u-col span="8">
79
-							<u-form-item label="表款" prop="tableFee">
80
-								<u-input v-model.number="form.tableFee" :disabled="!isEdit" placeholder="请输入表款" />
81
-							</u-form-item>
82
-						</u-col>
83
-						<u-col span="8">
84
-							<u-form-item label="好处费" prop="benefitFee">
85
-								<u-input v-model.number="form.benefitFee" :disabled="!isEdit" placeholder="请输入好处费" />
86
-							</u-form-item>
87
-						</u-col>
88
-						<u-col span="8">
89
-							<u-form-item label="运费" prop="freight">
90
-								<u-input v-model.number="form.freight" :disabled="!isEdit" placeholder="请输入运费" />
91
-							</u-form-item>
92
-						</u-col>
93
-					</u-row>
94
-
95
-					<!-- 第六行:成本合计、卖价、业绩 -->
96
-					<u-row gutter="20">
97
-						<u-col span="8">
98
-							<u-form-item label="成本合计" prop="totalCost">
99
-								<u-input v-model.number="form.totalCost" disabled placeholder="自动计算成本合计" />
100
-							</u-form-item>
101
-						</u-col>
102
-						<u-col span="8">
103
-							<u-form-item label="卖价" prop="sellingPrice">
104
-								<u-input v-model.number="form.sellingPrice" :disabled="!isEdit" placeholder="请输入卖价" />
105
-							</u-form-item>
106
-						</u-col>
107
-						<u-col span="8">
108
-							<u-form-item label="业绩" prop="performance">
109
-								<u-input v-model.number="form.performance" disabled placeholder="自动计算业绩" />
110
-							</u-form-item>
111
-						</u-col>
112
-					</u-row>
113
-
114
-					<!-- 第七行:分单比例、维修金额、毛业绩 -->
115
-					<u-row gutter="20">
116
-						<u-col span="8">
117
-							<u-form-item label="分单比例" prop="splitRatio">
118
-								<u-input v-model="form.splitRatio" type="number" step="0.01" :disabled="!isEdit"
119
-									@blur="validateSplitRatio" placeholder="0-100" />
120
-							</u-form-item>
121
-						</u-col>
122
-						<u-col span="8">
123
-							<u-form-item label="维修金额" prop="repairAmount">
124
-								<u-input v-model.number="form.repairAmount" :disabled="!isEdit" placeholder="请输入维修金额" />
125
-							</u-form-item>
126
-						</u-col>
127
-						<u-col span="8">
128
-							<u-form-item label="毛业绩" prop="grossPerformance">
129
-								<u-input v-model.number="form.grossPerformance" :disabled="!canEditGrossPerformance"
130
-									placeholder="自动计算毛业绩" />
131
-							</u-form-item>
132
-						</u-col>
133
-					</u-row>
134
-
135
-					<!-- 第八行:快递单号、附件 -->
136
-					<u-row gutter="20">
137
-						<u-col span="12">
138
-							<u-form-item label="快递单号" prop="expressOrderNo">
139
-								<u-input v-model="form.expressOrderNo" :disabled="!isEdit" placeholder="请输入快递单号" />
140
-							</u-form-item>
141
-						</u-col>
142
-						<u-col span="12">
143
-							<u-form-item label="附件" prop="fileIds">
144
-								<u-select
145
-									v-model="fileIds"
146
-									:disabled="!isEdit"
147
-									placeholder="请选择附件"
148
-									multiple
149
-									:list="fileOptions"
150
-									:custom-label="handleShowLabel"
151
-									:custom-value="file => file.id"
152
-									:show-value="false"
153
-								></u-select>
154
-							</u-form-item>
155
-						</u-col>
156
-					</u-row>
157
-
158
-					<!-- 第九行:收单备注 -->
159
-					<u-row gutter="20">
160
-						<u-col span="24">
161
-							<u-form-item label="收单备注" prop="receiptRemark">
162
-								<u-textarea v-model="form.receiptRemark" :disabled="!isEdit" placeholder="请输入收单备注"
163
-									:maxlength="500" :show-word-limit="true" />
164
-							</u-form-item>
165
-						</u-col>
166
-					</u-row>
167
-				</u-form>
168
-			</view>
169
-
170
-			<view class="dialog_footer" v-if="canShowSaveButton">
171
-				<u-button @click="handleDialogClose">取消</u-button>
172
-				<u-button type="primary" @click="submitForm">保存</u-button>
173
-			</view>
174
-		</view>
175
-	</u-popup>
176
-</template>
177
-
178
-<script>
179
-import { cloneDeep } from 'lodash'
180
-import ldSelect from '@/components/ld-select/ld-select.vue'
181
-
182
-export default {
183
-	name: 'ReceiptForm',
184
-	components: {
185
-		ldSelect
186
-	},
187
-	props: {
188
-		receiptDetail: {
189
-			type: Object,
190
-			required: true
191
-		},
192
-		visible: {
193
-			type: Boolean,
194
-			default: false
195
-		},
196
-		dialogTitle: {
197
-			type: String,
198
-			default: '收单信息'
199
-		},
200
-		dialogWidth: {
201
-			type: String,
202
-			default: '860rpx'
203
-		},
204
-		dialogMode: {
205
-			type: String,
206
-			default: 'center'
207
-		}
208
-	},
209
-	data() {
210
-		return {
211
-			loading: false,
212
-			form: {},
213
-			fileOptions: [],
214
-			needCheckCodeOptions: [
215
-				{ label: '是', value: 1 },
216
-				{ label: '否', value: 2 }
217
-			],
218
-			labelStyle: {
219
-				fontSize: '28rpx',
220
-				color: '#606266'
221
-			},
222
-			// 表单验证规则
223
-			rules: {
224
-				item: [{ required: true, message: '请输入物品名称', trigger: 'blur' }],
225
-				category: [{ required: true, message: '请选择类别', trigger: 'change' }]
226
-			}
227
-		}
228
-	},
229
-	computed: {
230
-		fileIds: {
231
-			get() {
232
-				if (this.form.fileIds) {
233
-					return this.form.fileIds.split(',').filter(id => id)
234
-				} else {
235
-					return []
236
-				}
237
-			},
238
-			set(value) {
239
-				this.form.fileIds = value.join(',')
240
-			}
241
-		},
242
-		isEdit() {
243
-			return true
244
-		},
245
-		// 判断是否显示保存按钮:status=2时,只有接单人本人可以编辑
246
-		canShowSaveButton() {
247
-			const { status, identification } = this.receiptDetail
248
-			const currentUserId = this.$store.state.user.userId
249
-
250
-			// 如果status不是"2",显示保存按钮
251
-			if (status !== '2') {
252
-				return true
253
-			}
254
-
255
-			// 如果status是"2",只有接单人本人可以看到保存按钮
256
-			return String(identification) === String(currentUserId)
257
-		},
258
-		// 自动计算成本合计 = 表款 + 好处费 + 运费 + 查码费 + 维修金额
259
-		calculatedTotalCost() {
260
-			const { tableFee, benefitFee, freight, checkCodeFee, repairAmount } = this.form
261
-			return (
262
-				(tableFee || 0) +
263
-				(benefitFee || 0) +
264
-				(freight || 0) +
265
-				(checkCodeFee || 0) +
266
-				(repairAmount || 0)
267
-			)
268
-		},
269
-		// 自动计算业绩 = 卖价 - 成本合计
270
-		// 卖价为空时不计算,避免出现负数业绩
271
-		calculatedPerformance() {
272
-			const { sellingPrice } = this.form
273
-			// 卖价为空或为0时,不计算业绩
274
-			if (!sellingPrice || sellingPrice <= 0) {
275
-				return null
276
-			}
277
-			return sellingPrice - this.calculatedTotalCost
278
-		},
279
-		// 自动计算毛业绩 = 业绩 × 分单比例(仅当两者都不为空时)
280
-		// 智能判断:<=1直接乘,>1先除以100再乘
281
-		// 四舍五入保留两位小数
282
-		calculatedGrossPerformance() {
283
-			const { splitRatio } = this.form
284
-			if (this.calculatedPerformance && splitRatio) {
285
-				// 智能判断:<=1直接乘,>1先除以100再乘
286
-				const ratio = splitRatio <= 1 ? splitRatio : splitRatio / 100
287
-				const result = this.calculatedPerformance * ratio
288
-				// 四舍五入保留两位小数
289
-				return Math.round(result * 100) / 100
290
-			}
291
-			return null
292
-		},
293
-		// 毛业绩是否可编辑:业绩不为空 且 比例为空
294
-		canEditGrossPerformance() {
295
-			return this.calculatedPerformance && !this.form.splitRatio && this.isEdit
296
-		}
297
-	},
298
-	watch: {
299
-		// 监听成本合计变化,自动更新form
300
-		calculatedTotalCost(val) {
301
-			this.form.totalCost = val
302
-		},
303
-		// 监听业绩业绩变化,自动更新form
304
-		calculatedPerformance(val) {
305
-			this.form.performance = val
306
-		},
307
-		// 监听毛业绩变化,仅当比例不为空时自动更新form
308
-		calculatedGrossPerformance(val) {
309
-			if (this.form.splitRatio && val !== null) {
310
-				this.form.grossPerformance = val
311
-			}
312
-		},
313
-		visible: {
314
-			handler(val) {
315
-				if (val) {
316
-					this.initForm()
317
-					this.fetchFileOptions()
318
-				}
319
-			},
320
-			immediate: true
321
-		}
322
-	},
323
-	methods: {
324
-		handleShowLabel(file) {
325
-			// 获取文件在fileIds数组中的索引,如果存在则显示序号
326
-			const index = this.fileIds.indexOf(file.id.toString())
327
-			let label = ''
328
-			if (index !== -1) {
329
-				label = `[${index + 1}] `
330
-			}
331
-			label += file.fileName + (file.remark ? '(' + file.remark + ')' : '')
332
-			if (file.receiptFormId !== null && file.receiptFormId !== this.form.id) {
333
-				label += '-已绑定' + file.item
334
-			}
335
-			return label
336
-		},
337
-		initForm() {
338
-			// 克隆父组件传递的驼峰格式数据到form
339
-			const form = cloneDeep(this.receiptDetail)
340
-			this.form = form
341
-		},
342
-		// 获取附件选项列表
343
-		async fetchFileOptions() {
344
-			try {
345
-				const params = {
346
-					clueId: this.receiptDetail.clueId,
347
-					sourceId: this.receiptDetail.sendFormId,
348
-					notBound: '1',
349
-					orderFileType: '3',
350
-					type: '2'
351
-				}
352
-				const response = await uni.$u.api.selectClueFileByDto(params)
353
-				this.fileOptions = response.rows || []
354
-			} catch (error) {
355
-				console.error('获取附件列表失败:', error)
356
-				uni.$u.toast('获取附件列表失败')
357
-			}
358
-		},
359
-		// 分单比例验证:非空时范围必须在0-100之间
360
-		validateSplitRatio() {
361
-			const { splitRatio } = this.form
362
-			if (splitRatio === null || splitRatio === undefined || splitRatio === '') {
363
-				return // 允许为空
364
-			}
365
-			// 范围验证:0-100
366
-			if (splitRatio < 0) {
367
-				uni.$u.toast('分单比例不能小于0')
368
-				this.form.splitRatio = 0
369
-			} else if (splitRatio > 100) {
370
-				uni.$u.toast('分单比例不能大于100')
371
-				this.form.splitRatio = 100
372
-			}
373
-		},
374
-		// 提交表单
375
-		submitForm() {
376
-			this.$refs.receiptFormRef.validate(async (valid) => {
377
-				if (valid) {
378
-					await this.handleUpdate()
379
-				} else {
380
-					uni.$u.toast('请检查表单填写是否正确')
381
-					return false
382
-				}
383
-			})
384
-		},
385
-		// 在提交表单前,将数组形式的fileIds转换为逗号分隔的字符串
386
-		async handleUpdate() {
387
-			this.loading = true
388
-			try {
389
-				if (this.form.id) {
390
-					await uni.$u.api.updateReceiptForm(this.form)
391
-					uni.$u.toast('修改成功')
392
-				} else {
393
-					await uni.$u.api.addReceiptForm(this.form)
394
-					uni.$u.toast('新增成功')
395
-				}
396
-				this.handleDialogClose()
397
-				this.$emit('updateSuccess')
398
-			} catch (error) {
399
-				uni.$u.toast('修改失败,请重试')
400
-			} finally {
401
-				this.loading = false
402
-			}
403
-		},
404
-		// 处理对话框关闭
405
-		handleDialogClose() {
406
-			this.$emit('update:visible', false)
407
-			this.$emit('dialogClosed')
408
-		}
409
-	}
410
-}
411
-</script>
412
-
413
-<style lang="scss" scoped>
414
-.receipt-form {
415
-	max-width: 100%;
416
-}
417
-
418
-.dialog_header {
419
-	padding: 20px;
420
-	font-size: 32rpx;
421
-	font-weight: bold;
422
-	text-align: center;
423
-	border-bottom: 1px solid #e9ecef;
424
-}
425
-
426
-.dialog_body {
427
-	padding: 20px;
428
-	max-height: 70vh;
429
-	overflow-y: auto;
430
-}
431
-
432
-.dialog_footer {
433
-	padding: 20px;
434
-	display: flex;
435
-	justify-content: space-around;
436
-	border-top: 1px solid #e9ecef;
437
-}
438
-
439
-.loading_wrap {
440
-	display: flex;
441
-	justify-content: center;
442
-	align-items: center;
443
-	height: 200px;
444
-}
445
-
446
-.u-form-item {
447
-	margin-bottom: 20px;
448
-}
449
-
450
-.u-textarea {
451
-	border-radius: 8px;
452
-	border: 1px solid #dcdfe6;
453
-}
454
-</style>

+ 7 - 81
pages/orderDetail/tabs/receiptFormList/receiptFormList.vue

@@ -1,12 +1,5 @@
1 1
 <template>
2 2
 	<view class="receipt-form-list">
3
-		<!-- 头部按钮组 -->
4
-		<view class="receipt-form-header">
5
-			<u-button type="primary" size="mini" icon="plus" @click="handleAdd">新增收单信息</u-button>
6
-			<u-button type="primary" size="mini" icon="share" @click="handleTransfer">去小葫芦线上支付</u-button>
7
-		</view>
8
-
9
-		<!-- 收单信息卡片列表 -->
10 3
 		<view v-if="loading" class="loading_wrap">
11 4
 			<u-loading-icon text="加载中" textSize="18"></u-loading-icon>
12 5
 		</view>
@@ -14,12 +7,11 @@
14 7
 			<u-empty text="暂无收单数据"></u-empty>
15 8
 		</view>
16 9
 		<view v-else class="card_list">
17
-			<view class="card_item" v-for="(row, index) in receiptFormList" :key="row.id">
10
+			<view class="card_item" v-for="(row) in receiptFormList" :key="row.id"  @click="handleUpdate(row)">
18 11
 				<view class="card_header">
19 12
 					<view class="card_title">{{ row.item || '-' }}</view>
20 13
 					<view class="card_actions">
21
-						<u-button size="mini" type="text" @click="handleUpdate(row)">详情</u-button>
22
-						<u-button size="mini" type="text" @click="handleDelete(row)" style="color: #f56c6c">删除</u-button>
14
+						<u-button size="mini" type="error" @click="handleDelete(row)" icon="trash"></u-button>
23 15
 					</view>
24 16
 				</view>
25 17
 				<view class="card_body">
@@ -42,30 +34,13 @@
42 34
 				</view>
43 35
 			</view>
44 36
 		</view>
45
-
46
-		<!-- 收单表单对话框 -->
47
-		<u-popup v-model="dialogVisible" mode="center" :width="dialogWidth" border-radius="16">
48
-			<view class="dialog_header">{{ dialogTitle }}</view>
49
-			<view class="dialog_body">
50
-				<receiptForm
51
-					:receipt-detail="form"
52
-					:send-form-id="sendFormId"
53
-					:clue-id="clueId"
54
-					@update-success="handleUpdateSuccess"
55
-				></receiptForm>
56
-			</view>
57
-		</u-popup>
58 37
 	</view>
59 38
 </template>
60 39
 
61 40
 <script>
62
-import receiptForm from './receiptForm/index.vue'
63 41
 
64 42
 export default {
65 43
 	name: 'ReceiptFormList',
66
-	components: {
67
-		receiptForm
68
-	},
69 44
 	props: {
70 45
 		receiptDetail: {
71 46
 			type: Object,
@@ -82,11 +57,6 @@ export default {
82 57
 	},
83 58
 	data() {
84 59
 		return {
85
-			// 对话框控制
86
-			dialogVisible: false,
87
-			dialogTitle: '收单信息',
88
-			dialogWidth: '860rpx',
89
-
90 60
 			// 遮罩层
91 61
 			loading: false,
92 62
 			// 收单信息列表
@@ -129,29 +99,15 @@ export default {
129 99
 			}
130 100
 		},
131 101
 
132
-		/** 新增按钮操作 */
133
-		handleAdd() {
134
-			this.reset()
135
-			this.dialogTitle = '新增收单信息'
136
-			this.dialogVisible = true
137
-		},
138
-
139 102
 		/** 修改按钮操作 */
140 103
 		async handleUpdate(row) {
141
-			this.reset()
142
-			try {
143
-				const response = await uni.$u.api.getReceiptForm(row.id)
144
-				this.form = response.data
145
-				this.dialogTitle = '修改收单信息'
146
-				this.dialogVisible = true
147
-			} catch (error) {
148
-				uni.$u.toast(`获取详情失败:${error.message}`)
149
-			}
104
+			uni.navigateTo({
105
+			  url: `/pages/receiptForm/index?orderId=${this.sendFormId}&clueId=${this.clueId}&receiptId=${row.id}`,
106
+			});
150 107
 		},
151 108
 
152 109
 		// 处理更新成功事件
153 110
 		handleUpdateSuccess() {
154
-			this.dialogVisible = false
155 111
 			this.getList()
156 112
 		},
157 113
 
@@ -177,31 +133,6 @@ export default {
177 133
 				uni.$u.toast(`删除失败:${error.message}`)
178 134
 			}
179 135
 		},
180
-
181
-		/** 表单重置 */
182
-		reset() {
183
-			this.form = Object.assign({
184
-				id: undefined,
185
-				sendFormId: this.sendFormId,
186
-				clueId: this.clueId,
187
-				item: '',
188
-				brand: '',
189
-				needCheckCode: 1,
190
-				code: '',
191
-				tableFee: undefined,
192
-				benefitFee: undefined,
193
-				freight: undefined,
194
-				checkCodeFee: undefined,
195
-				totalCost: undefined,
196
-				sellingPrice: undefined,
197
-				performance: undefined,
198
-				receiptRemark: '',
199
-				repairAmount: undefined,
200
-				grossPerformance: undefined,
201
-				expressOrderNo: '',
202
-				fileIds: ''
203
-			}, this.receiptDetail)
204
-		}
205 136
 	},
206 137
 	created() {
207 138
 		this.getList()
@@ -211,13 +142,8 @@ export default {
211 142
 
212 143
 <style lang="scss" scoped>
213 144
 .receipt-form-list {
214
-	padding: 0 20px 20px;
215
-}
216
-
217
-.receipt-form-header {
218
-	margin-bottom: 16px;
219
-	display: flex;
220
-	gap: 10px;
145
+	padding: 20px 20px 20px;
146
+	background: #f5f6f8;
221 147
 }
222 148
 
223 149
 .card_list {

+ 30 - 17
pages/orderForm/index.vue

@@ -40,8 +40,16 @@
40 40
 				</u-form-item>
41 41
 
42 42
 				<u-form-item label="物品品牌" prop="brand" borderBottom>
43
-					<u--input v-model="form.brand" placeholder="请输入物品品牌" border="none"></u--input>
44
-				</u-form-item>
43
+				<ld-select
44
+					:list="brandDict"
45
+					label-key="dictLabel"
46
+					value-key="dictValue"
47
+					placeholder="请选择物品品牌"
48
+					v-model="form.brand"
49
+					:border="false"
50
+				></ld-select>
51
+				<u-icon slot="right" name="arrow-right"></u-icon>
52
+			</u-form-item>
45 53
 
46 54
 				<u-form-item label="价格范围" prop="priceRange" borderBottom>
47 55
 					<u--input v-model="form.priceRange" placeholder="请输入价格范围" border="none"></u--input>
@@ -89,16 +97,19 @@
89 97
 <script>
90 98
 	import orderFileUpload from '@/components/order-file-upload/order-file-upload.vue'
91 99
 	import dateTimePicker from "@/components/dateTimePicker/dateTimePicker.vue"
100
+	import ldSelect from "@/components/ld-select/ld-select.vue"
92 101
 	export default {
93 102
 		components: {
94 103
 			orderFileUpload,
95
-			dateTimePicker
104
+			dateTimePicker,
105
+			ldSelect
96 106
 		},
97 107
 		data() {
98 108
 			return {
99 109
 				defaultRegion: ['广东省', '广州市', '番禺区'],
100 110
 				categoryDict: [],
101 111
 				tacticDict: [],
112
+				brandDict: [],
102 113
 
103 114
 				form: {
104 115
 					clueId: '',
@@ -169,7 +180,7 @@
169 180
 					brand: {
170 181
 						type: 'string',
171 182
 						required: true,
172
-						message: '请输入物品品牌',
183
+						message: '请选择物品品牌',
173 184
 						trigger: ['blur', 'change']
174 185
 					},
175 186
 					tactic: {
@@ -217,19 +228,21 @@
217 228
 				console.log('文件上传变化:', data);
218 229
 			},
219 230
 			// 获取字典数据
220
-			async getDicts() {
221
-				try {
222
-					const [categoryRes, tacticRes] = await Promise.all([
223
-						this.$getDicts('crm_form_category'),
224
-						this.$getDicts('crm_form_tactic')
225
-					]);
226
-
227
-					this.categoryDict = categoryRes;
228
-					this.tacticDict = tacticRes;
229
-				} catch (error) {
230
-					console.error('获取字典数据失败:', error);
231
-				}
232
-			},
231
+				async getDicts() {
232
+					try {
233
+						const [categoryRes, tacticRes, brandRes] = await Promise.all([
234
+							this.$getDicts('crm_form_category'),
235
+							this.$getDicts('crm_form_tactic'),
236
+							this.$getDicts('crm_form_brand')
237
+						]);
238
+
239
+						this.categoryDict = categoryRes;
240
+						this.tacticDict = tacticRes;
241
+						this.brandDict = brandRes;
242
+					} catch (error) {
243
+						console.error('获取字典数据失败:', error);
244
+					}
245
+				},
233 246
 
234 247
 			// 处理保存
235 248
 			async handleNavSaveClick() {

+ 829 - 0
pages/receiptForm/index.vue

@@ -0,0 +1,829 @@
1
+<template>
2
+  <view class="receipt-form-page">
3
+    <u-navbar
4
+      placeholder
5
+      :autoBack="true"
6
+      :title="pageTitle"
7
+      @rightClick="submitForm"
8
+    >
9
+      <view class="u-nav-slot" slot="right"> 保存 </view>
10
+    </u-navbar>
11
+
12
+    <!-- 表单内容 -->
13
+    <view class="follow_form_wrap">
14
+      <u--form
15
+          labelPosition="left"
16
+          labelWidth="100"
17
+          :model="form"
18
+          :rules="rules"
19
+          ref="receiptFormRef"
20
+          class="form_wrap"
21
+          :errorType="'toast'"
22
+        >
23
+        <u-form-item label="最后修改时间" prop="updateTime" borderBottom>
24
+          <u--input
25
+            v-model="form.updateTime"
26
+            placeholder="最后修改时间"
27
+            disabled
28
+          ></u--input>
29
+        </u-form-item>
30
+
31
+        <u-form-item label="客服" prop="customerServiceName" borderBottom>
32
+          <u--input
33
+            v-model="form.customerServiceName"
34
+            placeholder="请输入客服姓名"
35
+            border="none"
36
+            :disabled="!isEdit"
37
+          ></u--input>
38
+        </u-form-item>
39
+
40
+        <u-form-item
41
+          label="物品名称"
42
+          prop="item"
43
+          borderBottom
44
+          class="form_required"
45
+        >
46
+          <u--input
47
+            v-model="form.item"
48
+            placeholder="请输入物品名称"
49
+            border="none"
50
+            :disabled="!isEdit"
51
+          ></u--input>
52
+        </u-form-item>
53
+
54
+        <u-form-item
55
+          label="类别"
56
+          prop="category"
57
+          borderBottom
58
+          class="form_required"
59
+        >
60
+          <ld-select
61
+            :list="categoryDict"
62
+            label-key="dictLabel"
63
+            value-key="dictValue"
64
+            placeholder="请选择类别"
65
+            v-model="form.category"
66
+            :border="false"
67
+          ></ld-select>
68
+          <u-icon slot="right" name="arrow-right"></u-icon>
69
+        </u-form-item>
70
+
71
+        <u-form-item label="品牌" prop="brand" borderBottom>
72
+          <ld-select
73
+            :list="brandDict"
74
+            label-key="dictLabel"
75
+            value-key="dictValue"
76
+            placeholder="请选择品牌"
77
+            v-model="form.brand"
78
+            :border="false"
79
+          ></ld-select>
80
+          <u-icon slot="right" name="arrow-right"></u-icon>
81
+        </u-form-item>
82
+
83
+        <u-form-item
84
+          label="是否需要查码"
85
+          prop="needCheckCode"
86
+          borderBottom
87
+          class="form_required"
88
+        >
89
+          <u-radio-group
90
+            v-model="form.needCheckCode"
91
+            :disabled="!isEdit"
92
+            @change="(val) => (form.needCheckCode = val)"
93
+          >
94
+            <u-radio
95
+              v-for="item in needCheckCodeOptions"
96
+              :key="item.value"
97
+              :name="item.value"
98
+              :label="item.label"
99
+              style="margin-right: 10px"
100
+            ></u-radio>
101
+          </u-radio-group>
102
+        </u-form-item>
103
+
104
+        <u-form-item label="编码" prop="code" borderBottom>
105
+          <u--input
106
+            v-model="form.code"
107
+            :disabled="form.needCheckCode !== 1 || !isEdit"
108
+            placeholder="请输入编码"
109
+          ></u--input>
110
+        </u-form-item>
111
+
112
+        <u-form-item label="查码费" prop="checkCodeFee" borderBottom>
113
+          <u--input
114
+            v-model.number="form.checkCodeFee"
115
+            type="number"
116
+            placeholder="请输入查码费"
117
+            border="none"
118
+            :disabled="!isEdit"
119
+          ></u--input>
120
+        </u-form-item>
121
+
122
+        <u-form-item label="表款" prop="tableFee" borderBottom>
123
+          <u--input
124
+            v-model.number="form.tableFee"
125
+            type="number"
126
+            placeholder="请输入表款"
127
+            border="none"
128
+            :disabled="!isEdit"
129
+          ></u--input>
130
+        </u-form-item>
131
+
132
+        <u-form-item label="好处费" prop="benefitFee" borderBottom>
133
+          <u--input
134
+            v-model.number="form.benefitFee"
135
+            type="number"
136
+            placeholder="请输入好处费"
137
+            border="none"
138
+            :disabled="!isEdit"
139
+          ></u--input>
140
+        </u-form-item>
141
+
142
+        <u-form-item label="运费" prop="freight" borderBottom>
143
+          <u--input
144
+            v-model.number="form.freight"
145
+            type="number"
146
+            placeholder="请输入运费"
147
+            border="none"
148
+            :disabled="!isEdit"
149
+          ></u--input>
150
+        </u-form-item>
151
+
152
+        <u-form-item label="维修金额" prop="repairAmount" borderBottom>
153
+          <u--input
154
+            v-model.number="form.repairAmount"
155
+            type="number"
156
+            placeholder="请输入维修金额"
157
+            border="none"
158
+            :disabled="!isEdit"
159
+          ></u--input>
160
+        </u-form-item>
161
+
162
+        <u-form-item label="成本合计" prop="totalCost" borderBottom>
163
+          <u--input
164
+            v-model.number="form.totalCost"
165
+            type="number"
166
+            placeholder="自动计算成本合计"
167
+            disabled
168
+          ></u--input>
169
+        </u-form-item>
170
+
171
+        <u-form-item label="卖价" prop="sellingPrice" borderBottom>
172
+          <u--input
173
+            v-model.number="form.sellingPrice"
174
+            type="number"
175
+            placeholder="请输入卖价"
176
+            border="none"
177
+            :disabled="!isEdit"
178
+          ></u--input>
179
+        </u-form-item>
180
+
181
+        <u-form-item label="业绩" prop="performance" borderBottom>
182
+          <u--input
183
+            v-model.number="form.performance"
184
+            type="number"
185
+            placeholder="自动计算业绩"
186
+            disabled
187
+          ></u--input>
188
+        </u-form-item>
189
+
190
+        <u-form-item label="分单比例" prop="splitRatio" borderBottom>
191
+          <u--input
192
+            v-model="form.splitRatio"
193
+            type="number"
194
+            step="0.01"
195
+            placeholder="0-100"
196
+            :disabled="!isEdit"
197
+            @blur="validateSplitRatio"
198
+          ></u--input>
199
+        </u-form-item>
200
+
201
+        <u-form-item label="毛业绩" prop="grossPerformance" borderBottom>
202
+          <u--input
203
+            v-model.number="form.grossPerformance"
204
+            type="number"
205
+            :placeholder="
206
+              canEditGrossPerformance ? '请输入毛业绩' : '自动计算毛业绩'
207
+            "
208
+            :disabled="!canEditGrossPerformance"
209
+          ></u--input>
210
+        </u-form-item>
211
+
212
+        <u-form-item label="开户人姓名" prop="customName" borderBottom>
213
+          <u--input
214
+            v-model="form.customName"
215
+            placeholder="请输入开户人姓名"
216
+            border="none"
217
+            :disabled="!isEdit"
218
+          ></u--input>
219
+        </u-form-item>
220
+
221
+        <u-form-item label="身份证号码" prop="idCard" borderBottom>
222
+          <u--input
223
+            v-model="form.idCard"
224
+            placeholder="请输入客户身份证号码"
225
+            border="none"
226
+            :disabled="!isEdit"
227
+          ></u--input>
228
+        </u-form-item>
229
+
230
+        <u-form-item label="银行卡号" prop="bankCardNumber" borderBottom>
231
+          <u--input
232
+            v-model="form.bankCardNumber"
233
+            placeholder="请输入银行卡号"
234
+            border="none"
235
+            :disabled="!isEdit"
236
+          ></u--input>
237
+        </u-form-item>
238
+
239
+        <u-form-item label="银行名称" prop="bankName" borderBottom>
240
+          <u--input
241
+            v-model="form.bankName"
242
+            placeholder="请输入银行名称"
243
+            border="none"
244
+            :disabled="!isEdit"
245
+          ></u--input>
246
+        </u-form-item>
247
+
248
+        <u-form-item label="快递单号" prop="expressOrderNo" borderBottom>
249
+          <u--input
250
+            v-model="form.expressOrderNo"
251
+            placeholder="请输入快递单号"
252
+            border="none"
253
+            :disabled="!isEdit"
254
+          ></u--input>
255
+        </u-form-item>
256
+
257
+        <u-form-item label="附件" prop="fileIds" borderBottom>
258
+          <view class="file-list">
259
+            <view
260
+              v-for="file in fileOptions"
261
+              :key="file.id"
262
+              class="file-item"
263
+              @click="handleFileSelect(file)"
264
+              :class="{
265
+                fileSelected: fileIds.includes(file.id.toString()),
266
+                fileDisabled:
267
+                  file.receiptFormId !== null && file.receiptFormId !== form.id,
268
+              }"
269
+              :disabled="
270
+                file.receiptFormId !== null && file.receiptFormId !== form.id
271
+              "
272
+            >
273
+              <view class="file-info">
274
+                <text class="file-name">{{ handleShowLabel(file) }}</text>
275
+                <text
276
+                  v-if="
277
+                    file.receiptFormId !== null &&
278
+                    file.receiptFormId !== form.id
279
+                  "
280
+                  class="file-bound"
281
+                  >已绑定</text
282
+                >
283
+              </view>
284
+              <u-icon
285
+                v-if="fileIds.includes(file.id.toString())"
286
+                name="checkmark"
287
+                color="#409eff"
288
+                size="18"
289
+                class="fileSelectedIcon"
290
+              ></u-icon>
291
+            </view>
292
+          </view>
293
+          <view v-if="fileOptions.length === 0" class="no-files">
294
+            <u-empty mode="data" text="暂无附件"></u-empty>
295
+          </view>
296
+        </u-form-item>
297
+
298
+        <u-form-item label="收单备注" prop="receiptRemark" borderBottom>
299
+          <u-textarea
300
+            confirmType="done"
301
+            v-model="form.receiptRemark"
302
+            placeholder="请输入收单备注"
303
+            :disabled="!isEdit"
304
+            auto-height
305
+          ></u-textarea>
306
+        </u-form-item>
307
+      </u--form>
308
+    </view>
309
+  </view>
310
+</template>
311
+
312
+<script>
313
+import { cloneDeep } from "lodash";
314
+import ldSelect from "@/components/ld-select/ld-select.vue";
315
+
316
+export default {
317
+  name: "ReceiptForm",
318
+  components: {
319
+    ldSelect,
320
+  },
321
+  data() {
322
+    return {
323
+      loading: false,
324
+      form: {
325
+        id: undefined,
326
+        sendFormId: '',
327
+        clueId: '',
328
+        item: '',
329
+        brand: '',
330
+        needCheckCode: 1,
331
+        code: '',
332
+        tableFee: 0,
333
+        benefitFee: 0,
334
+        freight: 0,
335
+        checkCodeFee: 0,
336
+        totalCost: 0,
337
+        sellingPrice: 0,
338
+        performance: 0,
339
+        receiptRemark: '',
340
+        repairAmount: 0,
341
+        grossPerformance: 0,
342
+        expressOrderNo: '',
343
+        fileIds: '',
344
+        category: '',
345
+        customName: '',
346
+        idCard: '',
347
+        bankCardNumber: '',
348
+        bankName: '',
349
+        customerServiceName: '',
350
+        updateTime: '',
351
+        splitRatio: 0
352
+      },
353
+      fileOptions: [],
354
+      orderId: "",
355
+      clueId: "",
356
+      receiptId: "",
357
+      receiptDetail: {},
358
+      needCheckCodeOptions: [
359
+        { label: "是", value: 1 },
360
+        { label: "否", value: 2 },
361
+      ],
362
+      categoryDict: [],
363
+      brandDict: [],
364
+      labelStyle: {
365
+        fontSize: "28rpx",
366
+        color: "#606266",
367
+      },
368
+      // 表单验证规则
369
+      rules: {
370
+        item: [{ required: true, message: "请输入物品名称", trigger: "blur" }],
371
+        category: [
372
+          { required: true, message: "请选择类别", trigger: "change" },
373
+        ],
374
+        needCheckCode: [
375
+          {
376
+            validator: (rule, value, callback) => {
377
+              if (!value) {
378
+                callback(new Error("请选择是否查码"));
379
+              } else {
380
+                callback();
381
+              }
382
+            },
383
+            trigger: "blur",
384
+          },
385
+        ],
386
+        brand: [
387
+          {
388
+            required: true,
389
+            message: "请选择品牌",
390
+            trigger: ["blur", "change"]
391
+          }
392
+        ],
393
+        code: [
394
+          {
395
+            validator: (rule, value, callback) => {
396
+              if (this.form.needCheckCode === 1 && !value) {
397
+                callback(new Error("编码不能为空"));
398
+              } else {
399
+                callback();
400
+              }
401
+            },
402
+            trigger: "blur",
403
+          },
404
+        ],
405
+      },
406
+    };
407
+  },
408
+  computed: {
409
+    pageTitle() {
410
+      return this.form.id ? "编辑收单" : "新增收单";
411
+    },
412
+    fileIds: {
413
+      get() {
414
+        if (this.form.fileIds) {
415
+          return this.form.fileIds.split(",").filter((id) => id);
416
+        } else {
417
+          return [];
418
+        }
419
+      },
420
+      set(value) {
421
+        this.form.fileIds = value.join(",");
422
+      },
423
+    },
424
+    isEdit() {
425
+      return true;
426
+    },
427
+    // 判断是否显示保存按钮:status=2时,只有接单人本人可以编辑
428
+    canShowSaveButton() {
429
+      if (!this.receiptDetail.status) return true;
430
+      const { status, identification } = this.receiptDetail;
431
+      const currentUserId = this.$store.state.user.userId;
432
+
433
+      // 如果status不是"2",显示保存按钮
434
+      if (status !== "2") {
435
+        return true;
436
+      }
437
+
438
+      // 如果status是"2",只有接单人本人可以看到保存按钮
439
+      return String(identification) === String(currentUserId);
440
+    },
441
+    // 自动计算成本合计 = 表款 + 好处费 + 运费 + 查码费 + 维修金额
442
+    calculatedTotalCost() {
443
+      const { tableFee, benefitFee, freight, checkCodeFee, repairAmount } =
444
+        this.form;
445
+      return (
446
+        (tableFee || 0) +
447
+        (benefitFee || 0) +
448
+        (freight || 0) +
449
+        (checkCodeFee || 0) +
450
+        (repairAmount || 0)
451
+      );
452
+    },
453
+    // 自动计算业绩 = 卖价 - 成本合计
454
+    // 卖价为空时不计算,避免出现负数业绩
455
+    calculatedPerformance() {
456
+      const { sellingPrice } = this.form;
457
+      // 卖价为空或为0时,不计算业绩
458
+      if (!sellingPrice || sellingPrice <= 0) {
459
+        return null;
460
+      }
461
+      return sellingPrice - this.calculatedTotalCost;
462
+    },
463
+    // 自动计算毛业绩 = 业绩 × 分单比例(仅当两者都不为空时)
464
+    // 智能判断:<=1直接乘,>1先除以100再乘
465
+    // 四舍五入保留两位小数
466
+    calculatedGrossPerformance() {
467
+      const { splitRatio } = this.form;
468
+      if (
469
+        this.calculatedPerformance &&
470
+        splitRatio !== null &&
471
+        splitRatio !== undefined &&
472
+        splitRatio !== ""
473
+      ) {
474
+        // 智能判断:<=1直接乘,>1先除以100再乘
475
+        const ratio = splitRatio <= 1 ? splitRatio : splitRatio / 100;
476
+        const result = this.calculatedPerformance * ratio;
477
+        // 四舍五入保留两位小数
478
+        return Math.round(result * 100) / 100;
479
+      }
480
+      return null;
481
+    },
482
+    // 毛业绩是否可编辑:业绩不为空 且 比例为空
483
+    canEditGrossPerformance() {
484
+      return (
485
+        this.calculatedPerformance &&
486
+        !this.form.splitRatio &&
487
+        this.form.splitRatio !== 0 &&
488
+        this.isEdit
489
+      );
490
+    },
491
+  },
492
+  watch: {
493
+    // 监听成本合计变化,自动更新form
494
+    calculatedTotalCost(val) {
495
+      this.form.totalCost = val;
496
+    },
497
+    // 监听业绩业绩变化,自动更新form
498
+    calculatedPerformance(val) {
499
+      this.form.performance = val;
500
+    },
501
+    // 监听毛业绩变化,仅当比例不为空时自动更新form
502
+    calculatedGrossPerformance(val) {
503
+      if (
504
+        this.form.splitRatio !== null &&
505
+        this.form.splitRatio !== undefined &&
506
+        this.form.splitRatio !== "" &&
507
+        val !== null
508
+      ) {
509
+        this.form.grossPerformance = val;
510
+      }
511
+    },
512
+  },
513
+  onLoad(options) {
514
+    // 从路由参数获取orderId和clueId
515
+    this.orderId = options.orderId;
516
+    this.clueId = options.clueId;
517
+    this.receiptId = options.receiptId;
518
+    this.getDicts().then(() => {
519
+      this.initForm();
520
+    });
521
+  },
522
+  methods: {
523
+    // 获取字典数据
524
+    async getDicts() {
525
+      try {
526
+        const [categoryRes, brandRes] = await Promise.all([
527
+          this.$getDicts("crm_form_category"),
528
+          this.$getDicts("crm_form_brand")
529
+        ]);
530
+        this.categoryDict = categoryRes;
531
+        this.brandDict = brandRes;
532
+      } catch (error) {
533
+        console.error("获取字典数据失败:", error);
534
+      }
535
+    },
536
+    handleBack() {
537
+      uni.navigateBack();
538
+    },
539
+    /** 表单重置 */
540
+    reset() {
541
+      const form = Object.assign({
542
+        id: undefined,
543
+        sendFormId: this.orderId,
544
+        clueId: this.clueId,
545
+        item: undefined,
546
+        brand: undefined,
547
+        needCheckCode: 1,
548
+        code: undefined,
549
+        tableFee: undefined,
550
+        benefitFee: undefined,
551
+        freight: undefined,
552
+        checkCodeFee: undefined,
553
+        totalCost: undefined,
554
+        sellingPrice: undefined,
555
+        performance: undefined,
556
+        receiptRemark: undefined,
557
+        repairAmount: undefined,
558
+        grossPerformance: undefined,
559
+        expressOrderNo: undefined,
560
+        fileIds: "",
561
+        category: undefined,
562
+        customName: undefined,
563
+        idCard: undefined,
564
+        bankCardNumber: undefined,
565
+        bankName: undefined,
566
+        customerServiceName: undefined,
567
+        updateTime: undefined,
568
+        splitRatio: undefined,
569
+      });
570
+      // 从订单详情预填充字段
571
+      if (this.receiptDetail) {
572
+        form.status = this.receiptDetail.status;
573
+        form.identification = this.receiptDetail.identification;
574
+        form.item = this.receiptDetail.item;
575
+        form.category = this.receiptDetail.category;
576
+        form.brand = this.receiptDetail.brand;
577
+      }
578
+      this.form = form;
579
+    },
580
+    handleShowLabel(file) {
581
+      // 获取文件在fileIds数组中的索引,如果存在则显示序号
582
+      const index = this.fileIds.indexOf(file.id.toString());
583
+      let label = "";
584
+      if (index !== -1) {
585
+        label = `[${index + 1}] `;
586
+      }
587
+      label += file.fileName;
588
+      if (file.remark) {
589
+        label += `(${file.remark})`;
590
+      }
591
+      // 如果文件已绑定到其他收单,显示绑定信息
592
+      if (file.receiptFormId !== null && file.receiptFormId !== this.form.id) {
593
+        label += ` - 已绑定${file.item || ""}`;
594
+      }
595
+      return label;
596
+    },
597
+    initForm() {
598
+      // 获取订单详情数据
599
+      uni.$u.api
600
+        .getClueSendFormVoByOrderId({
601
+          id: this.orderId,
602
+        })
603
+        .then((res) => {
604
+          this.receiptDetail = res.data;
605
+
606
+          // 如果有receiptId,说明是编辑模式,获取收单详情
607
+          if (this.receiptId) {
608
+            return uni.$u.api
609
+              .getReceiptForm(this.receiptId)
610
+              .then((receiptRes) => {
611
+                this.form = receiptRes.data;
612
+                // 在数据初始化完成后获取附件列表
613
+                this.fetchFileOptions();
614
+              });
615
+          } else {
616
+            // 新增模式:初始化表单数据(需要receiptDetail数据才能预填充字段)
617
+            this.reset();
618
+            // 在数据初始化完成后获取附件列表
619
+            this.fetchFileOptions();
620
+          }
621
+        })
622
+        .catch((error) => {
623
+          console.error("初始化表单失败:", error);
624
+          uni.$u.toast("初始化表单失败");
625
+        });
626
+    },
627
+    // 获取附件选项列表
628
+    async fetchFileOptions() {
629
+      try {
630
+        const params = {
631
+          clueId: this.receiptDetail.clueId,
632
+          sourceId: this.receiptDetail.sendFormId,
633
+          notBound: "1",
634
+          orderFileType: "3",
635
+          type: "2",
636
+        };
637
+        const { rows } = await uni.$u.api.selectClueFileByDto(params);
638
+        this.fileOptions = rows || [];
639
+      } catch (error) {
640
+        console.error("获取附件列表失败:", error);
641
+        uni.$u.toast("获取附件列表失败");
642
+      }
643
+    },
644
+    // 文件选择
645
+    handleFileSelect(file) {
646
+      if (!this.isEdit) return;
647
+
648
+      const fileId = file.id.toString();
649
+      const index = this.fileIds.indexOf(fileId);
650
+      const newFileIds = [...this.fileIds]; // 创建数组副本
651
+
652
+      if (index > -1) {
653
+        // 已选中,取消选择
654
+        newFileIds.splice(index, 1);
655
+      } else {
656
+        // 未选中,添加选择
657
+        newFileIds.push(fileId);
658
+      }
659
+      
660
+      // 重新赋值,触发set方法
661
+      this.fileIds = newFileIds;
662
+    },
663
+    // 分单比例验证:非空时范围必须在0-100之间
664
+    validateSplitRatio() {
665
+      const { splitRatio } = this.form;
666
+      if (
667
+        splitRatio === null ||
668
+        splitRatio === undefined ||
669
+        splitRatio === ""
670
+      ) {
671
+        return; // 允许为空
672
+      }
673
+      const ratio = Number(splitRatio);
674
+      // 范围验证:0-100
675
+      if (ratio < 0) {
676
+        uni.$u.toast("分单比例不能小于0");
677
+        this.form.splitRatio = 0;
678
+      } else if (ratio > 100) {
679
+        uni.$u.toast("分单比例不能大于100");
680
+        this.form.splitRatio = 100;
681
+      }
682
+    },
683
+    // 提交表单
684
+    submitForm() {
685
+      this.$refs.receiptFormRef.validate().then(async (valid) => {
686
+        console.log(valid);
687
+        if (valid) {
688
+          await this.handleUpdate();
689
+        } else {
690
+          uni.$u.toast("请检查表单填写是否正确");
691
+        }
692
+      });
693
+    },
694
+    // 更新表单
695
+    async handleUpdate() {
696
+      try {
697
+        this.loading = true;
698
+
699
+        // 准备提交数据
700
+        const submitData = {
701
+          ...this.form,
702
+          sendFormId: this.orderId,
703
+          clueId: this.clueId,
704
+        };
705
+
706
+        // 根据是否有id判断是新增还是修改
707
+        if (this.form.id) {
708
+          // 修改
709
+          await uni.$u.api.updateReceiptForm(submitData);
710
+          uni.$u.toast("修改成功");
711
+        } else {
712
+          // 新增
713
+          await uni.$u.api.addReceiptForm(submitData);
714
+          uni.$u.toast("新增成功");
715
+        }
716
+
717
+        // 延迟返回上一页
718
+        setTimeout(() => {
719
+          uni.navigateBack();
720
+        }, 1500);
721
+      } catch (error) {
722
+        console.error("保存失败:", error);
723
+        uni.$u.toast("保存失败,请重试");
724
+      } finally {
725
+        this.loading = false;
726
+      }
727
+    },
728
+  },
729
+};
730
+</script>
731
+
732
+<style lang="scss">
733
+.receipt-form-page {
734
+  background-color: #fff;
735
+}
736
+@import "@/static/follow/index.scss";
737
+
738
+// 附件列表样式优化
739
+.file-list {
740
+  display: flex;
741
+  flex-direction: column;
742
+  gap: 16rpx;
743
+  padding: 10rpx 0;
744
+}
745
+
746
+.file-item {
747
+  display: flex;
748
+  align-items: center;
749
+  justify-content: space-between;
750
+  padding: 24rpx 30rpx;
751
+  background-color: #f8f9fa;
752
+  border-radius: 12rpx;
753
+  border: 2rpx solid transparent;
754
+  transition: all 0.3s ease;
755
+  position: relative;
756
+  cursor: pointer;
757
+
758
+  &:active {
759
+    background-color: #e9ecef;
760
+    transform: scale(0.98);
761
+  }
762
+
763
+  // 选中状态样式
764
+  &.fileSelected {
765
+    background-color: #e6f4ff;
766
+    border-color: #409eff;
767
+    box-shadow: 0 4rpx 12rpx rgba(64, 158, 255, 0.2);
768
+  }
769
+
770
+  // 禁用状态样式
771
+  &.fileDisabled {
772
+    background-color: #f5f5f5;
773
+    opacity: 0.6;
774
+    cursor: not-allowed;
775
+
776
+    &:active {
777
+      transform: none;
778
+      background-color: #f5f5f5;
779
+    }
780
+  }
781
+
782
+  .file-info {
783
+    flex: 1;
784
+    display: flex;
785
+    align-items: center;
786
+    gap: 16rpx;
787
+
788
+    .file-name {
789
+      font-size: 28rpx;
790
+      color: #303133;
791
+      line-height: 40rpx;
792
+      word-break: break-all;
793
+    }
794
+
795
+    .file-bound {
796
+      font-size: 24rpx;
797
+      color: #909399;
798
+      background-color: #f5f7fa;
799
+      padding: 2rpx 16rpx;
800
+      border-radius: 16rpx;
801
+    }
802
+  }
803
+
804
+  .u-icon {
805
+    // 为选中图标添加动画效果
806
+    &.fileSelectedIcon {
807
+      animation: fadeInScale 0.3s ease;
808
+    }
809
+  }
810
+}
811
+
812
+// 动画效果
813
+@keyframes fadeInScale {
814
+  0% {
815
+    opacity: 0;
816
+    transform: scale(0.5);
817
+  }
818
+  100% {
819
+    opacity: 1;
820
+    transform: scale(1);
821
+  }
822
+}
823
+
824
+// 无附件时的样式
825
+.no-files {
826
+  padding: 60rpx 0;
827
+  text-align: center;
828
+}
829
+</style>

+ 5 - 0
utils/api.js

@@ -97,6 +97,11 @@ const install = (Vue, vm) => {
97 97
 		// 文件相关接口
98 98
 		selectClueFileByDto:(data,config={})=>http.post(store.state.user.path + '/clueFile/selectClueFileByDto',data,config),
99 99
 		updateClueFile:(data,config={})=>http.put(store.state.user.path + '/clueFile/updateClueFile',data),
100
+		addReceiptForm:(data,config={})=>http.post(store.state.user.path + '/clueReceiptForm',data),
101
+		updateReceiptForm:(data,config={})=>http.put(store.state.user.path + '/updateReceiptForm',data),
102
+		listReceiptFormByOrderId:(orderFormId,config={})=>http.get(store.state.user.path + '/clueReceiptForm/listByOrderId/' + orderFormId),
103
+		getReceiptForm:(id,config={})=>http.get(store.state.user.path + '/clueReceiptForm/' + id),
104
+		delReceiptForm:(id,config={})=>http.delete(store.state.user.path + '/clueReceiptForm/' + id),
100 105
 	}
101 106
 }
102 107