zhangxin 1 dzień temu
rodzic
commit
1ba9f689b9

+ 2 - 2
src/manifest.json

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

+ 7 - 5
src/pages.json

@@ -29,13 +29,15 @@
29 29
 		{
30 30
 			"path": "pages/repair/index",
31 31
 			"style": {
32
-				"navigationBarTitleText": "维修中心"
32
+				"navigationBarTitleText": "",
33
+				"enablePullDownRefresh": false,
34
+				"navigationStyle": "custom"
33 35
 			}
34 36
 		},
35 37
 		{
36 38
 			"path": "pages/repair/components/apply/index",
37 39
 			"style": {
38
-				"navigationBarTitleText": "申请维修"
40
+				"navigationBarTitleText": "手表维保"
39 41
 			}
40 42
 		},
41 43
 		{
@@ -79,10 +81,10 @@
79 81
 		"fontSize": "12px",
80 82
 		"list": [
81 83
 			{
82
-				"text": "维修中心",
84
+				"text": "手表服务",
83 85
 				"pagePath": "pages/repair/index",
84
-				"iconPath": "static/tabs/repair.png",
85
-				"selectedIconPath": "static/tabs/repair-active.png",
86
+				"iconPath": "static/tabs/watch.png",
87
+				"selectedIconPath": "static/tabs/watch-active.png",
86 88
 				"visible": true
87 89
 			}
88 90
 		]

+ 24 - 10
src/pages/login/index.vue

@@ -40,6 +40,8 @@ import { ref, onMounted } from 'vue'
40 40
 import { loginApi } from '@/apis/login'
41 41
 import type { Login_Params, userInfo_Result } from '@/types/login'
42 42
 import { computed } from 'vue'
43
+import type { login_Result } from '@/types/login'
44
+
43 45
 const remember = computed(() => {
44 46
 	return checkboxs.value.length > 0
45 47
 })
@@ -113,6 +115,10 @@ const changeRemember = (value: string[]) => {
113 115
 }
114 116
 const loginFormRef = ref<any>(null)
115 117
 const loading = ref<boolean>(false)
118
+const loginResult = ref<login_Result>({
119
+	access_token: '',
120
+	expires_in: 0
121
+})
116 122
 const handleLogin = () => {
117 123
 	loginFormRef.value.validate().then(async () => {
118 124
 		loading.value = true
@@ -123,17 +129,25 @@ const handleLogin = () => {
123 129
 			uni.removeStorageSync('username');
124 130
 			uni.removeStorageSync('password');
125 131
 		}
126
-		const res = await loginApi.login(form.value)
127
-		uni.setStorageSync('token', res.data.access_token)
128
-		const infoRes = await loginApi.getInfo()
129
-		uni.setStorageSync('userInfo', infoRes.user)
132
+		loginApi.login(form.value).then(async(res: any) => {
133
+			loginResult.value = res.data
134
+			uni.setStorageSync('token', loginResult.value.access_token)
135
+			const infoRes = await loginApi.getInfo()
136
+			uni.setStorageSync('userInfo', infoRes.user)
137
+			uni.showToast({ title: '登录成功', icon: 'success' })
138
+			loading.value = false
139
+			setTimeout(() => {
140
+				uni.reLaunch({ url: '/pages/repair/index' })
141
+			}, 50)
142
+		}).catch((err: any) => {
143
+			loading.value = false
144
+			uni.$u.toast(err[0].message);
145
+			return
146
+		})
147
+		
148
+	}).catch((err: any) => {
130 149
 		loading.value = false
131
-		uni.showToast({ title: '登录成功', icon: 'success' })
132
-		setTimeout(() => {
133
-			uni.reLaunch({ url: '/pages/repair/index' })
134
-		}, 50)
135
-	}).catch((res: any) => {
136
-		uni.$u.toast(res[0].message);
150
+		uni.$u.toast(err[0].message);
137 151
 	})
138 152
 }
139 153
 

+ 346 - 413
src/pages/repair/components/apply/index.vue

@@ -1,65 +1,87 @@
1 1
 <template>
2 2
   <view class="repair-apply">
3
-    <!-- 步骤指示器 -->
4
-    <view class="steps-indicator">
5
-      <view v-for="(step, index) in steps" :key="index" class="step-item"
6
-        :class="{ active: currentStep === index + 1, completed: currentStep > index + 1 }">
7
-        <view class="step-number">{{ index + 1 }}</view>
8
-        <text class="step-text">{{ step }}</text>
3
+    <up-form :model="formData" :rules="rules" ref="formRef">
4
+      <!-- 业务类型选择:我要维修 / 我要养护 -->
5
+      <view class="form-section">
6
+        <view class="section-title">申请类型</view>
7
+        <up-form-item label="业务类型" prop="orderType" required :label-width="80">
8
+          <up-select showOptionsLabel v-model="formData.orderType" :options="orderTypeOptions"
9
+            :label="formData.orderType ? '' : '请选择业务类型'" @select="selectOrderType">
10
+            <template #text>
11
+              <text>{{ formData.orderType }}</text>
12
+            </template>
13
+          </up-select>
14
+        </up-form-item>
9 15
       </view>
10
-    </view>
11 16
 
12
-    <!-- 步骤内容 -->
13
-    <view class="step-content">
14
-      <!-- 步骤1:选择服务 -->
15
-      <view v-if="currentStep === 1" class="step-1">
16
-        <up-form :model="formData" :rules="rules" ref="formRef">
17
-          <view class="service-cards">
18
-            <view v-for="service in services" :key="service.value" class="service-card"
19
-              :class="{ active: formData.serviceType === service.value }" @click="formData.serviceType = service.value">
20
-              <view class="service-icon">{{ service.icon }}</view>
21
-              <text class="service-name">{{ service.name }}</text>
22
-              <text class="service-desc">{{ service.desc }}</text>
17
+      <!-- ===================== 我要维修 ===================== -->
18
+      <template v-if="formData.orderTypeId === 'repair'">
19
+        <!-- 服务方式 -->
20
+        <view class="form-section">
21
+          <view class="section-title">选择服务</view>
22
+          <up-form-item label="服务方式" prop="serviceType" required :label-width="80">
23
+            <up-select showOptionsLabel v-model="formData.serviceType" :options="serviceOptions"
24
+              :label="formData.serviceType ? '' : '请选择服务方式'" @select="selectServiceItem">
25
+              <template #text>
26
+                <text>{{ formData.serviceType }}</text>
27
+              </template>
28
+            </up-select>
29
+          </up-form-item>
30
+        </view>
31
+        <!-- 故障描述 -->
32
+        <view class="form-section">
33
+          <view class="section-title">故障描述</view>
34
+          <up-form-item label="故障现象" labelPosition="top" :label-width="80">
35
+            <up-checkbox-group v-model="formData.faultTypes">
36
+              <up-checkbox v-for="(fault, index) in faultTypeOptions" :key="index" :label="fault.name"
37
+                :name="fault.name">{{
38
+                fault.name }}</up-checkbox>
39
+            </up-checkbox-group>
40
+          </up-form-item>
41
+          <up-form-item label="详细描述" labelPosition="top" :label-width="80">
42
+            <up-textarea v-model="formData.faultDescription" placeholder="请详细描述故障情况" rows="4" />
43
+          </up-form-item>
44
+          <up-form-item label="故障特写图" labelPosition="top" :label-width="90">
45
+            <view class="upload-images">
46
+              <view v-for="(image, index) in formData.faultImages" :key="index" class="uploaded-image">
47
+                <image :src="image" mode="aspectFill"></image>
48
+                <view class="image-remove" @click="removeFaultImage(index)">×</view>
49
+              </view>
50
+              <view class="upload-btn" @click="uploadFaultImage">
51
+                <u-icon name="camera" size="32"></u-icon>
52
+                <text>上传图片</text>
53
+              </view>
23 54
             </view>
24
-          </view>
25
-        </up-form>
26
-      </view>
27
-
28
-      <!-- 步骤2:手表信息 -->
29
-      <view v-if="currentStep === 2" class="step-2">
30
-        <up-form :model="formData" :rules="rules" ref="formRef">
31
-          <up-form-item prop="brand" label="品牌" required>
55
+          </up-form-item>
56
+        </view>
57
+        <!-- 手表信息 -->
58
+        <view class="form-section">
59
+          <view class="section-title">手表信息</view>
60
+          <up-form-item label="品牌" :label-width="80">
32 61
             <up-select v-model="formData.brand" :options="brands" label="请选择品牌" @select="selectBrandItem">
33
-              <template #text="{ label }">
62
+              <template #text>
34 63
                 <text>{{ formData.brand }}</text>
35 64
               </template>
36 65
             </up-select>
37 66
           </up-form-item>
38
-
39
-          <up-form-item prop="model" label="型号" required>
67
+          <up-form-item label="型号" :label-width="80">
40 68
             <up-input v-model="formData.model" placeholder="请输入手表型号" />
41 69
           </up-form-item>
42
-
43
-          <up-form-item prop="movementType" label="机芯类型" required labelPosition="top" :label-width="70">
70
+          <up-form-item label="机芯类型" labelPosition="top" :label-width="80">
44 71
             <up-radio-group v-model="formData.movementType" size="default">
45
-              <up-radio v-for="type in movementTypes" :key="type" :label="type" :name="type">
46
-                {{ type }}
47
-              </up-radio>
72
+              <up-radio v-for="type in movementTypes" :key="type" :label="type" :name="type">{{ type }}</up-radio>
48 73
             </up-radio-group>
49 74
           </up-form-item>
50
-
51
-          <up-form-item prop="watchNumber" label="表身编号" required labelPosition="top" :label-width="70">
75
+          <up-form-item label="表身编号" labelPosition="top" :label-width="80">
52 76
             <up-input v-model="formData.watchNumber" placeholder="请输入表身编号" />
53 77
           </up-form-item>
54
-
55
-          <up-form-item prop="inWarranty" label="是否在保" required labelPosition="top" :label-width="70">
78
+          <up-form-item label="是否在保" labelPosition="top" :label-width="80">
56 79
             <up-radio-group v-model="formData.inWarranty" size="default">
57 80
               <up-radio label="是" name="true"></up-radio>
58 81
               <up-radio label="否" name="false"></up-radio>
59 82
             </up-radio-group>
60 83
           </up-form-item>
61
-
62
-          <up-form-item prop="images" label="上传图片" labelPosition="top" :label-width="70">
84
+          <up-form-item label="上传图片" labelPosition="top" :label-width="80">
63 85
             <view class="upload-images">
64 86
               <view v-for="(image, index) in formData.images" :key="index" class="uploaded-image">
65 87
                 <image :src="image" mode="aspectFill"></image>
@@ -71,71 +93,139 @@
71 93
               </view>
72 94
             </view>
73 95
           </up-form-item>
74
-        </up-form>
75
-      </view>
76
-
77
-      <!-- 步骤3:故障描述 -->
78
-      <view v-if="currentStep === 3" class="step-3">
79
-        <up-form :model="formData" :rules="rules" ref="formRef" :label-width="70" labelPosition="top">
80
-          <up-form-item prop="faultTypes" label="故障现象" required>
81
-            <up-checkbox-group v-model="formData.faultTypes">
82
-              <up-checkbox v-for="(fault, index) in faultTypeOptions" :key="index" :label="fault.name"
83
-                :name="fault.name">{{
84
-                  fault.name }}</up-checkbox>
96
+        </view>
97
+      </template>
98
+
99
+      <!-- ===================== 我要养护 ===================== -->
100
+      <template v-if="formData.orderTypeId === 'maintenance'">
101
+        <!-- 养护项目 -->
102
+        <view class="form-section">
103
+          <view class="section-title">养护项目</view>
104
+          <up-form-item label="养护类型" labelPosition="top" :label-width="80">
105
+            <up-checkbox-group v-model="formData.maintenanceTypes">
106
+              <up-checkbox v-for="item in maintenanceTypeOptions" :key="item.value" :label="item.label"
107
+                :name="item.value">{{ item.label }}</up-checkbox>
85 108
             </up-checkbox-group>
86 109
           </up-form-item>
87
-
88
-          <up-form-item prop="faultDescription" label="详细描述" required labelPosition="top">
89
-            <up-textarea v-model="formData.faultDescription" placeholder="请详细描述故障情况" rows="4" />
110
+          <up-form-item label="上次养护时间" :label-width="100" @click="lastMaintenanceDateShow = true">
111
+            <up-datetime-picker hasInput :show="lastMaintenanceDateShow" v-model="formData.lastMaintenanceDate"
112
+              mode="date" @cancel="lastMaintenanceDateShow = false" @confirm="(v: any) => datetimeConfirm(v)"
113
+              placeholder="请选择上次养护时间"></up-datetime-picker>
114
+          </up-form-item>
115
+          <up-form-item label="特殊要求" labelPosition="top" :label-width="80">
116
+            <up-textarea v-model="formData.maintenanceRemark" placeholder="如有特殊养护要求,请在此说明" rows="3" />
90 117
           </up-form-item>
118
+        </view>
91 119
 
92
-          <up-form-item prop="faultImages" label="上传故障特写图" labelPosition="top" :label-width="120">
120
+        <!-- 手表信息(养护) -->
121
+        <view class="form-section">
122
+          <view class="section-title">手表信息</view>
123
+          <up-form-item label="品牌" :label-width="80">
124
+            <up-select v-model="formData.brand" :options="brands" label="请选择品牌" @select="selectBrandItem">
125
+              <template #text>
126
+                <text>{{ formData.brand }}</text>
127
+              </template>
128
+            </up-select>
129
+          </up-form-item>
130
+          <up-form-item label="型号" :label-width="80">
131
+            <up-input v-model="formData.model" placeholder="请输入手表型号" />
132
+          </up-form-item>
133
+          <up-form-item label="表身编号" :label-width="80">
134
+            <up-input v-model="formData.watchNumber" placeholder="请输入表身编号" />
135
+          </up-form-item>
136
+          <up-form-item label="上传手表图片" labelPosition="top" :label-width="100">
93 137
             <view class="upload-images">
94
-              <view v-for="(image, index) in formData.faultImages" :key="index" class="uploaded-image">
138
+              <view v-for="(image, index) in formData.images" :key="index" class="uploaded-image">
95 139
                 <image :src="image" mode="aspectFill"></image>
96
-                <view class="image-remove" @click="removeFaultImage(index)">×</view>
140
+                <view class="image-remove" @click="removeImage(index)">×</view>
97 141
               </view>
98
-              <view class="upload-btn" @click="uploadFaultImage">
142
+              <view v-if="formData.images.length < 5" class="upload-btn" @click="uploadImage">
99 143
                 <u-icon name="camera" size="32"></u-icon>
100 144
                 <text>上传图片</text>
101 145
               </view>
102 146
             </view>
103 147
           </up-form-item>
104
-        </up-form>
105
-      </view>
106
-
107
-      <!-- 步骤4:提交订单 -->
108
-      <view v-if="currentStep === 4" class="step-4">
109
-        <up-form :model="formData" :rules="rules" ref="formRef">
110
-          <up-form-item prop="contactName" label="姓名" required>
111
-            <up-input v-model="formData.contactName" placeholder="请输入您的姓名" />
148
+        </view>
149
+      </template>
150
+
151
+      <!-- 联系方式(维修和养护共用) -->
152
+      <view class="form-section">
153
+        <view class="section-title">联系方式</view>
154
+        <up-form-item label="姓名" prop="contactName" required :label-width="80">
155
+          <up-input v-model="formData.contactName" placeholder="请输入您的姓名" />
156
+        </up-form-item>
157
+        <up-form-item label="电话" prop="contactPhone" required :label-width="80">
158
+          <up-input v-model="formData.contactPhone" placeholder="请输入您的电话" />
159
+        </up-form-item>
160
+
161
+        <!-- 维修:地址/门店/上门时间/快递单号 -->
162
+        <template v-if="formData.orderTypeId === 'repair'">
163
+          <up-form-item v-if="
164
+            formData.serviceId === 'home' || formData.serviceId === 'mail'
165
+          " prop="address" label="地址" required :label-width="80">
166
+            <up-textarea v-model="formData.address" placeholder="请输入您的地址"></up-textarea>
112 167
           </up-form-item>
113
-
114
-          <up-form-item prop="contactPhone" label="电话" required>
115
-            <up-input v-model="formData.contactPhone" placeholder="请输入您的电话" />
168
+          <up-form-item v-if="formData.serviceId === 'store'" prop="storeName" label="选择门店" required :label-width="80">
169
+            <up-select v-model="formData.storeName" :options="stores" label="请选择门店" @select="selectStoreItem">
170
+              <template #text>
171
+                <text>{{ formData.storeName }}</text>
172
+              </template>
173
+            </up-select>
116 174
           </up-form-item>
117
-
118
-          <up-form-item v-if="formData.serviceType === 'home' || formData.serviceType === 'mail'" prop="address"
119
-            label="地址" required>
175
+          <up-form-item v-if="formData.serviceId === 'home'" prop="delivery" label="上门时间" required :label-width="80"
176
+            labelPosition="top">
177
+            <up-cate-tab height="300px" :tab-list="deliveryOptions" v-model:current="deliveryCurrent">
178
+              <template v-slot:itemList="{ item }">
179
+                <view class="delivery-time-container">
180
+                  <view class="item-title"><text>{{ item.name }}</text></view>
181
+                  <view class="item-container">
182
+                    <up-choose v-model="item.selectedIndex" :options="item.times" item-width="300rpx"
183
+                      item-height="40rpx" @update:modelValue="(idx: any) => onDeliveryTimeChange(item, idx)">
184
+                    </up-choose>
185
+                  </view>
186
+                </view>
187
+              </template>
188
+            </up-cate-tab>
189
+          </up-form-item>
190
+          <up-form-item v-if="formData.serviceId === 'mail'" prop="expressNumber" label="快递单号" required
191
+            :label-width="80">
192
+            <view class="express">
193
+              <up-input v-model="formData.expressNumber" placeholder="请输入快递单号" />
194
+              <text class="express-hint">建议使用顺丰快递,确保手表安全</text>
195
+            </view>
196
+          </up-form-item>
197
+        </template>
198
+
199
+        <!-- 养护:服务方式 + 对应字段 -->
200
+        <template v-if="formData.orderTypeId === 'maintenance'">
201
+          <up-form-item label="服务方式" prop="maintenanceServiceType" required :label-width="80">
202
+            <up-select showOptionsLabel v-model="formData.maintenanceServiceType" :options="serviceOptions"
203
+              :label="formData.maintenanceServiceType ? '' : '请选择服务方式'" @select="selectMaintenanceServiceItem">
204
+              <template #text>
205
+                <text>{{ formData.maintenanceServiceType }}</text>
206
+              </template>
207
+            </up-select>
208
+          </up-form-item>
209
+          <up-form-item v-if="
210
+            formData.maintenanceServiceId === 'home' ||
211
+            formData.maintenanceServiceId === 'mail'
212
+          " prop="address" label="地址" required :label-width="80">
120 213
             <up-textarea v-model="formData.address" placeholder="请输入您的地址"></up-textarea>
121 214
           </up-form-item>
122
-
123
-          <up-form-item v-if="formData.serviceType === 'store'" prop="storeName" label="选择门店" required label-width="70">
215
+          <up-form-item v-if="formData.maintenanceServiceId === 'store'" prop="storeName" label="选择门店" required
216
+            :label-width="80">
124 217
             <up-select v-model="formData.storeName" :options="stores" label="请选择门店" @select="selectStoreItem">
125
-              <template #text="{ label }">
218
+              <template #text>
126 219
                 <text>{{ formData.storeName }}</text>
127 220
               </template>
128 221
             </up-select>
129 222
           </up-form-item>
130
-
131
-          <up-form-item v-if="formData.serviceType === 'home'" prop="delivery" label="上门时间" required :label-width="70"
132
-            @click="datetimerShow = true" labelPosition="top">
223
+          <up-form-item v-if="formData.maintenanceServiceId === 'home'" prop="delivery" label="上门时间" required
224
+            :label-width="80" labelPosition="top">
133 225
             <up-cate-tab height="300px" :tab-list="deliveryOptions" v-model:current="deliveryCurrent">
134 226
               <template v-slot:itemList="{ item }">
135 227
                 <view class="delivery-time-container">
136
-                  <view class="item-title">
137
-                    <text>{{ item.name }}</text>
138
-                  </view>
228
+                  <view class="item-title"><text>{{ item.name }}</text></view>
139 229
                   <view class="item-container">
140 230
                     <up-choose v-model="item.selectedIndex" :options="item.times" item-width="300rpx"
141 231
                       item-height="40rpx" @update:modelValue="(idx: any) => onDeliveryTimeChange(item, idx)">
@@ -145,27 +235,20 @@
145 235
               </template>
146 236
             </up-cate-tab>
147 237
           </up-form-item>
148
-
149
-          <up-form-item v-if="formData.serviceType === 'mail'" prop="expressNumber" label="快递单号" required
150
-            :label-width="70">
238
+          <up-form-item v-if="formData.maintenanceServiceId === 'mail'" prop="expressNumber" label="快递单号" required
239
+            :label-width="80">
151 240
             <view class="express">
152 241
               <up-input v-model="formData.expressNumber" placeholder="请输入快递单号" />
153 242
               <text class="express-hint">建议使用顺丰快递,确保手表安全</text>
154 243
             </view>
155 244
           </up-form-item>
156
-        </up-form>
245
+        </template>
157 246
       </view>
158
-    </view>
247
+    </up-form>
159 248
 
160
-    <!-- 底部按钮 -->
249
+    <!-- 底部提交按钮 -->
161 250
     <view class="bottom-buttons">
162
-      <u-button v-if="currentStep > 1" type="default" size="large" @click="prevStep">
163
-        上一步
164
-      </u-button>
165
-      <u-button v-if="currentStep < 4" type="primary" size="large" :disabled="!canNext" @click="nextStep">
166
-        下一步
167
-      </u-button>
168
-      <u-button v-if="currentStep === 4" type="primary" size="large" :disabled="!canSubmit" @click="submitOrder">
251
+      <u-button type="primary" size="large" @click="submitOrder">
169 252
         提交订单
170 253
       </u-button>
171 254
     </view>
@@ -173,71 +256,66 @@
173 256
 </template>
174 257
 
175 258
 <script setup lang="ts">
176
-import { ref, computed, reactive } from 'vue'
177
-import type { repair_Params } from '@/types/repair'
178
-import { deliveryOptions, steps, services, movementTypes, brands, faultTypeOptions, stores } from '../public'
179
-const currentStep = ref<number>(1)
180
-const datetimerShow = ref<boolean>(false)
181
-const deliveryCurrent = ref<number>(0)
259
+import { ref, computed, reactive } from "vue";
260
+import type { repair_Params } from "@/types/repair";
261
+import {
262
+  deliveryOptions,
263
+  services,
264
+  movementTypes,
265
+  brands,
266
+  faultTypeOptions,
267
+  stores,
268
+  orderTypeOptions,
269
+  maintenanceTypeOptions,
270
+} from "../public";
271
+
272
+const deliveryCurrent = ref<number>(0);
273
+const lastMaintenanceDateShow = ref<boolean>(false);
274
+
275
+// 维修服务方式选项
276
+const serviceOptions = services.map((s) => ({ name: s.name, id: s.value }));
182 277
 
183 278
 // 表单数据
184 279
 const formData = reactive<repair_Params>({
185
-  // 步骤1
186
-  serviceType: '',
187
-  // 步骤2
188
-  brand: '',
189
-  model: '',
190
-  movementType: '',
191
-  watchNumber: '',
280
+  orderType: "维修", // 默认:我要维修
281
+  orderTypeId: "repair",
282
+  serviceType: "邮寄",
283
+  serviceId: "mail",
284
+  brand: "",
285
+  model: "",
286
+  movementType: "",
287
+  watchNumber: "",
192 288
   inWarranty: false,
193 289
   images: [],
194
-  // 步骤3
195 290
   faultTypes: [],
196
-  faultDescription: '',
291
+  faultDescription: "",
197 292
   faultImages: [],
198
-  // 步骤4
199
-  contactName: '',
200
-  contactPhone: '',
201
-  address: '',
202
-  delivery: '',
203
-  storeName: '',
204
-  expressNumber: '',
205
-})
293
+  maintenanceTypes: [],
294
+  lastMaintenanceDate: "",
295
+  maintenanceRemark: "",
296
+  maintenanceServiceType: "邮寄", // 默认邮寄
297
+  maintenanceServiceId: "mail",
298
+  contactName: "",
299
+  contactPhone: "",
300
+  address: "",
301
+  delivery: "",
302
+  storeName: "",
303
+  expressNumber: "",
304
+});
305
+const datetimeConfirm = (v: any) => {
306
+  formData.lastMaintenanceDate = v.value;
307
+  lastMaintenanceDateShow.value = false;
308
+};
309
+
206 310
 // 表单验证规则
207 311
 const rules = {
208
-  // 步骤1
312
+  orderType: [{ required: true, message: "请选择业务类型", trigger: "change" }],
209 313
   serviceType: [
210
-    { required: true, message: '请选择服务类型', trigger: 'change' }
211
-  ],
212
-  // 步骤2
213
-  brand: [
214
-    { required: true, message: '请选择品牌', trigger: 'change' }
215
-  ],
216
-  model: [
217
-    { required: true, message: '请输入型号', trigger: 'blur' }
218
-  ],
219
-  movementType: [
220
-    { required: true, message: '请选择机芯类型', trigger: 'change' }
221
-  ],
222
-  watchNumber: [
223
-    { required: true, message: '请输入表身编号', trigger: 'blur' }
224
-  ],
225
-  inWarranty: [
226
-    { required: true, message: '请选择是否在保', trigger: 'change' }
227
-  ],
228
-  // 步骤3
229
-  faultTypes: [
230
-    { required: true, message: '请选择故障现象', trigger: 'change', type: 'array' }
231
-  ],
232
-  faultDescription: [
233
-    { required: true, message: '请输入详细描述', trigger: 'blur' }
234
-  ],
235
-  // 步骤4
236
-  contactName: [
237
-    { required: true, message: '请输入姓名', trigger: 'blur' }
314
+    { required: true, message: "请选择服务方式", trigger: "change" },
238 315
   ],
316
+  contactName: [{ required: true, message: "请输入姓名", trigger: "blur" }],
239 317
   contactPhone: [
240
-    { required: true, message: '请输入电话', trigger: 'blur' },
318
+    { required: true, message: "请输入电话", trigger: "blur" },
241 319
     {
242 320
       validator: (rule: any, value: string, callback: Function) => {
243 321
         if (!value) return callback();
@@ -245,322 +323,179 @@ const rules = {
245 323
         if (reg.test(value)) {
246 324
           callback();
247 325
         } else {
248
-          callback(new Error('请输入正确的手机号'));
326
+          callback(new Error("请输入正确的手机号"));
249 327
         }
250 328
       },
251
-      message: '请输入正确的手机号',
252
-      trigger: 'blur'
253
-    }
254
-  ],
255
-  address: [
256
-    { required: true, message: '请输入地址', trigger: 'blur' }
257
-  ],
258
-  delivery: [
259
-    { required: true, message: '请选择上门时间', trigger: 'change' }
260
-  ],
261
-  storeName: [
262
-    { required: true, message: '请选择门店', trigger: 'change' }
329
+      message: "请输入正确的手机号",
330
+      trigger: "blur",
331
+    },
263 332
   ],
333
+  address: [{ required: true, message: "请输入地址", trigger: "blur" }],
334
+  delivery: [{ required: true, message: "请选择上门时间", trigger: "change" }],
335
+  storeName: [{ required: true, message: "请选择门店", trigger: "change" }],
264 336
   expressNumber: [
265
-    { required: true, message: '请输入快递单号', trigger: 'blur' }
266
-  ]
267
-}
337
+    { required: true, message: "请输入快递单号", trigger: "blur" },
338
+  ],
339
+};
268 340
 
269 341
 // 表单引用
270
-const formRef = ref()
271
-
272
-// 计算属性
273
-const canNext = computed(() => {
274
-  switch (currentStep.value) {
275
-    case 1: return !!formData.serviceType
276
-    case 2: return formData.brand && formData.model && formData.movementType && formData.watchNumber
277
-    case 3: return formData.faultTypes.length > 0 && formData.faultDescription
278
-    default: return false
279
-  }
280
-})
281
-
282
-const canSubmit = computed(() => {
283
-  if (formData.serviceType === 'store') {
284
-    return formData.contactName && formData.contactPhone && formData.storeName
285
-  } else if (formData.serviceType === 'home') {
286
-    return formData.contactName && formData.contactPhone && formData.address && formData.delivery
287
-  } else if (formData.serviceType === 'mail') {
288
-    return formData.contactName && formData.contactPhone && formData.address && formData.expressNumber
289
-  }
290
-  return false
291
-})
342
+const formRef = ref();
343
+
344
+// 选择业务类型
345
+const selectOrderType = (item: any) => {
346
+  formData.orderType = item.name;
347
+  formData.orderTypeId = item.id;
348
+};
349
+
350
+// 选择服务方式(维修)
351
+const selectServiceItem = (item: any) => {
352
+  formData.serviceType = item.name;
353
+  formData.serviceId = item.id;
354
+};
355
+
356
+// 选择服务方式(养护)
357
+const selectMaintenanceServiceItem = (item: any) => {
358
+  formData.maintenanceServiceType = item.name;
359
+  formData.maintenanceServiceId = item.id;
360
+};
292 361
 
293 362
 // 选择品牌
294 363
 const selectBrandItem = (item: any) => {
295
-  formData.brand = item.id
296
-}
364
+  formData.brand = item.id;
365
+};
297 366
 
298 367
 // 选择门店
299 368
 const selectStoreItem = (item: any) => {
300
-  formData.storeName = item.name
301
-}
369
+  formData.storeName = item.name;
370
+};
302 371
 
303
-// 下一步
304
-const nextStep = async () => {
305
-  if (canNext.value) {
306
-    // 验证当前步骤的表单
307
-    const isValid = await formRef.value?.validate()
308
-    if (isValid) {
309
-      currentStep.value++
310
-    }
311
-  }
312
-}
313
-// 上一步
314
-const prevStep = () => {
315
-  if (currentStep.value > 1) {
316
-    currentStep.value--
317
-  }
318
-}
319 372
 // 选择上门时间
320 373
 const onDeliveryTimeChange = (item: any, index: number) => {
321
-  // 清除其他分组的选中状态
322
-  deliveryOptions.forEach(opt => {
323
-    if (opt !== item) opt.selectedIndex = -1
324
-  })
325
-  item.selectedIndex = index
326
-  const selectedTime = item.times[index]
374
+  deliveryOptions.forEach((opt) => {
375
+    if (opt !== item) opt.selectedIndex = -1;
376
+  });
377
+  item.selectedIndex = index;
378
+  const selectedTime = item.times[index];
327 379
   if (selectedTime) {
328
-    formData.delivery = selectedTime.title
380
+    formData.delivery = selectedTime.title;
329 381
   }
330
-}
331
-// 模拟上传图片
382
+};
383
+
384
+// 上传图片
332 385
 const uploadImage = () => {
333
-  formData.images.push('')
334
-}
335
-// 删除图片
386
+  formData.images.push("");
387
+};
336 388
 const removeImage = (index: number) => {
337
-  formData.images.splice(index, 1)
338
-}
339
-
340
-// 模拟上传图片
389
+  formData.images.splice(index, 1);
390
+};
341 391
 const uploadFaultImage = () => {
342
-  formData.faultImages.push('')
343
-}
344
-// 删除故障图片
392
+  formData.faultImages.push("");
393
+};
345 394
 const removeFaultImage = (index: number) => {
346
-  formData.faultImages.splice(index, 1)
347
-}
395
+  formData.faultImages.splice(index, 1);
396
+};
397
+
348 398
 // 提交订单
349 399
 const submitOrder = async () => {
350
-  console.log(formData);
351
-  if (canSubmit.value) {
352
-    const isValid = await formRef.value?.validate()
353
-    if (isValid) {
354
-      console.log(formData);
355
-
356
-      const orderId = 1
357
-      uni.navigateTo({
358
-        url: '/pages/repair/components/progress/index?orderId=' + orderId
359
-      })
360
-    }
400
+  const isValid = await formRef.value?.validate();
401
+  if (isValid) {
402
+    console.log(formData);
403
+    const orderId = 1;
404
+    uni.navigateTo({
405
+      url: "/pages/repair/components/progress/index?orderId=" + orderId,
406
+    });
361 407
   }
362
-}
408
+};
363 409
 </script>
364 410
 
365 411
 <style lang="scss" scoped>
366 412
 .repair-apply {
367 413
   min-height: 100vh;
368 414
   background: #f5f7fa;
369
-  padding-bottom: 100rpx;
415
+  padding-bottom: 120rpx;
370 416
 
371
-  /* 步骤指示器 */
372
-  .steps-indicator {
373
-    display: flex;
374
-    justify-content: space-around;
375
-    align-items: center;
376
-    padding: 32rpx 0;
377
-    background: #fff;
378
-    margin-bottom: 20rpx;
379
-    position: relative;
380
-
381
-    /* 横线 */
382
-    &::before {
383
-      content: '';
384
-      position: absolute;
385
-      top: 56rpx;
386
-      left: 10%;
387
-      right: 10%;
388
-      height: 4rpx;
389
-      background: #e8e8e8;
390
-      z-index: 1;
391
-    }
392
-
393
-    .step-item {
394
-      display: flex;
395
-      flex-direction: column;
396
-      align-items: center;
397
-      position: relative;
398
-      z-index: 2;
399
-      flex: 1;
400
-
401
-      .step-number {
402
-        width: 48rpx;
403
-        height: 48rpx;
404
-        border-radius: 50%;
405
-        background: #e8e8e8;
406
-        display: flex;
407
-        align-items: center;
408
-        justify-content: center;
409
-        font-weight: bold;
410
-        margin-bottom: 8rpx;
411
-        transition: all 0.3s;
412
-      }
413
-
414
-      .step-text {
415
-        font-size: 24rpx;
416
-        color: #999;
417
-        transition: all 0.3s;
418
-      }
419
-
420
-      &.active {
421
-        .step-number {
422
-          background: #2c94f6;
423
-          color: #fff;
424
-        }
425
-
426
-        .step-text {
427
-          color: #2c94f6;
428
-          font-weight: bold;
429
-        }
430
-      }
431
-
432
-      &.completed {
433
-        .step-number {
434
-          background: #52c41a;
435
-          color: #fff;
436
-        }
437
-
438
-        .step-text {
439
-          color: #52c41a;
440
-        }
441
-      }
442
-    }
443
-  }
444
-
445
-  /* 步骤内容 */
446
-  .step-content {
417
+  /* 表单分区 */
418
+  .form-section {
447 419
     background: #fff;
420
+    margin: 20rpx 0;
448 421
     padding: 32rpx;
449 422
 
450
-    /* 服务卡片 */
451
-    .service-cards {
452
-      display: flex;
453
-      flex-direction: column;
454
-      gap: 24rpx;
455
-
456
-      .service-card {
457
-        border: 2rpx solid #e8e8e8;
458
-        border-radius: 16rpx;
459
-        padding: 32rpx;
460
-        display: flex;
461
-        flex-direction: column;
462
-        align-items: center;
463
-        transition: all 0.3s;
464
-
465
-        &:active {
466
-          transform: scale(0.98);
467
-        }
468
-
469
-        &.active {
470
-          border-color: #2c94f6;
471
-          background: #eaf5ff;
472
-        }
473
-
474
-        .service-icon {
475
-          font-size: 64rpx;
476
-          margin-bottom: 16rpx;
477
-        }
478
-
479
-        .service-name {
480
-          font-size: 32rpx;
481
-          font-weight: bold;
482
-          margin-bottom: 8rpx;
483
-        }
484
-
485
-        .service-desc {
486
-          font-size: 24rpx;
487
-          color: #666;
488
-        }
489
-      }
423
+    .section-title {
424
+      font-size: 30rpx;
425
+      font-weight: bold;
426
+      color: #333;
427
+      margin-bottom: 24rpx;
428
+      padding-left: 16rpx;
429
+      border-left: 6rpx solid #2c94f6;
490 430
     }
431
+  }
491 432
 
492
-    /* 上传图片 */
493
-    .upload-images {
494
-      display: flex;
495
-      gap: 24rpx;
496
-      flex-wrap: wrap;
497
-      margin-top: 16rpx;
498
-
499
-      .uploaded-image {
500
-        position: relative;
501
-        width: 160rpx;
502
-        height: 160rpx;
503
-        border-radius: 12rpx;
504
-        overflow: hidden;
505
-
506
-        image {
507
-          width: 100%;
508
-          height: 100%;
509
-        }
433
+  /* 上传图片 */
434
+  .upload-images {
435
+    display: flex;
436
+    gap: 24rpx;
437
+    flex-wrap: wrap;
438
+    margin-top: 16rpx;
510 439
 
511
-        .image-remove {
512
-          position: absolute;
513
-          top: 8rpx;
514
-          right: 8rpx;
515
-          width: 32rpx;
516
-          height: 32rpx;
517
-          background: rgba(0, 0, 0, 0.6);
518
-          color: #fff;
519
-          border-radius: 50%;
520
-          display: flex;
521
-          align-items: center;
522
-          justify-content: center;
523
-          font-size: 24rpx;
524
-        }
440
+    .uploaded-image {
441
+      position: relative;
442
+      width: 160rpx;
443
+      height: 160rpx;
444
+      border-radius: 12rpx;
445
+      overflow: hidden;
446
+
447
+      image {
448
+        width: 100%;
449
+        height: 100%;
525 450
       }
526 451
 
527
-      .upload-btn {
528
-        width: 160rpx;
529
-        height: 160rpx;
530
-        border: 2rpx dashed #e8e8e8;
531
-        border-radius: 12rpx;
452
+      .image-remove {
453
+        position: absolute;
454
+        top: 8rpx;
455
+        right: 8rpx;
456
+        width: 32rpx;
457
+        height: 32rpx;
458
+        background: rgba(0, 0, 0, 0.6);
459
+        color: #fff;
460
+        border-radius: 50%;
532 461
         display: flex;
533
-        flex-direction: column;
534 462
         align-items: center;
535 463
         justify-content: center;
536
-        color: #999;
537
-
538
-        text {
539
-          font-size: 20rpx;
540
-          margin-top: 8rpx;
541
-        }
464
+        font-size: 24rpx;
542 465
       }
543 466
     }
544 467
 
545
-    /* 隐藏的表单项 */
546
-    .hidden-item {
547
-      display: none;
548
-    }
549
-
550
-    /* 快递提示 */
551
-    .express {
468
+    .upload-btn {
469
+      width: 160rpx;
470
+      height: 160rpx;
471
+      border: 2rpx dashed #e8e8e8;
472
+      border-radius: 12rpx;
552 473
       display: flex;
553 474
       flex-direction: column;
475
+      align-items: center;
476
+      justify-content: center;
477
+      color: #999;
554 478
 
555
-      .express-hint {
479
+      text {
556 480
         font-size: 20rpx;
557
-        color: #999;
558 481
         margin-top: 8rpx;
559
-        display: block;
560 482
       }
561 483
     }
562 484
   }
563 485
 
486
+  /* 快递提示 */
487
+  .express {
488
+    display: flex;
489
+    flex-direction: column;
490
+
491
+    .express-hint {
492
+      font-size: 20rpx;
493
+      color: #999;
494
+      margin-top: 8rpx;
495
+      display: block;
496
+    }
497
+  }
498
+
564 499
   /* 底部按钮 */
565 500
   .bottom-buttons {
566 501
     position: fixed;
@@ -570,14 +505,12 @@ const submitOrder = async () => {
570 505
     background: #fff;
571 506
     padding: 24rpx 32rpx;
572 507
     box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
573
-    display: flex;
574
-    gap: 24rpx;
575 508
 
576 509
     button {
577
-      flex: 1;
510
+      width: 100%;
578 511
       height: 88rpx;
579 512
       border-radius: 44rpx;
580 513
     }
581 514
   }
582 515
 }
583
-</style>
516
+</style>

+ 54 - 34
src/pages/repair/components/charts/line.vue

@@ -1,6 +1,6 @@
1 1
 <template>
2 2
     <view class="chart-box">
3
-        <ucharts type="area" :chartData="chartData" :opts="opts" />
3
+        <ucharts type="line" :chartData="chartData" :opts="opts" />
4 4
     </view>
5 5
 </template>
6 6
 
@@ -13,31 +13,30 @@ const props = defineProps({
13 13
         type: Object,
14 14
         default: () => ({
15 15
             categories: [
16
-                '2000',
17
-                '2001',
18
-                '2002',
19
-                '2003',
20
-                '2004',
21
-                '2005',
22
-                '2006',
23
-                '2007',
24
-                '2008',
25
-                '2009',
26
-                '2010',
27
-                '2011',
28
-                '2012',
29
-                '2013',
30
-                '2014',
31
-                '2015',
32
-                '2016',
33
-                '2017',
34
-                '2018',
35
-                '2019',
36
-                '2020',
16
+                '260319',
17
+                '260320',
18
+                '260321',
19
+                '260322',
20
+                '260323',
21
+                '260324',
22
+                '260325',
23
+                '260319',
24
+                '260320',
25
+                '260321',
26
+                '260322',
27
+                '260323',
28
+                '260324',
29
+                '260325',
30
+                '260319',
31
+                '260320',
32
+                '260321',
33
+                '260322',
34
+                '260323',
35
+                '260324',
36
+                '260325',
37 37
             ],
38 38
             series: [
39 39
                 {
40
-                    name: '',
41 40
                     data: [
42 41
                         35, 8, 25, 37, 4, 20, 35, 8, 25, 37, 4, 20, 35, 8, 25, 37, 4, 20,
43 42
                         11, 20, 15,
@@ -52,19 +51,40 @@ const props = defineProps({
52 51
     },
53 52
 })
54 53
 const opts = {
55
-    color: ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4", "#ea7ccc"],
56
-    padding: [10, 0, 0, 0],
57
-    dataLabel: true,
54
+    color: ["#1890FF"],
55
+    padding: [10, 10, 10, 10],
56
+    dataLabel: false,
57
+    dataPointShape: false,
58 58
     enableScroll: false,
59
+    legend: {
60
+        show: false
61
+    },
62
+    xAxis: {
63
+        disableGrid: true,
64
+        rotateLabel: true,
65
+        rotateAngle: 45,
66
+    },
67
+    yAxis: {
68
+        showTitle:true,
69
+        gridType: "dash",
70
+        dashLength: 2,
71
+        axisLabel: {
72
+            show: false
73
+        },
74
+        data:[
75
+            {
76
+                title: '日服务量/单',
77
+            }
78
+        ]
79
+    },
59 80
     extra: {
60
-        map: {
61
-            border: true,
62
-            borderWidth: 1,
63
-            borderColor: "#666666",
64
-            fillOpacity: 0.6,
65
-            activeBorderColor: "#F04864",
66
-            activeFillColor: "#FACC14",
67
-            activeFillOpacity: 1
81
+        line: {
82
+            type: "curve",
83
+            width: 3,
84
+            activeType: "hollow",
85
+            linearType: "custom",
86
+            onShadow: true,
87
+            animation: "horizontal"
68 88
         }
69 89
     }
70 90
 }

+ 457 - 243
src/pages/repair/components/orderDetail/index.vue

@@ -4,278 +4,378 @@
4 4
     <view class="status-card">
5 5
       <view class="status-header">
6 6
         <text class="order-number">工单号:{{ orderInfo.orderId }}</text>
7
-        <text class="order-status" :style="{ color: orderStatusColor }">{{ orderInfo.status }}</text>
7
+        <text class="order-status" :style="{ color: orderStatusColor }">{{ orderStatusFormatter(orderInfo.status) }}</text>
8 8
       </view>
9 9
       <view class="status-footer">
10 10
         <text class="order-time">下单时间:{{ orderInfo.createTime }}</text>
11
-        <text class="payment-status" :style="{ color: paymentStatusColor }">{{ orderInfo.paymentStatus }}</text>
11
+        <text class="payment-status" v-if="!isRejected" :style="{ color: paymentStatusColor }">{{ paymentStatusFormatter(orderInfo.paymentStatus) }}</text>
12 12
       </view>
13 13
       <view class="status-service">
14
-        <text class="service-type">服务类型:{{  serviceTypeFormatter(orderInfo.serviceType) }}</text>
14
+        <text class="service-type">业务类型:{{ orderInfo.orderType }}</text>
15 15
       </view>
16
-    </view>
17
-
18
-    <!-- 客户信息 -->
19
-    <view class="info-section">
20
-      <view class="section-header">
21
-        <text class="section-title">客户信息</text>
16
+      <view class="status-service">
17
+        <text class="service-type">服务类型:{{ serviceTypeFormatter(orderInfo.serviceType) }}</text>
22 18
       </view>
23
-      <view class="section-content">
24
-        <view class="info-item">
25
-          <text class="info-label">姓名</text>
26
-          <text class="info-value">{{ orderInfo.customer.name }}</text>
27
-        </view>
28
-        <view class="info-item">
29
-          <text class="info-label">电话</text>
30
-          <text class="info-value">{{ orderInfo.customer.phone }}</text>
31
-        </view>
32
-        <view class="info-item" v-if="orderInfo.serviceType === 'home' || orderInfo.serviceType === 'mail'">
33
-          <text class="info-label">地址</text>
34
-          <text class="info-value">{{ orderInfo.customer.address }}</text>
35
-        </view>
19
+      <view class="review-btn">
20
+        <up-button size="small" type="error" @click="handleRefund" v-if="orderInfo.paymentStatus === 'PAID' && hasRole('SERVICESALESREP', false)">
21
+          退款
22
+        </up-button>
23
+        <up-button size="small" type="warning" @click="handleProgress">
24
+          进度查询
25
+        </up-button>
26
+        <up-button size="small" type="primary" @click="handleQuote" v-if="orderInfo.status === 'INSPECTED' && hasRole('REPAIRMAN', false)">
27
+          报价
28
+        </up-button>
29
+        <up-button v-if="orderInfo.status === OrderStatus.DELIVERED" type="success" size="small" @click="handleReview">
30
+          评价
31
+        </up-button>
36 32
       </view>
33
+      <up-modal :show="showReviewModal" showCancelButton @confirm="handleSubmitReview"
34
+        @cancel="showReviewModal = false">
35
+        <up-rate :count="reviewCount" v-model="reviewValue" active-color="#2c94f6"></up-rate>
36
+      </up-modal>
37 37
     </view>
38 38
 
39
-    <!-- 手表信息 -->
40
-    <view class="info-section">
41
-      <view class="section-header">
42
-        <text class="section-title">手表信息</text>
43
-      </view>
44
-      <view class="section-content">
45
-        <view class="info-item">
46
-          <text class="info-label">品牌</text>
47
-          <text class="info-value">{{ orderInfo.watch.brand }}</text>
48
-        </view>
49
-        <view class="info-item">
50
-          <text class="info-label">型号</text>
51
-          <text class="info-value">{{ orderInfo.watch.model }}</text>
52
-        </view>
53
-        <view class="info-item">
54
-          <text class="info-label">机芯类型</text>
55
-          <text class="info-value">{{ orderInfo.watch.movementType }}</text>
56
-        </view>
57
-        <view class="info-item">
58
-          <text class="info-label">表身编号</text>
59
-          <text class="info-value">{{ orderInfo.watch.serialNumber }}</text>
60
-        </view>
61
-        <view class="info-item">
62
-          <text class="info-label">是否在保</text>
63
-          <text class="info-value">{{ orderInfo.watch.inWarranty ? '是' : '否' }}</text>
64
-        </view>
65
-        <view class="info-item">
66
-          <text class="info-label">手表图片</text>
67
-          <view class="image-grid">
68
-            <image v-for="(img, index) in orderInfo.watch.images" :key="index" :src="img" class="grid-image" mode="aspectFill"></image>
39
+    <!-- ========== 维修专属区块 ========== -->
40
+    <template v-if="orderInfo.orderTypeId === 'repair'">
41
+      <!-- 故障描述 -->
42
+      <view class="info-section row">
43
+        <view class="section-header">
44
+          <text class="section-title">故障描述</text>
45
+        </view>
46
+        <view class="section-content col">
47
+          <view class="info-item">
48
+            <text class="info-label">故障现象:</text>
49
+            <text class="info-value">{{ orderInfo.fault.description.join('、') }}</text>
50
+          </view>
51
+          <view class="info-item">
52
+            <text class="info-label">详细描述:</text>
53
+            <text class="info-value">{{ orderInfo.fault.details }}</text>
54
+          </view>
55
+          <view class="info-item">
56
+            <text class="info-label ">故障图片:</text>
57
+            <view class="image-grid">
58
+              <image v-for="(img, index) in orderInfo.fault.images" :key="index" :src="img" class="grid-image"
59
+                mode="aspectFill"></image>
60
+            </view>
69 61
           </view>
70 62
         </view>
71 63
       </view>
72
-    </view>
73 64
 
74
-    <!-- 故障描述 -->
75
-    <view class="info-section">
76
-      <view class="section-header">
77
-        <text class="section-title">故障描述</text>
78
-      </view>
79
-      <view class="section-content">
80
-        <view class="info-item">
81
-          <text class="info-label">故障现象</text>
82
-          <text class="info-value">{{ orderInfo.fault.description.join('、') }}</text>
65
+      <!-- 维修信息 -->
66
+      <view class="info-section">
67
+        <view class="section-header">
68
+          <text class="section-title">维保信息</text>
83 69
         </view>
84
-        <view class="info-item">
85
-          <text class="info-label">详细描述</text>
86
-          <text class="info-value">{{ orderInfo.fault.details }}</text>
87
-        </view>
88
-        <view class="info-item">
89
-          <text class="info-label">故障图片</text>
90
-          <view class="image-grid">
91
-            <image v-for="(img, index) in orderInfo.fault.images" :key="index" :src="img" class="grid-image" mode="aspectFill"></image>
70
+        <view class="section-content col">
71
+          <view class="info-item">
72
+            <text class="info-label">维保项目:</text>
73
+            <text class="info-value">{{ orderInfo.repairInfo.items.join('、') }}</text>
74
+          </view>
75
+          <view class="info-item">
76
+            <text class="info-label">维保工时:</text>
77
+            <text class="info-value">{{ orderInfo.repairInfo.laborHours }}</text>
78
+          </view>
79
+          <view class="info-item">
80
+            <text class="info-label">维保技师:</text>
81
+            <text class="info-value">{{ orderInfo.repairInfo.technician }}</text>
82
+          </view>
83
+          <view class="info-item">
84
+            <text class="info-label">预计完成日期:</text>
85
+            <text class="info-value">{{ orderInfo.repairInfo.estimatedCompletionDate }}</text>
86
+          </view>
87
+          <view class="info-item">
88
+            <text class="info-label">紧急程度:</text>
89
+            <text class="info-value">{{ orderInfo.repairInfo.urgency }}</text>
90
+          </view>
91
+          <view class="info-item">
92
+            <text class="info-label">备注:</text>
93
+            <text class="info-value">{{ orderInfo.repairInfo.remarks }}</text>
92 94
           </view>
93 95
         </view>
94 96
       </view>
95
-    </view>
96 97
 
97
-    <!-- 维修信息 -->
98
-    <view class="info-section">
99
-      <view class="section-header">
100
-        <text class="section-title">维修信息</text>
101
-      </view>
102
-      <view class="section-content">
103
-        <view class="info-item">
104
-          <text class="info-label">维修项目</text>
105
-          <text class="info-value">{{ orderInfo.repairInfo.items.join('、') }}</text>
106
-        </view>
107
-        <view class="info-item">
108
-          <text class="info-label">维修工时</text>
109
-          <text class="info-value">{{ orderInfo.repairInfo.laborHours }}</text>
98
+      <!-- 服务信息(维修) -->
99
+      <view class="info-section" v-if="orderInfo.serviceType === 'home'">
100
+        <view class="section-header">
101
+          <text class="section-title">服务信息</text>
110 102
         </view>
111
-        <view class="info-item">
112
-          <text class="info-label">维修技师</text>
113
-          <text class="info-value">{{ orderInfo.repairInfo.technician }}</text>
103
+        <view class="section-content col">
104
+          <view class="info-item">
105
+            <text class="info-label">上门服务时间</text>
106
+            <text class="info-value">{{ orderInfo.serviceInfo.deliveryTime }}</text>
107
+          </view>
108
+          <view class="info-item">
109
+            <text class="info-label">服务地址</text>
110
+            <text class="info-value">{{ orderInfo.serviceInfo.serviceAddress }}</text>
111
+          </view>
114 112
         </view>
115
-        <view class="info-item">
116
-          <text class="info-label">预计完成日期</text>
117
-          <text class="info-value">{{ orderInfo.repairInfo.estimatedCompletionDate }}</text>
113
+      </view>
114
+      <view class="info-section" v-if="orderInfo.serviceType === 'mail'">
115
+        <view class="section-header">
116
+          <text class="section-title">服务信息</text>
117
+        </view>
118
+        <view class="section-content col">
119
+          <view class="info-item">
120
+            <text class="info-label">快递单号</text>
121
+            <text class="info-value">{{ orderInfo.serviceInfo.expressNumber }}</text>
122
+          </view>
118 123
         </view>
119
-        <view class="info-item">
120
-          <text class="info-label">紧急程度</text>
121
-          <text class="info-value">{{ orderInfo.repairInfo.urgency }}</text>
124
+      </view>
125
+
126
+      <!-- 维修车间视频 -->
127
+      <view class="info-section" v-if="orderInfo.status === OrderStatus.REPAIRING">
128
+        <view class="section-header">
129
+          <text class="section-title">维保车间视频</text>
122 130
         </view>
123
-        <view class="info-item">
124
-          <text class="info-label">备注</text>
125
-          <text class="info-value">{{ orderInfo.repairInfo.remarks }}</text>
131
+        <view class="section-content">
132
+          <view class="video-section">
133
+            <video id="repairVideo" :src="videoUrl" class="video-player" controls></video>
134
+            <text class="video-hint">实时查看维修情况</text>
135
+          </view>
126 136
         </view>
127 137
       </view>
128
-    </view>
129 138
 
130
-    <!-- 服务信息 -->
131
-    <view class="info-section" v-if="orderInfo.serviceType === 'home'">
132
-      <view class="section-header">
133
-        <text class="section-title">服务信息</text>
139
+      <!-- 维修记录 -->
140
+      <view class="info-section">
141
+        <view class="section-header">
142
+          <text class="section-title">维保记录</text>
143
+        </view>
144
+        <view class="section-content">
145
+          <view v-for="(record, index) in orderInfo.repairRecords" :key="index" class="record-item">
146
+            <view class="record-header">
147
+              <text class="record-title">{{ record.title }}</text>
148
+              <text class="record-time">{{ record.time }}</text>
149
+            </view>
150
+            <text class="record-content">{{ record.content }}</text>
151
+          </view>
152
+        </view>
134 153
       </view>
135
-      <view class="section-content">
136
-        <view class="info-item">
137
-          <text class="info-label">上门服务时间</text>
138
-          <text class="info-value">{{ orderInfo.serviceInfo.deliveryTime }}</text>
154
+
155
+      <!-- 质保期 -->
156
+      <view class="info-section">
157
+        <view class="section-header">
158
+          <text class="section-title">质保期:</text>
139 159
         </view>
140
-        <view class="info-item">
141
-          <text class="info-label">服务地址</text>
142
-          <text class="info-value">{{ orderInfo.serviceInfo.serviceAddress }}</text>
160
+        <view class="section-content col">
161
+          <view class="info-item">
162
+            <text class="info-label">质保开始日期:</text>
163
+            <text class="info-value">{{ orderInfo.warranty.startDate }}</text>
164
+          </view>
165
+          <view class="info-item">
166
+            <text class="info-label">质保结束日期:</text>
167
+            <text class="info-value">{{ orderInfo.warranty.endDate }}</text>
168
+          </view>
169
+          <view class="info-item">
170
+            <text class="info-label">质保范围:</text>
171
+            <text class="info-value">{{ orderInfo.warranty.coverage }}</text>
172
+          </view>
143 173
         </view>
144 174
       </view>
145
-    </view>
146
-    <view class="info-item" v-if="orderInfo.serviceType === 'mail'">
147
-      <text class="info-label">快递单号</text>
148
-      <text class="info-value">{{ orderInfo.serviceInfo.expressNumber }}</text>
149
-    </view>
150
-    <!-- 维修车间视频 -->
151
-    <view class="info-section" v-if="orderInfo.status === OrderStatus.IN_REPAIR">
152
-      <view class="section-header">
153
-        <text class="section-title">维修车间视频</text>
154
-      </view>
155
-      <view class="section-content">
156
-        <view class="video-section">
157
-          <video id="repairVideo" :src="videoUrl" class="video-player" controls></video>
158
-          <text class="video-hint">实时查看维修情况</text>
175
+    </template>
176
+
177
+    <!-- ========== 养护专属区块 ========== -->
178
+    <template v-if="orderInfo.orderTypeId === 'maintenance'">
179
+      <!-- 养护项目 -->
180
+      <view class="info-section">
181
+        <view class="section-header">
182
+          <text class="section-title">养护项目</text>
183
+        </view>
184
+        <view class="section-content col">
185
+          <view class="info-item">
186
+            <text class="info-label">养护类型:</text>
187
+            <text class="info-value">{{ maintenanceTypesFormatter(orderInfo.maintenance.maintenanceTypes) }}</text>
188
+          </view>
189
+          <view class="info-item">
190
+            <text class="info-label">上次养护时间:</text>
191
+            <text class="info-value">{{ orderInfo.maintenance.lastMaintenanceDate || '-' }}</text>
192
+          </view>
193
+          <view class="info-item">
194
+            <text class="info-label">特殊要求:</text>
195
+            <text class="info-value">{{ orderInfo.maintenance.maintenanceRemark || '-' }}</text>
196
+          </view>
159 197
         </view>
160 198
       </view>
161
-    </view>
162 199
 
163
-    <!-- 维修记录 -->
164
-    <view class="info-section">
165
-      <view class="section-header">
166
-        <text class="section-title">维修记录</text>
200
+      <!-- 养护服务信息 -->
201
+      <view class="info-section">
202
+        <view class="section-header">
203
+          <text class="section-title">服务信息</text>
204
+        </view>
205
+        <view class="section-content col">
206
+          <view class="info-item">
207
+            <text class="info-label">服务方式:</text>
208
+            <text class="info-value">{{ maintenanceServiceFormatter(orderInfo.maintenance.maintenanceServiceId)
209
+            }}</text>
210
+          </view>
211
+          <view class="info-item"
212
+            v-if="orderInfo.maintenance.maintenanceServiceId === 'home' || orderInfo.maintenance.maintenanceServiceId === 'mail'">
213
+            <text class="info-label">地址:</text>
214
+            <text class="info-value">{{ orderInfo.maintenance.address || '-' }}</text>
215
+          </view>
216
+          <view class="info-item" v-if="orderInfo.maintenance.maintenanceServiceId === 'store'">
217
+            <text class="info-label">门店:</text>
218
+            <text class="info-value">{{ orderInfo.maintenance.storeName || '-' }}</text>
219
+          </view>
220
+          <view class="info-item" v-if="orderInfo.maintenance.maintenanceServiceId === 'home'">
221
+            <text class="info-label">上门时间:</text>
222
+            <text class="info-value">{{ orderInfo.maintenance.delivery || '-' }}</text>
223
+          </view>
224
+          <view class="info-item" v-if="orderInfo.maintenance.maintenanceServiceId === 'mail'">
225
+            <text class="info-label">快递单号:</text>
226
+            <text class="info-value">{{ orderInfo.maintenance.expressNumber || '-' }}</text>
227
+          </view>
228
+        </view>
167 229
       </view>
168
-      <view class="section-content">
169
-        <view v-for="(record, index) in orderInfo.repairRecords" :key="index" class="record-item">
170
-          <view class="record-header">
171
-            <text class="record-title">{{ record.title }}</text>
172
-            <text class="record-time">{{ record.time }}</text>
230
+
231
+      <!-- 养护记录 -->
232
+      <view class="info-section">
233
+        <view class="section-header">
234
+          <text class="section-title">养护记录</text>
235
+        </view>
236
+        <view class="section-content">
237
+          <view v-for="(record, index) in orderInfo.repairRecords" :key="index" class="record-item">
238
+            <view class="record-header">
239
+              <text class="record-title">{{ record.title }}</text>
240
+              <text class="record-time">{{ record.time }}</text>
241
+            </view>
242
+            <text class="record-content">{{ record.content }}</text>
173 243
           </view>
174
-          <text class="record-content">{{ record.content }}</text>
175 244
         </view>
176 245
       </view>
177
-    </view>
246
+    </template>
178 247
 
179
-    <!-- 费用明细 -->
248
+    <!-- 手表信息 -->
180 249
     <view class="info-section">
181 250
       <view class="section-header">
182
-        <text class="section-title">费用明细</text>
251
+        <text class="section-title">手表信息</text>
183 252
       </view>
184
-      <view class="section-content">
185
-        <view class="info-item">
186
-          <text class="info-label">维修项目</text>
187
-          <text class="info-value">{{ orderInfo.cost.items.join('、') }}</text>
188
-        </view>
253
+      <view class="section-content grid">
189 254
         <view class="info-item">
190
-          <text class="info-label">配件费</text>
191
-          <text class="info-value">¥{{ orderInfo.cost.partsFee.toFixed(2) }}</text>
255
+          <text class="info-label">品牌:</text>
256
+          <text class="info-value">{{ orderInfo.watch.brand }}</text>
192 257
         </view>
193 258
         <view class="info-item">
194
-          <text class="info-label">工时费</text>
195
-          <text class="info-value">¥{{ orderInfo.cost.laborFee.toFixed(2) }}</text>
259
+          <text class="info-label">型号:</text>
260
+          <text class="info-value">{{ orderInfo.watch.model }}</text>
196 261
         </view>
197 262
         <view class="info-item">
198
-          <text class="info-label">其他费用</text>
199
-          <text class="info-value">¥{{ orderInfo.cost.otherFee.toFixed(2) }}</text>
263
+          <text class="info-label">机芯类型:</text>
264
+          <text class="info-value">{{ orderInfo.watch.movementType }}</text>
200 265
         </view>
201 266
         <view class="info-item">
202
-          <text class="info-label">折扣率</text>
203
-          <text class="info-value">{{ orderInfo.cost.discountRate }}%</text>
267
+          <text class="info-label">表身编号:</text>
268
+          <text class="info-value">{{ orderInfo.watch.serialNumber }}</text>
204 269
         </view>
205 270
         <view class="info-item">
206
-          <text class="info-label">折扣金额</text>
207
-          <text class="info-value">-¥{{ orderInfo.cost.discountAmount.toFixed(2) }}</text>
208
-        </view>
209
-        <view class="info-item total">
210
-          <text class="info-label">最终金额</text>
211
-          <text class="info-value total-value">¥{{ orderInfo.cost.finalAmount.toFixed(2) }}</text>
271
+          <text class="info-label">是否在保:</text>
272
+          <text class="info-value">{{ orderInfo.watch.inWarranty ? '是' : '否' }}</text>
212 273
         </view>
213
-        
214
-        <!-- 详细费用明细 -->
215
-        <view class="cost-details">
216
-          <view class="cost-details-title">详细费用</view>
217
-          <view v-for="(item, index) in orderInfo.cost.details" :key="index" class="cost-detail-item">
218
-            <text class="cost-detail-type">{{ item.type }}</text>
219
-            <text class="cost-detail-amount">¥{{ item.amount.toFixed(2) }}</text>
220
-            <text class="cost-detail-description">{{ item.description }}</text>
274
+        <view class="info-item row">
275
+          <text class="info-label ">手表图片:</text>
276
+          <view class="image-grid">
277
+            <image v-for="(img, index) in orderInfo.watch.images" :key="index" :src="img" class="grid-image"
278
+              mode="aspectFill"></image>
221 279
           </view>
222 280
         </view>
223 281
       </view>
224 282
     </view>
225
-
226
-    <!-- 质保期 -->
283
+    <!-- 客户信息 -->
227 284
     <view class="info-section">
228 285
       <view class="section-header">
229
-        <text class="section-title">质保期</text>
286
+        <text class="section-title">客户信息:</text>
230 287
       </view>
231
-      <view class="section-content">
288
+      <view class="section-content col">
232 289
         <view class="info-item">
233
-          <text class="info-label">质保开始日期</text>
234
-          <text class="info-value">{{ orderInfo.warranty.startDate }}</text>
290
+          <text class="info-label">姓名:</text>
291
+          <text class="info-value">{{ orderInfo.customer.name }}</text>
235 292
         </view>
236 293
         <view class="info-item">
237
-          <text class="info-label">质保结束日期</text>
238
-          <text class="info-value">{{ orderInfo.warranty.endDate }}</text>
294
+          <text class="info-label">电话:</text>
295
+          <text class="info-value">{{ orderInfo.customer.phone }}</text>
239 296
         </view>
240
-        <view class="info-item">
241
-          <text class="info-label">质保范围</text>
242
-          <text class="info-value">{{ orderInfo.warranty.coverage }}</text>
297
+        <view class="info-item" v-if="orderInfo.serviceType === 'home' || orderInfo.serviceType === 'mail'">
298
+          <text class="info-label">地址:</text>
299
+          <text class="info-value">{{ orderInfo.customer.address }}</text>
243 300
         </view>
244 301
       </view>
245 302
     </view>
246 303
 
247 304
     <!-- 底部按钮 -->
248 305
     <view class="bottom-buttons">
249
-      <up-button v-if="orderInfo.paymentStatus === PaymentStatus.UNPAID" type="primary" size="normal"
306
+      <up-button v-if="orderInfo.paymentStatus === 'UNPAID' && orderInfo.status === 'QUOTED'" type="primary" size="small"
250 307
         @click="handlePayment">
251 308
         支付
252 309
       </up-button>
253
-      <up-button v-if="orderInfo.status === OrderStatus.COMPLETED" type="success" size="normal" @click="handleReview">
254
-        评价
255
-      </up-button>
256
-      <up-button type="warning" size="normal" plain @click="contactService">
310
+      <up-button type="warning" size="small" plain @click="contactService">
257 311
         联系客服
258 312
       </up-button>
259
-      <up-button size="normal" plain @click="closeOrder">
260
-        关闭订单
313
+      <!-- 维修师傅按钮 -->
314
+      <up-button type="primary" size="small" plain @click="acceptOrder"
315
+        v-if="orderInfo.status === 'SENT_REPAIR' && hasRole('REPAIRMAN', false)">
316
+        接单
317
+      </up-button>
318
+      <up-button type="error" size="small" plain @click="rejectOrder"
319
+        v-if="orderInfo.status === 'SENT_REPAIR' && hasRole('REPAIRMAN', false)">
320
+        拒单
321
+      </up-button>
322
+      <up-button type="primary" size="small" plain @click="rejectOrder"
323
+        v-if="orderInfo.status === 'PAID' && hasRole('REPAIRMAN', false)">
324
+        开始维修
325
+      </up-button>
326
+      <up-button type="success" size="small" plain @click="rejectOrder"
327
+        v-if="orderInfo.status === 'REPAIRING' && hasRole('REPAIRMAN', false)">
328
+        完成维修
329
+      </up-button>
330
+      <!-- 业务员按钮 -->
331
+      <up-button type="primary" size="small" plain v-if="orderInfo.status === 'REPAIRED' && hasRole('SERVICESALESREP', false)">
332
+        开始质检
333
+      </up-button>
334
+      <up-button type="success" size="small" plain v-if="orderInfo.status === 'CHECKING' && hasRole('SERVICESALESREP', false)">
335
+        完成质检
336
+      </up-button>
337
+      <up-button type="primary" size="small" plain v-if="orderInfo.status === 'CHECKED' && hasRole('SERVICESALESREP', false)">
338
+        开始交付
261 339
       </up-button>
340
+      <up-button type="success" size="small" plain v-if="orderInfo.status === 'DELIVERING' && hasRole('SERVICESALESREP', false)">
341
+        完成交付
342
+      </up-button>
343
+      <!-- 检测员按钮 -->
344
+      <up-button type="primary" size="small" plain @click="inspectOrder" v-if="orderInfo.status === 'ACCEPTED' && hasRole('INSPECTOR', false)">
345
+        检测
346
+      </up-button>
347
+      <up-modal :show="showInspectPopup" showCancelButton title="检测" @confirm="submitInspectForm"  @cancel="closeInspectPopup" class="inspect-modal">
348
+        <up-form labelPosition="top" :model="inspectForm">
349
+          <up-form-item label="图片">
350
+            <up-upload :fileList="inspectForm.imgList" @afterRead="inspectImgAfterRead" @delete="inspectImgdeletePic" name="1" multiple accept="image"
351
+              :maxCount="10"></up-upload>
352
+          </up-form-item> 
353
+          <up-form-item label="视频">
354
+            <up-upload :fileList="inspectForm.videoList" @afterRead="inspectVideoafterRead" @delete="inspectImgdeleteVideo" name="2" multiple accept="video"
355
+              :maxCount="10"></up-upload>
356
+          </up-form-item>
357
+        </up-form>
358
+      </up-modal>
262 359
     </view>
263 360
   </view>
264 361
 </template>
265 362
 
266 363
 <script setup lang="ts">
267 364
 import { ref, computed, onMounted } from 'vue'
268
-import {  paymentStatusColors, orderStatusColors,OrderStatus,PaymentStatus } from '../public'
365
+import { paymentStatusColors, orderStatusColors, OrderStatus, PaymentStatus, maintenanceTypeOptions } from '../public'
366
+import { hasRole } from '@/utils/utils'
367
+import { onLoad } from '@dcloudio/uni-app'
269 368
 
270 369
 // 视频文件路径
271 370
 const videoUrl = ref('/static/video/2.mp4')
272
-
273 371
 // 订单信息
274 372
 const orderInfo = ref({
275 373
   orderId: 'WX20260304001',
276
-  status: OrderStatus.IN_REPAIR,
374
+  orderType: '维修',
375
+  orderTypeId: 'repair', // 'repair' | 'maintenance'
376
+  status: 'QUOTED',
277 377
   createTime: '2026-03-04 10:30:00',
278
-  paymentStatus: PaymentStatus.UNPAID,
378
+  paymentStatus: 'PAID',
279 379
   serviceType: 'store',
280 380
   customer: {
281 381
     name: '张三',
@@ -359,30 +459,92 @@ const orderInfo = ref({
359 459
     startDate: '2026-03-05',
360 460
     endDate: '2027-03-05',
361 461
     coverage: '维修项目质保1年'
462
+  },
463
+  // 养护信息(orderTypeId === 'maintenance' 时使用)
464
+  maintenance: {
465
+    maintenanceTypes: ['MOVEMENT_CLEAN', 'CASE_POLISH'],
466
+    lastMaintenanceDate: '2025-09-01',
467
+    maintenanceRemark: '请轻拿轻放',
468
+    maintenanceServiceType: '邮寄维修',
469
+    maintenanceServiceId: 'mail',
470
+    address: '北京市朝阳区某某小区1号楼101室',
471
+    storeName: '',
472
+    delivery: '',
473
+    expressNumber: 'SF1234567890'
362 474
   }
363 475
 })
476
+const showReviewModal = ref(false)
477
+const reviewCount = ref(5)
478
+const reviewValue = ref(0)
479
+const showInspectPopup = ref(false)
364 480
 
365 481
 // 计算属性
366 482
 const orderStatusColor = computed(() => {
367
-  return orderStatusColors[orderInfo.value.status]
483
+  return orderStatusColors[orderStatusFormatter(orderInfo.value.status)]
368 484
 })
369
-
370 485
 const paymentStatusColor = computed(() => {
371
-  return paymentStatusColors[orderInfo.value.paymentStatus]
486
+  return paymentStatusColors[paymentStatusFormatter(orderInfo.value.paymentStatus)]
372 487
 })
488
+const orderStatusFormatter = (status: string) => {
489
+  return OrderStatus[status as keyof typeof OrderStatus]
490
+}
491
+const paymentStatusFormatter = (status: string) => {
492
+  return PaymentStatus[status as keyof typeof PaymentStatus]
493
+}
494
+// 退款
495
+const handleRefund = () => {
496
+  uni.showToast({
497
+    title: '退款成功',
498
+    icon: 'success'
499
+  })
500
+}
501
+// 判断是否拒单,拒单后,禁止支付、评价、检测、报价、维修、质检、交付
502
+const isRejected = computed(() => {
503
+  return orderInfo.value.status === 'REJECTED'
504
+})
505
+const handleProgress = () => {
506
+  console.log('处理进度查询')
507
+  uni.navigateTo({
508
+    url: '/pages/repair/components/progress/index?orderId=' + orderInfo.value.orderId
509
+  })
510
+}
373 511
 
512
+const handleQuote = () => {
513
+  uni.navigateTo({
514
+    url: '/pages/repair/components/price/index?orderId=' + orderInfo.value.orderId
515
+  })
516
+}
374 517
 // 服务类型格式化
375 518
 const serviceTypeFormatter = (type: string) => {
376 519
   return type === 'store' ? '到店维修' : type === 'home' ? '上门维修' : type === 'mail' ? '邮寄维修' : '-'
377 520
 }
378 521
 
522
+// 养护服务方式格式化
523
+const maintenanceServiceFormatter = (id: string) => {
524
+  return id === 'store' ? '到店养护' : id === 'home' ? '上门取送' : id === 'mail' ? '邮寄养护' : '-'
525
+}
526
+
527
+// 养护类型格式化(value数组 → 名称列表)
528
+const maintenanceTypesFormatter = (values: string[]) => {
529
+  if (!values || values.length === 0) return '-'
530
+  return values.map(v => {
531
+    const found = maintenanceTypeOptions.find(o => o.value === v)
532
+    return found ? found.label : v
533
+  }).join('、')
534
+}
535
+
379 536
 // 方法
380 537
 const handlePayment = () => {
381 538
   console.log('处理支付')
382 539
 }
383 540
 
384 541
 const handleReview = () => {
542
+  showReviewModal.value = true
543
+}
544
+const handleSubmitReview = () => {
385 545
   console.log('处理评价')
546
+  console.log(reviewValue.value, reviewCount.value)
547
+  showReviewModal.value = false
386 548
 }
387 549
 
388 550
 const contactService = () => {
@@ -390,15 +552,40 @@ const contactService = () => {
390 552
     phoneNumber: '400-123-4567'
391 553
   })
392 554
 }
393
-
394
-const closeOrder = () => {
395
-  console.log('关闭订单')
555
+const acceptOrder = () => {
556
+  console.log('接单')
396 557
 }
397
-
398
-// 生命周期
399
-onMounted(() => {
400
-  // 这里可以添加获取订单详情的逻辑
401
-  console.log('获取订单详情')
558
+const rejectOrder = () => {
559
+  console.log('拒单')
560
+}
561
+const inspectForm = ref<any>({
562
+  imgList: [],
563
+  videoList: []
564
+})
565
+const inspectOrder = () => {
566
+  console.log('检测')
567
+  showInspectPopup.value = true
568
+}
569
+const closeInspectPopup = () => {
570
+  showInspectPopup.value = false
571
+}
572
+const submitInspectForm = () => {
573
+  showInspectPopup.value = false
574
+}
575
+const inspectImgAfterRead = (file: any) => {
576
+  inspectForm.value.imgList.push(file)
577
+}
578
+const inspectImgdeletePic = (index: number) => {
579
+  inspectForm.value.imgList.splice(index, 1)
580
+}
581
+const inspectImgdeleteVideo = (index: number) => {
582
+  inspectForm.value.videoList.splice(index, 1)
583
+}
584
+const inspectVideoafterRead = (file: any) => {
585
+  inspectForm.value.videoList.push(file)
586
+}
587
+onLoad((options: any) => {
588
+  console.log(options)
402 589
 })
403 590
 </script>
404 591
 
@@ -411,54 +598,65 @@ onMounted(() => {
411 598
 
412 599
   /* 顶部状态卡片 */
413 600
   .status-card {
414
-      background: linear-gradient(135deg, #2c94f6 0%, #1e88e5 100%);
415
-      border-radius: 16rpx;
416
-      padding: 32rpx;
417
-      margin-bottom: 32rpx;
418
-      color: #fff;
419
-      box-shadow: 0 4rpx 12rpx rgba(44, 148, 246, 0.3);
601
+    background: linear-gradient(135deg, #2c94f6 0%, #1e88e5 100%);
602
+    border-radius: 16rpx;
603
+    padding: 32rpx;
604
+    margin-bottom: 32rpx;
605
+    color: #fff;
606
+    box-shadow: 0 4rpx 12rpx rgba(44, 148, 246, 0.3);
607
+
608
+    .status-header {
609
+      display: flex;
610
+      justify-content: space-between;
611
+      align-items: center;
612
+      margin-bottom: 16rpx;
613
+
614
+      .order-number {
615
+        font-size: 28rpx;
616
+        font-weight: bold;
617
+      }
420 618
 
421
-      .status-header {
422
-        display: flex;
423
-        justify-content: space-between;
424
-        align-items: center;
425
-        margin-bottom: 16rpx;
619
+      .order-status {
620
+        font-size: 32rpx;
621
+        font-weight: bold;
622
+      }
623
+    }
426 624
 
427
-        .order-number {
428
-          font-size: 28rpx;
429
-          font-weight: bold;
430
-        }
625
+    .status-footer {
626
+      display: flex;
627
+      justify-content: space-between;
628
+      align-items: center;
431 629
 
432
-        .order-status {
433
-          font-size: 32rpx;
434
-          font-weight: bold;
435
-        }
630
+      .order-time {
631
+        font-size: 24rpx;
632
+        opacity: 0.9;
436 633
       }
437 634
 
438
-      .status-footer {
439
-        display: flex;
440
-        justify-content: space-between;
441
-        align-items: center;
442
-        margin-bottom: 16rpx;
443
-
444
-        .order-time {
445
-          font-size: 24rpx;
446
-          opacity: 0.9;
447
-        }
635
+      .payment-status {
636
+        font-size: 24rpx;
637
+        font-weight: bold;
638
+      }
639
+    }
448 640
 
449
-        .payment-status {
450
-          font-size: 24rpx;
451
-          font-weight: bold;
452
-        }
641
+    .status-service {
642
+      .service-type {
643
+        font-size: 24rpx;
644
+        opacity: 0.9;
453 645
       }
646
+    }
454 647
 
455
-      .status-service {
456
-        .service-type {
457
-          font-size: 24rpx;
458
-          opacity: 0.9;
459
-        }
648
+    .review-btn {
649
+      width: 100%;
650
+      display: flex;
651
+      justify-content: flex-end;
652
+      gap: 12rpx;
653
+
654
+      :deep(.u-button) {
655
+        width: fit-content;
656
+        margin: 0;
460 657
       }
461 658
     }
659
+  }
462 660
 
463 661
   /* 信息区块 */
464 662
   .info-section {
@@ -478,6 +676,19 @@ onMounted(() => {
478 676
       }
479 677
     }
480 678
 
679
+    .grid {
680
+      display: grid;
681
+      grid-template-columns: 1fr 1fr;
682
+      grid-gap: 12rpx;
683
+
684
+    }
685
+
686
+    .col {
687
+      display: grid;
688
+      grid-template-columns: 1fr;
689
+      grid-gap: 12rpx;
690
+    }
691
+
481 692
     .section-content {
482 693
       padding: 32rpx;
483 694
 
@@ -485,7 +696,6 @@ onMounted(() => {
485 696
         display: flex;
486 697
         justify-content: space-between;
487 698
         align-items: flex-start;
488
-        margin-bottom: 24rpx;
489 699
 
490 700
         &:last-child {
491 701
           margin-bottom: 0;
@@ -497,24 +707,29 @@ onMounted(() => {
497 707
           margin-top: 8rpx;
498 708
         }
499 709
 
710
+        &.row {
711
+          grid-column: span 2;
712
+        }
713
+
500 714
         .info-label {
501 715
           font-size: 28rpx;
502 716
           color: #666;
503 717
           flex-shrink: 0;
504
-          min-width: 170rpx;
718
+          // min-width: 170rpx;
505 719
         }
506 720
 
507 721
         .info-value {
508 722
           font-size: 28rpx;
509 723
           color: #333;
510 724
           flex: 1;
511
-          text-align: right;
725
+          text-align: left;
512 726
         }
513 727
 
514 728
         .total-value {
515 729
           font-size: 32rpx;
516 730
           font-weight: bold;
517 731
           color: #ff6b6b;
732
+          text-align: right;
518 733
         }
519 734
 
520 735
         /* 图片网格 */
@@ -560,7 +775,6 @@ onMounted(() => {
560 775
             font-size: 26rpx;
561 776
             color: #666;
562 777
             flex-shrink: 0;
563
-            min-width: 120rpx;
564 778
           }
565 779
 
566 780
           .cost-detail-amount {
@@ -583,6 +797,7 @@ onMounted(() => {
583 797
       /* 视频区域 */
584 798
       .video-section {
585 799
         .video-player {
800
+          z-index: 0;
586 801
           width: 100%;
587 802
           height: 400rpx;
588 803
           border-radius: 16rpx;
@@ -671,11 +886,10 @@ onMounted(() => {
671 886
     box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
672 887
     display: flex;
673 888
     gap: 24rpx;
674
-
675
-    button {
676
-      flex: 1;
677
-      height: 88rpx;
678
-      border-radius: 44rpx;
889
+  }
890
+  .inspect-modal{
891
+    :deep(.u-modal__content){
892
+      justify-content: left;
679 893
     }
680 894
   }
681 895
 }

+ 140 - 65
src/pages/repair/components/orderList/index.vue

@@ -2,56 +2,74 @@
2 2
     <view class="order-list">
3 3
         <!-- 搜索和筛选 -->
4 4
         <view class="list-header">
5
-            <up-search v-model="searchKeyword" placeholder="搜索工单号或客户姓名" :show-action="false" :bg-color="'#f5f7fa'"
6
-                shape="round" @search="handleSearch"></up-search>
5
+            <up-search v-model="searchKeyword" placeholder="输入内容点击左侧图标搜索" actionText="筛选" :bg-color="'#f5f7fa'"
6
+                clearabled shape="round" @clickIcon="handleSearch" @clear="handleClear"
7
+                @custom="openFilter"></up-search>
7 8
             <view class="filter-bar">
8
-                <up-tabs v-model="activeFilter" :list="filterTabs" @click="handleFilter"></up-tabs>
9
+                <up-tabs :current="activeFilter" :list="filterTabs" @click="openFilter"></up-tabs>
9 10
             </view>
11
+            <up-popup :show="showFilter" @close="closeFilter" @open="openFilter" :round="10" mode="bottom" closeable
12
+                safeAreaInsetTop>
13
+                <up-form labelPosition="left" :model="filterForm" class="filter-form">
14
+                    <up-form-item label="订单状态" label-width="70">
15
+                        <up-choose v-model="filterForm.typeValue" :options="filterTypeOptions"
16
+                            item-height="50rpx"></up-choose>
17
+                    </up-form-item>
18
+                    <view class="filter-btn">
19
+                        <up-button plain type="primary" @click="handleClearFilter">清空筛选</up-button>
20
+                        <up-button type="primary" @click="handleFilter">确定</up-button>
21
+                    </view>
22
+                </up-form>
23
+            </up-popup>
10 24
         </view>
11 25
 
12 26
         <!-- 订单列表 -->
13 27
         <up-list @scroll-to-lower="loadMore" @refresh="refresh" :refreshing="refreshing" :loading="loading"
14 28
             loading-text="加载中..." finish-text="没有更多数据了">
15 29
             <up-list-item v-for="(item, index) in filteredOrders" :key="item.id">
16
-                <view class="order-item">
17
-                    <up-swipe-action>
30
+                <view class="order-item" @click="handleSwipeAction({ index: 1 }, item)">
31
+                    <!-- <up-swipe-action>
18 32
                         <up-swipe-action-item :threshold="100" v-model:show="swipeShow" :options="swipeOptions"
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>
23
-                            <view class="order-header">
24
-                                <view class="order-info">
25
-                                    <text class="order-number">工单号:{{ item.orderId }}</text>
26
-                                    <text class="order-time">{{ formatTime(item.createTime) }}</text>
27
-                                </view>
28
-                                <view class="order-right">
33
+                            @click="(val: any) => handleSwipeAction(val, item)"> -->
34
+                    <view class="more-btn" @tap.stop="openPopover(item, index)">
35
+                        <up-icon name="more-dot-fill" size="40rpx" color="#999"></up-icon>
36
+                    </view>
37
+                    <view class="order-header">
38
+                        <view class="order-info">
39
+                            <text class="order-number">工单号:{{ item.orderId }}</text>
40
+                            <text class="order-time">{{ formatTime(item.createTime) }}</text>
41
+                        </view>
42
+                        <view>
43
+                            <text :style="{ color: getOrderTypeColors(item.oderTypeId) }">{{
44
+                                formatOrderType(item.oderTypeId) }}</text>
45
+                        </view>
46
+                        <!-- <view class="order-right">
29 47
                                     <text :style="{ color: getStatusColor(item.status) }" class="status-text">{{
30 48
                                         item.status
31 49
                                     }}</text>
32
-                                </view>
33
-                            </view>
34
-                            <view class="order-content">
35
-                                <view class="watch-info">
36
-                                    <text class="watch-brand">{{ item.watchBrand }}</text>
37
-                                    <text class="watch-model">{{ item.watchModel }}</text>
38
-                                </view>
39
-                                <text class="fault-description">{{ item.faultDescription }}</text>
40
-                            </view>
41
-                            <view class="order-footer">
42
-                                <view class="customer-info">
43
-                                    <text class="customer-name">{{ item.customerName }}</text>
44
-                                    <text v-if="item.estimatedCompletionTime" class="estimated-time">
45
-                                        预计完成:{{ formatDate(item.estimatedCompletionTime) }}
46
-                                    </text>
47
-                                </view>
48
-                                <view class="amount-info">
49
-                                    <text class="amount-label">总价:</text>
50
-                                    <text class="amount-value">¥{{ item.totalAmount.toFixed(2) }}</text>
51
-                                </view>
52
-                            </view>
53
-                        </up-swipe-action-item>
54
-                    </up-swipe-action>
50
+                                </view> -->
51
+                    </view>
52
+                    <view class="order-content">
53
+                        <view class="watch-info">
54
+                            <text class="watch-brand">{{ item.watchBrand }}</text>
55
+                            <text class="watch-model">{{ item.watchModel }}</text>
56
+                        </view>
57
+                        <text class="fault-description">{{ item.faultDescription }}</text>
58
+                    </view>
59
+                    <view class="order-footer">
60
+                        <view class="customer-info">
61
+                            <text class="customer-name">{{ item.customerName }}</text>
62
+                            <text v-if="item.estimatedCompletionTime" class="estimated-time">
63
+                                预计完成:{{ formatDate(item.estimatedCompletionTime) }}
64
+                            </text>
65
+                        </view>
66
+                        <view class="amount-info">
67
+                            <text class="amount-label">总价:</text>
68
+                            <text class="amount-value">¥{{ item.totalAmount.toFixed(2) }}</text>
69
+                        </view>
70
+                    </view>
71
+                    <!-- </up-swipe-action-item>
72
+                    </up-swipe-action> -->
55 73
 
56 74
                     <!-- 浮层蒙版(点击空白关闭) -->
57 75
                     <view v-if="popoverIndex === index" class="popover-mask" @tap.stop="closePopover"></view>
@@ -67,7 +85,8 @@
67 85
                             <text class="popover-text">详情</text>
68 86
                         </view>
69 87
                         <view class="popover-divider"></view>
70
-                        <view class="popover-item" @tap="handlePopoverAction({ index: 2 }, item)">
88
+                        <view class="popover-item" @tap="handlePopoverAction({ index: 2 }, item)"
89
+                            v-if="item.status === 'INSPECTED' && hasRole('REPAIRMAN', false)">
71 90
                             <up-icon name="rmb-circle" size="32rpx" color="#FF9900"></up-icon>
72 91
                             <text class="popover-text">报价</text>
73 92
                         </view>
@@ -87,20 +106,18 @@ import { onLoad } from '@dcloudio/uni-app'
87 106
 import {
88 107
     orderStatusColors,
89 108
     OrderStatus,
90
-    PaymentStatus
109
+    PaymentStatus,
110
+    OrderType,
111
+    orderTypeColors,
112
+    filterTabs,
113
+    filterTypeOptions
91 114
 } from '../public'
92 115
 import type { OrderListItem_Result } from '@/types/repair'
93 116
 import { repairApi } from '@/apis/repair'
94 117
 import { hasRole } from '@/utils/utils'
95 118
 
96 119
 
97
-
98
-// Props
99 120
 const props = defineProps({
100
-    type: {
101
-        type: String,
102
-        default: '已支付'
103
-    }
104 121
 })
105 122
 
106 123
 // 响应式数据
@@ -130,33 +147,55 @@ const swipeOptions = [
130 147
         }
131 148
     }
132 149
 ]
150
+const showFilter = ref(false)
151
+const filterForm = ref({
152
+    typeValue: ''
153
+})
133 154
 
134
-// 筛选标签
135
-const filterTabs = [
136
-    { name: '全部', value: 'all' },
137
-    { name: '待确认', value: 'PENDING_CONFIRM' },
138
-    { name: '维修中', value: 'IN_REPAIR' },
139
-    { name: '已完成', value: 'COMPLETED' }
140
-]
141 155
 
142 156
 // 计算属性
143 157
 const filteredOrders = computed(() => {
144 158
     return orders.value
145 159
 })
146
-
160
+const openFilter = () => {
161
+    showFilter.value = true
162
+}
163
+const closeFilter = () => {
164
+    showFilter.value = false
165
+}
147 166
 // 方法
148 167
 const handleSearch = () => {
149 168
     // 搜索逻辑
169
+    console.log(1);
170
+
171
+}
172
+const handleClear = () => {
173
+    console.log(2);
174
+    searchKeyword.value = ''
175
+    refresh()
176
+}
177
+const handleClearFilter = () => {
178
+    filterForm.value.typeValue = ''
179
+    refresh()
150 180
 }
151 181
 
152
-const handleFilter = () => {
153
-    // 筛选逻辑
182
+const handleFilter = (e: any) => {
183
+    console.log(e);
184
+    showFilter.value = false
154 185
 }
155 186
 
156 187
 const getStatusColor = (status: string) => {
157 188
     return orderStatusColors[status]
158 189
 }
159 190
 
191
+const getOrderTypeColors = (type: string) => {
192
+    return orderTypeColors[type]
193
+}
194
+
195
+const formatOrderType = (type: string) => {
196
+    return type === OrderType.REPAIR ? '维修' : '保养'
197
+}
198
+
160 199
 const formatTime = (time: string) => {
161 200
     return time
162 201
 }
@@ -165,16 +204,16 @@ const formatDate = (date: string) => {
165 204
     return date.split(' ')[0]
166 205
 }
167 206
 const handleSwipeAction = (val: any, item: any) => {
168
-    if(val.index == 0){//进度
207
+    if (val.index == 0) {//进度
169 208
         uni.navigateTo({
170 209
             url: '/pages/repair/components/progress/index?orderId=' + item.id,
171 210
         })
172
-    }else if(val.index == 1){//详情
211
+    } else if (val.index == 1) {//详情
173 212
         uni.navigateTo({
174 213
             url: '/pages/repair/components/orderDetail/index?orderId=' + item.id,
175 214
         })
176
-    }else if(val.index == 2){//报价
177
-        if(!hasRole('REPAIRMAN')) return false
215
+    } else if (val.index == 2) {//报价
216
+        if (!hasRole('REPAIRMAN')) return false
178 217
         uni.navigateTo({
179 218
             url: '/pages/repair/components/price/index?orderId=' + item.id,
180 219
         })
@@ -200,7 +239,7 @@ const closePopover = () => {
200 239
     currentPopoverItem.value = null
201 240
 }
202 241
 
203
-const handlePopoverAction = (val:object, item: OrderListItem_Result) => {
242
+const handlePopoverAction = (val: object, item: OrderListItem_Result) => {
204 243
     closePopover()
205 244
     handleSwipeAction(val, item)
206 245
 }
@@ -224,6 +263,8 @@ const refresh = () => {
224 263
     refreshing.value = true
225 264
     orders.value = [
226 265
         {
266
+            orderType: '维修',
267
+            oderTypeId: 'repair',
227 268
             id: '1',
228 269
             orderId: '202312010001',
229 270
             createTime: '2023-12-01 09:00:00',
@@ -234,8 +275,24 @@ const refresh = () => {
234 275
             customerName: '张三',
235 276
             estimatedCompletionTime: '2023-12-01 10:00:00',
236 277
             totalAmount: 200,
237
-            status: OrderStatus.PENDING_CONFIRM,
238
-            paymentStatus: PaymentStatus.PAID,
278
+            status: 'INSPECTED',
279
+            paymentStatus: 'PAID',
280
+        },
281
+        {
282
+            orderType: '保养',
283
+            oderTypeId: 'maintenance',
284
+            id: '2',
285
+            orderId: '202312010002',
286
+            createTime: '2023-12-01 10:00:00',
287
+            type: 'MAINTENANCE',
288
+            watchBrand: 'Apple',
289
+            watchModel: 'Apple Watch Series 7',
290
+            faultDescription: '屏幕故障',
291
+            customerName: '李四',
292
+            estimatedCompletionTime: '2023-12-01 10:00:00',
293
+            totalAmount: 200,
294
+            status: 'IN_REPAIR',
295
+            paymentStatus: 'PAID',
239 296
         },
240 297
     ]
241 298
     // repairApi.getOrderList({
@@ -247,7 +304,13 @@ const refresh = () => {
247 304
     //     refreshing.value = false
248 305
     // })
249 306
 }
250
-
307
+onLoad((options: any) => {
308
+    if (options.activeFilter == 'IN_PROGRESS') {
309
+        activeFilter.value = 2
310
+    } else {
311
+        activeFilter.value = 0
312
+    }
313
+})
251 314
 onMounted(() => {
252 315
     refresh()
253 316
 })
@@ -281,12 +344,14 @@ onMounted(() => {
281 344
         border-radius: 20rpx;
282 345
         margin: 20rpx 20rpx;
283 346
         position: relative;
347
+
284 348
         .more-btn {
285 349
             display: flex;
286 350
             align-items: center;
287 351
             justify-content: flex-end;
288 352
             border-radius: 50%;
289 353
         }
354
+
290 355
         .order-header {
291 356
             display: flex;
292 357
             justify-content: space-between;
@@ -320,7 +385,7 @@ onMounted(() => {
320 385
                     font-weight: bold;
321 386
                 }
322 387
 
323
-                
388
+
324 389
             }
325 390
         }
326 391
 
@@ -417,6 +482,8 @@ onMounted(() => {
417 482
             align-items: center;
418 483
             gap: 16rpx;
419 484
             padding: 20rpx 32rpx;
485
+            border-bottom: 1px solid #f0f0f0;
486
+
420 487
             &:active {
421 488
                 background: #f5f7fa;
422 489
             }
@@ -462,4 +529,12 @@ onMounted(() => {
462 529
         }
463 530
     }
464 531
 }
532
+
533
+.filter-form {
534
+    padding: 32rpx;
535
+    .filter-btn {
536
+        display: flex;
537
+        gap: 20rpx;
538
+    }
539
+}
465 540
 </style>

+ 13 - 20
src/pages/repair/components/price/index.vue

@@ -114,8 +114,9 @@
114 114
 
115 115
     <!-- 底部按钮 -->
116 116
     <view class="bottom-buttons">
117
-      <up-button type="info" size="large" plain @click="handleCancel">取消</up-button>
118
-      <up-button type="primary" size="large" @click="handleSubmit">提交报价</up-button>
117
+      <up-button type="primary" size="small" @click="handleSubmit">提交报价</up-button>
118
+      <up-button type="success" plain size="small" @click="handleDetail">详情</up-button>
119
+      <up-button type="warning" plain size="small" @click="handleProgress">进度</up-button>
119 120
     </view>
120 121
   </view>
121 122
 </template>
@@ -185,7 +186,16 @@ const discountAmount = computed(() => {
185 186
 const finalAmount = computed(() => {
186 187
   return totalAmount.value - discountAmount.value
187 188
 })
188
-
189
+const handleDetail = () => {
190
+  uni.navigateTo({
191
+    url: '/pages/repair/components/orderDetail/index?orderId=' + formData.value.orderId,
192
+  })
193
+}
194
+const handleProgress = () => {
195
+  uni.navigateTo({
196
+    url: '/pages/repair/components/progress/index?orderId=' + formData.value.orderId,
197
+  })
198
+}
189 199
 // 方法
190 200
 const addCost = () => {
191 201
   formData.value.costs.push({
@@ -218,17 +228,6 @@ const handleSubmit = () => {
218 228
   })
219 229
 }
220 230
 
221
-const handleCancel = () => {
222
-  uni.showModal({
223
-    title: '提示',
224
-    content: '确定要取消报价吗?',
225
-    success: (res) => {
226
-      if (res.confirm) {
227
-        uni.navigateBack()
228
-      }
229
-    }
230
-  })
231
-}
232 231
 
233 232
 const handleLaborHoursSelect = (value: any) => {
234 233
   formData.value.laborHours = value.name
@@ -383,12 +382,6 @@ onMounted(() => {
383 382
     box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
384 383
     display: flex;
385 384
     gap: 24rpx;
386
-
387
-    button {
388
-      flex: 1;
389
-      height: 88rpx;
390
-      border-radius: 44rpx;
391
-    }
392 385
   }
393 386
 }
394 387
 </style>

+ 30 - 12
src/pages/repair/components/progress/index.vue

@@ -57,12 +57,18 @@
57 57
 
58 58
     <!-- 按钮区域 -->
59 59
     <view class="button-area">
60
-      <up-button v-if="status === '待确认'" type="primary" size="large" @click="confirmQuote">
60
+      <up-button v-if="status === '待确认'" type="primary" size="small" @click="confirmQuote">
61 61
         确认报价
62 62
       </up-button>
63
-      <up-button type="info" size="large" plain @click="contactService">
63
+      <up-button type="warning" size="small" plain @click="contactService">
64 64
         联系客服
65 65
       </up-button>
66
+      <up-button type="success" size="small" plain @click="handleDetail">
67
+        详情
68
+      </up-button>
69
+      <up-button type="primary" size="small" plain @click="handleQuote" v-if="hasRole('REPAIRMAN', false)">
70
+        报价
71
+      </up-button>
66 72
     </view>
67 73
 
68 74
   </view>
@@ -71,6 +77,7 @@
71 77
 <script setup lang="ts">
72 78
 import { ref, computed, onMounted } from 'vue'
73 79
 import {  repairStatusColors, repairProgressSteps } from '../public'
80
+import { hasRole } from '@/utils/utils'
74 81
 
75 82
 // 订单信息
76 83
 const orderId = ref('WX20260304001')
@@ -93,6 +100,20 @@ const statusColor = computed(() => {
93 100
   return repairStatusColors[status.value]
94 101
 })
95 102
 
103
+// 处理详情点击事件
104
+const handleDetail = () => {
105
+  // 这里可以添加跳转详情页的逻辑
106
+  uni.navigateTo({
107
+    url: '/pages/repair/components/orderDetail/index?orderId=' + orderId.value
108
+  })
109
+}
110
+
111
+// 处理报价点击事件
112
+const handleQuote = () => {
113
+  uni.navigateTo({
114
+    url: '/pages/repair/components/price/index?orderId=' + orderId.value
115
+  })
116
+}
96 117
 // 方法
97 118
 const confirmQuote = () => {
98 119
   status.value = '维修中'
@@ -299,18 +320,15 @@ onMounted(() => {
299 320
 
300 321
   /* 按钮区域 */
301 322
   .button-area {
323
+    position: fixed;
324
+    bottom: 0;
325
+    left: 0;
326
+    right: 0;
327
+    background: #fff;
328
+    padding: 24rpx 32rpx;
329
+    box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
302 330
     display: flex;
303 331
     gap: 24rpx;
304
-    position: fixed;
305
-    bottom: 20rpx;
306
-    left: 10rpx;
307
-    right: 10rpx;
308
-
309
-    button {
310
-      flex: 1;
311
-      height: 88rpx;
312
-      border-radius: 44rpx;
313
-    }
314 332
   }
315 333
 
316 334
   /* 底部预计完成时间 */

+ 240 - 24
src/pages/repair/components/public.ts

@@ -71,7 +71,7 @@ export const deliveryOptions: DeliveryOption_Result[] = [
71 71
 ]
72 72
 export const gridList = [
73 73
   {
74
-    name: '我要维修',
74
+    name: '手表维保',
75 75
     src: '/static/repair/1.png',
76 76
     bgColor: '#2c94f6',
77 77
     path: '/pages/repair/components/apply/index',
@@ -99,6 +99,14 @@ export const gridList = [
99 99
     label: 'order'
100 100
   }
101 101
 ]
102
+
103
+// 筛选标签
104
+export const filterTabs = [
105
+    { name: '全部', value: 'all' },
106
+    { name: '待确认', value: 'PENDING_CONFIRM' },
107
+    { name: '进行中', value: 'IN_PROGRESS' },
108
+    { name: '已完成', value: 'COMPLETED' }
109
+]
102 110
 export const steps = ['选择服务', '手表信息', '故障描述', '提交订单']
103 111
 // 服务流程选项
104 112
 export const processSteps = [
@@ -111,10 +119,15 @@ export const processSteps = [
111 119
 ]
112 120
 // 服务类型选项
113 121
 export const services = [
114
-  { value: 'store', name: '到店送修', desc: '前往门店进行维修', icon: '🏪' },
122
+  { value: 'store', name: '到店', desc: '前往门店进行维修', icon: '🏪' },
115 123
   { value: 'home', name: '上门取送', desc: '专业人员上门取送', icon: '🚪' },
116
-  { value: 'mail', name: '邮寄维修', desc: '邮寄至维修中心', icon: '📦' }
124
+  { value: 'mail', name: '邮寄', desc: '邮寄至维修中心', icon: '📦' }
117 125
 ]
126
+// 业务类型选项
127
+export const orderTypeOptions = [
128
+  { id: 'repair', name: '维修' },
129
+  { id: 'maintenance', name: '养护' },
130
+];
118 131
 // 机芯类型选项
119 132
 export const movementTypes = ['机械', '石英', '智能']
120 133
 // 品牌选项
@@ -136,7 +149,7 @@ export const stores = [
136 149
 // 故障类型选项
137 150
 export const faultTypeOptions = [
138 151
   {
139
-    name: '停走',
152
+    name: '完全停走',
140 153
     disabled: false
141 154
   },
142 155
   {
@@ -144,11 +157,39 @@ export const faultTypeOptions = [
144 157
     disabled: false
145 158
   },
146 159
   {
147
-    name: '进水',
160
+    name: '走时偏快',
161
+    disabled: false
162
+  },
163
+  {
164
+    name: '走时偏慢',
165
+    disabled: false
166
+  },
167
+  {
168
+    name: '走走停停',
169
+    disabled: false
170
+  },
171
+  {
172
+    name: '受磁走快',
173
+    disabled: false
174
+  },
175
+  {
176
+    name: '进水起雾',
148 177
     disabled: false
149 178
   },
150 179
   {
151
-    name: '磕碰',
180
+    name: '进水生锈',
181
+    disabled: false
182
+  },
183
+  {
184
+    name: '磕碰损坏',
185
+    disabled: false
186
+  },
187
+  {
188
+    name: '表镜碎裂',
189
+    disabled: false
190
+  },
191
+  {
192
+    name: '表镜刮花',
152 193
     disabled: false
153 194
   },
154 195
   {
@@ -156,17 +197,121 @@ export const faultTypeOptions = [
156 197
     disabled: false
157 198
   },
158 199
   {
200
+    name: '日历卡滞',
201
+    disabled: false
202
+  },
203
+  {
204
+    name: '日历跳错',
205
+    disabled: false
206
+  },
207
+  {
159 208
     name: '表带损坏',
160 209
     disabled: false
161 210
   },
162 211
   {
163
-    name: '表镜碎裂',
212
+    name: '表带断裂',
213
+    disabled: false
214
+  },
215
+  {
216
+    name: '表带松动',
217
+    disabled: false
218
+  },
219
+  {
220
+    name: '表冠松动',
221
+    disabled: false
222
+  },
223
+  {
224
+    name: '表冠打滑',
225
+    disabled: false
226
+  },
227
+  {
228
+    name: '表冠脱落',
229
+    disabled: false
230
+  },
231
+  {
232
+    name: '无法调时',
233
+    disabled: false
234
+  },
235
+  {
236
+    name: '无法上弦',
237
+    disabled: false
238
+  },
239
+  {
240
+    name: '上弦过紧',
241
+    disabled: false
242
+  },
243
+  {
244
+    name: '自动上弦失效',
164 245
     disabled: false
165 246
   },
166 247
   {
167
-    name: '其他',
248
+    name: '机芯异响',
168 249
     disabled: false
169 250
   },
251
+  {
252
+    name: '机芯磨损',
253
+    disabled: false
254
+  },
255
+  {
256
+    name: '电池没电',
257
+    disabled: false
258
+  },
259
+  {
260
+    name: '电池漏液',
261
+    disabled: false
262
+  },
263
+  {
264
+    name: '秒针跳针',
265
+    disabled: false
266
+  },
267
+  {
268
+    name: '表盘氧化',
269
+    disabled: false
270
+  },
271
+  {
272
+    name: '表盘脱落',
273
+    disabled: false
274
+  },
275
+  {
276
+    name: '指针松动',
277
+    disabled: false
278
+  },
279
+  {
280
+    name: '指针弯曲',
281
+    disabled: false
282
+  },
283
+  {
284
+    name: '夜光失效',
285
+    disabled: false
286
+  },
287
+  {
288
+    name: '计时功能失灵',
289
+    disabled: false
290
+  },
291
+  {
292
+    name: '计时不归零',
293
+    disabled: false
294
+  },
295
+  {
296
+    name: '按键失灵',
297
+    disabled: false
298
+  },
299
+  {
300
+    name: '表壳变形',
301
+    disabled: false
302
+  },
303
+  {
304
+    name: '表底盖松动',
305
+    disabled: false
306
+  },
307
+  {
308
+    name: '密封圈老化',
309
+    disabled: false
310
+  },
311
+  {
312
+    name: '其他故障',
313
+    disabled: false
314
+  }
170 315
 ]
171 316
 
172 317
 // 维修状态枚举
@@ -185,12 +330,20 @@ export const repairStatusColors: Record<string, string> = {
185 330
 
186 331
 // 维修进度步骤
187 332
 export const repairProgressSteps = [
188
-  { key: 'received', label: '已接单' },
189
-  { key: 'inspected', label: '已检测' },
190
-  { key: 'pending_quote', label: '待您确认报价' },
191
-  { key: 'repairing', label: '维修中' },
192
-  { key: 'quality_check', label: '质检完成' },
193
-  { key: 'deliver', label: '待取件 / 已发货' }
333
+  { key: 'sentRepair', label: '已送修' },//已送修,待接单/拒单
334
+  { key: 'rejected', label: '已拒单' },//已拒单
335
+  // { key: 'receiving', label: '接单中' },//接单中
336
+  { key: 'received', label: '已接单' },//已接单,待检测
337
+  // { key: 'inspecting', label: '检测中' },//检测中
338
+  { key: 'inspected', label: '已检测' },//已检测,待报价
339
+  // { key: 'quoting', label: '报价中' },//报价中
340
+  { key: 'quoted', label: '已报价' },//已报价,待维修
341
+  // { key: 'repairing', label: '维修中' },//维修中
342
+  { key: 'repaired', label: '已维修' },//已维修,待质检
343
+  // { key: 'checking', label: '质检中' },//质检中
344
+  { key: 'checked', label: '已质检' },//已质检,待交付
345
+  // { key: 'delivering', label: '交付中' },//交付中
346
+  { key: 'delivered', label: '已交付' }//已交付,已完成
194 347
 ]
195 348
 
196 349
 // 支付状态枚举
@@ -209,25 +362,47 @@ export const paymentStatusColors: Record<string, string> = {
209 362
 
210 363
 // 订单状态枚举
211 364
 export enum OrderStatus {
212
-  PENDING_CONFIRM = '待确认',
213
-  IN_REPAIR = '维修中',
214
-  COMPLETED = '已完成',
215
-  CANCELLED = '已取消'
365
+  SENT_REPAIR = '已送修',
366
+  REJECTED = '已拒单',
367
+  ACCEPTED = '已接单',
368
+  // INSPECTING = '检测中',
369
+  INSPECTED = '已检测',
370
+  // QUOTING = '报价中',
371
+  QUOTED = '已报价',
372
+  PAID = '已支付',
373
+  REPAIRING = '维修中',
374
+  REPAIRED = '已维修',
375
+  CHECKING = '质检中',
376
+  CHECKED = '已质检',
377
+  DELIVERING = '交付中',
378
+  DELIVERED = '已交付',
216 379
 }
217 380
 
218 381
 // 订单状态颜色
219 382
 export const orderStatusColors: Record<string, string> = {
220
-  [OrderStatus.PENDING_CONFIRM]: '#ff9800',
221
-  [OrderStatus.IN_REPAIR]: '#f3d421',
222
-  [OrderStatus.COMPLETED]: '#4caf50',
223
-  [OrderStatus.CANCELLED]: '#9e9e9e'
383
+  [OrderStatus.SENT_REPAIR]: '#ffae00ff',//已送检,待接单/拒单
384
+  [OrderStatus.REJECTED]: '#f01b1bff',//已拒单
385
+  [OrderStatus.ACCEPTED]: '#2caf50',//已接单,待检测
386
+  // [OrderStatus.INSPECTING]: '#21dbf3ff',//检测中
387
+  [OrderStatus.INSPECTED]: '#4c7fafff',//已检测,待报价
388
+  // [OrderStatus.QUOTING]: '#2159f3ff',//报价中
389
+  [OrderStatus.QUOTED]: '#534cafff',//已报价,待支付
390
+  [OrderStatus.PAID]: '#4caf50',//已支付,待维修
391
+  [OrderStatus.REPAIRING]: '#f321f3ff',//维修中
392
+  [OrderStatus.REPAIRED]: '#af4c96ff',//已维修,待质检
393
+  [OrderStatus.CHECKING]: '#b4f321ff',//质检中
394
+  [OrderStatus.CHECKED]: '#81af4cff',//已质检,待交付
395
+  [OrderStatus.DELIVERING]: '#21f39cff',//交付中
396
+  [OrderStatus.DELIVERED]: '#1ee649ff',//已交付,已完成
224 397
 }
225
-
226 398
 // 订单类型枚举
227 399
 export enum OrderType {
228 400
   REPAIR = 'repair',
229 401
   MAINTENANCE = 'maintenance',
230
-  CUSTOMIZATION = 'customization'
402
+}
403
+export const orderTypeColors: Record<string, string> = {
404
+  [OrderType.REPAIR]: '#ff9800',
405
+  [OrderType.MAINTENANCE]: '#21f33dff',
231 406
 }
232 407
 
233 408
 // 维修项目列表
@@ -288,3 +463,44 @@ export const warrantyPeriodOptions = [
288 463
   { id: '2年', name: '2年' },
289 464
   { id: '3年', name: '3年' }
290 465
 ]
466
+
467
+// 养护类型选项
468
+export const maintenanceTypeOptions = [
469
+  { label: '机芯清洗保养', value: 'MOVEMENT_CLEAN' },
470
+  { label: '表壳抛光翻新', value: 'CASE_POLISH' },
471
+  { label: '表带清洗保养', value: 'STRAP_CLEAN' },
472
+  { label: '防水检测', value: 'WATERPROOF_CHECK' },
473
+  { label: '电池更换', value: 'BATTERY' },
474
+  { label: '整表全面保养', value: 'FULL_MAINTENANCE' },
475
+  { label: '表镜抛光', value: 'CRYSTAL_POLISH' },
476
+  { label: '磁化检测消磁', value: 'DEMAGNETIZE' },
477
+  { label: '表冠更换/保养', value: 'CROWN_MAINTENANCE' },
478
+  { label: '密封圈更换', value: 'SEAL_RING_REPLACE' },
479
+  { label: '指针调校', value: 'POINTER_ADJUST' },
480
+  { label: '日历功能调校', value: 'CALENDAR_ADJUST' },
481
+  { label: '表扣保养/更换', value: 'BUCKLE_MAINTENANCE' },
482
+  { label: '表底盖密封处理', value: 'CASE_BACK_SEAL' },
483
+  { label: '计时功能校准', value: 'TIMER_CALIBRATE' },
484
+  { label: '表油加注', value: 'LUBRICANT_ADD' },
485
+  { label: '表壳缝隙清洁', value: 'CASE_GAP_CLEAN' },
486
+  { label: '金属表带打磨', value: 'METAL_STRAP_POLISH' },
487
+  { label: '皮质表带养护', value: 'LEATHER_STRAP_CARE' },
488
+  { label: '陶瓷部件保养', value: 'CERAMIC_PART_CARE' },
489
+  { label: '夜光涂层修复', value: 'LUMINOUS_REPAIR' },
490
+  { label: '走时精度校准', value: 'TIME_ACCURACY_CALIBRATE' },
491
+  { label: '螺丝紧固/更换', value: 'SCREW_FIX_REPLACE' }
492
+];
493
+export const filterTypeOptions = [
494
+    {
495
+        id: 'all',
496
+        title: '全部'
497
+    },
498
+    {
499
+        id: 'repair',
500
+        title: '维修'
501
+    },
502
+    {
503
+        id: 'maintenance',
504
+        title: '保养'
505
+    },
506
+]

+ 167 - 36
src/pages/repair/index.vue

@@ -1,9 +1,5 @@
1 1
 <template>
2 2
   <view class="repair-home">
3
-    <!-- 顶部导航栏 -->
4
-    <view class="nav-bar">
5
-      <text class="nav-title">手表维修服务</text>
6
-    </view>
7 3
 
8 4
     <!-- 搜索栏 -->
9 5
     <view class="search-wrap">
@@ -16,6 +12,14 @@
16 12
       ></up-search>
17 13
     </view>
18 14
 
15
+    <!-- 新增:待处理工单提示 -->
16
+    <view class="order-tips" v-if="pendingOrders.length" @click="goToOrderList">
17
+      <view class="tips-icon">
18
+        <u-icon name="warning" color="#FF9500" size="24"></u-icon>
19
+      </view>
20
+      <text class="tips-text">您有 {{ pendingOrders.length }} 个工单待跟进</text>
21
+    </view>
22
+
19 23
     <!-- 功能宫格(Grid 布局) -->
20 24
     <view class="grid-container">
21 25
       <view
@@ -32,6 +36,24 @@
32 36
       </view>
33 37
     </view>
34 38
 
39
+    <!-- 图表组件 -->
40
+    <view class="chart-container">
41
+      <LineChart />
42
+    </view>
43
+
44
+    <!-- 新增:保养小贴士 -->
45
+    <view class="maintain-tips">
46
+      <view class="tips-header">
47
+        <u-icon name="bell" color="#FFCC00" size="28"></u-icon>
48
+        <text class="tips-title">保养小贴士</text>
49
+      </view>
50
+      <swiper class="tips-swiper" autoplay circular :interval="5000">
51
+        <swiper-item v-for="(tip, index) in maintainTipsList" :key="index">
52
+          <text class="tips-content">{{ tip.content }}</text>
53
+        </swiper-item>
54
+      </swiper>
55
+    </view>
56
+
35 57
     <!-- 服务流程卡片 -->
36 58
     <view class="process-card">
37 59
       <view class="process-header">
@@ -44,29 +66,76 @@
44 66
         </view>
45 67
       </view>
46 68
     </view>
47
-    <!-- 图表组件 -->
48
-    <view class="chart-container">
49
-      <LineChart />
50
-    </view>
51
-    <!-- 退出登录按钮 -->
52
-    <view class="logout-btn-wrap">
69
+
70
+    <!-- 调整:退出登录 + 客服按钮 -->
71
+    <view class="bottom-actions">
53 72
       <up-button type="primary" @click="handleLogout">退出登录</up-button>
73
+      <up-button type="info" icon="phone" @click="callService">联系客服</up-button>
54 74
     </view>
55 75
   </view>
56 76
 </template>
57 77
 
58 78
 <script setup lang="ts">
59 79
 import { ref } from 'vue'
60
-import type { Repair_Result } from '@/types/repair'
61
-import { gridList,processSteps} from './components/public'
80
+import type { Repair_Result, OrderListItem_Result } from '@/types/repair'
81
+import { gridList, processSteps, OrderStatus, PaymentStatus, filterTabs } from './components/public'
62 82
 import LineChart from './components/charts/line.vue'
63 83
 import { loginApi } from '@/apis/login'
84
+
85
+// 搜索关键词
64 86
 const searchKeyword = ref<string>('')
87
+
88
+// 新增:待处理工单数据
89
+const pendingOrders = ref<OrderListItem_Result[]>([
90
+  {   
91
+      orderType:'维修',
92
+      oderTypeId:'repair',
93
+      id: '1',
94
+      orderId: '202312010001',
95
+      createTime: '2023-12-01 09:00:00',
96
+      type: 'REPAIR',
97
+      watchBrand: 'Apple',
98
+      watchModel: 'Apple Watch Series 7',
99
+      faultDescription: '屏幕故障',
100
+      customerName: '张三',
101
+      estimatedCompletionTime: '2023-12-01 10:00:00',
102
+      totalAmount: 200,
103
+      status: OrderStatus.PENDING_CONFIRM,
104
+      paymentStatus: PaymentStatus.PAID,
105
+  },
106
+  {   
107
+      orderType:'保养',
108
+      oderTypeId:'maintain',
109
+      id: '2',
110
+      orderId: '202312010002',
111
+      createTime: '2023-12-01 10:00:00',
112
+      type: 'MAINTAIN',
113
+      watchBrand: 'Apple',
114
+      watchModel: 'Apple Watch Series 7',
115
+      faultDescription: '电池故障',
116
+      customerName: '李四',
117
+      estimatedCompletionTime: '2023-12-01 11:00:00',
118
+      totalAmount: 150,
119
+      status: OrderStatus.PENDING_CONFIRM,
120
+      paymentStatus: PaymentStatus.PAID,
121
+  },
122
+])
123
+
124
+// 新增:保养小贴士数据
125
+const maintainTipsList = ref([
126
+  { content: '机械表建议每2-3年进行一次全面保养,延长使用寿命' },
127
+  { content: '石英表电池更换后需重新检测防水性能,避免进水' },
128
+  { content: '避免手表接触高温、磁场环境,防止机芯损坏' }
129
+])
130
+
131
+// 功能宫格点击事件
65 132
 const handleGridClick = (item: Repair_Result) => {
66 133
   uni.navigateTo({
67 134
     url: item.path,
68 135
   })
69 136
 }
137
+
138
+// 退出登录事件
70 139
 const handleLogout = () => {
71 140
   uni.removeStorageSync('userInfo')
72 141
   uni.removeStorageSync('token')
@@ -83,30 +152,34 @@ const handleLogout = () => {
83 152
     console.error('退出登录接口调用失败:', err)
84 153
   })
85 154
 }
155
+
156
+const goToOrderList = () => {
157
+  if(pendingOrders.value.length === 1) {
158
+    uni.navigateTo({ url: '/pages/repair/components/orderDetail/index?orderId=' + pendingOrders.value[0].id })
159
+    return
160
+  }
161
+  uni.navigateTo({ url: '/pages/repair/components/orderList/index?activeFilter=' + filterTabs[2].value })
162
+}
163
+
164
+const callService = () => {
165
+  uni.makePhoneCall({
166
+    phoneNumber: '123-456-7890',
167
+    fail: (err) => {
168
+      uni.showToast({ title: '拨打电话失败', icon: 'none' })
169
+    }
170
+  })
171
+}
86 172
 </script>
87 173
 
88 174
 <style lang="scss" scoped>
175
+page{
176
+  height:100%;
177
+}
89 178
 .repair-home {
90
-  min-height: 100vh;
179
+  min-height: calc(100%);
91 180
   background: linear-gradient(180deg, #eaf5ff 0%, #f5faff 100%);
92 181
 }
93 182
 
94
-/* 顶部导航栏 */
95
-.nav-bar {
96
-  background: #165DFF;
97
-  padding: 32rpx;
98
-  display: flex;
99
-  align-items: center;
100
-  justify-content: center;
101
-  box-shadow: 0 4rpx 12rpx rgba(22, 93, 255, 0.1);
102
-
103
-  .nav-title {
104
-    font-size: 32rpx;
105
-    font-weight: bold;
106
-    color: #fff;
107
-  }
108
-}
109
-
110 183
 /* 搜索栏 */
111 184
 .search-wrap {
112 185
   padding: 32rpx;
@@ -116,6 +189,28 @@ const handleLogout = () => {
116 189
   }
117 190
 }
118 191
 
192
+/* 新增:待处理工单提示 */
193
+.order-tips {
194
+  display: flex;
195
+  align-items: center;
196
+  justify-content: space-between;
197
+  padding: 20rpx 32rpx;
198
+  margin: 0 32rpx 24rpx;
199
+  background: #FFF8E6;
200
+  border-radius: 16rpx;
201
+  border-left: 6rpx solid #FF9500;
202
+
203
+  .tips-icon {
204
+    margin-right: 12rpx;
205
+  }
206
+
207
+  .tips-text {
208
+    font-size: 26rpx;
209
+    color: #FF8C00;
210
+    line-height: 1.4;
211
+  }
212
+}
213
+
119 214
 /* 功能宫格 */
120 215
 .grid-container {
121 216
   display: grid;
@@ -161,6 +256,41 @@ const handleLogout = () => {
161 256
   }
162 257
 }
163 258
 
259
+
260
+/* 图表组件 */
261
+.chart-container {
262
+  padding: 32rpx;
263
+}
264
+
265
+.maintain-tips {
266
+  margin: 0 32rpx 32rpx;
267
+  background: #F0F8FF;
268
+  border-radius: 20rpx;
269
+
270
+  .tips-header {
271
+    display: flex;
272
+    align-items: center;
273
+    margin-bottom: 16rpx;
274
+
275
+    .tips-title {
276
+      font-size: 28rpx;
277
+      font-weight: 600;
278
+      color: #FFCC00;
279
+      margin-left: 12rpx;
280
+    }
281
+  }
282
+
283
+  .tips-swiper {
284
+    height: 60rpx;
285
+
286
+    .tips-content {
287
+      font-size: 26rpx;
288
+      color: #666;
289
+      line-height: 1.5;
290
+    }
291
+  }
292
+}
293
+
164 294
 /* 服务流程卡片 */
165 295
 .process-card {
166 296
   background: #fff;
@@ -234,14 +364,15 @@ const handleLogout = () => {
234 364
   }
235 365
 }
236 366
 
237
-/* 图表组件 */
238
-.chart-container {
367
+.bottom-actions {
368
+  display: flex;
369
+  gap: 24rpx;
239 370
   padding: 32rpx;
240
-}
241
-.logout-btn-wrap{
242
-  .u-button{
243
-    width: 200rpx;
244
-    margin-top: 32rpx;
371
+  justify-content: center;
372
+
373
+  .u-button {
374
+    flex: 1;
375
+    max-width: 200rpx;
245 376
   }
246 377
 }
247 378
 </style>

BIN
src/static/tabs/repair-active.png


BIN
src/static/tabs/repair.png


BIN
src/static/tabs/watch-active.png


BIN
src/static/tabs/watch.png


+ 16 - 2
src/types/repair.d.ts

@@ -8,6 +8,11 @@ export interface Repair_Result {
8 8
   label: string
9 9
 }
10 10
 export interface repair_Params {
11
+  // 业务类型:维修 / 养护
12
+  orderType: string;
13
+  orderTypeId: string;
14
+  // 维修相关
15
+  serviceId: string;
11 16
   serviceType: string;
12 17
   brand: string;
13 18
   model: string;
@@ -18,6 +23,13 @@ export interface repair_Params {
18 23
   faultTypes: string[];
19 24
   faultDescription: string;
20 25
   faultImages: string[];
26
+  // 养护相关
27
+  maintenanceTypes: string[];
28
+  lastMaintenanceDate: string;
29
+  maintenanceRemark: string;
30
+  maintenanceServiceType: string;
31
+  maintenanceServiceId: string;
32
+  // 联系方式
21 33
   contactName: string;
22 34
   contactPhone: string;
23 35
   address: string;
@@ -61,11 +73,13 @@ export interface RepairQuoteForm_Params {
61 73
 
62 74
 // 订单列表项类型
63 75
 export interface OrderListItem_Result {
76
+  orderType: string
77
+  oderTypeId: string
64 78
   id: string
65 79
   orderId: string
66 80
   type: OrderType
67
-  status: OrderStatus
68
-  paymentStatus: PaymentStatus
81
+  status: string
82
+  paymentStatus: string
69 83
   createTime: string
70 84
   customerName: string
71 85
   watchBrand: string

+ 3 - 0
src/utils/utils.ts

@@ -2,6 +2,9 @@ export function getRoles() {
2 2
     return uni.getStorageSync('userInfo')?.roles.map((item: any) => item.roleKey) || []
3 3
 }
4 4
 export function hasRole(roleName: string, showToast: boolean = true) {
5
+    if(getRoles().includes('admin') || getRoles().includes('ADMIN')){
6
+        return true
7
+    }
5 8
     let hasRole = getRoles().includes(roleName) || getRoles().some((role: any) => role.roleKey && role.roleKey.includes(roleName))
6 9
     if (hasRole) {
7 10
         return true