@uxda/appkit 4.2.12 → 4.2.16

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 (118) hide show
  1. package/.eslintrc.mjs +7 -7
  2. package/README.md +187 -187
  3. package/babel.config.js +12 -12
  4. package/dist/appkit.css +6 -7
  5. package/dist/index.js +1086 -1172
  6. package/package.json +79 -79
  7. package/project.config.json +15 -15
  8. package/project.tt.json +13 -13
  9. package/rollup.config.mjs +67 -67
  10. package/src/Appkit.ts +66 -66
  11. package/src/balance/api/endpoints.ts +133 -133
  12. package/src/balance/api/index.ts +106 -106
  13. package/src/balance/components/AccountView.vue +750 -750
  14. package/src/balance/components/BalanceCard.vue +215 -215
  15. package/src/balance/components/BalanceReminder.vue +85 -85
  16. package/src/balance/components/ConsumptionFilter.vue +218 -218
  17. package/src/balance/components/ConsumptionRules.vue +68 -68
  18. package/src/balance/components/DateFilter.vue +250 -250
  19. package/src/balance/components/DateRange.vue +80 -80
  20. package/src/balance/components/ListFilter.vue +62 -62
  21. package/src/balance/components/ListFilterPicker.vue +191 -191
  22. package/src/balance/components/PromoterCard.vue +237 -237
  23. package/src/balance/components/SecondBalance.vue +71 -71
  24. package/src/balance/components/Tip.vue +45 -45
  25. package/src/balance/components/index.ts +8 -8
  26. package/src/balance/types.ts +97 -97
  27. package/src/components/bt-cropper/index.vue +774 -774
  28. package/src/components/bt-cropper/utils/calcCropper.js +42 -42
  29. package/src/components/bt-cropper/utils/calcImagePosition.js +23 -23
  30. package/src/components/bt-cropper/utils/calcImageSize.js +37 -37
  31. package/src/components/bt-cropper/utils/calcPointDistance.js +12 -12
  32. package/src/components/bt-cropper/utils/calcRightAndBottom.js +7 -7
  33. package/src/components/bt-cropper/utils/ratio.js +3 -3
  34. package/src/components/bt-cropper/utils/tools.js +25 -25
  35. package/src/components/dd-area/index.vue +225 -225
  36. package/src/components/dd-icon/doc.md +21 -21
  37. package/src/components/dd-icon/index.vue +23 -23
  38. package/src/components/dd-notice-bar/index.vue +78 -78
  39. package/src/components/dd-search/doc.md +34 -34
  40. package/src/components/dd-search/index.vue +168 -168
  41. package/src/components/dd-selector/index.vue +124 -124
  42. package/src/components/dd-skeleton/doc.md +19 -19
  43. package/src/components/dd-skeleton/index.vue +36 -36
  44. package/src/global.ts +6 -6
  45. package/src/index.ts +89 -89
  46. package/src/main.scss +1 -1
  47. package/src/notice/api/endpoints.ts +17 -17
  48. package/src/notice/api/index.ts +106 -106
  49. package/src/notice/components/NoticeBanner.vue +243 -243
  50. package/src/notice/components/NoticeEntry.vue +99 -99
  51. package/src/notice/components/NoticeList.vue +315 -315
  52. package/src/notice/components/NoticePopup.vue +162 -162
  53. package/src/notice/components/index.ts +5 -5
  54. package/src/notice/components/useCommonList.ts +86 -86
  55. package/src/notice/components/useNotice.ts +35 -35
  56. package/src/notice/index.ts +1 -1
  57. package/src/notice/types.ts +25 -25
  58. package/src/payment/api/config.ts +7 -7
  59. package/src/payment/api/endpoints.ts +103 -103
  60. package/src/payment/api/index.ts +100 -100
  61. package/src/payment/components/AmountPicker.vue +90 -90
  62. package/src/payment/components/RechargeResult.vue +69 -69
  63. package/src/payment/components/RechargeView.vue +155 -155
  64. package/src/payment/components/RightsPicker.vue +105 -105
  65. package/src/payment/components/TradeView.vue +317 -317
  66. package/src/payment/components/UserAgreement.vue +234 -234
  67. package/src/payment/components/index.ts +22 -22
  68. package/src/payment/index.ts +5 -5
  69. package/src/payment/services/index.ts +16 -16
  70. package/src/payment/services/invoke-recharge.ts +25 -25
  71. package/src/payment/services/request-payment.ts +58 -58
  72. package/src/payment/types.ts +28 -28
  73. package/src/register/components/SelfRegistration.vue +233 -233
  74. package/src/register/components/index.ts +2 -2
  75. package/src/shared/components/AppDrawer.vue +54 -58
  76. package/src/shared/components/AppVerify.vue +128 -128
  77. package/src/shared/components/DeviceVersion.vue +68 -68
  78. package/src/shared/components/EmptyView.vue +33 -33
  79. package/src/shared/components/OcrBusinessLicense.vue +130 -130
  80. package/src/shared/components/OcrIcon.vue +202 -202
  81. package/src/shared/components/PageHeader.vue +79 -79
  82. package/src/shared/components/index.ts +8 -8
  83. package/src/shared/composables/index.ts +8 -8
  84. package/src/shared/composables/useAmount.ts +46 -46
  85. package/src/shared/composables/useCountdown.ts +46 -46
  86. package/src/shared/composables/useCrypto.ts +76 -76
  87. package/src/shared/composables/useDragBox.ts +97 -97
  88. package/src/shared/composables/useEncode.ts +43 -43
  89. package/src/shared/composables/useLogger.ts +123 -123
  90. package/src/shared/composables/useSafeArea.ts +46 -46
  91. package/src/shared/composables/useTabbar.ts +24 -24
  92. package/src/shared/composables/useUpload.ts +54 -54
  93. package/src/shared/composables/useValidator.ts +32 -32
  94. package/src/shared/http/Http.ts +136 -136
  95. package/src/shared/http/index.ts +1 -1
  96. package/src/shared/http/types.ts +157 -157
  97. package/src/shared/index.ts +3 -3
  98. package/src/shared/weixin/payment.ts +38 -38
  99. package/src/styles/vars.scss +3 -3
  100. package/src/user/api/endpoints.ts +17 -17
  101. package/src/user/api/index.ts +111 -111
  102. package/src/user/components/LoginSetting.vue +114 -114
  103. package/src/user/components/UserAuth.vue +257 -257
  104. package/src/user/components/UserBinding.vue +307 -307
  105. package/src/user/components/UserBindingSuccess.vue +80 -80
  106. package/src/user/components/UserEntry.vue +133 -133
  107. package/src/user/components/UserFeedback.vue +431 -431
  108. package/src/user/components/UserFeedbackEntry.vue +192 -192
  109. package/src/user/components/UserHeadCrop.vue +65 -65
  110. package/src/user/components/UserInfo.vue +826 -820
  111. package/src/user/components/UserResourceEmpty.vue +75 -75
  112. package/src/user/components/index.ts +23 -23
  113. package/src/user/index.ts +1 -1
  114. package/tsconfig.json +30 -30
  115. package/types/global.d.ts +21 -21
  116. package/types/vue.d.ts +10 -10
  117. package/dist/assets/asset-3B_CoPto +0 -1
  118. package/stats.html +0 -4842
@@ -1,431 +1,431 @@
1
- <template>
2
- <div class="user-feedback" :class="{ isSuccess }">
3
- <div v-if="!isSuccess" class="user-feedback-wrap">
4
- <img src="https://cdn.ddjf.com/static/images/customer-center/feedback-img.png"
5
- style="width: 311px;height:149.5px;margin: 0 auto;" />
6
- <div class="user-feedback-tit">补充描述</div>
7
- <div class="user-feedback-body">
8
- <editor
9
- id="myEditor"
10
- :show-img-resize="true"
11
- class="user-feedback-editor"
12
- placeholder="请输入补充描述"
13
- />
14
- <div class="user-feedback-handle">
15
- <div
16
- class="user-feedback-handle-item"
17
- v-for="(item, key) in formState.attachment"
18
- :key="key"
19
- >
20
- <template v-if="item.status === 'done'">
21
- <img
22
- v-if="item.type === 'image'"
23
- class="user-feedback-handle-item-img"
24
- mode="aspectFit"
25
- :src="item.url"
26
- />
27
- <video class="user-feedback-handle-item-img" :src="item.url" v-else />
28
- <div class="user-feedback-handle-item-close" @click="onDelete(key)">
29
- <img
30
- class="user-feedback-handle-item-close-img"
31
- src="https://cdn.ddjf.com/static/images/customer-center/close-filled.png"
32
- />
33
- </div>
34
- </template>
35
- <img
36
- class="user-feedback-handle-item-loading"
37
- mode="aspectFit"
38
- src="https://cdn.ddjf.com/static/images/customer-center/loading.png"
39
- alt=""
40
- />
41
- </div>
42
- <div class="user-feedback-handle-item" @click="toUpload">+</div>
43
- </div>
44
- </div>
45
-
46
- <div class="user-feedback-footer">
47
- <nut-button class="user-feedback-footer-btn" plain type="primary" @click="onCancel"
48
- >最小化</nut-button
49
- >
50
- <nut-button class="user-feedback-footer-btn" type="primary" @click="onOk">反馈</nut-button>
51
- </div>
52
- </div>
53
- <div class="user-feedback-wrap" v-else>
54
- <img
55
- class="user-feedback-success-img"
56
- mode="aspectFit"
57
- src="https://cdn.ddjf.com/static/images/customer-center/success-icon.png"
58
- />
59
- <div class="user-feedback-success-info">提交成功</div>
60
- </div>
61
- </div>
62
- </template>
63
-
64
- <script lang="ts" setup>
65
- import Taro from '@tarojs/taro'
66
- import { onMounted, reactive, ref } from 'vue'
67
- import { useAppKitOptions } from '../../Appkit'
68
- import { useHttp } from '../api'
69
-
70
- interface FormStateType {
71
- content: string
72
- attachment: any[]
73
- }
74
-
75
- const props = withDefaults(
76
- defineProps<{
77
- app?: string
78
- traceIds: string
79
- value?: FormStateType | null
80
- captureScreen?: any
81
- }>(),
82
- {
83
- app: '',
84
- traceIds: '',
85
- value: null,
86
- captureScreen: () => {},
87
- }
88
- )
89
-
90
- onMounted(() => {
91
- setTimeout(() => {
92
- initEditor()
93
- }, 500)
94
- })
95
-
96
- const formState = reactive<FormStateType>({
97
- content: '',
98
- attachment: [],
99
- })
100
- const isSuccess = ref(false) // 反馈提交成功标记
101
-
102
- const editorCtx = ref()
103
- // 编辑器初始化
104
- function initEditor() {
105
- const query = Taro.createSelectorQuery()
106
- query
107
- .select('#myEditor')
108
- .context((res) => {
109
- editorCtx.value = res.context
110
-
111
- if (props.value) {
112
- if (props.value.content) {
113
- formState.content = props.value.content
114
- editorCtx.value.setContents({
115
- html: props.value.content,
116
- })
117
- }
118
- if (props.value.attachment && props.value.attachment.length) {
119
- formState.attachment = props.value.attachment
120
- }
121
- }
122
- })
123
- .exec()
124
- }
125
-
126
- // 上传图片、视频
127
- function toUpload() {
128
- if (formState.attachment.length >= 20) {
129
- return Taro.showToast({
130
- title: '最多上传20份附件',
131
- icon: 'none',
132
- })
133
- }
134
- Taro.chooseMedia({
135
- count: 20 - formState.attachment.length,
136
- mediaType: ['mix'],
137
- maxDuration: 60,
138
- success: async (res) => {
139
- console.log(res.tempFiles, 'res.tempFiles')
140
- for (const item of res.tempFiles) {
141
- if (item.fileType === 'video') {
142
- if (item.size > 1024 * 1024 * 50) {
143
- return Taro.showToast({
144
- title: '视频大小不能超过50M',
145
- icon: 'none',
146
- })
147
- }
148
- }
149
-
150
- formState.attachment.push({
151
- url: item.tempFilePath,
152
- type: item.fileType,
153
- status: 'uploading',
154
- })
155
- await uploadFile(formState.attachment[formState.attachment.length - 1])
156
- }
157
- },
158
- })
159
- }
160
-
161
- // 文件上传请求
162
- async function uploadFile(item: any) {
163
- const appkitOptions = useAppKitOptions()
164
- const token = appkitOptions.tempToken() || appkitOptions.token()
165
-
166
- let Res: any = await Taro.uploadFile({
167
- url: `${appkitOptions.baseUrl()}/saas-base/file/upload`,
168
- filePath: item.url,
169
- name: 'file',
170
- formData: {
171
- objectNo: `${token}${Date.now()}`,
172
- objectTypeCode: `PROBLEM_FEEDBACK_MINI`,
173
- },
174
- header: { token },
175
- })
176
-
177
- const res = JSON.parse(Res.data)
178
- if (res.success) {
179
- item.url = res.result
180
- item.status = 'done'
181
- }
182
- }
183
-
184
- // 删除附件
185
- function onDelete(index: number) {
186
- formState.attachment.splice(index, 1)
187
- }
188
-
189
- // 点击最小化
190
- function onCancel() {
191
- editorCtx.value.getContents({
192
- success: (res) => {
193
- const html = res.html
194
- formState.content = html
195
-
196
- if (html && html !== '<p><br></p>') {
197
- formState.content = html
198
- }
199
- emits('minimize', formState)
200
- Taro.navigateBack()
201
- },
202
- })
203
- }
204
-
205
- // 点击确定反馈时
206
- function onOk() {
207
- editorCtx.value.getContents({
208
- success: (res) => {
209
- const html = res.html
210
- if (html === '<p><br></p>' || !html) {
211
- Taro.showToast({
212
- title: '请输入问题描述',
213
- icon: 'none',
214
- })
215
- } else {
216
- formState.content = html
217
- requestFeedback()
218
- }
219
- },
220
- })
221
- }
222
-
223
- // 请求反馈接口
224
- function requestFeedback() {
225
- const $http = useHttp()
226
- const appkitOptions = useAppKitOptions()
227
-
228
- Taro.showLoading({
229
- title: '反馈中...',
230
- })
231
-
232
- const attachment = JSON.parse(
233
- JSON.stringify(
234
- formState.attachment
235
- .filter((item) => item.status === 'done')
236
- .map((item) => ({ url: item.url, type: item.type }))
237
- )
238
- )
239
- if (props.captureScreen && Object.keys(props.captureScreen).length) {
240
- attachment.push({
241
- captureScreen: props.captureScreen,
242
- })
243
- }
244
-
245
- const sendData = {
246
- appCode: props.app || appkitOptions.app(),
247
- content: formState.content,
248
- id: '',
249
- remarks: '',
250
- title: '',
251
- traceIds: props.traceIds,
252
- device: 'MINI',
253
- attachment: JSON.stringify(attachment),
254
- }
255
- $http
256
- .post('/saas-base/problemFeedback/add', sendData)
257
- .then(() => {
258
- Taro.hideLoading()
259
- emits('success')
260
- // Taro.showToast({ title: '反馈提交成功', icon: 'none' })
261
- // Taro.navigateBack()
262
- isSuccess.value = true
263
- clearFormState()
264
- })
265
- .catch(() => {
266
- Taro.hideLoading()
267
- })
268
- }
269
-
270
- // 反馈成功后,清空反馈内容
271
- function clearFormState() {
272
- formState.content = ''
273
- formState.attachment = []
274
- }
275
-
276
- // 父组件事件
277
- const emits = defineEmits(['minimize', 'success'])
278
- </script>
279
-
280
- <style lang="scss">
281
- .user-feedback {
282
- margin: 10px 12px;
283
- padding-bottom: calc(75px + env(safe-area-inset-bottom, 0px));
284
- min-height: 100vh;
285
- box-sizing: border-box;
286
- overflow: hidden;
287
- &.isSuccess {
288
- margin: 0;
289
- padding: 0;
290
- height: 100%;
291
- .user-feedback-wrap {
292
- height: 100vh;
293
- align-items: center;
294
- }
295
- }
296
- &-wrap {
297
- min-height: calc(100vh - calc(75px + env(safe-area-inset-bottom, 0px)));
298
- height: 100%;
299
- background: #fff;
300
- display: flex;
301
- flex-direction: column;
302
- box-sizing: border-box;
303
- padding: 10px;
304
- border-radius: 5px;
305
- }
306
- &-head {
307
- background: #f2f9ff;
308
- padding: 15px;
309
- border-radius: 2px;
310
- font-size: 12px;
311
- margin-bottom: 8px;
312
- &-info {
313
- padding-left: 9px;
314
- text-indent: -9px;
315
- }
316
- }
317
- &-tit {
318
- height: 44px;
319
- display: flex;
320
- align-items: center;
321
- color: #1a1a1a;
322
- font-weight: 500;
323
- font-size: 17px;
324
- }
325
- &-body {
326
- border-radius: 4px 4px 0px 0px;
327
- display: flex;
328
- flex-direction: column;
329
- overflow: hidden;
330
- }
331
- &-editor {
332
- padding: 10px;
333
- font-size: 14px;
334
- background: #f5f5f5;
335
- overflow-y: auto;
336
- min-height: 165px;
337
- }
338
- &-handle {
339
- display: flex;
340
- align-items: center;
341
- flex-wrap: wrap;
342
- margin-top: 10px;
343
- margin-right: -4px;
344
- &-item {
345
- position: relative;
346
- border-radius: 2px;
347
- display: flex;
348
- align-items: center;
349
- justify-content: center;
350
- width: 79px;
351
- height: 79px;
352
- background: #f5f5f5;
353
- font-size: 30px;
354
- color: rgba(53, 53, 53, 0.3);
355
- margin-right: 4px;
356
- margin-bottom: 4px;
357
- &-img {
358
- max-width: 100%;
359
- max-height: 100%;
360
- }
361
- &-close {
362
- position: absolute;
363
- top: -2px;
364
- right: -2px;
365
- width: 20px;
366
- height: 20px;
367
- display: flex;
368
- justify-content: center;
369
- align-items: center;
370
- &-img {
371
- width: 10px;
372
- height: 10px;
373
- }
374
- }
375
- &-loading {
376
- width: 17px;
377
- height: 17px;
378
- animation: rotate 2s linear infinite;
379
- }
380
- }
381
- }
382
- .ql-editor.ql-blank:before {
383
- color: rgba(0, 0, 0, 0.3);
384
- font-style: normal;
385
- }
386
-
387
- &-footer {
388
- position: fixed;
389
- z-index: 10;
390
- bottom: 0;
391
- left: 0;
392
- width: 100%;
393
- min-height: 63px;
394
- background: #ffffff;
395
- box-shadow: 0 -3px 11px 0 rgba(224, 224, 224, 0.5);
396
- padding: 10px 12px calc(8px + env(safe-area-inset-bottom, 0px));
397
- display: flex;
398
- justify-content: space-between;
399
- box-sizing: border-box;
400
- &-btn {
401
- flex: 1;
402
- &:first-child {
403
- margin-right: 10px;
404
- }
405
- }
406
- }
407
-
408
- &-success {
409
- &-img {
410
- width: 87px;
411
- height: 90px;
412
- margin-top: 110px;
413
- margin-bottom: 20px;
414
- }
415
- &-info {
416
- color: #353535;
417
- font-size: 16px;
418
- font-weight: 500;
419
- }
420
- }
421
-
422
- @keyframes rotate {
423
- from {
424
- transform: rotate(0);
425
- }
426
- to {
427
- transform: rotate(360deg);
428
- }
429
- }
430
- }
431
- </style>
1
+ <template>
2
+ <div class="user-feedback" :class="{ isSuccess }">
3
+ <div v-if="!isSuccess" class="user-feedback-wrap">
4
+ <img src="https://cdn.ddjf.com/static/images/customer-center/feedback-img.png"
5
+ style="width: 311px;height:149.5px;margin: 0 auto;" />
6
+ <div class="user-feedback-tit">补充描述</div>
7
+ <div class="user-feedback-body">
8
+ <editor
9
+ id="myEditor"
10
+ :show-img-resize="true"
11
+ class="user-feedback-editor"
12
+ placeholder="请输入补充描述"
13
+ />
14
+ <div class="user-feedback-handle">
15
+ <div
16
+ class="user-feedback-handle-item"
17
+ v-for="(item, key) in formState.attachment"
18
+ :key="key"
19
+ >
20
+ <template v-if="item.status === 'done'">
21
+ <img
22
+ v-if="item.type === 'image'"
23
+ class="user-feedback-handle-item-img"
24
+ mode="aspectFit"
25
+ :src="item.url"
26
+ />
27
+ <video class="user-feedback-handle-item-img" :src="item.url" v-else />
28
+ <div class="user-feedback-handle-item-close" @click="onDelete(key)">
29
+ <img
30
+ class="user-feedback-handle-item-close-img"
31
+ src="https://cdn.ddjf.com/static/images/customer-center/close-filled.png"
32
+ />
33
+ </div>
34
+ </template>
35
+ <img
36
+ class="user-feedback-handle-item-loading"
37
+ mode="aspectFit"
38
+ src="https://cdn.ddjf.com/static/images/customer-center/loading.png"
39
+ alt=""
40
+ />
41
+ </div>
42
+ <div class="user-feedback-handle-item" @click="toUpload">+</div>
43
+ </div>
44
+ </div>
45
+
46
+ <div class="user-feedback-footer">
47
+ <nut-button class="user-feedback-footer-btn" plain type="primary" @click="onCancel"
48
+ >最小化</nut-button
49
+ >
50
+ <nut-button class="user-feedback-footer-btn" type="primary" @click="onOk">反馈</nut-button>
51
+ </div>
52
+ </div>
53
+ <div class="user-feedback-wrap" v-else>
54
+ <img
55
+ class="user-feedback-success-img"
56
+ mode="aspectFit"
57
+ src="https://cdn.ddjf.com/static/images/customer-center/success-icon.png"
58
+ />
59
+ <div class="user-feedback-success-info">提交成功</div>
60
+ </div>
61
+ </div>
62
+ </template>
63
+
64
+ <script lang="ts" setup>
65
+ import Taro from '@tarojs/taro'
66
+ import { onMounted, reactive, ref } from 'vue'
67
+ import { useAppKitOptions } from '../../Appkit'
68
+ import { useHttp } from '../api'
69
+
70
+ interface FormStateType {
71
+ content: string
72
+ attachment: any[]
73
+ }
74
+
75
+ const props = withDefaults(
76
+ defineProps<{
77
+ app?: string
78
+ traceIds: string
79
+ value?: FormStateType | null
80
+ captureScreen?: any
81
+ }>(),
82
+ {
83
+ app: '',
84
+ traceIds: '',
85
+ value: null,
86
+ captureScreen: () => {},
87
+ }
88
+ )
89
+
90
+ onMounted(() => {
91
+ setTimeout(() => {
92
+ initEditor()
93
+ }, 500)
94
+ })
95
+
96
+ const formState = reactive<FormStateType>({
97
+ content: '',
98
+ attachment: [],
99
+ })
100
+ const isSuccess = ref(false) // 反馈提交成功标记
101
+
102
+ const editorCtx = ref()
103
+ // 编辑器初始化
104
+ function initEditor() {
105
+ const query = Taro.createSelectorQuery()
106
+ query
107
+ .select('#myEditor')
108
+ .context((res) => {
109
+ editorCtx.value = res.context
110
+
111
+ if (props.value) {
112
+ if (props.value.content) {
113
+ formState.content = props.value.content
114
+ editorCtx.value.setContents({
115
+ html: props.value.content,
116
+ })
117
+ }
118
+ if (props.value.attachment && props.value.attachment.length) {
119
+ formState.attachment = props.value.attachment
120
+ }
121
+ }
122
+ })
123
+ .exec()
124
+ }
125
+
126
+ // 上传图片、视频
127
+ function toUpload() {
128
+ if (formState.attachment.length >= 20) {
129
+ return Taro.showToast({
130
+ title: '最多上传20份附件',
131
+ icon: 'none',
132
+ })
133
+ }
134
+ Taro.chooseMedia({
135
+ count: 20 - formState.attachment.length,
136
+ mediaType: ['mix'],
137
+ maxDuration: 60,
138
+ success: async (res) => {
139
+ console.log(res.tempFiles, 'res.tempFiles')
140
+ for (const item of res.tempFiles) {
141
+ if (item.fileType === 'video') {
142
+ if (item.size > 1024 * 1024 * 50) {
143
+ return Taro.showToast({
144
+ title: '视频大小不能超过50M',
145
+ icon: 'none',
146
+ })
147
+ }
148
+ }
149
+
150
+ formState.attachment.push({
151
+ url: item.tempFilePath,
152
+ type: item.fileType,
153
+ status: 'uploading',
154
+ })
155
+ await uploadFile(formState.attachment[formState.attachment.length - 1])
156
+ }
157
+ },
158
+ })
159
+ }
160
+
161
+ // 文件上传请求
162
+ async function uploadFile(item: any) {
163
+ const appkitOptions = useAppKitOptions()
164
+ const token = appkitOptions.tempToken() || appkitOptions.token()
165
+
166
+ let Res: any = await Taro.uploadFile({
167
+ url: `${appkitOptions.baseUrl()}/saas-base/file/upload`,
168
+ filePath: item.url,
169
+ name: 'file',
170
+ formData: {
171
+ objectNo: `${token}${Date.now()}`,
172
+ objectTypeCode: `PROBLEM_FEEDBACK_MINI`,
173
+ },
174
+ header: { token },
175
+ })
176
+
177
+ const res = JSON.parse(Res.data)
178
+ if (res.success) {
179
+ item.url = res.result
180
+ item.status = 'done'
181
+ }
182
+ }
183
+
184
+ // 删除附件
185
+ function onDelete(index: number) {
186
+ formState.attachment.splice(index, 1)
187
+ }
188
+
189
+ // 点击最小化
190
+ function onCancel() {
191
+ editorCtx.value.getContents({
192
+ success: (res) => {
193
+ const html = res.html
194
+ formState.content = html
195
+
196
+ if (html && html !== '<p><br></p>') {
197
+ formState.content = html
198
+ }
199
+ emits('minimize', formState)
200
+ Taro.navigateBack()
201
+ },
202
+ })
203
+ }
204
+
205
+ // 点击确定反馈时
206
+ function onOk() {
207
+ editorCtx.value.getContents({
208
+ success: (res) => {
209
+ const html = res.html
210
+ if (html === '<p><br></p>' || !html) {
211
+ Taro.showToast({
212
+ title: '请输入问题描述',
213
+ icon: 'none',
214
+ })
215
+ } else {
216
+ formState.content = html
217
+ requestFeedback()
218
+ }
219
+ },
220
+ })
221
+ }
222
+
223
+ // 请求反馈接口
224
+ function requestFeedback() {
225
+ const $http = useHttp()
226
+ const appkitOptions = useAppKitOptions()
227
+
228
+ Taro.showLoading({
229
+ title: '反馈中...',
230
+ })
231
+
232
+ const attachment = JSON.parse(
233
+ JSON.stringify(
234
+ formState.attachment
235
+ .filter((item) => item.status === 'done')
236
+ .map((item) => ({ url: item.url, type: item.type }))
237
+ )
238
+ )
239
+ if (props.captureScreen && Object.keys(props.captureScreen).length) {
240
+ attachment.push({
241
+ captureScreen: props.captureScreen,
242
+ })
243
+ }
244
+
245
+ const sendData = {
246
+ appCode: props.app || appkitOptions.app(),
247
+ content: formState.content,
248
+ id: '',
249
+ remarks: '',
250
+ title: '',
251
+ traceIds: props.traceIds,
252
+ device: 'MINI',
253
+ attachment: JSON.stringify(attachment),
254
+ }
255
+ $http
256
+ .post('/saas-base/problemFeedback/add', sendData)
257
+ .then(() => {
258
+ Taro.hideLoading()
259
+ emits('success')
260
+ // Taro.showToast({ title: '反馈提交成功', icon: 'none' })
261
+ // Taro.navigateBack()
262
+ isSuccess.value = true
263
+ clearFormState()
264
+ })
265
+ .catch(() => {
266
+ Taro.hideLoading()
267
+ })
268
+ }
269
+
270
+ // 反馈成功后,清空反馈内容
271
+ function clearFormState() {
272
+ formState.content = ''
273
+ formState.attachment = []
274
+ }
275
+
276
+ // 父组件事件
277
+ const emits = defineEmits(['minimize', 'success'])
278
+ </script>
279
+
280
+ <style lang="scss">
281
+ .user-feedback {
282
+ margin: 10px 12px;
283
+ padding-bottom: calc(75px + env(safe-area-inset-bottom, 0px));
284
+ min-height: 100vh;
285
+ box-sizing: border-box;
286
+ overflow: hidden;
287
+ &.isSuccess {
288
+ margin: 0;
289
+ padding: 0;
290
+ height: 100%;
291
+ .user-feedback-wrap {
292
+ height: 100vh;
293
+ align-items: center;
294
+ }
295
+ }
296
+ &-wrap {
297
+ min-height: calc(100vh - calc(75px + env(safe-area-inset-bottom, 0px)));
298
+ height: 100%;
299
+ background: #fff;
300
+ display: flex;
301
+ flex-direction: column;
302
+ box-sizing: border-box;
303
+ padding: 10px;
304
+ border-radius: 5px;
305
+ }
306
+ &-head {
307
+ background: #f2f9ff;
308
+ padding: 15px;
309
+ border-radius: 2px;
310
+ font-size: 12px;
311
+ margin-bottom: 8px;
312
+ &-info {
313
+ padding-left: 9px;
314
+ text-indent: -9px;
315
+ }
316
+ }
317
+ &-tit {
318
+ height: 44px;
319
+ display: flex;
320
+ align-items: center;
321
+ color: #1a1a1a;
322
+ font-weight: 500;
323
+ font-size: 17px;
324
+ }
325
+ &-body {
326
+ border-radius: 4px 4px 0px 0px;
327
+ display: flex;
328
+ flex-direction: column;
329
+ overflow: hidden;
330
+ }
331
+ &-editor {
332
+ padding: 10px;
333
+ font-size: 14px;
334
+ background: #f5f5f5;
335
+ overflow-y: auto;
336
+ min-height: 165px;
337
+ }
338
+ &-handle {
339
+ display: flex;
340
+ align-items: center;
341
+ flex-wrap: wrap;
342
+ margin-top: 10px;
343
+ margin-right: -4px;
344
+ &-item {
345
+ position: relative;
346
+ border-radius: 2px;
347
+ display: flex;
348
+ align-items: center;
349
+ justify-content: center;
350
+ width: 79px;
351
+ height: 79px;
352
+ background: #f5f5f5;
353
+ font-size: 30px;
354
+ color: rgba(53, 53, 53, 0.3);
355
+ margin-right: 4px;
356
+ margin-bottom: 4px;
357
+ &-img {
358
+ max-width: 100%;
359
+ max-height: 100%;
360
+ }
361
+ &-close {
362
+ position: absolute;
363
+ top: -2px;
364
+ right: -2px;
365
+ width: 20px;
366
+ height: 20px;
367
+ display: flex;
368
+ justify-content: center;
369
+ align-items: center;
370
+ &-img {
371
+ width: 10px;
372
+ height: 10px;
373
+ }
374
+ }
375
+ &-loading {
376
+ width: 17px;
377
+ height: 17px;
378
+ animation: rotate 2s linear infinite;
379
+ }
380
+ }
381
+ }
382
+ .ql-editor.ql-blank:before {
383
+ color: rgba(0, 0, 0, 0.3);
384
+ font-style: normal;
385
+ }
386
+
387
+ &-footer {
388
+ position: fixed;
389
+ z-index: 10;
390
+ bottom: 0;
391
+ left: 0;
392
+ width: 100%;
393
+ min-height: 63px;
394
+ background: #ffffff;
395
+ box-shadow: 0 -3px 11px 0 rgba(224, 224, 224, 0.5);
396
+ padding: 10px 12px calc(8px + env(safe-area-inset-bottom, 0px));
397
+ display: flex;
398
+ justify-content: space-between;
399
+ box-sizing: border-box;
400
+ &-btn {
401
+ flex: 1;
402
+ &:first-child {
403
+ margin-right: 10px;
404
+ }
405
+ }
406
+ }
407
+
408
+ &-success {
409
+ &-img {
410
+ width: 87px;
411
+ height: 90px;
412
+ margin-top: 110px;
413
+ margin-bottom: 20px;
414
+ }
415
+ &-info {
416
+ color: #353535;
417
+ font-size: 16px;
418
+ font-weight: 500;
419
+ }
420
+ }
421
+
422
+ @keyframes rotate {
423
+ from {
424
+ transform: rotate(0);
425
+ }
426
+ to {
427
+ transform: rotate(360deg);
428
+ }
429
+ }
430
+ }
431
+ </style>