ソースを参照

feat:更新

zhangxin 2 日 前
コミット
1ba9f689b9

+ 2 - 2
src/manifest.json

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

+ 7 - 5
src/pages.json

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

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

@@ -40,6 +40,8 @@ import { ref, onMounted } from 'vue'
40
 import { loginApi } from '@/apis/login'
40
 import { loginApi } from '@/apis/login'
41
 import type { Login_Params, userInfo_Result } from '@/types/login'
41
 import type { Login_Params, userInfo_Result } from '@/types/login'
42
 import { computed } from 'vue'
42
 import { computed } from 'vue'
43
+import type { login_Result } from '@/types/login'
44
+
43
 const remember = computed(() => {
45
 const remember = computed(() => {
44
 	return checkboxs.value.length > 0
46
 	return checkboxs.value.length > 0
45
 })
47
 })
@@ -113,6 +115,10 @@ const changeRemember = (value: string[]) => {
113
 }
115
 }
114
 const loginFormRef = ref<any>(null)
116
 const loginFormRef = ref<any>(null)
115
 const loading = ref<boolean>(false)
117
 const loading = ref<boolean>(false)
118
+const loginResult = ref<login_Result>({
119
+	access_token: '',
120
+	expires_in: 0
121
+})
116
 const handleLogin = () => {
122
 const handleLogin = () => {
117
 	loginFormRef.value.validate().then(async () => {
123
 	loginFormRef.value.validate().then(async () => {
118
 		loading.value = true
124
 		loading.value = true
@@ -123,17 +129,25 @@ const handleLogin = () => {
123
 			uni.removeStorageSync('username');
129
 			uni.removeStorageSync('username');
124
 			uni.removeStorageSync('password');
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
 		loading.value = false
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
 <template>
1
 <template>
2
   <view class="repair-apply">
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
       </view>
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
             </view>
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
             <up-select v-model="formData.brand" :options="brands" label="请选择品牌" @select="selectBrandItem">
61
             <up-select v-model="formData.brand" :options="brands" label="请选择品牌" @select="selectBrandItem">
33
-              <template #text="{ label }">
62
+              <template #text>
34
                 <text>{{ formData.brand }}</text>
63
                 <text>{{ formData.brand }}</text>
35
               </template>
64
               </template>
36
             </up-select>
65
             </up-select>
37
           </up-form-item>
66
           </up-form-item>
38
-
39
-          <up-form-item prop="model" label="型号" required>
67
+          <up-form-item label="型号" :label-width="80">
40
             <up-input v-model="formData.model" placeholder="请输入手表型号" />
68
             <up-input v-model="formData.model" placeholder="请输入手表型号" />
41
           </up-form-item>
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
             <up-radio-group v-model="formData.movementType" size="default">
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
             </up-radio-group>
73
             </up-radio-group>
49
           </up-form-item>
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
             <up-input v-model="formData.watchNumber" placeholder="请输入表身编号" />
76
             <up-input v-model="formData.watchNumber" placeholder="请输入表身编号" />
53
           </up-form-item>
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
             <up-radio-group v-model="formData.inWarranty" size="default">
79
             <up-radio-group v-model="formData.inWarranty" size="default">
57
               <up-radio label="是" name="true"></up-radio>
80
               <up-radio label="是" name="true"></up-radio>
58
               <up-radio label="否" name="false"></up-radio>
81
               <up-radio label="否" name="false"></up-radio>
59
             </up-radio-group>
82
             </up-radio-group>
60
           </up-form-item>
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
             <view class="upload-images">
85
             <view class="upload-images">
64
               <view v-for="(image, index) in formData.images" :key="index" class="uploaded-image">
86
               <view v-for="(image, index) in formData.images" :key="index" class="uploaded-image">
65
                 <image :src="image" mode="aspectFill"></image>
87
                 <image :src="image" mode="aspectFill"></image>
@@ -71,71 +93,139 @@
71
               </view>
93
               </view>
72
             </view>
94
             </view>
73
           </up-form-item>
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
             </up-checkbox-group>
108
             </up-checkbox-group>
86
           </up-form-item>
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
           </up-form-item>
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
             <view class="upload-images">
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
                 <image :src="image" mode="aspectFill"></image>
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
               </view>
141
               </view>
98
-              <view class="upload-btn" @click="uploadFaultImage">
142
+              <view v-if="formData.images.length < 5" class="upload-btn" @click="uploadImage">
99
                 <u-icon name="camera" size="32"></u-icon>
143
                 <u-icon name="camera" size="32"></u-icon>
100
                 <text>上传图片</text>
144
                 <text>上传图片</text>
101
               </view>
145
               </view>
102
             </view>
146
             </view>
103
           </up-form-item>
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
           </up-form-item>
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
           </up-form-item>
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
             <up-textarea v-model="formData.address" placeholder="请输入您的地址"></up-textarea>
213
             <up-textarea v-model="formData.address" placeholder="请输入您的地址"></up-textarea>
121
           </up-form-item>
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
             <up-select v-model="formData.storeName" :options="stores" label="请选择门店" @select="selectStoreItem">
217
             <up-select v-model="formData.storeName" :options="stores" label="请选择门店" @select="selectStoreItem">
125
-              <template #text="{ label }">
218
+              <template #text>
126
                 <text>{{ formData.storeName }}</text>
219
                 <text>{{ formData.storeName }}</text>
127
               </template>
220
               </template>
128
             </up-select>
221
             </up-select>
129
           </up-form-item>
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
             <up-cate-tab height="300px" :tab-list="deliveryOptions" v-model:current="deliveryCurrent">
225
             <up-cate-tab height="300px" :tab-list="deliveryOptions" v-model:current="deliveryCurrent">
134
               <template v-slot:itemList="{ item }">
226
               <template v-slot:itemList="{ item }">
135
                 <view class="delivery-time-container">
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
                   <view class="item-container">
229
                   <view class="item-container">
140
                     <up-choose v-model="item.selectedIndex" :options="item.times" item-width="300rpx"
230
                     <up-choose v-model="item.selectedIndex" :options="item.times" item-width="300rpx"
141
                       item-height="40rpx" @update:modelValue="(idx: any) => onDeliveryTimeChange(item, idx)">
231
                       item-height="40rpx" @update:modelValue="(idx: any) => onDeliveryTimeChange(item, idx)">
@@ -145,27 +235,20 @@
145
               </template>
235
               </template>
146
             </up-cate-tab>
236
             </up-cate-tab>
147
           </up-form-item>
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
             <view class="express">
240
             <view class="express">
152
               <up-input v-model="formData.expressNumber" placeholder="请输入快递单号" />
241
               <up-input v-model="formData.expressNumber" placeholder="请输入快递单号" />
153
               <text class="express-hint">建议使用顺丰快递,确保手表安全</text>
242
               <text class="express-hint">建议使用顺丰快递,确保手表安全</text>
154
             </view>
243
             </view>
155
           </up-form-item>
244
           </up-form-item>
156
-        </up-form>
245
+        </template>
157
       </view>
246
       </view>
158
-    </view>
247
+    </up-form>
159
 
248
 
160
-    <!-- 底部按钮 -->
249
+    <!-- 底部提交按钮 -->
161
     <view class="bottom-buttons">
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
       </u-button>
253
       </u-button>
171
     </view>
254
     </view>
@@ -173,71 +256,66 @@
173
 </template>
256
 </template>
174
 
257
 
175
 <script setup lang="ts">
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
 const formData = reactive<repair_Params>({
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
   inWarranty: false,
288
   inWarranty: false,
193
   images: [],
289
   images: [],
194
-  // 步骤3
195
   faultTypes: [],
290
   faultTypes: [],
196
-  faultDescription: '',
291
+  faultDescription: "",
197
   faultImages: [],
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
 const rules = {
311
 const rules = {
208
-  // 步骤1
312
+  orderType: [{ required: true, message: "请选择业务类型", trigger: "change" }],
209
   serviceType: [
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
   contactPhone: [
317
   contactPhone: [
240
-    { required: true, message: '请输入电话', trigger: 'blur' },
318
+    { required: true, message: "请输入电话", trigger: "blur" },
241
     {
319
     {
242
       validator: (rule: any, value: string, callback: Function) => {
320
       validator: (rule: any, value: string, callback: Function) => {
243
         if (!value) return callback();
321
         if (!value) return callback();
@@ -245,322 +323,179 @@ const rules = {
245
         if (reg.test(value)) {
323
         if (reg.test(value)) {
246
           callback();
324
           callback();
247
         } else {
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
   expressNumber: [
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
 const selectBrandItem = (item: any) => {
363
 const selectBrandItem = (item: any) => {
295
-  formData.brand = item.id
296
-}
364
+  formData.brand = item.id;
365
+};
297
 
366
 
298
 // 选择门店
367
 // 选择门店
299
 const selectStoreItem = (item: any) => {
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
 const onDeliveryTimeChange = (item: any, index: number) => {
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
   if (selectedTime) {
379
   if (selectedTime) {
328
-    formData.delivery = selectedTime.title
380
+    formData.delivery = selectedTime.title;
329
   }
381
   }
330
-}
331
-// 模拟上传图片
382
+};
383
+
384
+// 上传图片
332
 const uploadImage = () => {
385
 const uploadImage = () => {
333
-  formData.images.push('')
334
-}
335
-// 删除图片
386
+  formData.images.push("");
387
+};
336
 const removeImage = (index: number) => {
388
 const removeImage = (index: number) => {
337
-  formData.images.splice(index, 1)
338
-}
339
-
340
-// 模拟上传图片
389
+  formData.images.splice(index, 1);
390
+};
341
 const uploadFaultImage = () => {
391
 const uploadFaultImage = () => {
342
-  formData.faultImages.push('')
343
-}
344
-// 删除故障图片
392
+  formData.faultImages.push("");
393
+};
345
 const removeFaultImage = (index: number) => {
394
 const removeFaultImage = (index: number) => {
346
-  formData.faultImages.splice(index, 1)
347
-}
395
+  formData.faultImages.splice(index, 1);
396
+};
397
+
348
 // 提交订单
398
 // 提交订单
349
 const submitOrder = async () => {
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
 </script>
409
 </script>
364
 
410
 
365
 <style lang="scss" scoped>
411
 <style lang="scss" scoped>
366
 .repair-apply {
412
 .repair-apply {
367
   min-height: 100vh;
413
   min-height: 100vh;
368
   background: #f5f7fa;
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
     background: #fff;
419
     background: #fff;
420
+    margin: 20rpx 0;
448
     padding: 32rpx;
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
         display: flex;
461
         display: flex;
533
-        flex-direction: column;
534
         align-items: center;
462
         align-items: center;
535
         justify-content: center;
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
       display: flex;
473
       display: flex;
553
       flex-direction: column;
474
       flex-direction: column;
475
+      align-items: center;
476
+      justify-content: center;
477
+      color: #999;
554
 
478
 
555
-      .express-hint {
479
+      text {
556
         font-size: 20rpx;
480
         font-size: 20rpx;
557
-        color: #999;
558
         margin-top: 8rpx;
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
   .bottom-buttons {
500
   .bottom-buttons {
566
     position: fixed;
501
     position: fixed;
@@ -570,14 +505,12 @@ const submitOrder = async () => {
570
     background: #fff;
505
     background: #fff;
571
     padding: 24rpx 32rpx;
506
     padding: 24rpx 32rpx;
572
     box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
507
     box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
573
-    display: flex;
574
-    gap: 24rpx;
575
 
508
 
576
     button {
509
     button {
577
-      flex: 1;
510
+      width: 100%;
578
       height: 88rpx;
511
       height: 88rpx;
579
       border-radius: 44rpx;
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
 <template>
1
 <template>
2
     <view class="chart-box">
2
     <view class="chart-box">
3
-        <ucharts type="area" :chartData="chartData" :opts="opts" />
3
+        <ucharts type="line" :chartData="chartData" :opts="opts" />
4
     </view>
4
     </view>
5
 </template>
5
 </template>
6
 
6
 
@@ -13,31 +13,30 @@ const props = defineProps({
13
         type: Object,
13
         type: Object,
14
         default: () => ({
14
         default: () => ({
15
             categories: [
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
             series: [
38
             series: [
39
                 {
39
                 {
40
-                    name: '',
41
                     data: [
40
                     data: [
42
                         35, 8, 25, 37, 4, 20, 35, 8, 25, 37, 4, 20, 35, 8, 25, 37, 4, 20,
41
                         35, 8, 25, 37, 4, 20, 35, 8, 25, 37, 4, 20, 35, 8, 25, 37, 4, 20,
43
                         11, 20, 15,
42
                         11, 20, 15,
@@ -52,19 +51,40 @@ const props = defineProps({
52
     },
51
     },
53
 })
52
 })
54
 const opts = {
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
     enableScroll: false,
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
     extra: {
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
     <view class="status-card">
4
     <view class="status-card">
5
       <view class="status-header">
5
       <view class="status-header">
6
         <text class="order-number">工单号:{{ orderInfo.orderId }}</text>
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
       </view>
8
       </view>
9
       <view class="status-footer">
9
       <view class="status-footer">
10
         <text class="order-time">下单时间:{{ orderInfo.createTime }}</text>
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
       </view>
12
       </view>
13
       <view class="status-service">
13
       <view class="status-service">
14
-        <text class="service-type">服务类型:{{  serviceTypeFormatter(orderInfo.serviceType) }}</text>
14
+        <text class="service-type">业务类型:{{ orderInfo.orderType }}</text>
15
       </view>
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
       </view>
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
       </view>
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
     </view>
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
           </view>
61
           </view>
70
         </view>
62
         </view>
71
       </view>
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
         </view>
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
           </view>
94
           </view>
93
         </view>
95
         </view>
94
       </view>
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
         </view>
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
         </view>
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
         </view>
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
         </view>
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
         </view>
136
         </view>
127
       </view>
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
       </view>
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
         </view>
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
         </view>
173
         </view>
144
       </view>
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
         </view>
197
         </view>
160
       </view>
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
       </view>
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
           </view>
243
           </view>
174
-          <text class="record-content">{{ record.content }}</text>
175
         </view>
244
         </view>
176
       </view>
245
       </view>
177
-    </view>
246
+    </template>
178
 
247
 
179
-    <!-- 费用明细 -->
248
+    <!-- 手表信息 -->
180
     <view class="info-section">
249
     <view class="info-section">
181
       <view class="section-header">
250
       <view class="section-header">
182
-        <text class="section-title">费用明细</text>
251
+        <text class="section-title">手表信息</text>
183
       </view>
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
         <view class="info-item">
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
         </view>
257
         </view>
193
         <view class="info-item">
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
         </view>
261
         </view>
197
         <view class="info-item">
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
         </view>
265
         </view>
201
         <view class="info-item">
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
         </view>
269
         </view>
205
         <view class="info-item">
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
         </view>
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
           </view>
279
           </view>
222
         </view>
280
         </view>
223
       </view>
281
       </view>
224
     </view>
282
     </view>
225
-
226
-    <!-- 质保期 -->
283
+    <!-- 客户信息 -->
227
     <view class="info-section">
284
     <view class="info-section">
228
       <view class="section-header">
285
       <view class="section-header">
229
-        <text class="section-title">质保期</text>
286
+        <text class="section-title">客户信息:</text>
230
       </view>
287
       </view>
231
-      <view class="section-content">
288
+      <view class="section-content col">
232
         <view class="info-item">
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
         </view>
292
         </view>
236
         <view class="info-item">
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
         </view>
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
         </view>
300
         </view>
244
       </view>
301
       </view>
245
     </view>
302
     </view>
246
 
303
 
247
     <!-- 底部按钮 -->
304
     <!-- 底部按钮 -->
248
     <view class="bottom-buttons">
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
         @click="handlePayment">
307
         @click="handlePayment">
251
         支付
308
         支付
252
       </up-button>
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
       </up-button>
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
       </up-button>
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
     </view>
359
     </view>
263
   </view>
360
   </view>
264
 </template>
361
 </template>
265
 
362
 
266
 <script setup lang="ts">
363
 <script setup lang="ts">
267
 import { ref, computed, onMounted } from 'vue'
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
 const videoUrl = ref('/static/video/2.mp4')
370
 const videoUrl = ref('/static/video/2.mp4')
272
-
273
 // 订单信息
371
 // 订单信息
274
 const orderInfo = ref({
372
 const orderInfo = ref({
275
   orderId: 'WX20260304001',
373
   orderId: 'WX20260304001',
276
-  status: OrderStatus.IN_REPAIR,
374
+  orderType: '维修',
375
+  orderTypeId: 'repair', // 'repair' | 'maintenance'
376
+  status: 'QUOTED',
277
   createTime: '2026-03-04 10:30:00',
377
   createTime: '2026-03-04 10:30:00',
278
-  paymentStatus: PaymentStatus.UNPAID,
378
+  paymentStatus: 'PAID',
279
   serviceType: 'store',
379
   serviceType: 'store',
280
   customer: {
380
   customer: {
281
     name: '张三',
381
     name: '张三',
@@ -359,30 +459,92 @@ const orderInfo = ref({
359
     startDate: '2026-03-05',
459
     startDate: '2026-03-05',
360
     endDate: '2027-03-05',
460
     endDate: '2027-03-05',
361
     coverage: '维修项目质保1年'
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
 const orderStatusColor = computed(() => {
482
 const orderStatusColor = computed(() => {
367
-  return orderStatusColors[orderInfo.value.status]
483
+  return orderStatusColors[orderStatusFormatter(orderInfo.value.status)]
368
 })
484
 })
369
-
370
 const paymentStatusColor = computed(() => {
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
 const serviceTypeFormatter = (type: string) => {
518
 const serviceTypeFormatter = (type: string) => {
376
   return type === 'store' ? '到店维修' : type === 'home' ? '上门维修' : type === 'mail' ? '邮寄维修' : '-'
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
 const handlePayment = () => {
537
 const handlePayment = () => {
381
   console.log('处理支付')
538
   console.log('处理支付')
382
 }
539
 }
383
 
540
 
384
 const handleReview = () => {
541
 const handleReview = () => {
542
+  showReviewModal.value = true
543
+}
544
+const handleSubmitReview = () => {
385
   console.log('处理评价')
545
   console.log('处理评价')
546
+  console.log(reviewValue.value, reviewCount.value)
547
+  showReviewModal.value = false
386
 }
548
 }
387
 
549
 
388
 const contactService = () => {
550
 const contactService = () => {
@@ -390,15 +552,40 @@ const contactService = () => {
390
     phoneNumber: '400-123-4567'
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
 </script>
590
 </script>
404
 
591
 
@@ -411,54 +598,65 @@ onMounted(() => {
411
 
598
 
412
   /* 顶部状态卡片 */
599
   /* 顶部状态卡片 */
413
   .status-card {
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
   .info-section {
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
     .section-content {
692
     .section-content {
482
       padding: 32rpx;
693
       padding: 32rpx;
483
 
694
 
@@ -485,7 +696,6 @@ onMounted(() => {
485
         display: flex;
696
         display: flex;
486
         justify-content: space-between;
697
         justify-content: space-between;
487
         align-items: flex-start;
698
         align-items: flex-start;
488
-        margin-bottom: 24rpx;
489
 
699
 
490
         &:last-child {
700
         &:last-child {
491
           margin-bottom: 0;
701
           margin-bottom: 0;
@@ -497,24 +707,29 @@ onMounted(() => {
497
           margin-top: 8rpx;
707
           margin-top: 8rpx;
498
         }
708
         }
499
 
709
 
710
+        &.row {
711
+          grid-column: span 2;
712
+        }
713
+
500
         .info-label {
714
         .info-label {
501
           font-size: 28rpx;
715
           font-size: 28rpx;
502
           color: #666;
716
           color: #666;
503
           flex-shrink: 0;
717
           flex-shrink: 0;
504
-          min-width: 170rpx;
718
+          // min-width: 170rpx;
505
         }
719
         }
506
 
720
 
507
         .info-value {
721
         .info-value {
508
           font-size: 28rpx;
722
           font-size: 28rpx;
509
           color: #333;
723
           color: #333;
510
           flex: 1;
724
           flex: 1;
511
-          text-align: right;
725
+          text-align: left;
512
         }
726
         }
513
 
727
 
514
         .total-value {
728
         .total-value {
515
           font-size: 32rpx;
729
           font-size: 32rpx;
516
           font-weight: bold;
730
           font-weight: bold;
517
           color: #ff6b6b;
731
           color: #ff6b6b;
732
+          text-align: right;
518
         }
733
         }
519
 
734
 
520
         /* 图片网格 */
735
         /* 图片网格 */
@@ -560,7 +775,6 @@ onMounted(() => {
560
             font-size: 26rpx;
775
             font-size: 26rpx;
561
             color: #666;
776
             color: #666;
562
             flex-shrink: 0;
777
             flex-shrink: 0;
563
-            min-width: 120rpx;
564
           }
778
           }
565
 
779
 
566
           .cost-detail-amount {
780
           .cost-detail-amount {
@@ -583,6 +797,7 @@ onMounted(() => {
583
       /* 视频区域 */
797
       /* 视频区域 */
584
       .video-section {
798
       .video-section {
585
         .video-player {
799
         .video-player {
800
+          z-index: 0;
586
           width: 100%;
801
           width: 100%;
587
           height: 400rpx;
802
           height: 400rpx;
588
           border-radius: 16rpx;
803
           border-radius: 16rpx;
@@ -671,11 +886,10 @@ onMounted(() => {
671
     box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
886
     box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
672
     display: flex;
887
     display: flex;
673
     gap: 24rpx;
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
     <view class="order-list">
2
     <view class="order-list">
3
         <!-- 搜索和筛选 -->
3
         <!-- 搜索和筛选 -->
4
         <view class="list-header">
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
             <view class="filter-bar">
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
             </view>
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
         </view>
24
         </view>
11
 
25
 
12
         <!-- 订单列表 -->
26
         <!-- 订单列表 -->
13
         <up-list @scroll-to-lower="loadMore" @refresh="refresh" :refreshing="refreshing" :loading="loading"
27
         <up-list @scroll-to-lower="loadMore" @refresh="refresh" :refreshing="refreshing" :loading="loading"
14
             loading-text="加载中..." finish-text="没有更多数据了">
28
             loading-text="加载中..." finish-text="没有更多数据了">
15
             <up-list-item v-for="(item, index) in filteredOrders" :key="item.id">
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
                         <up-swipe-action-item :threshold="100" v-model:show="swipeShow" :options="swipeOptions"
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
                                     <text :style="{ color: getStatusColor(item.status) }" class="status-text">{{
47
                                     <text :style="{ color: getStatusColor(item.status) }" class="status-text">{{
30
                                         item.status
48
                                         item.status
31
                                     }}</text>
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
                     <view v-if="popoverIndex === index" class="popover-mask" @tap.stop="closePopover"></view>
75
                     <view v-if="popoverIndex === index" class="popover-mask" @tap.stop="closePopover"></view>
@@ -67,7 +85,8 @@
67
                             <text class="popover-text">详情</text>
85
                             <text class="popover-text">详情</text>
68
                         </view>
86
                         </view>
69
                         <view class="popover-divider"></view>
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
                             <up-icon name="rmb-circle" size="32rpx" color="#FF9900"></up-icon>
90
                             <up-icon name="rmb-circle" size="32rpx" color="#FF9900"></up-icon>
72
                             <text class="popover-text">报价</text>
91
                             <text class="popover-text">报价</text>
73
                         </view>
92
                         </view>
@@ -87,20 +106,18 @@ import { onLoad } from '@dcloudio/uni-app'
87
 import {
106
 import {
88
     orderStatusColors,
107
     orderStatusColors,
89
     OrderStatus,
108
     OrderStatus,
90
-    PaymentStatus
109
+    PaymentStatus,
110
+    OrderType,
111
+    orderTypeColors,
112
+    filterTabs,
113
+    filterTypeOptions
91
 } from '../public'
114
 } from '../public'
92
 import type { OrderListItem_Result } from '@/types/repair'
115
 import type { OrderListItem_Result } from '@/types/repair'
93
 import { repairApi } from '@/apis/repair'
116
 import { repairApi } from '@/apis/repair'
94
 import { hasRole } from '@/utils/utils'
117
 import { hasRole } from '@/utils/utils'
95
 
118
 
96
 
119
 
97
-
98
-// Props
99
 const props = defineProps({
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
 const filteredOrders = computed(() => {
157
 const filteredOrders = computed(() => {
144
     return orders.value
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
 const handleSearch = () => {
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
 const getStatusColor = (status: string) => {
187
 const getStatusColor = (status: string) => {
157
     return orderStatusColors[status]
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
 const formatTime = (time: string) => {
199
 const formatTime = (time: string) => {
161
     return time
200
     return time
162
 }
201
 }
@@ -165,16 +204,16 @@ const formatDate = (date: string) => {
165
     return date.split(' ')[0]
204
     return date.split(' ')[0]
166
 }
205
 }
167
 const handleSwipeAction = (val: any, item: any) => {
206
 const handleSwipeAction = (val: any, item: any) => {
168
-    if(val.index == 0){//进度
207
+    if (val.index == 0) {//进度
169
         uni.navigateTo({
208
         uni.navigateTo({
170
             url: '/pages/repair/components/progress/index?orderId=' + item.id,
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
         uni.navigateTo({
212
         uni.navigateTo({
174
             url: '/pages/repair/components/orderDetail/index?orderId=' + item.id,
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
         uni.navigateTo({
217
         uni.navigateTo({
179
             url: '/pages/repair/components/price/index?orderId=' + item.id,
218
             url: '/pages/repair/components/price/index?orderId=' + item.id,
180
         })
219
         })
@@ -200,7 +239,7 @@ const closePopover = () => {
200
     currentPopoverItem.value = null
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
     closePopover()
243
     closePopover()
205
     handleSwipeAction(val, item)
244
     handleSwipeAction(val, item)
206
 }
245
 }
@@ -224,6 +263,8 @@ const refresh = () => {
224
     refreshing.value = true
263
     refreshing.value = true
225
     orders.value = [
264
     orders.value = [
226
         {
265
         {
266
+            orderType: '维修',
267
+            oderTypeId: 'repair',
227
             id: '1',
268
             id: '1',
228
             orderId: '202312010001',
269
             orderId: '202312010001',
229
             createTime: '2023-12-01 09:00:00',
270
             createTime: '2023-12-01 09:00:00',
@@ -234,8 +275,24 @@ const refresh = () => {
234
             customerName: '张三',
275
             customerName: '张三',
235
             estimatedCompletionTime: '2023-12-01 10:00:00',
276
             estimatedCompletionTime: '2023-12-01 10:00:00',
236
             totalAmount: 200,
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
     // repairApi.getOrderList({
298
     // repairApi.getOrderList({
@@ -247,7 +304,13 @@ const refresh = () => {
247
     //     refreshing.value = false
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
 onMounted(() => {
314
 onMounted(() => {
252
     refresh()
315
     refresh()
253
 })
316
 })
@@ -281,12 +344,14 @@ onMounted(() => {
281
         border-radius: 20rpx;
344
         border-radius: 20rpx;
282
         margin: 20rpx 20rpx;
345
         margin: 20rpx 20rpx;
283
         position: relative;
346
         position: relative;
347
+
284
         .more-btn {
348
         .more-btn {
285
             display: flex;
349
             display: flex;
286
             align-items: center;
350
             align-items: center;
287
             justify-content: flex-end;
351
             justify-content: flex-end;
288
             border-radius: 50%;
352
             border-radius: 50%;
289
         }
353
         }
354
+
290
         .order-header {
355
         .order-header {
291
             display: flex;
356
             display: flex;
292
             justify-content: space-between;
357
             justify-content: space-between;
@@ -320,7 +385,7 @@ onMounted(() => {
320
                     font-weight: bold;
385
                     font-weight: bold;
321
                 }
386
                 }
322
 
387
 
323
-                
388
+
324
             }
389
             }
325
         }
390
         }
326
 
391
 
@@ -417,6 +482,8 @@ onMounted(() => {
417
             align-items: center;
482
             align-items: center;
418
             gap: 16rpx;
483
             gap: 16rpx;
419
             padding: 20rpx 32rpx;
484
             padding: 20rpx 32rpx;
485
+            border-bottom: 1px solid #f0f0f0;
486
+
420
             &:active {
487
             &:active {
421
                 background: #f5f7fa;
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
 </style>
540
 </style>

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

@@ -114,8 +114,9 @@
114
 
114
 
115
     <!-- 底部按钮 -->
115
     <!-- 底部按钮 -->
116
     <view class="bottom-buttons">
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
     </view>
120
     </view>
120
   </view>
121
   </view>
121
 </template>
122
 </template>
@@ -185,7 +186,16 @@ const discountAmount = computed(() => {
185
 const finalAmount = computed(() => {
186
 const finalAmount = computed(() => {
186
   return totalAmount.value - discountAmount.value
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
 const addCost = () => {
200
 const addCost = () => {
191
   formData.value.costs.push({
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
 const handleLaborHoursSelect = (value: any) => {
232
 const handleLaborHoursSelect = (value: any) => {
234
   formData.value.laborHours = value.name
233
   formData.value.laborHours = value.name
@@ -383,12 +382,6 @@ onMounted(() => {
383
     box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
382
     box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
384
     display: flex;
383
     display: flex;
385
     gap: 24rpx;
384
     gap: 24rpx;
386
-
387
-    button {
388
-      flex: 1;
389
-      height: 88rpx;
390
-      border-radius: 44rpx;
391
-    }
392
   }
385
   }
393
 }
386
 }
394
 </style>
387
 </style>

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

@@ -57,12 +57,18 @@
57
 
57
 
58
     <!-- 按钮区域 -->
58
     <!-- 按钮区域 -->
59
     <view class="button-area">
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
       </up-button>
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
       </up-button>
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
     </view>
72
     </view>
67
 
73
 
68
   </view>
74
   </view>
@@ -71,6 +77,7 @@
71
 <script setup lang="ts">
77
 <script setup lang="ts">
72
 import { ref, computed, onMounted } from 'vue'
78
 import { ref, computed, onMounted } from 'vue'
73
 import {  repairStatusColors, repairProgressSteps } from '../public'
79
 import {  repairStatusColors, repairProgressSteps } from '../public'
80
+import { hasRole } from '@/utils/utils'
74
 
81
 
75
 // 订单信息
82
 // 订单信息
76
 const orderId = ref('WX20260304001')
83
 const orderId = ref('WX20260304001')
@@ -93,6 +100,20 @@ const statusColor = computed(() => {
93
   return repairStatusColors[status.value]
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
 const confirmQuote = () => {
118
 const confirmQuote = () => {
98
   status.value = '维修中'
119
   status.value = '维修中'
@@ -299,18 +320,15 @@ onMounted(() => {
299
 
320
 
300
   /* 按钮区域 */
321
   /* 按钮区域 */
301
   .button-area {
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
     display: flex;
330
     display: flex;
303
     gap: 24rpx;
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
 export const gridList = [
72
 export const gridList = [
73
   {
73
   {
74
-    name: '我要维修',
74
+    name: '手表维保',
75
     src: '/static/repair/1.png',
75
     src: '/static/repair/1.png',
76
     bgColor: '#2c94f6',
76
     bgColor: '#2c94f6',
77
     path: '/pages/repair/components/apply/index',
77
     path: '/pages/repair/components/apply/index',
@@ -99,6 +99,14 @@ export const gridList = [
99
     label: 'order'
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
 export const steps = ['选择服务', '手表信息', '故障描述', '提交订单']
110
 export const steps = ['选择服务', '手表信息', '故障描述', '提交订单']
103
 // 服务流程选项
111
 // 服务流程选项
104
 export const processSteps = [
112
 export const processSteps = [
@@ -111,10 +119,15 @@ export const processSteps = [
111
 ]
119
 ]
112
 // 服务类型选项
120
 // 服务类型选项
113
 export const services = [
121
 export const services = [
114
-  { value: 'store', name: '到店送修', desc: '前往门店进行维修', icon: '🏪' },
122
+  { value: 'store', name: '到店', desc: '前往门店进行维修', icon: '🏪' },
115
   { value: 'home', name: '上门取送', desc: '专业人员上门取送', icon: '🚪' },
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
 export const movementTypes = ['机械', '石英', '智能']
132
 export const movementTypes = ['机械', '石英', '智能']
120
 // 品牌选项
133
 // 品牌选项
@@ -136,7 +149,7 @@ export const stores = [
136
 // 故障类型选项
149
 // 故障类型选项
137
 export const faultTypeOptions = [
150
 export const faultTypeOptions = [
138
   {
151
   {
139
-    name: '停走',
152
+    name: '完全停走',
140
     disabled: false
153
     disabled: false
141
   },
154
   },
142
   {
155
   {
@@ -144,11 +157,39 @@ export const faultTypeOptions = [
144
     disabled: false
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
     disabled: false
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
     disabled: false
193
     disabled: false
153
   },
194
   },
154
   {
195
   {
@@ -156,17 +197,121 @@ export const faultTypeOptions = [
156
     disabled: false
197
     disabled: false
157
   },
198
   },
158
   {
199
   {
200
+    name: '日历卡滞',
201
+    disabled: false
202
+  },
203
+  {
204
+    name: '日历跳错',
205
+    disabled: false
206
+  },
207
+  {
159
     name: '表带损坏',
208
     name: '表带损坏',
160
     disabled: false
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
     disabled: false
245
     disabled: false
165
   },
246
   },
166
   {
247
   {
167
-    name: '其他',
248
+    name: '机芯异响',
168
     disabled: false
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
 export const repairProgressSteps = [
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
 export enum OrderStatus {
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
 export const orderStatusColors: Record<string, string> = {
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
 export enum OrderType {
399
 export enum OrderType {
228
   REPAIR = 'repair',
400
   REPAIR = 'repair',
229
   MAINTENANCE = 'maintenance',
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
   { id: '2年', name: '2年' },
463
   { id: '2年', name: '2年' },
289
   { id: '3年', name: '3年' }
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
 <template>
1
 <template>
2
   <view class="repair-home">
2
   <view class="repair-home">
3
-    <!-- 顶部导航栏 -->
4
-    <view class="nav-bar">
5
-      <text class="nav-title">手表维修服务</text>
6
-    </view>
7
 
3
 
8
     <!-- 搜索栏 -->
4
     <!-- 搜索栏 -->
9
     <view class="search-wrap">
5
     <view class="search-wrap">
@@ -16,6 +12,14 @@
16
       ></up-search>
12
       ></up-search>
17
     </view>
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
     <!-- 功能宫格(Grid 布局) -->
23
     <!-- 功能宫格(Grid 布局) -->
20
     <view class="grid-container">
24
     <view class="grid-container">
21
       <view
25
       <view
@@ -32,6 +36,24 @@
32
       </view>
36
       </view>
33
     </view>
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
     <view class="process-card">
58
     <view class="process-card">
37
       <view class="process-header">
59
       <view class="process-header">
@@ -44,29 +66,76 @@
44
         </view>
66
         </view>
45
       </view>
67
       </view>
46
     </view>
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
       <up-button type="primary" @click="handleLogout">退出登录</up-button>
72
       <up-button type="primary" @click="handleLogout">退出登录</up-button>
73
+      <up-button type="info" icon="phone" @click="callService">联系客服</up-button>
54
     </view>
74
     </view>
55
   </view>
75
   </view>
56
 </template>
76
 </template>
57
 
77
 
58
 <script setup lang="ts">
78
 <script setup lang="ts">
59
 import { ref } from 'vue'
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
 import LineChart from './components/charts/line.vue'
82
 import LineChart from './components/charts/line.vue'
63
 import { loginApi } from '@/apis/login'
83
 import { loginApi } from '@/apis/login'
84
+
85
+// 搜索关键词
64
 const searchKeyword = ref<string>('')
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
 const handleGridClick = (item: Repair_Result) => {
132
 const handleGridClick = (item: Repair_Result) => {
66
   uni.navigateTo({
133
   uni.navigateTo({
67
     url: item.path,
134
     url: item.path,
68
   })
135
   })
69
 }
136
 }
137
+
138
+// 退出登录事件
70
 const handleLogout = () => {
139
 const handleLogout = () => {
71
   uni.removeStorageSync('userInfo')
140
   uni.removeStorageSync('userInfo')
72
   uni.removeStorageSync('token')
141
   uni.removeStorageSync('token')
@@ -83,30 +152,34 @@ const handleLogout = () => {
83
     console.error('退出登录接口调用失败:', err)
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
 </script>
172
 </script>
87
 
173
 
88
 <style lang="scss" scoped>
174
 <style lang="scss" scoped>
175
+page{
176
+  height:100%;
177
+}
89
 .repair-home {
178
 .repair-home {
90
-  min-height: 100vh;
179
+  min-height: calc(100%);
91
   background: linear-gradient(180deg, #eaf5ff 0%, #f5faff 100%);
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
 .search-wrap {
184
 .search-wrap {
112
   padding: 32rpx;
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
 .grid-container {
215
 .grid-container {
121
   display: grid;
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
 .process-card {
295
 .process-card {
166
   background: #fff;
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
   padding: 32rpx;
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
 </style>
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
   label: string
8
   label: string
9
 }
9
 }
10
 export interface repair_Params {
10
 export interface repair_Params {
11
+  // 业务类型:维修 / 养护
12
+  orderType: string;
13
+  orderTypeId: string;
14
+  // 维修相关
15
+  serviceId: string;
11
   serviceType: string;
16
   serviceType: string;
12
   brand: string;
17
   brand: string;
13
   model: string;
18
   model: string;
@@ -18,6 +23,13 @@ export interface repair_Params {
18
   faultTypes: string[];
23
   faultTypes: string[];
19
   faultDescription: string;
24
   faultDescription: string;
20
   faultImages: string[];
25
   faultImages: string[];
26
+  // 养护相关
27
+  maintenanceTypes: string[];
28
+  lastMaintenanceDate: string;
29
+  maintenanceRemark: string;
30
+  maintenanceServiceType: string;
31
+  maintenanceServiceId: string;
32
+  // 联系方式
21
   contactName: string;
33
   contactName: string;
22
   contactPhone: string;
34
   contactPhone: string;
23
   address: string;
35
   address: string;
@@ -61,11 +73,13 @@ export interface RepairQuoteForm_Params {
61
 
73
 
62
 // 订单列表项类型
74
 // 订单列表项类型
63
 export interface OrderListItem_Result {
75
 export interface OrderListItem_Result {
76
+  orderType: string
77
+  oderTypeId: string
64
   id: string
78
   id: string
65
   orderId: string
79
   orderId: string
66
   type: OrderType
80
   type: OrderType
67
-  status: OrderStatus
68
-  paymentStatus: PaymentStatus
81
+  status: string
82
+  paymentStatus: string
69
   createTime: string
83
   createTime: string
70
   customerName: string
84
   customerName: string
71
   watchBrand: string
85
   watchBrand: string

+ 3 - 0
src/utils/utils.ts

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