business_tms_program 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/.editorconfig +12 -0
  2. package/.eslintrc-auto-import.json +113 -0
  3. package/.eslintrc.js +121 -0
  4. package/.prettierrc.js +9 -0
  5. package/.stylelintignore +4 -0
  6. package/README.md +43 -0
  7. package/components.d.ts +23 -0
  8. package/index.html +20 -0
  9. package/package.json +70 -0
  10. package/shims-uni.d.ts +10 -0
  11. package/src/App.vue +81 -0
  12. package/src/api/afterSale.ts +184 -0
  13. package/src/api/context.ts +26 -0
  14. package/src/api/device.ts +134 -0
  15. package/src/api/index.ts +80 -0
  16. package/src/api/installtion.ts +155 -0
  17. package/src/api/model/index.ts +15 -0
  18. package/src/api/model/userModel.ts +62 -0
  19. package/src/api/order.ts +49 -0
  20. package/src/api/system.ts +19 -0
  21. package/src/api/user.ts +171 -0
  22. package/src/auto-imports.d.ts +108 -0
  23. package/src/components/ConfirmDialog.vue +101 -0
  24. package/src/components/DaySelect.vue +212 -0
  25. package/src/components/Drawer.vue +104 -0
  26. package/src/components/DrawerSelect.vue +105 -0
  27. package/src/components/DropMenu.vue +144 -0
  28. package/src/components/Empty.vue +49 -0
  29. package/src/components/Loading.vue +41 -0
  30. package/src/components/RippleBtn.vue +159 -0
  31. package/src/components/SinglePick.vue +120 -0
  32. package/src/components/Skeleton.vue +43 -0
  33. package/src/components/Timeline.vue +85 -0
  34. package/src/components/Upload.vue +217 -0
  35. package/src/config/app.ts +32 -0
  36. package/src/config/env.ts +29 -0
  37. package/src/dict/afterSale.ts +161 -0
  38. package/src/dict/device.ts +29 -0
  39. package/src/dict/installtion.ts +141 -0
  40. package/src/dict/systems.ts +4 -0
  41. package/src/env.d.ts +8 -0
  42. package/src/hooks/useForm.ts +222 -0
  43. package/src/hooks/useUpload.ts +80 -0
  44. package/src/main.ts +8 -0
  45. package/src/manifest.json +39 -0
  46. package/src/pages/acceptance/DeviceInfo.vue +132 -0
  47. package/src/pages/acceptance/list.vue +276 -0
  48. package/src/pages/afterSale/DeviceInfo.vue +128 -0
  49. package/src/pages/afterSale/Step.vue +0 -0
  50. package/src/pages/afterSale/faultReport.vue +552 -0
  51. package/src/pages/afterSale/orderDetail.vue +327 -0
  52. package/src/pages/afterSale/orderFinish.vue +517 -0
  53. package/src/pages/afterSale/orderList.vue +305 -0
  54. package/src/pages/afterSale/returnVisit.vue +288 -0
  55. package/src/pages/afterSale/searchDeviceList.vue +148 -0
  56. package/src/pages/device/Search.vue +201 -0
  57. package/src/pages/device/acceptance.vue +270 -0
  58. package/src/pages/device/detail.vue +165 -0
  59. package/src/pages/device/index.vue +322 -0
  60. package/src/pages/device/info.vue +140 -0
  61. package/src/pages/device/list.vue +219 -0
  62. package/src/pages/device/materialTowerCode.vue +589 -0
  63. package/src/pages/device/searchList.vue +224 -0
  64. package/src/pages/installtion/Record.vue +145 -0
  65. package/src/pages/installtion/StatusTimeline.vue +85 -0
  66. package/src/pages/installtion/addAcceptance.vue +409 -0
  67. package/src/pages/installtion/addRecord.vue +338 -0
  68. package/src/pages/installtion/orderDetail.vue +220 -0
  69. package/src/pages/installtion/orderList.vue +100 -0
  70. package/src/pages/user/component/PersonAgree.vue +226 -0
  71. package/src/pages/user/component/PrivayAgree.vue +221 -0
  72. package/src/pages/user/component/SliderCode.vue +173 -0
  73. package/src/pages/user/forgetPassword.vue +249 -0
  74. package/src/pages/user/index.vue +139 -0
  75. package/src/pages/user/login.vue +342 -0
  76. package/src/pages/user/register.vue +348 -0
  77. package/src/pages/user/repassword.vue +329 -0
  78. package/src/pages/user/utils/mcaptcha.js +75 -0
  79. package/src/pages/user/utils/verifyCode.ts +41 -0
  80. package/src/pages/workspace/index.vue +225 -0
  81. package/src/pages.json +203 -0
  82. package/src/shime-uni.d.ts +6 -0
  83. package/src/static/icon/system/breeder_icon.png +0 -0
  84. package/src/static/icon/system/check.png +0 -0
  85. package/src/static/icon/system/factory_icon.png +0 -0
  86. package/src/static/icon/system/plus.png +0 -0
  87. package/src/static/icon/system/right.png +0 -0
  88. package/src/static/icon/system/unCheck.png +0 -0
  89. package/src/static/icon/tab/search.png +0 -0
  90. package/src/static/icon/tab/user.png +0 -0
  91. package/src/static/icon/tab/user_active.png +0 -0
  92. package/src/static/icon/tab/workspace.png +0 -0
  93. package/src/static/icon/tab/workspace_active.png +0 -0
  94. package/src/static/img/active_dot.png +0 -0
  95. package/src/static/img/afterSale_icon.png +0 -0
  96. package/src/static/img/check.png +0 -0
  97. package/src/static/img/close.png +0 -0
  98. package/src/static/img/confirm.png +0 -0
  99. package/src/static/img/empty.png +0 -0
  100. package/src/static/img/equipment_icon.png +0 -0
  101. package/src/static/img/fault_icon.png +0 -0
  102. package/src/static/img/install_icon.png +0 -0
  103. package/src/static/img/login_bg2.png +0 -0
  104. package/src/static/img/movable_right.png +0 -0
  105. package/src/static/img/navigation.png +0 -0
  106. package/src/static/img/psw_off.png +0 -0
  107. package/src/static/img/psw_on.png +0 -0
  108. package/src/static/img/scan.png +0 -0
  109. package/src/static/img/scan_icon.png +0 -0
  110. package/src/static/img/search.png +0 -0
  111. package/src/static/img/turn_right.png +0 -0
  112. package/src/static/img/unActive_dot.png +0 -0
  113. package/src/static/img/verifyBg.png +0 -0
  114. package/src/stores/index.ts +11 -0
  115. package/src/stores/modules/customer.ts +146 -0
  116. package/src/stores/modules/installtion.ts +30 -0
  117. package/src/stores/modules/system.ts +56 -0
  118. package/src/stores/modules/user.ts +133 -0
  119. package/src/stores/types.ts +16 -0
  120. package/src/stores/utils.ts +6 -0
  121. package/src/styles/index.less +63 -0
  122. package/src/types/chengyiApi.d.ts +36 -0
  123. package/src/types/index.d.ts +95 -0
  124. package/src/utils/address.ts +17 -0
  125. package/src/utils/cipher.ts +61 -0
  126. package/src/utils/form.ts +155 -0
  127. package/src/utils/httpEnum.ts +31 -0
  128. package/src/utils/image.ts +21 -0
  129. package/src/utils/index.ts +111 -0
  130. package/src/utils/request.ts +139 -0
  131. package/src/utils/requestCancelHandle.ts +67 -0
  132. package/stylelint.config.js +87 -0
  133. package/tsconfig.docs.json +11 -0
  134. package/tsconfig.json +30 -0
  135. package/typedoc.json +6 -0
  136. package/vite.config.ts +55 -0
@@ -0,0 +1,329 @@
1
+ <script setup lang="ts">
2
+ import { useForm } from '@/hooks/useForm';
3
+ import { reactive } from 'vue';
4
+ import { type ValidationRules, validatePsw, verifyCodeRule } from '@/utils/form';
5
+ import { useVerifyCode } from "./utils/verifyCode";
6
+ import { throttle } from 'lodash-es';
7
+ import RippleBtn from '@/components/RippleBtn.vue';
8
+ import { type resetPasswordParams, resetPassword, getResetPswAuth } from '@/api/user';
9
+ import { sendResetPasswordCode } from '@/api/system';
10
+ import { useUserStore } from '@/stores/modules/user';
11
+ import SliderCode from './component/SliderCode.vue';
12
+
13
+ defineOptions({
14
+ name: "rePassword"
15
+ });
16
+ const validationRules: ValidationRules<Partial<resetPasswordParams>> = {
17
+ mobile: {
18
+ required: true,
19
+ message: '请输入手机号码',
20
+ validatFunc: (value: string): any => {
21
+ return new Promise((resolve, reject) => {
22
+ if (!value) {
23
+ reject('请输入手机号码')
24
+ } else if (!/^1[3-9]\d{9}$/.test(value)) {
25
+ reject('请输入正确的手机号码')
26
+ } else {
27
+ state.isPhoneValidateCode = true;
28
+ resolve(true)
29
+ }
30
+ })
31
+ }
32
+ },
33
+ code: { required: true, validatFunc: (value: any): any => {
34
+ return new Promise(async(resolve, reject) => {
35
+ if (value === "") {
36
+ state.isValidateCode = false;
37
+ reject("请输入验证码");
38
+ return
39
+ } if (!/^\d{6}$/.test(value)) {
40
+ state.isValidateCode = false;
41
+ reject("验证码格式不正确");
42
+ return
43
+ }
44
+ getResetPswAuth({ mobile: formData.mobile, code: value }).then(res => {
45
+ if (!res) {
46
+ state.isValidateCode = false;
47
+ reject("验证码验证失败");
48
+ } else {
49
+ resolve(true);
50
+ state.isValidateCode = true;
51
+ }
52
+ })
53
+ }
54
+ )}
55
+ },
56
+ password: { required: true, validatFunc: validatePsw },
57
+ confirmPassword: { required: true, validatFunc: (value: string): any => {
58
+ return new Promise(async(resolve, reject) => {
59
+ if (value === "") {
60
+ reject("请输入");
61
+ } else if (value !== formData.password) {
62
+ reject("两次密码不一致");
63
+ } else {
64
+ resolve(true);
65
+ }
66
+ }
67
+ );
68
+ }}
69
+ }
70
+
71
+ const initialData = {
72
+ "mobile": undefined,
73
+ "code": undefined,
74
+ "password": '',
75
+ "confirmPassword": ''
76
+ } as resetPasswordParams;
77
+
78
+ const focusState = reactive({
79
+ mobile: false,
80
+ code: false,
81
+ password: false,
82
+ confirmPassword: false
83
+ })
84
+ const useStore = useUserStore();
85
+ const { text, end } = useVerifyCode();
86
+ const { formData, errState, setFormItem, validateForm, validateOneField } = useForm<resetPasswordParams>(
87
+ initialData,
88
+ validationRules,
89
+ true,
90
+ )
91
+ const changeValue = (key: keyof resetPasswordParams, detail: any) => {
92
+ setFormItem(key, detail.value);
93
+ }
94
+ const state = reactive({
95
+ showPassword: false,
96
+ isValidateCode: false,
97
+ isPhoneValidateCode: false,
98
+ isSliderValidate: false,
99
+ showCaptcha: false
100
+ })
101
+ const submitForm = throttle(async() => {
102
+ const validate = await validateForm();
103
+ if (!validate) {
104
+ return
105
+ }
106
+ const res = await resetPassword(formData)
107
+ if (res) {
108
+ uni.showToast({ title: '重置成功,登录中', icon:'success' })
109
+ useStore.toLogin({
110
+ name: formData.mobile,
111
+ password: formData.password
112
+ })
113
+ }
114
+ }, 1000)
115
+
116
+ const placeholderStyle = `color: rgba(0, 0, 0, 0.4)`;
117
+ const togglePasswordVisibility = () => {
118
+ state.showPassword = !state.showPassword;
119
+ }
120
+ const clearIcon = (type: keyof resetPasswordParams) => {
121
+ setFormItem(type, '')
122
+ }
123
+ const getVerifyCode = async() => {
124
+ const validate = await validateOneField('mobile')
125
+ if (!validate) {
126
+ return
127
+ }
128
+ if (state.isSliderValidate) {
129
+ successVlidate()
130
+ return
131
+ }
132
+ state.showCaptcha = true;
133
+ }
134
+ const successVlidate = async() => {
135
+ state.isSliderValidate = true;
136
+ const res = await sendResetPasswordCode({ mobile: formData.mobile })
137
+ if (res) {
138
+ uni.showToast({ title: '验证码已发送', icon:'success' })
139
+ useVerifyCode().start()
140
+ }
141
+ }
142
+ onShow(() => {
143
+ end()
144
+ })
145
+ </script>
146
+
147
+ <template>
148
+ <view class="content">
149
+ <form class="form-content">
150
+ <view class="systemName">忘记密码</view>
151
+ <view class="uni-form-item uni-column">
152
+ <input class="uni-input"
153
+ :class="focusState.mobile ? 'focus' : ''"
154
+ :placeholder-style="placeholderStyle" type="text" placeholder="请输入手机号"
155
+ :value="formData['mobile']"
156
+ @input="(e: any) => changeValue('mobile', e.detail)"
157
+ @focus="focusState.mobile = true"
158
+ @blur="focusState.mobile = false"
159
+ />
160
+ <image class="uni-icon"
161
+ src="/static/img/close.png"
162
+ v-if="formData['mobile']"
163
+ @click="clearIcon('mobile')">
164
+ </image>
165
+ </view>
166
+ <view class="err-text" v-if="errState.mobile">{{errState.mobile}}</view>
167
+ <SliderCode v-model="state.showCaptcha" @successVlidate="successVlidate" />
168
+
169
+ <view class="uni-form-item uni-column">
170
+ <input class="uni-input"
171
+ :class="focusState.code ? 'focus' : ''"
172
+ :placeholder-style="placeholderStyle" placeholder="请输入验证码"
173
+ @input="(e: any) => changeValue('code', e.detail)"
174
+ @focus="focusState.code = true"
175
+ @blur="focusState.code = false"
176
+ />
177
+ <view class="getCode">
178
+ <view v-if="text.length > 0">{{`${text}s`}}</view>
179
+ <view v-else @click="getVerifyCode()">获取验证码</view>
180
+ <image class="confirm-icon"
181
+ v-if="state.isValidateCode"
182
+ src="/static/img/confirm.png"
183
+ ></image>
184
+ </view>
185
+ </view>
186
+ <view class="err-text" v-if="errState.code">{{errState.code}}</view>
187
+ <view class="uni-form-item uni-column">
188
+ <input class="uni-input"
189
+ :class="focusState.password ? 'focus' : ''"
190
+ :placeholder-style="placeholderStyle"
191
+ :password="!state.showPassword" placeholder="请输入密码"
192
+ @input="(e: any) => changeValue('password', e.detail)"
193
+ @focus="focusState.password = true"
194
+ @blur="focusState.password = false"
195
+ />
196
+ <image class="uni-icon" :src="
197
+ !state.showPassword
198
+ ? '/static/img/psw_off.png'
199
+ : '/static/img/psw_on.png'
200
+ " @click="togglePasswordVisibility"></image>
201
+ </view>
202
+ <view class="err-text" v-if="errState.password">{{errState.password}}</view>
203
+
204
+ <view class="uni-form-item uni-column">
205
+ <input class="uni-input"
206
+ :class="focusState.confirmPassword ? 'focus' : ''"
207
+ :placeholder-style="placeholderStyle" password placeholder="请确认密码"
208
+ @input="(e: any) => changeValue('confirmPassword', e.detail)"
209
+ @focus="focusState.confirmPassword = true"
210
+ @blur="focusState.confirmPassword = false"
211
+ />
212
+ </view>
213
+ <view class="err-text" v-if="errState.confirmPassword">{{errState.confirmPassword}}</view>
214
+ </form>
215
+ <RippleBtn @click="submitForm" :customStyle="'padding-bottom: 10rpx'">
216
+ <view class="submit-btn">重置密码</view>
217
+ </RippleBtn>
218
+ </view>
219
+ </template>
220
+
221
+ <style scoped>
222
+ .content {
223
+ display: flex;
224
+ flex-direction: column;
225
+ background-color: #fff;
226
+ .form-content{
227
+ margin-bottom: 216rpx;
228
+ padding: 0 48rpx;
229
+ }
230
+ .systemName{
231
+ margin-top: 64rpx;
232
+ margin-bottom: 64rpx;
233
+ font-family: Source Han Sans SC, Source Han Sans SC;
234
+ font-weight: bold;
235
+ font-size: 48rpx;
236
+ color: #000000;
237
+ line-height: 70rpx;
238
+ }
239
+ .uni-form-item {
240
+ display: flex;
241
+ justify-content: space-between;
242
+ width: 100%;
243
+ margin-top: 40rpx;
244
+ padding: 0 16rpx;
245
+ position: relative;
246
+ .uni-input {
247
+ flex: 1;
248
+ padding: 12rpx 0;
249
+ border-bottom: 1rpx solid rgba(0,0,0,0.05);
250
+ font-family: Source Han Sans SC, Source Han Sans SC;
251
+ font-weight: bold;
252
+ font-size: 32rpx;
253
+ color: black;
254
+ line-height: 48rpx;
255
+ text-align: left;
256
+ font-style: normal;
257
+ &.focus {
258
+ border-bottom-color: #1D6FE9 ; /* 聚焦时改变下划线颜色 */
259
+ outline: none; /* 移除默认的聚焦轮廓 */
260
+ }
261
+ }
262
+ .eyeBox{
263
+ position: absolute;
264
+ right: 16rpx;
265
+ top: 12rpx;
266
+ width: 120rpx;
267
+ height: 48rpx;
268
+ }
269
+ .uni-icon{
270
+ position: absolute;
271
+ right: 32rpx;
272
+ bottom: 16rpx;
273
+ width: 32rpx;
274
+ height: 32rpx;
275
+ z-index: 5;
276
+ }
277
+ .getCode{
278
+ position: absolute;
279
+ right: 32rpx;
280
+ bottom: 16rpx;
281
+ font-size: 28rpx;
282
+ color: #1D6FE9;
283
+ line-height: 40rpx;
284
+ display: flex;
285
+ align-items: center;
286
+ z-index: 5;
287
+ }
288
+ .confirm-icon{
289
+ width: 36rpx;
290
+ height: 36rpx;
291
+ margin-left: 16rpx;
292
+ }
293
+ }
294
+ .err-text{
295
+ margin-top: 8rpx;
296
+ padding-left: 16rpx;
297
+ font-size: 24rpx;
298
+ color: rgb(250, 100, 100);
299
+ }
300
+ .submit-btn{
301
+ margin-left: 32rpx;
302
+ padding: 24rpx 262rpx 26rpx 262rpx;
303
+ line-height: 58rpx;
304
+ font-family: Source Han Sans SC, Source Han Sans SC;
305
+ font-weight: 400;
306
+ font-size: 40rpx;
307
+ color: rgba(255, 255, 255, 0.9);
308
+ background: linear-gradient( 270deg, #1D9DE9 0%, #1D6FE9 100%);
309
+ box-shadow: 0px 16rpx 16rpx 2rpx rgba(29,157,233,0.1);
310
+ border-radius: 52rpx;
311
+ }
312
+ .otherControl{
313
+ display: flex;
314
+ justify-content: space-between;
315
+ margin-top: 28rpx;
316
+ margin-bottom: 244rpx;
317
+ padding: 0 64rpx;
318
+ font-size: 28rpx;
319
+ color: rgba(0,0,0,0.4);
320
+ line-height: 40rpx;
321
+ }
322
+ .agreement{
323
+ font-size: 24rpx;
324
+ color: rgba(0,0,0,0.2);
325
+ line-height: 34rpx;
326
+ text-align: center;
327
+ }
328
+ }
329
+ </style>
@@ -0,0 +1,75 @@
1
+ // mcaptcha.js
2
+
3
+ export class Mcaptcha {
4
+ constructor(options) {
5
+ this.options = options;
6
+ this.fontSize = options.height * 3 / 6;
7
+ this.init();
8
+ this.refresh();
9
+ }
10
+ init() {
11
+ this.ctx = uni.createCanvasContext(this.options.el);
12
+ this.ctx.setTextBaseline("middle");
13
+ this.ctx.setFillStyle(this.randomColor(180, 240));
14
+ }
15
+ refresh() {
16
+ var code = '';
17
+ var txtArr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q','r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O','P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',0,1,2,3,4,5,6,7,8,9]
18
+ for(var i=0;i<4;i++){
19
+ code += txtArr[this.randomNum(0, txtArr.length)];
20
+ }
21
+ this.options.createCodeImg = code;
22
+ let arr = (code + '').split('');
23
+ if (arr.length === 0) {
24
+ arr = ['e', 'r', 'r','o','r'];
25
+ };
26
+ let offsetLeft = this.options.width * 0.6 / (arr.length - 1);
27
+ let marginLeft = this.options.width * 0.2;
28
+ arr.forEach((item, index) => {
29
+ this.ctx.setFillStyle(this.randomColor(0, 180));
30
+ let size = this.randomNum(24, this.fontSize);
31
+ this.ctx.setFontSize(size);
32
+ let dis = offsetLeft * index + marginLeft - size * 0.3;
33
+ let deg = this.randomNum(-30, 30);
34
+ this.ctx.translate(dis, this.options.height*0.5);
35
+ this.ctx.rotate(deg * Math.PI / 180);
36
+ this.ctx.fillText(item, 0, 0);
37
+ this.ctx.rotate(-deg * Math.PI / 180);
38
+ this.ctx.translate(-dis, -this.options.height * 0.5);
39
+ })
40
+ for (var i = 0; i < 4; i++) {
41
+ this.ctx.strokeStyle = this.randomColor(40, 180);
42
+ this.ctx.beginPath();
43
+ this.ctx.moveTo(this.randomNum(0, this.options.width), this.randomNum(0, this.options.height));
44
+ this.ctx.lineTo(this.randomNum(0, this.options.width), this.randomNum(0, this.options.height));
45
+ this.ctx.stroke();
46
+ }
47
+ for (var i = 0; i < this.options.width / 4; i++) {
48
+ this.ctx.fillStyle = this.randomColor(0, 255);
49
+ this.ctx.beginPath();
50
+ this.ctx.arc(this.randomNum(0, this.options.width), this.randomNum(0, this.options.height), 1, 0, 2 * Math.PI);
51
+ this.ctx.fill();
52
+ }
53
+ this.ctx.draw();
54
+ }
55
+ validate(code){
56
+ var code = code.toLowerCase();
57
+ var v_code = this.options.createCodeImg.toLowerCase();
58
+ console.log(code)
59
+ console.log(v_code.substring(v_code.length - 4))
60
+ if (code == v_code.substring(v_code.length - 4)) {
61
+ return true;
62
+ } else {
63
+ return false;
64
+ }
65
+ }
66
+ randomNum(min, max) {
67
+ return Math.floor(Math.random() * (max - min) + min);
68
+ }
69
+ randomColor(min, max) {
70
+ let r = this.randomNum(min, max);
71
+ let g = this.randomNum(min, max);
72
+ let b = this.randomNum(min, max);
73
+ return "rgb(" + r + "," + g + "," + b + ")";
74
+ }
75
+ }
@@ -0,0 +1,41 @@
1
+ import { ref } from "vue";
2
+
3
+ const isDisabled = ref(false);
4
+ const timer = ref<number | undefined>(undefined);
5
+ const text = ref("");
6
+
7
+ export const useVerifyCode = () => {
8
+ const start = async (
9
+ time = 60
10
+ ) => {
11
+ const initTime = time;
12
+ clearInterval(timer.value);
13
+ isDisabled.value = true;
14
+ text.value = `${time}`;
15
+ timer.value = setInterval(() => {
16
+ if (time > 0) {
17
+ time -= 1;
18
+ text.value = `${time}`;
19
+ } else {
20
+ text.value = "";
21
+ isDisabled.value = false;
22
+ clearInterval(timer.value);
23
+ time = initTime;
24
+ }
25
+ }, 1000);
26
+ };
27
+
28
+ const end = () => {
29
+ text.value = "";
30
+ isDisabled.value = false;
31
+ clearInterval(timer.value);
32
+ };
33
+
34
+ return {
35
+ isDisabled,
36
+ timer,
37
+ text,
38
+ start,
39
+ end
40
+ };
41
+ };
@@ -0,0 +1,225 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import { getOrderList } from '@/api/afterSale'
4
+ import { getInstalltionStatusOrderList } from '@/api/installtion'
5
+ import { installAcceptanceStatusEnum } from '@/dict/installtion'
6
+ import Skeleton from '@/components/Skeleton.vue';
7
+ import { workStatus as workStatusEnum } from '@/dict/afterSale'
8
+ import { useUserStore } from '@/stores/modules/user';
9
+
10
+ defineOptions({
11
+ name: "workspace"
12
+ });
13
+ const useStore = useUserStore();
14
+ const itemList = [
15
+ {
16
+ title: '故障提报',
17
+ icon: '/static/img/fault_icon.png',
18
+ url: '/pages/afterSale/faultReport',
19
+ type: 'faultReport'
20
+ },
21
+ {
22
+ title: '售后工单',
23
+ icon: '/static/img/afterSale_icon.png',
24
+ url: '/pages/afterSale/orderList',
25
+ type: 'afterSale',
26
+ hasNum: true
27
+ },
28
+ {
29
+ title: '安装工单',
30
+ icon: '/static/img/install_icon.png',
31
+ url: '/pages/installtion/orderList',
32
+ type: 'installOrder',
33
+ hasNum: true
34
+ },
35
+ {
36
+ title: '扫一扫',
37
+ icon: '/static/img/scan_icon.png',
38
+ url: '/pages/workspace/orderList',
39
+ type: 'scan'
40
+ },
41
+ {
42
+ title: '设备管理',
43
+ icon: '/static/img/equipment_icon.png',
44
+ url: '/pages/device/index',
45
+ type:'equipment'
46
+ },
47
+ ]
48
+ const perms = computed(() => useStore.getPermsList)
49
+ const state = reactive({
50
+ afterSaleOrderNum: 0, // 售后工单数量
51
+ installNum: 0, // 安装工单数量
52
+ loading: false
53
+ })
54
+ const menuList = computed(() => {
55
+ return itemList.filter((item) => {
56
+ return perms.value.includes(item.url.slice(1).replaceAll('/', ':'))
57
+ })
58
+ })
59
+ const getAfterSaleOrderNum = async () => {
60
+ const res = await getOrderList({
61
+ size: 10,
62
+ current: 1,
63
+ status: workStatusEnum.uncomplete,
64
+ });
65
+ const { total } = res
66
+ if (!res) {
67
+ uni.showToast({
68
+ title: '获取售后工单数量失败',
69
+ icon: 'none'
70
+ })
71
+ return
72
+ }
73
+ state.afterSaleOrderNum = total
74
+ }
75
+ const getInstallNum = async () => {
76
+ state.loading = true
77
+ const res = await getInstalltionStatusOrderList({
78
+ size: 10,
79
+ current: 1,
80
+ status: installAcceptanceStatusEnum.NO,
81
+ });
82
+ const { total } = res
83
+ state.loading = false
84
+ if (!res) {
85
+ uni.showToast({
86
+ title: '获取安装工单数量失败',
87
+ icon: 'none'
88
+ })
89
+ return
90
+ }
91
+ state.installNum = total
92
+ }
93
+ const toScan = () => {
94
+ uni.scanCode({
95
+ scanType: ['barCode', 'qrCode', 'wxCode'],
96
+ success: (res: UniApp.ScanCodeSuccessRes) => {
97
+ const { scanType, result } = res;
98
+ if (scanType === "QR_CODE" && result) {
99
+ // 截取url的deviceSn参数
100
+ const hash = result.split("?")[1] || "";
101
+ const deviceSn = hash.replaceAll("deviceSn=", '');
102
+ if (deviceSn) {
103
+ uni.navigateTo({
104
+ url: `/pages/device/detail?deviceCode=${deviceSn}`
105
+ });
106
+ return;
107
+ }
108
+ }
109
+ if (!res.result) {
110
+ uni.showToast({
111
+ title: '未识别到设备',
112
+ icon: 'none'
113
+ });
114
+ return;
115
+ }
116
+ if (res.scanType === "CODE_128") {
117
+ uni.navigateTo({
118
+ url: `/pages/device/detail?deviceCode=${res.result}`
119
+ })
120
+ }
121
+ },
122
+ fail: (err) => {
123
+ uni.showToast({
124
+ title: '扫码失败',
125
+ icon: 'none'
126
+ });
127
+ }
128
+ });
129
+ }
130
+ const navigateTo = (item: any) => {
131
+ var b = ssss
132
+ if (item.type === 'scan') {
133
+ toScan()
134
+ return
135
+ }
136
+ uni.navigateTo({
137
+ url: item.url
138
+ })
139
+ }
140
+ onShow(async () => {
141
+ useStore.getUserInfoFunc(() => {
142
+ getAfterSaleOrderNum()
143
+ getInstallNum()
144
+ })
145
+ uni.$on('workspace-updated', () => {
146
+ getAfterSaleOrderNum(); // 刷新数据
147
+ getInstallNum()
148
+ });
149
+ })
150
+ </script>
151
+
152
+ <template>
153
+ <view class="content">
154
+ <view class="title">常用功能</view>
155
+ <view class="skeleton-box" v-if="state.loading">
156
+ <Skeleton></Skeleton>
157
+ <Skeleton></Skeleton>
158
+ <Skeleton></Skeleton>
159
+ </view>
160
+ <view class="grid-container" v-else>
161
+ <view class="grid-item" v-for="item in menuList" :key="item.type" @click="navigateTo(item)">
162
+ <image class="uni-icon" :src="item.icon"></image>
163
+ <view v-if="item.hasNum" class="num">
164
+ {{ item.type === 'afterSale' ? state.afterSaleOrderNum : state.installNum}}
165
+ </view>
166
+ {{ item.title }}
167
+ </view>
168
+ </view>
169
+ </view>
170
+ </template>
171
+
172
+ <style scoped>
173
+ .content {
174
+ background-color: #F5F5F5;
175
+ min-height: 100vh;
176
+ padding: 32rpx;
177
+ .title {
178
+ font-size: 36rpx;
179
+ color: #000000;
180
+ line-height: 52rpx;
181
+ font-weight: 500;
182
+ }
183
+ .grid-container {
184
+ display: flex;
185
+ flex-wrap: wrap;
186
+ margin-top: 32rpx;
187
+ background-color: #fff;
188
+ border-radius: 16rpx;
189
+ }
190
+ .grid-item {
191
+ flex: 0 0 33.3333%;
192
+ display: flex;
193
+ flex-direction: column;
194
+ align-items: center;
195
+ box-sizing: border-box;
196
+ padding: 44rpx 0;
197
+ font-size: 28rpx;
198
+ color: rgba(0,0,0,0.85);
199
+ line-height: 44rpx;
200
+ position: relative;
201
+ .num{
202
+ position: absolute;
203
+ top: 32rpx;
204
+ right: 72rpx;
205
+ padding: 2rpx 12rpx 2rpx 10rpx;
206
+ font-weight: 500;
207
+ font-size: 20rpx;
208
+ color: #FFFFFF;
209
+ line-height: 26rpx;
210
+ z-index: 3;
211
+ background: #E34D59;
212
+ border-radius: 32rpx;
213
+ }
214
+ }
215
+ .uni-icon {
216
+ margin-bottom: 12rpx;
217
+ width: 80rpx;
218
+ height: 80rpx;
219
+ }
220
+ .skeleton-box {
221
+ margin-top: 32rpx;
222
+ border-radius: 16rpx;
223
+ }
224
+ }
225
+ </style>