mg-ocr-invoice 0.1.7 → 0.1.9

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.
@@ -0,0 +1,593 @@
1
+ <template>
2
+ <div class="InvoiceList">
3
+ <div class="card">
4
+ <ul>
5
+ <li
6
+ v-for="(item, index) in list"
7
+ :key="index"
8
+ @click="openDetails(item)">
9
+ <div class="li">
10
+ <div
11
+ class="item-card"
12
+ v-if="
13
+ item.taskStatus === 'finish' || item.taskStatus === 'repeat'
14
+ ">
15
+ <div
16
+ class="title"
17
+ :class="{ person: item.invoiceCompanyType === '公司' }">
18
+ {{ item.invoiceCompanyType }}
19
+ </div>
20
+ <div class="storeName">
21
+ <div class="name">
22
+ <div class="left">{{ item.sellerName }}</div>
23
+ <div class="right" v-if="item.taskStatus === 'repeat'">
24
+ 重复录入
25
+ </div>
26
+ </div>
27
+ <div class="tags">
28
+ <span :class="setClass(item.realStatus)">{{
29
+ const_realStatus[item.realStatus]
30
+ }}</span>
31
+ <span :class="setClass(item.invoiceStatus)">{{
32
+ const_invoiceStatus[item.invoiceStatus]
33
+ }}</span>
34
+ <!-- <span class="error">识别失败</span>
35
+ <span class="default">已使用</span>
36
+ <span class="success">识别成功</span> -->
37
+ </div>
38
+ <div class="InvoiceInfo">
39
+ <div class="leftinfo">
40
+ <div class="item-info">
41
+ <span class="label">发票金额</span>
42
+ <span class="value price"
43
+ >¥{{
44
+ item.priceTaxTotalFigure || item.noTaxAmountTotal
45
+ }}</span
46
+ >
47
+ </div>
48
+ <div class="item-info">
49
+ <span class="label">发票抬头</span>
50
+ <span class="value">{{ item.payerName }}</span>
51
+ </div>
52
+ <div class="item-info">
53
+ <span class="label">发票类型</span>
54
+ <span class="value">{{ item.description }}</span>
55
+ </div>
56
+ <div class="item-info">
57
+ <span class="label">发票时间</span>
58
+ <span class="value">{{ item.invoiceDate }}</span>
59
+ </div>
60
+ </div>
61
+ <div class="rightCheckbox">
62
+ <Checkbox
63
+ @click.stop
64
+ v-model="item.selected"
65
+ icon-size="22px"></Checkbox>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+
71
+ <div
72
+ v-else-if="
73
+ item.taskStatus === 'ocr_doing' ||
74
+ item.taskStatus === 'ocr_success'
75
+ "
76
+ style="
77
+ background-color: #fff;
78
+ padding: 21px 0 15px 12px;
79
+ position: relative;
80
+ width: 100%;
81
+ ">
82
+ <Loading class="loading" color="#0094ff">识别中...</Loading>
83
+ <Skeleton :loading="true" title :row="6"></Skeleton>
84
+ </div>
85
+ <div class="err-card" v-else>
86
+ <div class="left-img">
87
+ <img :src="item.fileUrlKey" alt="" />
88
+ </div>
89
+ <div class="right-errText">
90
+ <div class="title"><span>识别失败</span></div>
91
+ <div class="content">
92
+ <div class="recognitionResult">
93
+ <p class="errContent">OCR识别失败</p>
94
+ <p>可以重新拍照识别,或手动添加信息导入</p>
95
+ </div>
96
+ <div>
97
+ <Checkbox
98
+ @click.stop
99
+ v-model="item.selected"
100
+ icon-size="22px"></Checkbox>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ </li>
107
+ </ul>
108
+ </div>
109
+ <div class="btn-form">
110
+ <div class="operate">
111
+ <div class="delete">
112
+ <span @click="deleteSelectItem">移除</span>
113
+ </div>
114
+ <div class="right-btn">
115
+ <span class="add" @click="add">继续添加发票</span>
116
+ <span class="ok" :class="{ disabledColor: !submitBtn }" @click="ok"
117
+ >确定选择</span
118
+ >
119
+ </div>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ <Popup v-model:show="showPopup" position="bottom" closeable>
124
+ <div class="boxPopup">
125
+ <div class="title">
126
+ <span>添加发票</span>
127
+ </div>
128
+ <div style="padding-bottom: 24px" @click="selectImg(1)">从相册中选择</div>
129
+ <div @click="selectImg(2)">拍照上传</div>
130
+ </div>
131
+ </Popup>
132
+ <Overlay
133
+ style="display: flex; justify-content: center; align-items: center"
134
+ :show="showLoading">
135
+ <Loading class="loading" color="#0094ff">上传中...</Loading>
136
+ </Overlay>
137
+ </template>
138
+ <!-- 无效发票, 已使用的, 使用中的, 识别失败 禁止提交 -->
139
+ <script setup lang="ts">
140
+ import 'vant/lib/index.css'
141
+ import { ref, onMounted, computed } from 'vue'
142
+ import {
143
+ Popup,
144
+ Checkbox,
145
+ Skeleton,
146
+ Loading,
147
+ showToast,
148
+ showConfirmDialog,
149
+ Overlay,
150
+ } from 'vant'
151
+ import '@/utils/disableZoom'
152
+ import { selectPhoto, takePhoto } from '@/utils/upload'
153
+ import {
154
+ __deleteInvoice,
155
+ __getUploadInvoiceList,
156
+ __uploadInvoice,
157
+ } from '@/api/invoice'
158
+ import {
159
+ const_invoiceStatus,
160
+ const_realStatus,
161
+ const_taskStatus,
162
+ setClass,
163
+ } from './const'
164
+
165
+ // console.log(addNumber('495.28', '29.72'))
166
+ const emit = defineEmits(['edit', 'ok'])
167
+ const { listId, multiple } = defineProps({
168
+ listId: {
169
+ required: true,
170
+ type: String,
171
+ // default: '1676054202318585856',
172
+ },
173
+ multiple: {
174
+ required: true,
175
+ type: Boolean,
176
+ },
177
+ })
178
+ const token: any = ref(sessionStorage.getItem('token'))
179
+ const showLoading = ref(false)
180
+ const showPopup = ref(false)
181
+ const list = ref<Array<any>>([])
182
+ const batchId = ref('')
183
+ const selectId = computed(() => {
184
+ return list.value.filter((item) => item.selected).map((item) => item.taskId)
185
+ })
186
+ const getList = async () => {
187
+ return new Promise(async (resolve, reject) => {
188
+ const data: any = {}
189
+ data.batchId = listId
190
+ try {
191
+ const res: any = await __getUploadInvoiceList(data, token.value)
192
+ if (res.code === 200) {
193
+ list.value = res.data.invoiceList.map((item: any) => {
194
+ return {
195
+ ...item,
196
+ selected: false,
197
+ }
198
+ })
199
+ batchId.value = res.data.batchId
200
+ resolve(list.value)
201
+ }
202
+ } catch (error) {
203
+ console.log(error)
204
+ reject(error)
205
+ }
206
+ })
207
+ }
208
+
209
+ const openDetails = (row: any) => {
210
+ if (row.taskStatus !== 'finish' && row.taskStatus !== 'repeat') {
211
+ return
212
+ }
213
+ emit(
214
+ 'edit',
215
+ {
216
+ taskId: row.taskId,
217
+ batchId: batchId.value,
218
+ },
219
+ row
220
+ )
221
+ }
222
+ const deleteSelectItem = () => {
223
+ if (selectId.value.length <= 0) {
224
+ showToast({ type: 'text', message: '请先选择发票' })
225
+ return
226
+ }
227
+ showConfirmDialog({
228
+ title: '提醒',
229
+ message: '请确认是否需要移除选中项,移除后不可恢复!',
230
+ })
231
+ .then(async () => {
232
+ var urlencoded = new URLSearchParams()
233
+ urlencoded.append('batchId', batchId.value)
234
+ const idsStr = selectId.value.join(',')
235
+ urlencoded.append('taskIds', idsStr)
236
+ const res: any = await __deleteInvoice(urlencoded, token.value)
237
+ if (res.code === 200) {
238
+ showToast({ type: 'success', message: '移除成功' })
239
+ getList()
240
+ }
241
+ })
242
+ .catch(() => {
243
+ // on cancel
244
+ })
245
+ }
246
+ const add = () => {
247
+ showPopup.value = true
248
+ }
249
+
250
+ const selectImg = async (type: number) => {
251
+ let fd: FormData
252
+ if (type === 1) {
253
+ fd = await selectPhoto(multiple)
254
+ } else {
255
+ fd = await takePhoto()
256
+ }
257
+ fd.append('batchId', batchId.value)
258
+ showLoading.value = true
259
+ showPopup.value = false
260
+ try {
261
+ const res: any = await __uploadInvoice(fd)
262
+ if (res.code === 200) {
263
+ showToast({
264
+ type: 'success',
265
+ message: '上传成功',
266
+ })
267
+ setTimeGetList()
268
+ }
269
+ } catch (error) {
270
+ console.log(error)
271
+ setTimeGetList()
272
+ }
273
+ showLoading.value = false
274
+ }
275
+ const submitBtn = computed(() => {
276
+ const flag = list.value
277
+ .filter((item) => item.selected)
278
+ .every((item) => {
279
+ return (
280
+ item.invoiceStatus === 'unused' &&
281
+ (item.taskStatus === 'ocr_success' ||
282
+ item.taskStatus === 'repeat' ||
283
+ item.taskStatus === 'finish')
284
+ )
285
+ })
286
+ return flag && list.value.filter((item) => item.selected).length > 0
287
+ })
288
+
289
+ const ok = () => {
290
+ if (selectId.value.length <= 0) {
291
+ showToast({ type: 'text', message: '请先选择发票' })
292
+ return
293
+ }
294
+ if (!submitBtn.value) {
295
+ showToast({ type: 'text', message: '存在无效发票,无法提交' })
296
+ return
297
+ }
298
+ // showDeleteBtn.value = false
299
+ const selectData = list.value.filter((item) => item.selected)
300
+ emit('ok', selectData, batchId.value)
301
+ }
302
+ const timeID: any = ref(null)
303
+ const setTimeGetList = () => {
304
+ getList()
305
+ .then((res: any) => {
306
+ const flag = res.some(
307
+ (item: any) =>
308
+ item.taskStatus === 'ocr_doing' || item.taskStatus === 'ocr_success'
309
+ )
310
+ if (!flag) {
311
+ return
312
+ }
313
+ setTimeout(setTimeGetList, 2000)
314
+ })
315
+ .catch((err) => {
316
+ setTimeout(setTimeGetList, 2000)
317
+ })
318
+ }
319
+ onMounted(() => {
320
+ setTimeGetList()
321
+ })
322
+ </script>
323
+ <style lang="scss" scoped>
324
+ .disabledColor {
325
+ opacity: 0.6; /* 降低不透明度以表示禁用状态 */
326
+ cursor: not-allowed; /* 鼠标指针样式设置为禁用 */
327
+ }
328
+ .InvoiceList {
329
+ padding-bottom: 100px;
330
+ position: relative;
331
+ width: 100%;
332
+ min-height: 100%;
333
+ background-color: #f3f4f6;
334
+ .card {
335
+ width: 100%;
336
+ ul {
337
+ padding-top: 12px;
338
+ display: flex;
339
+ flex-direction: column;
340
+ gap: 12px;
341
+ .li {
342
+ width: 100%;
343
+ border-radius: 4px;
344
+ padding: 0 12px;
345
+ display: flex;
346
+ align-items: center;
347
+ gap: 12px;
348
+ overflow: hidden;
349
+ .item-card {
350
+ flex: 1;
351
+ background-color: #fff;
352
+ padding-bottom: 20px;
353
+ min-width: 100%;
354
+ div.person {
355
+ background-color: #ff8b26;
356
+ }
357
+ .title {
358
+ color: #fff;
359
+ display: flex;
360
+ width: max-content;
361
+ text-align: center;
362
+ align-items: center;
363
+ font-size: 12px;
364
+ border-radius: 4px 0px 4px 0px;
365
+ padding: 2px 4px;
366
+ background-color: #266fe8;
367
+ margin-bottom: 5px;
368
+ }
369
+
370
+ .storeName {
371
+ padding: 0 12px;
372
+ .name {
373
+ display: flex;
374
+ justify-content: space-between;
375
+ width: 100%;
376
+ align-items: center;
377
+ .left {
378
+ font-size: 15px;
379
+ color: #333333;
380
+ font-weight: 700;
381
+ }
382
+ .right {
383
+ font-size: 12px;
384
+ color: #ee0a24;
385
+ }
386
+ }
387
+ .tags {
388
+ display: flex;
389
+ align-items: center;
390
+ gap: 7px;
391
+ margin-top: 8px;
392
+ margin-bottom: 15px;
393
+ span {
394
+ padding: 2px 4px;
395
+ font-size: 12px;
396
+ font-weight: 400;
397
+ border-radius: 2px;
398
+ &.ok {
399
+ background-color: rgba(38, 111, 232, 0.1);
400
+ color: #266fe8;
401
+ }
402
+ &.warning {
403
+ background-color: rgba(255, 251, 230, 1);
404
+ color: #ff8f26;
405
+ }
406
+ &.error {
407
+ background-color: rgba(255, 242, 240, 1);
408
+ color: #ff4d4f;
409
+ }
410
+ &.default {
411
+ background-color: rgba(153, 153, 153, 0.2);
412
+ color: #666666;
413
+ }
414
+ &.success {
415
+ background-color: #e6ffdd;
416
+ color: #2ba500;
417
+
418
+ opacity: 0.68;
419
+ }
420
+ }
421
+ }
422
+ .InvoiceInfo {
423
+ display: flex;
424
+ justify-content: space-between;
425
+ .leftinfo {
426
+ .item-info {
427
+ display: flex;
428
+ align-items: center;
429
+ gap: 16px;
430
+ margin-bottom: 8px;
431
+ &:last-child {
432
+ margin-bottom: 0;
433
+ }
434
+ span {
435
+ font-size: 14px;
436
+ &.label {
437
+ color: #999999;
438
+ }
439
+ &.value {
440
+ color: #333333;
441
+ }
442
+ &.price {
443
+ color: #266fe8;
444
+ font-weight: 700;
445
+ }
446
+ }
447
+ }
448
+ }
449
+ .rightCheckbox {
450
+ height: max-content;
451
+ }
452
+ }
453
+ }
454
+ }
455
+
456
+ .err-card {
457
+ background-color: #fff;
458
+ width: 100%;
459
+ display: flex;
460
+ gap: 9px;
461
+ .left-img {
462
+ width: 113px;
463
+ height: 79px;
464
+ margin: 10px 0;
465
+ margin-left: 10px;
466
+ flex-shrink: 0;
467
+ img {
468
+ width: 100%;
469
+ height: 100%;
470
+ }
471
+ }
472
+ .right-errText {
473
+ .title {
474
+ color: #fff;
475
+ display: flex;
476
+ text-align: right;
477
+ align-items: center;
478
+ justify-content: flex-end;
479
+ margin-bottom: 5px;
480
+ span {
481
+ background-color: rgba(238, 10, 36, 0.1);
482
+ color: #ee0a24;
483
+ padding: 2px 4px;
484
+ font-size: 12px;
485
+ border-radius: 0 4px 0 4px;
486
+ }
487
+ }
488
+ .content {
489
+ display: flex;
490
+ padding-right: 12px;
491
+ align-items: center;
492
+ font-size: 12px;
493
+ gap: 18px;
494
+ .recognitionResult {
495
+ gap: 5px;
496
+ display: flex;
497
+ flex-direction: column;
498
+ justify-content: space-between;
499
+ p {
500
+ color: #666666;
501
+ }
502
+ }
503
+ }
504
+ }
505
+ }
506
+ }
507
+ .loading {
508
+ position: absolute;
509
+ left: 50%;
510
+ top: 50%;
511
+ transform: translate(-50%, -50%);
512
+ z-index: 100;
513
+ }
514
+ }
515
+ }
516
+
517
+ .btn-form {
518
+ z-index: 999;
519
+ position: fixed;
520
+ bottom: 0;
521
+ left: 0;
522
+ background-color: #fff;
523
+ height: 90px;
524
+ width: 100%;
525
+
526
+ .operate {
527
+ width: 100%;
528
+ height: 100%;
529
+ display: flex;
530
+ gap: 12px;
531
+ padding: 12px;
532
+ padding-bottom: 34px;
533
+ font-size: 15px;
534
+ span {
535
+ flex: 1;
536
+ display: flex;
537
+ align-items: center;
538
+ font-weight: 500;
539
+ font-size: 15px;
540
+ justify-content: center;
541
+ }
542
+ .delete {
543
+ border: 1px solid #e8e8e8;
544
+ display: flex;
545
+ align-items: center;
546
+ padding: 0 44px;
547
+ }
548
+ .right-btn {
549
+ display: flex;
550
+ flex: 1;
551
+ span {
552
+ color: #fff;
553
+ }
554
+ .add {
555
+ background-color: #ff8b26;
556
+ border-radius: 4px 0px 0px 4px;
557
+ }
558
+ .ok {
559
+ border-radius: 0px 4px 4px 0px;
560
+ background-color: #266fe8;
561
+ }
562
+ }
563
+ }
564
+ }
565
+ }
566
+ .boxPopup {
567
+ height: 100%;
568
+ display: flex;
569
+ flex-direction: column;
570
+ // align-items: center;
571
+ // justify-content: space-around;
572
+ padding: 16px 23px 34px;
573
+ div {
574
+ width: 100%;
575
+ font-size: 14px;
576
+ display: flex;
577
+ justify-content: center;
578
+ align-items: center;
579
+ }
580
+ .title {
581
+ width: 100%;
582
+ display: flex;
583
+ justify-content: center;
584
+ // padding: 24px 0;
585
+ font-size: 16px;
586
+ font-weight: 700;
587
+ padding-bottom: 24px;
588
+ .close {
589
+ font-size: 22px;
590
+ }
591
+ }
592
+ }
593
+ </style>
@@ -0,0 +1,117 @@
1
+ <template>
2
+ <div class="OCRInvoice">
3
+ <PaymentMode
4
+ :batchId="batchId"
5
+ :multiple="multiple"
6
+ @uploadSuccess="uploadSuccess"
7
+ v-if="activePage === 1"></PaymentMode>
8
+ <InvoiceList
9
+ :multiple="multiple"
10
+ :listId="listId"
11
+ @ok="submit"
12
+ @edit="edit"
13
+ v-if="activePage === 2">
14
+ </InvoiceList>
15
+ <Invoice
16
+ @saveSuccess="saveSuccess"
17
+ :ids="ids"
18
+ :invoiceData="invoiceData"
19
+ v-if="activePage === 3"></Invoice>
20
+ </div>
21
+ </template>
22
+ <script lang="ts" setup>
23
+ import { ref, onMounted } from 'vue'
24
+ import { PaymentMode, Invoice, InvoiceList } from '@/components/index'
25
+ import { getUrlParams } from '@/utils/getUrlParams'
26
+ import { showToast } from 'vant'
27
+ import Cookies from 'js-cookie'
28
+ // import eruda from 'eruda'
29
+ // eruda.init()
30
+ import VConsole from 'vconsole'
31
+ const setLog = () => {
32
+ if (getUrlParams(window.location.href).log == 1) {
33
+ const vConsole = new VConsole()
34
+ }
35
+ }
36
+ setLog()
37
+ const emit = defineEmits(['submit'])
38
+ const { token, multiple, Base_URL, OCR_AuthDomain, batchId } = defineProps({
39
+ token: {
40
+ required: false,
41
+ type: String,
42
+ },
43
+ multiple: {
44
+ required: false,
45
+ default: true,
46
+ },
47
+ Base_URL: {
48
+ required: false,
49
+ },
50
+ OCR_AuthDomain: {
51
+ required: false,
52
+ },
53
+ batchId: {
54
+ required: false,
55
+ },
56
+ })
57
+ const tempToken =
58
+ 'z7v5gIDRD-sMhb9lRaspBmrWU8y--e5YK2xqWxMCIMTqBReMcoHNTJQ3XyNBdHmLVHiwommCp-HtP4wWSiK9oZDwTWvCW9CZswjLNNL4nGM'
59
+ const initToken = () => {
60
+ activePage.value = 1
61
+ const params = getUrlParams(window.location.href)
62
+ const linkToken = params.token
63
+ if (!linkToken && !token) {
64
+ // showToast({
65
+ // type: 'fail',
66
+ // message: '请在URL拼接token或者组件传递token',
67
+ // duration: 0,
68
+ // })
69
+ sessionStorage.setItem('token', tempToken)
70
+ } else {
71
+ sessionStorage.setItem(
72
+ 'token',
73
+ linkToken ||
74
+ token ||
75
+ 'z7v5gIDRD-tZIqYE1f7is2aWk6u0y6RCK2xqWxMCIMTqBReMcoHNTJQ3XyNBdHmLVHiwommCp-HtP4wWSiK9oZDwTWvCW9CZswjLNNL4nGM'
76
+ )
77
+ activePage.value = 1
78
+ }
79
+ }
80
+ const init_Base_URL = () => {
81
+ const params = getUrlParams(window.location.href)
82
+ Cookies.set(
83
+ 'OCR_Base_URL',
84
+ params.Base_URL || Base_URL || 'https://invoice.imugua.team/'
85
+ )
86
+ Cookies.set('OCR_AuthDomain', params.OCR_AuthDomain || OCR_AuthDomain)
87
+ }
88
+ const activePage = ref(0)
89
+ const listId: any = ref(null)
90
+ const ids = ref({})
91
+ const invoiceData = ref({})
92
+ const uploadSuccess = (id: any) => {
93
+ listId.value = id
94
+ activePage.value = 2
95
+ }
96
+ const edit = (id: object, row: any) => {
97
+ ids.value = id
98
+ invoiceData.value = row
99
+ activePage.value = 3
100
+ }
101
+ const saveSuccess = (id: object) => {
102
+ activePage.value = 2
103
+ }
104
+ const submit = (data: any, id: string) => {
105
+ emit('submit', data, id)
106
+ }
107
+ onMounted(() => {
108
+ initToken()
109
+ init_Base_URL()
110
+ })
111
+ </script>
112
+ <style lang="scss" scoped>
113
+ .OCRInvoice {
114
+ width: 100%;
115
+ height: 100%;
116
+ }
117
+ </style>