zhangxin пре 1 недеља
родитељ
комит
3de7571f79

+ 1 - 0
.env.development

@@ -1,3 +1,4 @@
1
 NODE_ENV = development
1
 NODE_ENV = development
2
 VUE_APP_ENV = dev
2
 VUE_APP_ENV = dev
3
 VITE_API_BASE_URL=https://crmtest.nanjingshiyu.com/prod-api
3
 VITE_API_BASE_URL=https://crmtest.nanjingshiyu.com/prod-api
4
+#VITE_API_BASE_URL=https://crm.nanjingshiyu.com/prod-api

+ 2 - 2
src/manifest.json

@@ -2,8 +2,8 @@
2
     "name" : "小葫芦",
2
     "name" : "小葫芦",
3
     "appid" : "__UNI__6259019",
3
     "appid" : "__UNI__6259019",
4
     "description" : "",
4
     "description" : "",
5
-    "versionName" : "1.0.2",
6
-    "versionCode" : 102,
5
+    "versionName" : "1.0.3",
6
+    "versionCode" : 103,
7
     "transformPx" : false,
7
     "transformPx" : false,
8
     /* 5+App特有相关 */
8
     /* 5+App特有相关 */
9
     "app-plus" : {
9
     "app-plus" : {

+ 2 - 2
src/pages/login/index.vue

@@ -124,14 +124,14 @@ const handleLogin = () => {
124
 			uni.removeStorageSync('password');
124
 			uni.removeStorageSync('password');
125
 		}
125
 		}
126
 		const res = await loginApi.login(form.value)
126
 		const res = await loginApi.login(form.value)
127
-		loading.value = false
128
 		uni.setStorageSync('token', res.data.access_token)
127
 		uni.setStorageSync('token', res.data.access_token)
129
 		const infoRes = await loginApi.getInfo()
128
 		const infoRes = await loginApi.getInfo()
130
 		uni.setStorageSync('userInfo', infoRes.user)
129
 		uni.setStorageSync('userInfo', infoRes.user)
130
+		loading.value = false
131
 		uni.showToast({ title: '登录成功', icon: 'success' })
131
 		uni.showToast({ title: '登录成功', icon: 'success' })
132
 		setTimeout(() => {
132
 		setTimeout(() => {
133
 			uni.reLaunch({ url: '/pages/repair/index' })
133
 			uni.reLaunch({ url: '/pages/repair/index' })
134
-		}, 500)
134
+		}, 50)
135
 	}).catch((res: any) => {
135
 	}).catch((res: any) => {
136
 		uni.$u.toast(res[0].message);
136
 		uni.$u.toast(res[0].message);
137
 	})
137
 	})

+ 2 - 2
src/pages/repair/components/apply/index.vue

@@ -120,7 +120,7 @@
120
             <up-textarea v-model="formData.address" placeholder="请输入您的地址"></up-textarea>
120
             <up-textarea v-model="formData.address" placeholder="请输入您的地址"></up-textarea>
121
           </up-form-item>
121
           </up-form-item>
122
 
122
 
123
-          <up-form-item v-if="formData.serviceType === 'store'" prop="storeName" label="选择门店" required>
123
+          <up-form-item v-if="formData.serviceType === 'store'" prop="storeName" label="选择门店" required label-width="70">
124
             <up-select v-model="formData.storeName" :options="stores" label="请选择门店" @select="selectStoreItem">
124
             <up-select v-model="formData.storeName" :options="stores" label="请选择门店" @select="selectStoreItem">
125
               <template #text="{ label }">
125
               <template #text="{ label }">
126
                 <text>{{ formData.storeName }}</text>
126
                 <text>{{ formData.storeName }}</text>
@@ -128,7 +128,7 @@
128
             </up-select>
128
             </up-select>
129
           </up-form-item>
129
           </up-form-item>
130
 
130
 
131
-          <up-form-item v-if="formData.serviceType === 'home'" prop="delivery" label="上门时间" required
131
+          <up-form-item v-if="formData.serviceType === 'home'" prop="delivery" label="上门时间" required :label-width="70"
132
             @click="datetimerShow = true" labelPosition="top">
132
             @click="datetimerShow = true" labelPosition="top">
133
             <up-cate-tab height="300px" :tab-list="deliveryOptions" v-model:current="deliveryCurrent">
133
             <up-cate-tab height="300px" :tab-list="deliveryOptions" v-model:current="deliveryCurrent">
134
               <template v-slot:itemList="{ item }">
134
               <template v-slot:itemList="{ item }">

+ 119 - 3
src/pages/repair/components/orderList/index.vue

@@ -17,12 +17,15 @@
17
                     <up-swipe-action>
17
                     <up-swipe-action>
18
                         <up-swipe-action-item :threshold="100" v-model:show="swipeShow" :options="swipeOptions"
18
                         <up-swipe-action-item :threshold="100" v-model:show="swipeShow" :options="swipeOptions"
19
                             @click="(val: any) => handleSwipeAction(val, item)">
19
                             @click="(val: any) => handleSwipeAction(val, item)">
20
+                            <view class="more-btn" @tap.stop="openPopover(item, index)">
21
+                                <up-icon name="more-dot-fill" size="40rpx" color="#999"></up-icon>
22
+                            </view>
20
                             <view class="order-header">
23
                             <view class="order-header">
21
                                 <view class="order-info">
24
                                 <view class="order-info">
22
                                     <text class="order-number">工单号:{{ item.orderId }}</text>
25
                                     <text class="order-number">工单号:{{ item.orderId }}</text>
23
                                     <text class="order-time">{{ formatTime(item.createTime) }}</text>
26
                                     <text class="order-time">{{ formatTime(item.createTime) }}</text>
24
                                 </view>
27
                                 </view>
25
-                                <view class="order-status">
28
+                                <view class="order-right">
26
                                     <text :style="{ color: getStatusColor(item.status) }" class="status-text">{{
29
                                     <text :style="{ color: getStatusColor(item.status) }" class="status-text">{{
27
                                         item.status
30
                                         item.status
28
                                     }}</text>
31
                                     }}</text>
@@ -49,6 +52,28 @@
49
                             </view>
52
                             </view>
50
                         </up-swipe-action-item>
53
                         </up-swipe-action-item>
51
                     </up-swipe-action>
54
                     </up-swipe-action>
55
+
56
+                    <!-- 浮层蒙版(点击空白关闭) -->
57
+                    <view v-if="popoverIndex === index" class="popover-mask" @tap.stop="closePopover"></view>
58
+                    <!-- Popover 浮层 -->
59
+                    <view v-if="popoverIndex === index" class="popover-menu" @tap.stop>
60
+                        <view class="popover-item" @tap="handlePopoverAction({ index: 0 }, item)">
61
+                            <up-icon name="play-right-fill" size="32rpx" color="#42b983"></up-icon>
62
+                            <text class="popover-text">进度</text>
63
+                        </view>
64
+                        <view class="popover-divider"></view>
65
+                        <view class="popover-item" @tap="handlePopoverAction({ index: 1 }, item)">
66
+                            <up-icon name="list" size="32rpx" color="#2c94f6"></up-icon>
67
+                            <text class="popover-text">详情</text>
68
+                        </view>
69
+                        <view class="popover-divider"></view>
70
+                        <view class="popover-item" @tap="handlePopoverAction({ index: 2 }, item)">
71
+                            <up-icon name="rmb-circle" size="32rpx" color="#FF9900"></up-icon>
72
+                            <text class="popover-text">报价</text>
73
+                        </view>
74
+                        <!-- 小三角箭头 -->
75
+                        <view class="popover-arrow"></view>
76
+                    </view>
52
                 </view>
77
                 </view>
53
             </up-list-item>
78
             </up-list-item>
54
             <up-empty v-if="filteredOrders.length === 0" image="empty" text="暂无订单数据"></up-empty>
79
             <up-empty v-if="filteredOrders.length === 0" image="empty" text="暂无订单数据"></up-empty>
@@ -156,6 +181,30 @@ const handleSwipeAction = (val: any, item: any) => {
156
     }
181
     }
157
 }
182
 }
158
 
183
 
184
+// Popover 相关
185
+const popoverIndex = ref<number | null>(null)
186
+const currentPopoverItem = ref<OrderListItem_Result | null>(null)
187
+
188
+const openPopover = (item: OrderListItem_Result, index: number) => {
189
+    if (popoverIndex.value === index) {
190
+        popoverIndex.value = null
191
+        currentPopoverItem.value = null
192
+    } else {
193
+        popoverIndex.value = index
194
+        currentPopoverItem.value = item
195
+    }
196
+}
197
+
198
+const closePopover = () => {
199
+    popoverIndex.value = null
200
+    currentPopoverItem.value = null
201
+}
202
+
203
+const handlePopoverAction = (val:object, item: OrderListItem_Result) => {
204
+    closePopover()
205
+    handleSwipeAction(val, item)
206
+}
207
+
159
 const loadMore = () => {
208
 const loadMore = () => {
160
     if (loading.value) return
209
     if (loading.value) return
161
 
210
 
@@ -231,7 +280,13 @@ onMounted(() => {
231
         box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
280
         box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
232
         border-radius: 20rpx;
281
         border-radius: 20rpx;
233
         margin: 20rpx 20rpx;
282
         margin: 20rpx 20rpx;
234
-
283
+        position: relative;
284
+        .more-btn {
285
+            display: flex;
286
+            align-items: center;
287
+            justify-content: flex-end;
288
+            border-radius: 50%;
289
+        }
235
         .order-header {
290
         .order-header {
236
             display: flex;
291
             display: flex;
237
             justify-content: space-between;
292
             justify-content: space-between;
@@ -255,11 +310,17 @@ onMounted(() => {
255
                 }
310
                 }
256
             }
311
             }
257
 
312
 
258
-            .order-status {
313
+            .order-right {
314
+                display: flex;
315
+                align-items: center;
316
+                gap: 16rpx;
317
+
259
                 .status-text {
318
                 .status-text {
260
                     font-size: 28rpx;
319
                     font-size: 28rpx;
261
                     font-weight: bold;
320
                     font-weight: bold;
262
                 }
321
                 }
322
+
323
+                
263
             }
324
             }
264
         }
325
         }
265
 
326
 
@@ -330,6 +391,61 @@ onMounted(() => {
330
         }
391
         }
331
     }
392
     }
332
 
393
 
394
+    /* Popover 浮层 */
395
+    .popover-mask {
396
+        position: fixed;
397
+        top: 0;
398
+        left: 0;
399
+        right: 0;
400
+        bottom: 0;
401
+        z-index: 998;
402
+    }
403
+
404
+    .popover-menu {
405
+        position: absolute;
406
+        top: 80rpx;
407
+        right: 10rpx;
408
+        z-index: 999;
409
+        background: #fff;
410
+        border-radius: 16rpx;
411
+        box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.16);
412
+        padding: 8rpx 0;
413
+        min-width: 180rpx;
414
+
415
+        .popover-item {
416
+            display: flex;
417
+            align-items: center;
418
+            gap: 16rpx;
419
+            padding: 20rpx 32rpx;
420
+            &:active {
421
+                background: #f5f7fa;
422
+            }
423
+
424
+            .popover-text {
425
+                font-size: 28rpx;
426
+                color: #333;
427
+            }
428
+        }
429
+
430
+        .popover-divider {
431
+            height: 1rpx;
432
+            background: #f0f0f0;
433
+            margin: 0 16rpx;
434
+        }
435
+
436
+        .popover-arrow {
437
+            position: absolute;
438
+            top: -16rpx;
439
+            right: 32rpx;
440
+            width: 0;
441
+            height: 0;
442
+            border-left: 16rpx solid transparent;
443
+            border-right: 16rpx solid transparent;
444
+            border-bottom: 16rpx solid #fff;
445
+            filter: drop-shadow(0 -2rpx 2rpx rgba(0, 0, 0, 0.08));
446
+        }
447
+    }
448
+
333
     /* 空状态 */
449
     /* 空状态 */
334
     .empty {
450
     .empty {
335
         padding: 80rpx 0;
451
         padding: 80rpx 0;

+ 19 - 17
src/pages/repair/components/price/index.vue

@@ -49,8 +49,8 @@
49
         <view class="cost-list">
49
         <view class="cost-list">
50
           <view v-for="(cost, index) in formData.costs" :key="index" class="cost-item">
50
           <view v-for="(cost, index) in formData.costs" :key="index" class="cost-item">
51
             <view class="cost-header">
51
             <view class="cost-header">
52
-              <up-select v-model="cost.type" @select="handleCostTypeSelect(cost, index)" :options="costTypeOptions"
53
-                :label="cost.type ? '' : '费用类型'">
52
+              <up-select v-model="cost.type" @select="(e:any) => handleCostTypeSelect(e,index)" :options="costTypeOptions"
53
+                :label="cost.type ? '' : '费用类型'" showOptionsLabel>
54
                 <template v-slot:text="{ value }">
54
                 <template v-slot:text="{ value }">
55
                   <text>{{ cost.type }}</text>
55
                   <text>{{ cost.type }}</text>
56
                 </template>
56
                 </template>
@@ -93,11 +93,9 @@
93
       <view class="form-section">
93
       <view class="form-section">
94
         <view class="section-title">其他信息</view>
94
         <view class="section-title">其他信息</view>
95
         <up-form-item label="预计完成日期" prop="estimatedCompletionDate" required :label-width="100" @click="showDatePicker">
95
         <up-form-item label="预计完成日期" prop="estimatedCompletionDate" required :label-width="100" @click="showDatePicker">
96
-          <template #right>
97
             <up-datetime-picker hasInput :show="datePickerShow" v-model="formData.estimatedCompletionDate" mode="date"
96
             <up-datetime-picker hasInput :show="datePickerShow" v-model="formData.estimatedCompletionDate" mode="date"
98
-              @cancel="datePickerShow = false" @confirm="datePickerShow = false"
97
+              @cancel="handleDateCancel" @confirm="handleDateConfirm($event)"
99
               placeholder="请选择预计完成日期"></up-datetime-picker>
98
               placeholder="请选择预计完成日期"></up-datetime-picker>
100
-          </template>
101
         </up-form-item>
99
         </up-form-item>
102
         <up-form-item label="质保期" prop="warrantyPeriod" :label-width="70">
100
         <up-form-item label="质保期" prop="warrantyPeriod" :label-width="70">
103
           <up-select v-model="formData.warrantyPeriod" :options="warrantyPeriodOptions" label="请选择质保期"
101
           <up-select v-model="formData.warrantyPeriod" :options="warrantyPeriodOptions" label="请选择质保期"
@@ -132,6 +130,8 @@ import {
132
   warrantyPeriodOptions
130
   warrantyPeriodOptions
133
 } from '../public'
131
 } from '../public'
134
 import type { RepairQuoteForm_Params } from '@/types/repair'
132
 import type { RepairQuoteForm_Params } from '@/types/repair'
133
+import dayjs from 'dayjs'
134
+
135
 const formRef = ref()
135
 const formRef = ref()
136
 const datePickerShow = ref(false)
136
 const datePickerShow = ref(false)
137
 // 表单数据
137
 // 表单数据
@@ -140,7 +140,7 @@ const formData = ref<RepairQuoteForm_Params>({
140
   repairItems: [],
140
   repairItems: [],
141
   laborHours: '2小时',
141
   laborHours: '2小时',
142
   costs: [
142
   costs: [
143
-    { type: 'PARTS', amount: 0, description: '' },
143
+    { type: '', amount: 0, description: '' },
144
   ],
144
   ],
145
   totalAmount: 0,
145
   totalAmount: 0,
146
   remarks: '',
146
   remarks: '',
@@ -201,28 +201,20 @@ const removeCost = (index: number) => {
201
 
201
 
202
 const handleSubmit = () => {
202
 const handleSubmit = () => {
203
   formRef.value.validate().then(() => {
203
   formRef.value.validate().then(() => {
204
-    // 更新表单数据
205
     formData.value.totalAmount = totalAmount.value
204
     formData.value.totalAmount = totalAmount.value
206
     formData.value.discountAmount = discountAmount.value
205
     formData.value.discountAmount = discountAmount.value
207
     formData.value.finalAmount = finalAmount.value
206
     formData.value.finalAmount = finalAmount.value
208
 
207
 
209
-    console.log('提交报价', formData.value)
210
     uni.showToast({
208
     uni.showToast({
211
       title: '报价提交成功',
209
       title: '报价提交成功',
212
       icon: 'success'
210
       icon: 'success'
213
     })
211
     })
214
 
212
 
215
-    // 跳转到订单详情页
216
     setTimeout(() => {
213
     setTimeout(() => {
217
       uni.navigateTo({
214
       uni.navigateTo({
218
         url: '/pages/repair/components/orderDetail/index?orderId=' + formData.value.orderId,
215
         url: '/pages/repair/components/orderDetail/index?orderId=' + formData.value.orderId,
219
       })
216
       })
220
     }, 1500)
217
     }, 1500)
221
-  }).catch(() => {
222
-    uni.showToast({
223
-      title: '请完善表单信息',
224
-      icon: 'none'
225
-    })
226
   })
218
   })
227
 }
219
 }
228
 
220
 
@@ -242,10 +234,10 @@ const handleLaborHoursSelect = (value: any) => {
242
   formData.value.laborHours = value.name
234
   formData.value.laborHours = value.name
243
 }
235
 }
244
 
236
 
245
-const handleCostTypeSelect = (value: any, index: number) => {
237
+const handleCostTypeSelect = (val:any,index:number) => {
246
   formData.value.costs.forEach((cost, i) => {
238
   formData.value.costs.forEach((cost, i) => {
247
-    if (index === i) {
248
-      cost.type = value.type
239
+    if (index == i) {
240
+      cost.type = val.name
249
     }
241
     }
250
   })
242
   })
251
 }
243
 }
@@ -258,6 +250,16 @@ const showDatePicker = () => {
258
   datePickerShow.value = true
250
   datePickerShow.value = true
259
 }
251
 }
260
 
252
 
253
+const handleDateCancel = () => {
254
+  datePickerShow.value = false
255
+}
256
+
257
+const handleDateConfirm = (value: any) => {
258
+  datePickerShow.value = false
259
+  formData.value.estimatedCompletionDate = dayjs(value.value).format('YYYY-MM-DD')
260
+  formRef.value?.validateField('estimatedCompletionDate')
261
+}
262
+
261
 
263
 
262
 
264
 
263
 // 生命周期
265
 // 生命周期

+ 3 - 18
src/pages/repair/index.vue

@@ -60,27 +60,12 @@ import { ref } from 'vue'
60
 import type { Repair_Result } from '@/types/repair'
60
 import type { Repair_Result } from '@/types/repair'
61
 import { gridList,processSteps} from './components/public'
61
 import { gridList,processSteps} from './components/public'
62
 import LineChart from './components/charts/line.vue'
62
 import LineChart from './components/charts/line.vue'
63
-import { hasRole } from '@/utils/utils'
64
 import { loginApi } from '@/apis/login'
63
 import { loginApi } from '@/apis/login'
65
 const searchKeyword = ref<string>('')
64
 const searchKeyword = ref<string>('')
66
 const handleGridClick = (item: Repair_Result) => {
65
 const handleGridClick = (item: Repair_Result) => {
67
-  if(item.label == 'apply'){
68
-    if(hasRole('REPAIRMAN',false)){
69
-      uni.showToast({
70
-        title: '您没有权限访问该功能',
71
-        icon: 'none'
72
-      })
73
-      return false
74
-    }else{
75
-      uni.navigateTo({
76
-        url: item.path,
77
-      })
78
-    }
79
-  }else if(item.label == 'order'){
80
-    uni.navigateTo({
81
-      url: item.path,
82
-    })
83
-  }
66
+  uni.navigateTo({
67
+    url: item.path,
68
+  })
84
 }
69
 }
85
 const handleLogout = () => {
70
 const handleLogout = () => {
86
   uni.removeStorageSync('userInfo')
71
   uni.removeStorageSync('userInfo')

+ 1 - 1
src/types/repair.d.ts

@@ -51,7 +51,7 @@ export interface RepairQuoteForm_Params {
51
   totalAmount: number
51
   totalAmount: number
52
   remarks: string
52
   remarks: string
53
   technician: string
53
   technician: string
54
-  estimatedCompletionDate: string
54
+  estimatedCompletionDate: string | Date
55
   warrantyPeriod: string
55
   warrantyPeriod: string
56
   urgency: 'normal' | 'urgent' | 'emergency'
56
   urgency: 'normal' | 'urgent' | 'emergency'
57
   discountRate: number
57
   discountRate: number

+ 2 - 3
src/utils/http.ts

@@ -13,7 +13,6 @@ interface RequestOptions {
13
   toastError?: boolean
13
   toastError?: boolean
14
 }
14
 }
15
 
15
 
16
-// 扩展 Headers 类型,添加 Authorization
17
 interface CustomHeaders {
16
 interface CustomHeaders {
18
   'Content-Type': string
17
   'Content-Type': string
19
   Authorization?: string
18
   Authorization?: string
@@ -35,7 +34,7 @@ class HttpRequest {
35
       'Content-Type': 'application/json'
34
       'Content-Type': 'application/json'
36
     }
35
     }
37
     if (token) {
36
     if (token) {
38
-      headers.Authorization = token
37
+      headers.Authorization = 'Bearer ' + token
39
     }
38
     }
40
     return headers
39
     return headers
41
   }
40
   }
@@ -328,4 +327,4 @@ const http = new HttpRequest({})
328
 export { http }
327
 export { http }
329
 export const createHttp = (config?: { timeout?: number }) => {
328
 export const createHttp = (config?: { timeout?: number }) => {
330
   return new HttpRequest(config || {})
329
   return new HttpRequest(config || {})
331
-}
330
+}