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,222 @@
1
+ import { reactive } from 'vue'
2
+ import { throttle } from 'lodash-es'
3
+ import { scrollToFirstError } from '@/utils/form'
4
+ // 定义校验规则类型
5
+ export interface ValidationRule<T = any> {
6
+ required?: boolean;
7
+ pattern?: RegExp;
8
+ message?: string;
9
+ validatFunc?: (
10
+ value: T
11
+ ) => boolean | string | Promise<boolean> | Promise<string>;
12
+ }
13
+
14
+ // 定义校验规则对象类型
15
+ export type ValidationRules<T = any> = {
16
+ [K in keyof T]?: ValidationRule<T[K]>
17
+ }
18
+ /**
19
+ * 表单钩子函数,提供表单数据管理、验证和重置功能
20
+ * @template T 表单数据类型
21
+ * @param initialData 表单初始数据
22
+ * @param rules 表单验证规则
23
+ * @param validateScroll 是否在验证失败时滚动到第一个错误字段
24
+ * @returns 返回表单相关方法和状态
25
+ */
26
+ export function useForm<T extends Record<string, any>>(
27
+ initialData: T,
28
+ rules: ValidationRules<T>,
29
+ validateScroll: boolean = false
30
+ ) {
31
+ // 表单数据
32
+ const formData = reactive<T>({ ...initialData }) as T
33
+
34
+ // 错误状态类型定义
35
+ const errState: any = reactive(
36
+ Object.keys(initialData).reduce((acc, key) => {
37
+ acc[key as keyof T] = ''
38
+ return acc
39
+ }, {} as Record<keyof T, string>)
40
+ )
41
+
42
+ /**
43
+ * 验证整个表单
44
+ * @returns 表单是否验证通过
45
+ */
46
+ const validateForm = async() => {
47
+ if (!rules) return true
48
+ const [isValid, firstErrorKey] = await formValidateFunc()
49
+ if (validateScroll && firstErrorKey) {
50
+ scrollToFirstError(firstErrorKey)
51
+ }
52
+ return isValid
53
+ }
54
+
55
+ /**
56
+ * 重置表单到初始状态
57
+ */
58
+ const resetForm = () => {
59
+ Object.keys(initialData).forEach(key => {
60
+ formData[key as keyof T] = initialData[key as keyof T]
61
+ errState[key as keyof T] = ''
62
+ })
63
+ }
64
+
65
+ /**
66
+ * 设置表单字段值
67
+ * @param key 字段键名
68
+ * @param value 字段值
69
+ */
70
+ const setFormItem = (key: keyof T, value: T[keyof T]) => {
71
+ formData[key] = value
72
+ const validateField = throttle(() => {
73
+ if (!rules || !rules[key]) return
74
+ validateOneField(key)
75
+ }, 300)
76
+ validateField()
77
+ }
78
+
79
+ /**
80
+ * 初始化表单数据
81
+ * @param data 初始数据
82
+ */
83
+ const initialForm = (data: Partial<T>) => {
84
+ Object.keys(data).forEach(key => {
85
+ formData[key as keyof T] = data[key as keyof T] || initialData[key as keyof T]
86
+ })
87
+ }
88
+ /**
89
+ * 异步验证单个字段
90
+ * @param key 字段键名
91
+ * @returns 字段是否验证通过
92
+ */
93
+ const validateOneField = async (
94
+ key: keyof T,
95
+ ): Promise<boolean> => {
96
+ let isValid = true;
97
+ const value = formData[key];
98
+ const rule = rules[key];
99
+ if (!rule) {
100
+ return isValid;
101
+ }
102
+
103
+ // 处理数组类型
104
+ if (Array.isArray(value)) {
105
+ if (rule.required && value.length === 0) {
106
+ errState[key] = rule.message;
107
+ isValid = false;
108
+ } else if (rule.validatFunc) {
109
+ try {
110
+ const result = await Promise.resolve(rule.validatFunc(value));
111
+ if (!result) {
112
+ errState[key] = rule.message;
113
+ isValid = false;
114
+ } else {
115
+ errState[key] = '';
116
+ isValid = true;
117
+ }
118
+ } catch (error) {
119
+ // 捕获reject的错误信息
120
+ errState[key] = typeof error === 'string' ? error : rule.message;
121
+ isValid = false;
122
+ }
123
+ } else {
124
+ // 数组类型,没有validatFunc且必填校验通过,清空错误状态
125
+ errState[key] = '';
126
+ isValid = true;
127
+ }
128
+ } else {
129
+ // 处理字符串类型
130
+ const stringValue = value || '';
131
+ let hasError = false;
132
+ let errorMessage = '';
133
+
134
+ // 必填校验
135
+ if (rule.required && !stringValue) {
136
+ errorMessage = rule.message || '';
137
+ isValid = false;
138
+ hasError = true;
139
+ }
140
+
141
+ // 正则校验(只有在有值且必填校验通过时才检查)
142
+ if (!hasError && rule.pattern && stringValue && !rule.pattern.test(stringValue)) {
143
+ errorMessage = rule.message || '';
144
+ isValid = false;
145
+ hasError = true;
146
+ }
147
+
148
+ // 自定义校验函数(只有在必填和正则校验都通过时才检查)
149
+ if (!hasError && rule.validatFunc) {
150
+ try {
151
+ const result = await Promise.resolve(rule.validatFunc(value));
152
+ if (!result) {
153
+ errorMessage = typeof result === 'string' ? result : (rule.message || '');
154
+ isValid = false;
155
+ hasError = true;
156
+ }
157
+ } catch (error) {
158
+ // 捕获reject的错误信息
159
+ errorMessage = typeof error === 'string' ? error : (rule.message || '');
160
+ isValid = false;
161
+ hasError = true;
162
+ }
163
+ }
164
+
165
+ // 根据是否有错误来设置或清空错误状态
166
+ if (hasError) {
167
+ errState[key] = errorMessage;
168
+ } else {
169
+ // 所有校验都通过,清空错误状态
170
+ errState[key] = '';
171
+ }
172
+ }
173
+
174
+ return isValid;
175
+ };
176
+
177
+ /**
178
+ * 表单校验函数
179
+ * @param key 可选参数,用于指定校验单个字段
180
+ * @returns 校验结果和第一个错误字段的键名
181
+ */
182
+ const formValidateFunc = async (
183
+ key?: keyof T
184
+ ): Promise<[boolean, keyof T | undefined]> => {
185
+ let isValid = true;
186
+ let firstErrorKey: keyof T | undefined;
187
+
188
+ if (key) {
189
+ // 校验单个字段
190
+ const rule = rules[key];
191
+ if (rule) {
192
+ isValid = await validateOneField(key);
193
+ }
194
+ } else {
195
+ // 校验整个表单
196
+ for (const vKey of Object.keys(rules) as Array<keyof T>) {
197
+ const rule = rules[vKey];
198
+ if (rule) {
199
+ const fieldValid = await validateOneField(vKey);
200
+ if (!fieldValid) {
201
+ isValid = false;
202
+ if (!firstErrorKey) {
203
+ firstErrorKey = vKey;
204
+ }
205
+ }
206
+ }
207
+ }
208
+ }
209
+
210
+ return [isValid, firstErrorKey];
211
+ };
212
+
213
+ return {
214
+ formData,
215
+ errState,
216
+ validateForm,
217
+ setFormItem,
218
+ resetForm,
219
+ initialForm,
220
+ validateOneField
221
+ }
222
+ }
@@ -0,0 +1,80 @@
1
+ import { ref } from 'vue';
2
+ import { uploadWxFile } from '@/api';
3
+
4
+ /**
5
+ * @description: 文件路径数组
6
+ * @param {string} url 文件路径
7
+ * @param {string} uid 文件唯一标识
8
+ * @param {string} id 文件id
9
+ */
10
+ type filePaths = {
11
+ url: string;
12
+ uid: string;
13
+ id?: string;
14
+ }
15
+
16
+ /**
17
+ * @description: 上传文件
18
+ * @param {filePaths} files 文件路径数组
19
+ * @param {any} formData 表单数据
20
+ * @return {Promise} Promise对象
21
+ */
22
+ export function useUpload() {
23
+ const progress = ref(0);
24
+ const fileProgress = reactive<Record<string, number>>({});
25
+ const uploading = ref(false);
26
+
27
+ const upload = async (files: filePaths[], formData?: any) => {
28
+ const filePaths = files.map(file => file.url);
29
+ const paths = Array.isArray(filePaths) ? filePaths : [filePaths];
30
+ uploading.value = true;
31
+
32
+ // 初始化进度
33
+ files.forEach(fItem => {
34
+ fileProgress[fItem.uid] = 0;
35
+ });
36
+
37
+ try {
38
+ return Promise.all(
39
+ files.map(fitem =>
40
+ uploadWxFile(fitem, formData, (p) => {
41
+ fileProgress[fitem.uid] = p;
42
+ updateTotalProgress();
43
+ })
44
+ )
45
+ );
46
+ } finally {
47
+ uploading.value = false;
48
+ }
49
+ };
50
+
51
+ /**
52
+ * @description: 更新总进度
53
+ * @return {void} void
54
+ */
55
+ const updateTotalProgress = () => {
56
+ const progresses = Object.values(fileProgress);
57
+ if (progresses.length === 0) {
58
+ progress.value = 0;
59
+ return;
60
+ }
61
+ progress.value = Math.round(
62
+ progresses.reduce((sum, p) => sum + p, 0) / progresses.length
63
+ );
64
+ };
65
+
66
+ return {
67
+ upload,
68
+ progress,
69
+ fileProgress: readonly(fileProgress),
70
+ uploading
71
+ };
72
+ }
73
+
74
+ export const formatFileRes = (res: any) => {
75
+ if (Array.isArray(res)) {
76
+ return res.map(item => {
77
+ return item.data?.data?.fileUrl;
78
+ })
79
+ }
80
+ }
package/src/main.ts ADDED
@@ -0,0 +1,8 @@
1
+ import App from './App.vue'
2
+ import { setupStore } from './stores'
3
+ import { createApp } from 'vue'
4
+
5
+
6
+ const app = createApp(App)
7
+ setupStore(app) // 注册pinia
8
+ app.mount('#app')
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "chengyi",
3
+ "appid": "",
4
+ "description": "chengyi-app",
5
+ "versionName": "1.0.0",
6
+ "versionCode": "100",
7
+ "transformPx": false,
8
+ "mp-weixin": {
9
+ "appid": "",
10
+ "setting": {
11
+ "urlCheck": false
12
+ },
13
+ "usingComponents": true
14
+ },
15
+ "uniStatistics": {
16
+ "enable": false
17
+ },
18
+ "vueVersion": "3",
19
+ "permission": {
20
+ "scope.writePhotosAlbum": {
21
+ "desc": "需要访问您的相册"
22
+ },
23
+ "scope.userLocation": {
24
+ "desc": "你的位置信息将用于小程序位置接口的效果展示"
25
+ },
26
+ "scope.systemAlbum": {
27
+ "desc": "需要访问您的系统相册"
28
+ },
29
+ "scope.userLocationBackground": {
30
+ "desc": "需要在后台获取您的位置信息"
31
+ },
32
+ "scope.camera": {
33
+ "desc": "需要摄像头权限进行扫码"
34
+ },
35
+ "scope.chooseLocation": {
36
+ "desc": "需要从地图选择位置"
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,132 @@
1
+ <script setup lang="ts">
2
+ import { PLACEHOLDER } from '@/config/app'
3
+ import { installWorkNodeEnum, installWorkNodeMap } from '@/dict/installtion'
4
+ const props = defineProps({
5
+ data: {
6
+ type: Object,
7
+ default: {}
8
+ },
9
+ })
10
+ const emit = defineEmits(['clickDevice']);
11
+ const handleClickDevice = (item: any) => {
12
+ emit('clickDevice', {
13
+ ...item,
14
+ planId: props.data.planId
15
+ });
16
+ }
17
+ </script>
18
+ <template>
19
+ <view class="urgency" v-if="data.isUrgency">紧急</view>
20
+ <view class="customerName">{{ data.customerName || PLACEHOLDER }}</view>
21
+ <view class="orderCode">{{ data.workOrderCode || PLACEHOLDER }}</view>
22
+ <view class="contact">
23
+ <view class="name">联系人姓名:{{ data.contactsName || PLACEHOLDER }}</view>
24
+ <view class="mobile">联系电话:{{ data.contactsMobile || PLACEHOLDER }}</view>
25
+ </view>
26
+ <view class="device-box" v-for="item in data.deviceList"
27
+ :key="item.id" @click="handleClickDevice(item)">
28
+ <view class="deviceName">{{ item.deviceName || PLACEHOLDER }}</view>
29
+ <view class="deviceStatus" v-if="installWorkNodeMap[item.status]">
30
+ {{ installWorkNodeMap[item.status]?.label }}-正常</view>
31
+ <view class="deviceCode">{{ item.deviceCode || PLACEHOLDER }}</view>
32
+ <view class="addressInfo">
33
+ 设备地址:
34
+ <view class="address">{{ `${item.provinceName || PLACEHOLDER}
35
+ ${item.cityName || PLACEHOLDER}
36
+ ${item.areaName || PLACEHOLDER}
37
+ ${item.address || PLACEHOLDER}` }}</view>
38
+ </view>
39
+ </view>
40
+ </template>
41
+
42
+ <style scoped>
43
+ .urgency {
44
+ position: absolute;
45
+ right: 32rpx;
46
+ top: 32rpx;
47
+ display: block;
48
+ padding: 6rpx 16rpx 8rpx 16rpx;
49
+ font-size: 24rpx;
50
+ color: #E34D59;
51
+ line-height: 34rpx;
52
+ background: rgba(227,77,89,0.1);
53
+ border-radius: 8rpx;
54
+ border: 1rpx solid #E34D59;
55
+ }
56
+ .customerName {
57
+ font-size: 32rpx;
58
+ color: #000000;
59
+ line-height: 48rpx;
60
+ margin-bottom: 16rpx;
61
+ font-weight: 600;
62
+ }
63
+
64
+ .orderCode {
65
+ font-size: 28rpx;
66
+ color: rgba(0, 0, 0, 0.4);
67
+ line-height: 34rpx;
68
+ margin-bottom: 16rpx;
69
+ }
70
+
71
+ .contact {
72
+ display: flex;
73
+ justify-content: space-between;
74
+ margin-bottom: 32rpx;
75
+ font-size: 28rpx;
76
+ color: #000000;
77
+ line-height: 40rpx;
78
+ }
79
+ .device-box {
80
+ margin-bottom: 16rpx;
81
+ padding: 16rpx 16rpx 24rpx 16rpx;
82
+ background-color: #F3F8FE;
83
+ border-radius: 8rpx;
84
+ position: relative;
85
+ .deviceName {
86
+ font-size: 28rpx;
87
+ color: #000000;
88
+ line-height: 40rpx;
89
+ font-weight: 500;
90
+ }
91
+
92
+ .deviceCode {
93
+ font-size: 28rpx;
94
+ color: rgba(0, 0, 0, 0.4);
95
+ line-height: 34px;
96
+ }
97
+
98
+ .addressInfo {
99
+ display: flex;
100
+ font-size: 28rpx;
101
+ }
102
+
103
+ .address {
104
+ flex: 1;
105
+ width: 380rpx;
106
+ font-size: 28rpx;
107
+ color: #000000;
108
+ line-height: 40rpx;
109
+ }
110
+
111
+ .address-icon {
112
+ width: 48rpx;
113
+ height: 48rpx;
114
+ image-rendering: -webkit-optimize-contrast; /* 优化图片渲染 */
115
+ image-rendering: pixelated; /* 像素化渲染 */
116
+ transform: translateZ(0); /* 开启GPU加速 */
117
+ }
118
+
119
+ .deviceStatus {
120
+ display: block;
121
+ position: absolute;
122
+ top: 16rpx;
123
+ right: 16rpx;
124
+ padding: 4rpx 16rpx 6rpx 16rpx;
125
+ font-size: 20rpx;
126
+ color: #1D6FE9;
127
+ line-height: 30rpx;
128
+ border-radius: 8rpx;
129
+ border: 1rpx solid #1D6FE9;
130
+ }
131
+ }
132
+ </style>