af-mobile-client-vue3 1.4.45 → 1.4.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,547 +1,547 @@
1
- <script setup lang="ts">
2
- import {
3
- Button as VanButton,
4
- Field as VanField,
5
- Icon as VanIcon,
6
- Picker as VanPicker,
7
- Popup as VanPopup,
8
- Stepper as VanStepper,
9
- } from 'vant'
10
- import { computed, reactive, ref, watch } from 'vue'
11
-
12
- // 型号接口
13
- interface ModelItem {
14
- name: string
15
- price?: number
16
- }
17
-
18
- // 物品接口
19
- interface ItemType {
20
- name: string
21
- children?: ModelItem[]
22
- }
23
-
24
- // 分类接口
25
- interface CategoryItem {
26
- name: string
27
- children: ItemType[]
28
- }
29
-
30
- // 配置接口
31
- interface ChargeTypeConfig {
32
- value: CategoryItem[]
33
- }
34
-
35
- interface FormData {
36
- workOrderCode?: string
37
- workOrderId?: string
38
- type: string
39
- item: string
40
- model: string // 新增型号字段
41
- unitPrice: string
42
- quantity: number
43
- }
44
-
45
- const props = defineProps<{
46
- isWorkOrder?: boolean
47
- workOrderData?: any
48
- show: boolean
49
- chargeTypes: ChargeTypeConfig
50
- }>()
51
-
52
- const emit = defineEmits<{
53
- (e: 'update:show', value: boolean): void
54
- (e: 'add', item: {
55
- workOrderCode?: string
56
- workOrderId?: string
57
- id: number
58
- type: string
59
- item: string
60
- model: string // 新增型号字段
61
- unitPrice: number
62
- quantity: number
63
- total: string
64
- }): void
65
- }>()
66
-
67
- const showModal = computed({
68
- get: () => props.show,
69
- set: value => emit('update:show', value),
70
- })
71
-
72
- // 工单选项
73
- const workOrderOptions = computed(() => {
74
- return props.workOrderData || []
75
- })
76
-
77
- const formData = reactive<FormData>({
78
- workOrderCode: workOrderOptions.value.length === 1 ? workOrderOptions.value[0].text : '',
79
- workOrderId: workOrderOptions.value.length === 1 ? workOrderOptions.value[0].value : '',
80
- type: '',
81
- item: '',
82
- model: '', // 新增型号字段
83
- unitPrice: '',
84
- quantity: 1,
85
- })
86
-
87
- const showWorkOrderSelector = ref(false)
88
- const showTypeSelector = ref(false)
89
- const showItemSelector = ref(false)
90
- const showModelSelector = ref(false) // 新增型号选择器状态
91
- const workOrderError = ref(false)
92
- const typeError = ref(false)
93
- const itemError = ref(false)
94
- const priceError = ref(false)
95
- const isWorkOrder = computed(() => props.isWorkOrder)
96
-
97
- // 分类选项
98
- const typeOptions = computed(() => {
99
- return props.chargeTypes.value.map(category => ({ text: category.name, value: category.name }))
100
- })
101
-
102
- // 物品选项
103
- const itemOptions = computed(() => {
104
- if (!formData.type)
105
- return []
106
-
107
- const category = props.chargeTypes.value.find(c => c.name === formData.type)
108
- if (!category)
109
- return []
110
-
111
- return category.children.map(item => ({ text: item.name, value: item.name }))
112
- })
113
-
114
- // 型号选项
115
- const modelOptions = computed(() => {
116
- if (!formData.type || !formData.item)
117
- return []
118
-
119
- const category = props.chargeTypes.value.find(c => c.name === formData.type)
120
- if (!category)
121
- return []
122
-
123
- const item = category.children.find(i => i.name === formData.item)
124
- if (!item || !item.children)
125
- return []
126
-
127
- return item.children.map(model => ({ text: model.name, value: model.name }))
128
- })
129
-
130
- // 是否有型号选项
131
- const hasModelOptions = computed(() => {
132
- if (!formData.type || !formData.item)
133
- return false
134
-
135
- const category = props.chargeTypes.value.find(c => c.name === formData.type)
136
- if (!category)
137
- return false
138
- console.log('category', category)
139
- const item = category.children.find(i => i.name === formData.item)
140
- console.log('item', item)
141
- return !!(item && item.children && item.children.length > 0)
142
- })
143
-
144
- // 选择处理函数
145
- function onWorkOrderSelected({ selectedValues, selectedOptions }) {
146
- formData.workOrderId = selectedValues[0]
147
- formData.workOrderCode = selectedOptions[0].text
148
- showWorkOrderSelector.value = false
149
- workOrderError.value = false
150
- }
151
- function onTypeSelected({ selectedValues }) {
152
- console.log('onTypeSelected', selectedValues)
153
- formData.type = selectedValues[0]
154
- formData.item = ''
155
- formData.model = '' // 重置型号
156
- formData.unitPrice = '' // 重置单价
157
- showTypeSelector.value = false
158
- typeError.value = false
159
- }
160
-
161
- function onItemSelected({ selectedValues }) {
162
- formData.item = selectedValues[0]
163
- formData.model = '' // 重置型号
164
- formData.unitPrice = '' // 重置单价
165
- showItemSelector.value = false
166
- itemError.value = false
167
- }
168
-
169
- // 型号选择处理函数
170
- function onModelSelected({ selectedValues }) {
171
- formData.model = selectedValues[0]
172
-
173
- // 设置对应型号的单价
174
- if (formData.type && formData.item && formData.model) {
175
- const category = props.chargeTypes.value.find(c => c.name === formData.type)
176
- if (category) {
177
- const item = category.children.find(i => i.name === formData.item)
178
- if (item && item.children) {
179
- const model = item.children.find(m => m.name === formData.model)
180
- if (model && model.price !== undefined) {
181
- formData.unitPrice = model.price.toString()
182
- }
183
- }
184
- }
185
- }
186
-
187
- showModelSelector.value = false
188
- }
189
-
190
- function handleSubmit() {
191
- // 验证表单
192
- let isValid = true
193
- // 验证工单
194
- if (isWorkOrder.value && !formData.workOrderId) {
195
- workOrderError.value = true
196
- isValid = false
197
- }
198
-
199
- if (!formData.type) {
200
- typeError.value = true
201
- isValid = false
202
- }
203
-
204
- if (!formData.item) {
205
- itemError.value = true
206
- isValid = false
207
- }
208
-
209
- if (!formData.unitPrice || Number.parseFloat(formData.unitPrice) <= 0) {
210
- priceError.value = true
211
- isValid = false
212
- }
213
-
214
- if (!isValid)
215
- return
216
-
217
- const unitPrice = Number.parseFloat(formData.unitPrice)
218
- const quantity = formData.quantity
219
- const total = (unitPrice * quantity).toFixed(2)
220
-
221
- // 提交新费用项
222
- emit('add', {
223
- id: Date.now(),
224
- workOrderId: formData.workOrderId || '',
225
- workOrderCode: formData.workOrderCode || '',
226
- type: formData.type,
227
- item: formData.item,
228
- model: formData.model, // 添加型号
229
- unitPrice,
230
- quantity,
231
- total,
232
- })
233
-
234
- // 重置表单
235
- resetForm()
236
-
237
- // 关闭弹窗
238
- closeModal()
239
- }
240
-
241
- function resetForm() {
242
- if (isWorkOrder.value && workOrderOptions.value.length === 1) {
243
- formData.workOrderId = workOrderOptions.value[0].value || ''
244
- formData.workOrderCode = workOrderOptions.value[0].text || ''
245
- }
246
- else {
247
- formData.workOrderId = ''
248
- formData.workOrderCode = ''
249
- }
250
- formData.type = ''
251
- formData.item = ''
252
- formData.model = '' // 重置型号
253
- formData.unitPrice = ''
254
- formData.quantity = 1
255
- workOrderError.value = false
256
- typeError.value = false
257
- itemError.value = false
258
- priceError.value = false
259
- }
260
-
261
- function closeModal() {
262
- showModal.value = false
263
- resetForm()
264
- }
265
-
266
- // 监听输入,清除错误状态
267
- // eslint-disable-next-line style/max-statements-per-line
268
- watch(() => formData.workOrderId, () => { workOrderError.value = false })
269
- // eslint-disable-next-line style/max-statements-per-line
270
- watch(() => formData.type, () => { typeError.value = false })
271
- // eslint-disable-next-line style/max-statements-per-line
272
- watch(() => formData.item, () => { itemError.value = false })
273
- // eslint-disable-next-line style/max-statements-per-line
274
- watch(() => formData.unitPrice, () => { priceError.value = false })
275
- </script>
276
-
277
- <template>
278
- <VanPopup
279
- v-model:show="showModal"
280
- round
281
- position="center"
282
- :style="{ width: '90%', maxWidth: '420px' }"
283
- close-icon-position="top-right"
284
- class="other-charge-item-modal"
285
- >
286
- <div class="other-charge-item-modal__container">
287
- <div class="other-charge-item-modal__header">
288
- <h3 class="other-charge-item-modal__title">
289
- 添加费用项
290
- </h3>
291
- <VanIcon name="cross" @click="closeModal" />
292
- </div>
293
-
294
- <form class="other-charge-item-modal__form" @submit.prevent="handleSubmit">
295
- <div v-if="isWorkOrder" class="other-charge-item-modal__field">
296
- <label class="other-charge-item-modal__label">所属工单</label>
297
- <VanField
298
- v-model="formData.workOrderCode"
299
- placeholder="请选择工单"
300
- readonly
301
- right-icon="arrow-down"
302
- :error="workOrderError"
303
- @click="showWorkOrderSelector = true"
304
- />
305
- </div>
306
-
307
- <div class="other-charge-item-modal__field">
308
- <label class="other-charge-item-modal__label">收费类型</label>
309
- <VanField
310
- v-model="formData.type"
311
- placeholder="请选择收费类型"
312
- readonly
313
- right-icon="arrow-down"
314
- :error="typeError"
315
- @click="showTypeSelector = true"
316
- />
317
- </div>
318
-
319
- <div class="other-charge-item-modal__field">
320
- <label class="other-charge-item-modal__label">具体项目</label>
321
- <VanField
322
- v-model="formData.item"
323
- placeholder="请选择具体项目"
324
- readonly
325
- right-icon="arrow-down"
326
- :disabled="!formData.type"
327
- :error="itemError"
328
- @click="showItemSelector = true"
329
- />
330
- </div>
331
-
332
- <!-- 新增型号选择字段 -->
333
- <div v-if="hasModelOptions" class="other-charge-item-modal__field">
334
- <label class="other-charge-item-modal__label">型号</label>
335
- <VanField
336
- v-model="formData.model"
337
- placeholder="请选择型号"
338
- readonly
339
- right-icon="arrow-down"
340
- :disabled="!formData.item"
341
- @click="showModelSelector = true"
342
- />
343
- </div>
344
-
345
- <div class="other-charge-item-modal__price-quantity">
346
- <div class="other-charge-item-modal__field">
347
- <label class="other-charge-item-modal__label">单价 (元)</label>
348
- <div class="other-charge-item-modal__price-input">
349
- <VanField
350
- v-model="formData.unitPrice"
351
- type="digit"
352
- placeholder="0.00"
353
- :error="priceError"
354
- >
355
- <template #prefix>
356
- <span class="other-charge-item-modal__prefix">¥</span>
357
- </template>
358
- </VanField>
359
- </div>
360
- </div>
361
-
362
- <div class="other-charge-item-modal__field">
363
- <label class="other-charge-item-modal__label">数量</label>
364
- <VanStepper
365
- v-model="formData.quantity"
366
- min="1"
367
- step="1"
368
- input-width="60px"
369
- button-size="28px"
370
- theme="round"
371
- />
372
- </div>
373
- </div>
374
-
375
- <div class="other-charge-item-modal__buttons">
376
- <VanButton
377
- plain
378
- type="default"
379
- size="normal"
380
- class="other-charge-item-modal__cancel-btn"
381
- @click="closeModal"
382
- >
383
- 取消
384
- </VanButton>
385
- <VanButton
386
- type="primary"
387
- size="normal"
388
- native-type="submit"
389
- class="other-charge-item-modal__confirm-btn"
390
- >
391
- 添加
392
- </VanButton>
393
- </div>
394
- </form>
395
- </div>
396
-
397
- <!-- 工单 -->
398
- <VanPopup
399
- v-model:show="showWorkOrderSelector"
400
- position="bottom"
401
-
402
- teleport="#other-charge-form"
403
- round destroy-on-close
404
- >
405
- <VanPicker
406
- :columns="workOrderOptions"
407
- show-toolbar
408
- title="选择工单"
409
- @confirm="onWorkOrderSelected"
410
- @cancel="showWorkOrderSelector = false"
411
- />
412
- </VanPopup>
413
-
414
- <!-- 收费类型选择器 -->
415
- <VanPopup
416
- v-model:show="showTypeSelector"
417
- position="bottom"
418
- destroy-on-close
419
- teleport="#other-charge-form"
420
- round
421
- >
422
- <VanPicker
423
- :columns="typeOptions"
424
- show-toolbar
425
- title="选择收费类型"
426
- @confirm="onTypeSelected"
427
- @cancel="showTypeSelector = false"
428
- />
429
- </VanPopup>
430
-
431
- <!-- 具体项目选择器 -->
432
- <VanPopup
433
- v-model:show="showItemSelector"
434
- destroy-on-close
435
- position="bottom"
436
- round
437
- teleport="#other-charge-form"
438
- >
439
- <VanPicker
440
- :columns="itemOptions"
441
- show-toolbar
442
- title="选择具体项目"
443
- @confirm="onItemSelected"
444
- @cancel="showItemSelector = false"
445
- />
446
- </VanPopup>
447
-
448
- <!-- 型号选择器 -->
449
- <VanPopup
450
- v-model:show="showModelSelector"
451
- destroy-on-close
452
- position="bottom"
453
- round
454
- teleport="#other-charge-form"
455
- >
456
- <VanPicker
457
- :columns="modelOptions"
458
- show-toolbar
459
- title="选择型号"
460
- @confirm="onModelSelected"
461
- @cancel="showModelSelector = false"
462
- />
463
- </VanPopup>
464
- </VanPopup>
465
- </template>
466
-
467
- <style scoped lang="less">
468
- .other-charge-item-modal {
469
- &__container {
470
- padding: 16px;
471
- }
472
-
473
- &__header {
474
- display: flex;
475
- justify-content: space-between;
476
- align-items: center;
477
- margin-bottom: 16px;
478
- }
479
-
480
- &__title {
481
- font-size: 18px;
482
- font-weight: 500;
483
- color: #1f2937;
484
- margin: 0;
485
- }
486
-
487
- &__form {
488
- .van-field {
489
- background-color: #f9fafb;
490
- border-radius: 6px;
491
- }
492
- }
493
-
494
- &__field {
495
- margin-bottom: 16px;
496
- }
497
-
498
- &__label {
499
- display: block;
500
- font-size: 14px;
501
- font-weight: 500;
502
- color: #374151;
503
- margin-bottom: 4px;
504
- }
505
-
506
- &__price-quantity {
507
- display: grid;
508
- grid-template-columns: 1fr 1fr;
509
- gap: 16px;
510
- }
511
-
512
- &__price-input {
513
- position: relative;
514
- }
515
-
516
- &__prefix {
517
- color: #6b7280;
518
- font-size: 14px;
519
- }
520
-
521
- &__buttons {
522
- display: flex;
523
- justify-content: flex-end;
524
- margin-top: 24px;
525
- gap: 12px;
526
- }
527
-
528
- &__cancel-btn {
529
- border-color: #d1d5db;
530
- color: #374151;
531
- }
532
-
533
- &__confirm-btn {
534
- background-color: #2563eb;
535
- }
536
-
537
- :deep(.van-stepper__input) {
538
- background-color: #f9fafb;
539
- }
540
-
541
- :deep(.van-stepper__minus),
542
- :deep(.van-stepper__plus) {
543
- background-color: #f3f4f6;
544
- border: 1px solid #d1d5db;
545
- }
546
- }
547
- </style>
1
+ <script setup lang="ts">
2
+ import {
3
+ Button as VanButton,
4
+ Field as VanField,
5
+ Icon as VanIcon,
6
+ Picker as VanPicker,
7
+ Popup as VanPopup,
8
+ Stepper as VanStepper,
9
+ } from 'vant'
10
+ import { computed, reactive, ref, watch } from 'vue'
11
+
12
+ // 型号接口
13
+ interface ModelItem {
14
+ name: string
15
+ price?: number
16
+ }
17
+
18
+ // 物品接口
19
+ interface ItemType {
20
+ name: string
21
+ children?: ModelItem[]
22
+ }
23
+
24
+ // 分类接口
25
+ interface CategoryItem {
26
+ name: string
27
+ children: ItemType[]
28
+ }
29
+
30
+ // 配置接口
31
+ interface ChargeTypeConfig {
32
+ value: CategoryItem[]
33
+ }
34
+
35
+ interface FormData {
36
+ workOrderCode?: string
37
+ workOrderId?: string
38
+ type: string
39
+ item: string
40
+ model: string // 新增型号字段
41
+ unitPrice: string
42
+ quantity: number
43
+ }
44
+
45
+ const props = defineProps<{
46
+ isWorkOrder?: boolean
47
+ workOrderData?: any
48
+ show: boolean
49
+ chargeTypes: ChargeTypeConfig
50
+ }>()
51
+
52
+ const emit = defineEmits<{
53
+ (e: 'update:show', value: boolean): void
54
+ (e: 'add', item: {
55
+ workOrderCode?: string
56
+ workOrderId?: string
57
+ id: number
58
+ type: string
59
+ item: string
60
+ model: string // 新增型号字段
61
+ unitPrice: number
62
+ quantity: number
63
+ total: string
64
+ }): void
65
+ }>()
66
+
67
+ const showModal = computed({
68
+ get: () => props.show,
69
+ set: value => emit('update:show', value),
70
+ })
71
+
72
+ // 工单选项
73
+ const workOrderOptions = computed(() => {
74
+ return props.workOrderData || []
75
+ })
76
+
77
+ const formData = reactive<FormData>({
78
+ workOrderCode: workOrderOptions.value.length === 1 ? workOrderOptions.value[0].text : '',
79
+ workOrderId: workOrderOptions.value.length === 1 ? workOrderOptions.value[0].value : '',
80
+ type: '',
81
+ item: '',
82
+ model: '', // 新增型号字段
83
+ unitPrice: '',
84
+ quantity: 1,
85
+ })
86
+
87
+ const showWorkOrderSelector = ref(false)
88
+ const showTypeSelector = ref(false)
89
+ const showItemSelector = ref(false)
90
+ const showModelSelector = ref(false) // 新增型号选择器状态
91
+ const workOrderError = ref(false)
92
+ const typeError = ref(false)
93
+ const itemError = ref(false)
94
+ const priceError = ref(false)
95
+ const isWorkOrder = computed(() => props.isWorkOrder)
96
+
97
+ // 分类选项
98
+ const typeOptions = computed(() => {
99
+ return props.chargeTypes.value.map(category => ({ text: category.name, value: category.name }))
100
+ })
101
+
102
+ // 物品选项
103
+ const itemOptions = computed(() => {
104
+ if (!formData.type)
105
+ return []
106
+
107
+ const category = props.chargeTypes.value.find(c => c.name === formData.type)
108
+ if (!category)
109
+ return []
110
+
111
+ return category.children.map(item => ({ text: item.name, value: item.name }))
112
+ })
113
+
114
+ // 型号选项
115
+ const modelOptions = computed(() => {
116
+ if (!formData.type || !formData.item)
117
+ return []
118
+
119
+ const category = props.chargeTypes.value.find(c => c.name === formData.type)
120
+ if (!category)
121
+ return []
122
+
123
+ const item = category.children.find(i => i.name === formData.item)
124
+ if (!item || !item.children)
125
+ return []
126
+
127
+ return item.children.map(model => ({ text: model.name, value: model.name }))
128
+ })
129
+
130
+ // 是否有型号选项
131
+ const hasModelOptions = computed(() => {
132
+ if (!formData.type || !formData.item)
133
+ return false
134
+
135
+ const category = props.chargeTypes.value.find(c => c.name === formData.type)
136
+ if (!category)
137
+ return false
138
+ console.log('category', category)
139
+ const item = category.children.find(i => i.name === formData.item)
140
+ console.log('item', item)
141
+ return !!(item && item.children && item.children.length > 0)
142
+ })
143
+
144
+ // 选择处理函数
145
+ function onWorkOrderSelected({ selectedValues, selectedOptions }) {
146
+ formData.workOrderId = selectedValues[0]
147
+ formData.workOrderCode = selectedOptions[0].text
148
+ showWorkOrderSelector.value = false
149
+ workOrderError.value = false
150
+ }
151
+ function onTypeSelected({ selectedValues }) {
152
+ console.log('onTypeSelected', selectedValues)
153
+ formData.type = selectedValues[0]
154
+ formData.item = ''
155
+ formData.model = '' // 重置型号
156
+ formData.unitPrice = '' // 重置单价
157
+ showTypeSelector.value = false
158
+ typeError.value = false
159
+ }
160
+
161
+ function onItemSelected({ selectedValues }) {
162
+ formData.item = selectedValues[0]
163
+ formData.model = '' // 重置型号
164
+ formData.unitPrice = '' // 重置单价
165
+ showItemSelector.value = false
166
+ itemError.value = false
167
+ }
168
+
169
+ // 型号选择处理函数
170
+ function onModelSelected({ selectedValues }) {
171
+ formData.model = selectedValues[0]
172
+
173
+ // 设置对应型号的单价
174
+ if (formData.type && formData.item && formData.model) {
175
+ const category = props.chargeTypes.value.find(c => c.name === formData.type)
176
+ if (category) {
177
+ const item = category.children.find(i => i.name === formData.item)
178
+ if (item && item.children) {
179
+ const model = item.children.find(m => m.name === formData.model)
180
+ if (model && model.price !== undefined) {
181
+ formData.unitPrice = model.price.toString()
182
+ }
183
+ }
184
+ }
185
+ }
186
+
187
+ showModelSelector.value = false
188
+ }
189
+
190
+ function handleSubmit() {
191
+ // 验证表单
192
+ let isValid = true
193
+ // 验证工单
194
+ if (isWorkOrder.value && !formData.workOrderId) {
195
+ workOrderError.value = true
196
+ isValid = false
197
+ }
198
+
199
+ if (!formData.type) {
200
+ typeError.value = true
201
+ isValid = false
202
+ }
203
+
204
+ if (!formData.item) {
205
+ itemError.value = true
206
+ isValid = false
207
+ }
208
+
209
+ if (!formData.unitPrice || Number.parseFloat(formData.unitPrice) <= 0) {
210
+ priceError.value = true
211
+ isValid = false
212
+ }
213
+
214
+ if (!isValid)
215
+ return
216
+
217
+ const unitPrice = Number.parseFloat(formData.unitPrice)
218
+ const quantity = formData.quantity
219
+ const total = (unitPrice * quantity).toFixed(2)
220
+
221
+ // 提交新费用项
222
+ emit('add', {
223
+ id: Date.now(),
224
+ workOrderId: formData.workOrderId || '',
225
+ workOrderCode: formData.workOrderCode || '',
226
+ type: formData.type,
227
+ item: formData.item,
228
+ model: formData.model, // 添加型号
229
+ unitPrice,
230
+ quantity,
231
+ total,
232
+ })
233
+
234
+ // 重置表单
235
+ resetForm()
236
+
237
+ // 关闭弹窗
238
+ closeModal()
239
+ }
240
+
241
+ function resetForm() {
242
+ if (isWorkOrder.value && workOrderOptions.value.length === 1) {
243
+ formData.workOrderId = workOrderOptions.value[0].value || ''
244
+ formData.workOrderCode = workOrderOptions.value[0].text || ''
245
+ }
246
+ else {
247
+ formData.workOrderId = ''
248
+ formData.workOrderCode = ''
249
+ }
250
+ formData.type = ''
251
+ formData.item = ''
252
+ formData.model = '' // 重置型号
253
+ formData.unitPrice = ''
254
+ formData.quantity = 1
255
+ workOrderError.value = false
256
+ typeError.value = false
257
+ itemError.value = false
258
+ priceError.value = false
259
+ }
260
+
261
+ function closeModal() {
262
+ showModal.value = false
263
+ resetForm()
264
+ }
265
+
266
+ // 监听输入,清除错误状态
267
+ // eslint-disable-next-line style/max-statements-per-line
268
+ watch(() => formData.workOrderId, () => { workOrderError.value = false })
269
+ // eslint-disable-next-line style/max-statements-per-line
270
+ watch(() => formData.type, () => { typeError.value = false })
271
+ // eslint-disable-next-line style/max-statements-per-line
272
+ watch(() => formData.item, () => { itemError.value = false })
273
+ // eslint-disable-next-line style/max-statements-per-line
274
+ watch(() => formData.unitPrice, () => { priceError.value = false })
275
+ </script>
276
+
277
+ <template>
278
+ <VanPopup
279
+ v-model:show="showModal"
280
+ round
281
+ position="center"
282
+ :style="{ width: '90%', maxWidth: '420px' }"
283
+ close-icon-position="top-right"
284
+ class="other-charge-item-modal"
285
+ >
286
+ <div class="other-charge-item-modal__container">
287
+ <div class="other-charge-item-modal__header">
288
+ <h3 class="other-charge-item-modal__title">
289
+ 添加费用项
290
+ </h3>
291
+ <VanIcon name="cross" @click="closeModal" />
292
+ </div>
293
+
294
+ <form class="other-charge-item-modal__form" @submit.prevent="handleSubmit">
295
+ <div v-if="isWorkOrder" class="other-charge-item-modal__field">
296
+ <label class="other-charge-item-modal__label">所属工单</label>
297
+ <VanField
298
+ v-model="formData.workOrderCode"
299
+ placeholder="请选择工单"
300
+ readonly
301
+ right-icon="arrow-down"
302
+ :error="workOrderError"
303
+ @click="showWorkOrderSelector = true"
304
+ />
305
+ </div>
306
+
307
+ <div class="other-charge-item-modal__field">
308
+ <label class="other-charge-item-modal__label">收费类型</label>
309
+ <VanField
310
+ v-model="formData.type"
311
+ placeholder="请选择收费类型"
312
+ readonly
313
+ right-icon="arrow-down"
314
+ :error="typeError"
315
+ @click="showTypeSelector = true"
316
+ />
317
+ </div>
318
+
319
+ <div class="other-charge-item-modal__field">
320
+ <label class="other-charge-item-modal__label">具体项目</label>
321
+ <VanField
322
+ v-model="formData.item"
323
+ placeholder="请选择具体项目"
324
+ readonly
325
+ right-icon="arrow-down"
326
+ :disabled="!formData.type"
327
+ :error="itemError"
328
+ @click="showItemSelector = true"
329
+ />
330
+ </div>
331
+
332
+ <!-- 新增型号选择字段 -->
333
+ <div v-if="hasModelOptions" class="other-charge-item-modal__field">
334
+ <label class="other-charge-item-modal__label">型号</label>
335
+ <VanField
336
+ v-model="formData.model"
337
+ placeholder="请选择型号"
338
+ readonly
339
+ right-icon="arrow-down"
340
+ :disabled="!formData.item"
341
+ @click="showModelSelector = true"
342
+ />
343
+ </div>
344
+
345
+ <div class="other-charge-item-modal__price-quantity">
346
+ <div class="other-charge-item-modal__field">
347
+ <label class="other-charge-item-modal__label">单价 (元)</label>
348
+ <div class="other-charge-item-modal__price-input">
349
+ <VanField
350
+ v-model="formData.unitPrice"
351
+ type="digit"
352
+ placeholder="0.00"
353
+ :error="priceError"
354
+ >
355
+ <template #prefix>
356
+ <span class="other-charge-item-modal__prefix">¥</span>
357
+ </template>
358
+ </VanField>
359
+ </div>
360
+ </div>
361
+
362
+ <div class="other-charge-item-modal__field">
363
+ <label class="other-charge-item-modal__label">数量</label>
364
+ <VanStepper
365
+ v-model="formData.quantity"
366
+ min="1"
367
+ step="1"
368
+ input-width="60px"
369
+ button-size="28px"
370
+ theme="round"
371
+ />
372
+ </div>
373
+ </div>
374
+
375
+ <div class="other-charge-item-modal__buttons">
376
+ <VanButton
377
+ plain
378
+ type="default"
379
+ size="normal"
380
+ class="other-charge-item-modal__cancel-btn"
381
+ @click="closeModal"
382
+ >
383
+ 取消
384
+ </VanButton>
385
+ <VanButton
386
+ type="primary"
387
+ size="normal"
388
+ native-type="submit"
389
+ class="other-charge-item-modal__confirm-btn"
390
+ >
391
+ 添加
392
+ </VanButton>
393
+ </div>
394
+ </form>
395
+ </div>
396
+
397
+ <!-- 工单 -->
398
+ <VanPopup
399
+ v-model:show="showWorkOrderSelector"
400
+ position="bottom"
401
+
402
+ teleport="#other-charge-form"
403
+ round destroy-on-close
404
+ >
405
+ <VanPicker
406
+ :columns="workOrderOptions"
407
+ show-toolbar
408
+ title="选择工单"
409
+ @confirm="onWorkOrderSelected"
410
+ @cancel="showWorkOrderSelector = false"
411
+ />
412
+ </VanPopup>
413
+
414
+ <!-- 收费类型选择器 -->
415
+ <VanPopup
416
+ v-model:show="showTypeSelector"
417
+ position="bottom"
418
+ destroy-on-close
419
+ teleport="#other-charge-form"
420
+ round
421
+ >
422
+ <VanPicker
423
+ :columns="typeOptions"
424
+ show-toolbar
425
+ title="选择收费类型"
426
+ @confirm="onTypeSelected"
427
+ @cancel="showTypeSelector = false"
428
+ />
429
+ </VanPopup>
430
+
431
+ <!-- 具体项目选择器 -->
432
+ <VanPopup
433
+ v-model:show="showItemSelector"
434
+ destroy-on-close
435
+ position="bottom"
436
+ round
437
+ teleport="#other-charge-form"
438
+ >
439
+ <VanPicker
440
+ :columns="itemOptions"
441
+ show-toolbar
442
+ title="选择具体项目"
443
+ @confirm="onItemSelected"
444
+ @cancel="showItemSelector = false"
445
+ />
446
+ </VanPopup>
447
+
448
+ <!-- 型号选择器 -->
449
+ <VanPopup
450
+ v-model:show="showModelSelector"
451
+ destroy-on-close
452
+ position="bottom"
453
+ round
454
+ teleport="#other-charge-form"
455
+ >
456
+ <VanPicker
457
+ :columns="modelOptions"
458
+ show-toolbar
459
+ title="选择型号"
460
+ @confirm="onModelSelected"
461
+ @cancel="showModelSelector = false"
462
+ />
463
+ </VanPopup>
464
+ </VanPopup>
465
+ </template>
466
+
467
+ <style scoped lang="less">
468
+ .other-charge-item-modal {
469
+ &__container {
470
+ padding: 16px;
471
+ }
472
+
473
+ &__header {
474
+ display: flex;
475
+ justify-content: space-between;
476
+ align-items: center;
477
+ margin-bottom: 16px;
478
+ }
479
+
480
+ &__title {
481
+ font-size: 18px;
482
+ font-weight: 500;
483
+ color: #1f2937;
484
+ margin: 0;
485
+ }
486
+
487
+ &__form {
488
+ .van-field {
489
+ background-color: #f9fafb;
490
+ border-radius: 6px;
491
+ }
492
+ }
493
+
494
+ &__field {
495
+ margin-bottom: 16px;
496
+ }
497
+
498
+ &__label {
499
+ display: block;
500
+ font-size: 14px;
501
+ font-weight: 500;
502
+ color: #374151;
503
+ margin-bottom: 4px;
504
+ }
505
+
506
+ &__price-quantity {
507
+ display: grid;
508
+ grid-template-columns: 1fr 1fr;
509
+ gap: 16px;
510
+ }
511
+
512
+ &__price-input {
513
+ position: relative;
514
+ }
515
+
516
+ &__prefix {
517
+ color: #6b7280;
518
+ font-size: 14px;
519
+ }
520
+
521
+ &__buttons {
522
+ display: flex;
523
+ justify-content: flex-end;
524
+ margin-top: 24px;
525
+ gap: 12px;
526
+ }
527
+
528
+ &__cancel-btn {
529
+ border-color: #d1d5db;
530
+ color: #374151;
531
+ }
532
+
533
+ &__confirm-btn {
534
+ background-color: #2563eb;
535
+ }
536
+
537
+ :deep(.van-stepper__input) {
538
+ background-color: #f9fafb;
539
+ }
540
+
541
+ :deep(.van-stepper__minus),
542
+ :deep(.van-stepper__plus) {
543
+ background-color: #f3f4f6;
544
+ border: 1px solid #d1d5db;
545
+ }
546
+ }
547
+ </style>