mg-ocr-invoice 0.2.2 → 0.2.3-beta

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,343 @@
1
+ <template>
2
+ <div class="Invoice">
3
+ <div class="top" @click="openPreViewImg">
4
+ <img :src="invoiceData.attachmentLink" alt="" />
5
+ </div>
6
+ <div class="company">
7
+ <div class="title"><i></i> <span>公司</span></div>
8
+ <div class="cardText">
9
+ <p style="font-size: 13px; color: #888888">销售方</p>
10
+ <div class="card">
11
+ <div class="companyName">{{ invoiceData.sellerName }}</div>
12
+ <div class="sellerInformation">
13
+ <ul>
14
+ <li>
15
+ <i></i>
16
+ <span class="label"> 纳税人识别号:</span>
17
+ <span class="value">{{ invoiceData.sellerId }}</span>
18
+ </li>
19
+ <li>
20
+ <i></i>
21
+ <span class="label">地址、电话:</span>
22
+ <span class="value">{{ invoiceData.sellerContact }}</span>
23
+ </li>
24
+ <li>
25
+ <i></i>
26
+ <span class="label">开户银行及账户:</span>
27
+ <span class="value">{{ invoiceData.sellerAccount }}</span>
28
+ </li>
29
+ </ul>
30
+ </div>
31
+ </div>
32
+ </div>
33
+
34
+ <div class="purchaser">
35
+ <ul>
36
+ <li>
37
+ <div class="label">购买方:</div>
38
+ <div class="input">{{ invoiceData.payerName }}</div>
39
+ </li>
40
+ <li>
41
+ <div class="label">发票类型:</div>
42
+ <div class="input">{{ invoiceData.description }}</div>
43
+ </li>
44
+ <li>
45
+ <div class="label">费用类型:</div>
46
+ <div class="input">其他费</div>
47
+ </li>
48
+ <li>
49
+ <div class="label">票面金额:</div>
50
+ <div class="input">
51
+ {{
52
+ invoiceData.priceTaxTotalFigure || invoiceData.noTaxAmountTotal
53
+ }}
54
+ </div>
55
+ </li>
56
+ <li>
57
+ <div class="label">发票代码:</div>
58
+ <div class="input">
59
+ <ElInputNumber
60
+ :min="0"
61
+ :controls="false"
62
+ @input="editFlag = true"
63
+ v-model.trim="invoiceData.invoiceCode"></ElInputNumber>
64
+ </div>
65
+ </li>
66
+ <li>
67
+ <div class="label">
68
+ <span class="van-field__label--required"></span>发票号码:
69
+ </div>
70
+ <div class="input">
71
+ <ElInputNumber
72
+ :min="0"
73
+ :controls="false"
74
+ @input="editFlag = true"
75
+ v-model.trim="invoiceData.invoiceNum"></ElInputNumber>
76
+ <!-- <input type="text" v-model.trim="invoiceData.invoiceNum" /> -->
77
+ <div v-if="showRequiredMsg" class="errColor">
78
+ 发票号码不能为空!
79
+ </div>
80
+ </div>
81
+ </li>
82
+ <li>
83
+ <div class="label">校验码:</div>
84
+ <div class="input">{{ invoiceData.checkCode }}</div>
85
+ </li>
86
+ <li>
87
+ <div class="label">开票日期:</div>
88
+ <div class="input">{{ invoiceData.invoiceDate }}</div>
89
+ </li>
90
+ </ul>
91
+ </div>
92
+ </div>
93
+ <div class="submit">
94
+ <span
95
+ style="width: 30%; background-color: #07c160; color: #fff"
96
+ @click="close"
97
+ >返回</span
98
+ >
99
+ <span @click="submit">保存</span>
100
+ </div>
101
+ </div>
102
+ <Overlay
103
+ style="
104
+ display: flex;
105
+ justify-content: center;
106
+ align-items: center;
107
+ z-index: 999;
108
+ "
109
+ :show="showLoading">
110
+ <Loading class="loading" color="#0094ff">保存中...</Loading>
111
+ </Overlay>
112
+ </template>
113
+ <script setup lang="ts">
114
+ import { ref } from 'vue'
115
+ import '@/utils/disableZoom'
116
+ import { __updateInvoice } from '@/api/invoice'
117
+ import {
118
+ showToast,
119
+ Overlay,
120
+ Loading,
121
+ showFailToast,
122
+ showImagePreview,
123
+ } from 'vant'
124
+ import { ElInputNumber } from 'element-plus'
125
+ const { ids, invoiceData } = defineProps({
126
+ ids: {
127
+ type: Object,
128
+ required: true,
129
+ },
130
+ invoiceData: {
131
+ type: Object,
132
+ required: true,
133
+ },
134
+ })
135
+ const emit = defineEmits(['saveSuccess'])
136
+ document.title = '发票信息'
137
+ const showRequiredMsg = ref(false)
138
+ // const modules = ref([Pagination])
139
+ const showLoading = ref(false)
140
+ const close = () => {
141
+ emit('saveSuccess')
142
+ }
143
+ const openPreViewImg = () => {
144
+ showImagePreview([invoiceData.attachmentLink])
145
+ }
146
+ const editFlag = ref(false)
147
+ const submit = async () => {
148
+ if (!editFlag.value) {
149
+ emit('saveSuccess')
150
+ return
151
+ }
152
+ if (!invoiceData.invoiceNum) {
153
+ showRequiredMsg.value = true
154
+ return
155
+ } else {
156
+ showRequiredMsg.value = false
157
+ }
158
+ showLoading.value = true
159
+ try {
160
+ const res: any = await __updateInvoice({
161
+ ...ids,
162
+ invoiceNum: invoiceData.invoiceNum,
163
+ invoiceCode: invoiceData.invoiceCode,
164
+ })
165
+ if (res.code === 200) {
166
+ showToast({
167
+ type: 'success',
168
+ message: '保存成功',
169
+ })
170
+ emit('saveSuccess')
171
+ }
172
+ } catch (err: any) {
173
+ showFailToast(err.msg)
174
+ emit('saveSuccess')
175
+ }
176
+ showLoading.value = false
177
+ }
178
+ </script>
179
+
180
+ <style lang="scss" scoped>
181
+ ::v-deep {
182
+ .el-input-number {
183
+ width: 100%;
184
+ }
185
+ .el-input {
186
+ .el-input__wrapper {
187
+ width: 100%;
188
+ padding: 0 !important;
189
+ box-shadow: none;
190
+ }
191
+ .el-input__inner {
192
+ width: 100%;
193
+ text-align: left;
194
+ box-shadow: none;
195
+ padding: 0 !important;
196
+ }
197
+ }
198
+ }
199
+ * {
200
+ padding: 0;
201
+ margin: 0;
202
+ box-sizing: border-box;
203
+ }
204
+ .Invoice {
205
+ padding-bottom: 100px;
206
+ width: 100%;
207
+ min-height: 100%;
208
+ background-color: #f3f4f6;
209
+ .top {
210
+ width: 100%;
211
+ padding: 12px 4px;
212
+ img {
213
+ width: 100%;
214
+ height: 179px;
215
+ display: block;
216
+ }
217
+ }
218
+
219
+ .company {
220
+ height: inherit;
221
+ padding: 0 12px;
222
+ background-color: #fff;
223
+ .title {
224
+ padding: 18px 0;
225
+ font-size: 16px;
226
+ font-weight: 500;
227
+ display: flex;
228
+ align-items: center;
229
+ border-bottom: 1px dashed #e8e8e8;
230
+ margin-bottom: 18px;
231
+ i {
232
+ display: inline-block;
233
+ width: 4px;
234
+ height: 14px;
235
+ background: #266fe8;
236
+ border-radius: 2px;
237
+ }
238
+
239
+ span {
240
+ font-size: 16px;
241
+ padding-left: 8px;
242
+ }
243
+ }
244
+ .cardText {
245
+ .card {
246
+ margin-top: 8px;
247
+ border-radius: 4px;
248
+ background-color: #f6f6f6;
249
+ font-size: 14px;
250
+ .companyName {
251
+ padding: 15px;
252
+ }
253
+ .sellerInformation {
254
+ padding-left: 12px;
255
+ padding-bottom: 20px;
256
+ li {
257
+ list-style: none;
258
+ margin-bottom: 12px;
259
+ display: flex;
260
+ align-items: center;
261
+ i {
262
+ width: 4px;
263
+ height: 4px;
264
+ border-radius: 50%;
265
+ background-color: #266fe8;
266
+ }
267
+ .label {
268
+ width: 120px;
269
+ margin-left: 12px;
270
+ white-space: nowrap;
271
+ }
272
+ .value {
273
+ flex: 1;
274
+ }
275
+ }
276
+ }
277
+ }
278
+ }
279
+ .purchaser {
280
+ ul {
281
+ li {
282
+ display: flex;
283
+ font-size: 16px;
284
+ height: 50px;
285
+ align-items: center;
286
+ gap: 45px;
287
+ .label {
288
+ width: 80px;
289
+ white-space: nowrap;
290
+ position: relative;
291
+ .van-field__label--required {
292
+ position: absolute;
293
+ top: 0;
294
+ left: -8px;
295
+ }
296
+ }
297
+ .input {
298
+ flex-grow: 1;
299
+ color: #999999;
300
+
301
+ input {
302
+ width: 100%;
303
+ color: #999999;
304
+ outline: none;
305
+ font-size: 16px;
306
+ border: none;
307
+ }
308
+ .errColor {
309
+ color: red;
310
+ font-size: 12px;
311
+ }
312
+ }
313
+ }
314
+ }
315
+ }
316
+ }
317
+ .submit {
318
+ position: fixed;
319
+ bottom: 0;
320
+ left: 0;
321
+ width: 100%;
322
+ box-shadow: 0px -1px 12px 0px rgba(173, 173, 173, 0.1);
323
+ height: 90px;
324
+ display: flex;
325
+ justify-content: space-between;
326
+ align-items: center;
327
+ padding: 0 12px;
328
+ z-index: 2;
329
+ background-color: #fff;
330
+ span {
331
+ background-color: #266fe8;
332
+ width: 68%;
333
+ font-size: 15px;
334
+ color: #fff;
335
+ height: 44px;
336
+ text-align: center;
337
+ display: flex;
338
+ justify-content: center;
339
+ align-items: center;
340
+ }
341
+ }
342
+ }
343
+ </style>
@@ -7,9 +7,17 @@ export const const_invoiceStatus: any = {
7
7
  export const const_realStatus: any = {
8
8
  noNeed: '无需验真',
9
9
  notCheck: '未验真',
10
- checked: '已验真',
10
+ checked: '验真通过',
11
11
  checkFail: '验真异常',
12
12
  }
13
+ export const const_invoiceExceptionInfo: any = {
14
+ abnormal: '异常发票',
15
+ invalid: '无效发票',
16
+ }
17
+ export const const_manualModify: any = {
18
+ '手动录入': true,
19
+ '非手动录入': false,
20
+ }
13
21
  export const const_taskStatus: any = {
14
22
  init: '初始状态',
15
23
  upload_fail_cos: 'COS 上传失败',
@@ -45,7 +45,10 @@
45
45
  </div>
46
46
  </div>
47
47
  <div class="tags">
48
- <span :class="setClass(item.realStatus)">{{
48
+ <span v-if="item.manualModify" class="manual">
49
+ 手工录入
50
+ </span>
51
+ <span v-else :class="setClass(item.realStatus)">{{
49
52
  const_realStatus[item.realStatus]
50
53
  }}</span>
51
54
  <span :class="setClass(item.invoiceStatus)">{{
@@ -185,27 +188,32 @@ import {
185
188
  setClass,
186
189
  } from './const'
187
190
  import { showImagePreview } from 'vant'
188
- // console.log(addNumber('495.28', '29.72'))
189
191
  const emit = defineEmits(['edit', 'ok'])
190
- const { listId, multiple } = defineProps({
192
+ const { listId, multiple, catchList } = defineProps({
191
193
  listId: {
192
- required: true,
194
+ required: false,
193
195
  type: String,
194
- default: '1690918419274137620',
195
196
  },
196
197
  multiple: {
197
198
  required: true,
198
199
  type: Boolean,
199
200
  },
201
+ catchList: {},
200
202
  })
201
203
  const token: any = ref(sessionStorage.getItem('token'))
202
204
  const showLoading = ref(false)
203
205
  const showPopup = ref(false)
204
- const list = ref<Array<any>>([])
206
+ const list: any = ref<Array<any>>([])
207
+ if (catchList) {
208
+ list.value = catchList
209
+ }
205
210
  const batchId = ref('')
206
211
  const selectId = computed(() => {
207
- return list.value.filter((item) => item.selected).map((item) => item.taskId)
212
+ return list.value
213
+ .filter((item: any) => item.selected)
214
+ .map((item: any) => item.taskId)
208
215
  })
216
+
209
217
  const getList = async () => {
210
218
  return new Promise(async (resolve, reject) => {
211
219
  const data: any = {}
@@ -215,12 +223,17 @@ const getList = async () => {
215
223
  const res: any = await __getUploadInvoiceList(data, token.value)
216
224
  if (res.code === 200) {
217
225
  list.value = res.data.invoiceList.map((item: any) => {
226
+ let data = list.value.find((v: any) => v.taskId === item.taskId) || {}
218
227
  return {
219
- ...item,
220
- selected: false,
228
+ ...Object.assign(data, item),
221
229
  }
222
230
  })
223
231
  batchId.value = res.data.batchId
232
+ if (list.value.length <= 0) {
233
+ selectedAll.value = false
234
+ } else {
235
+ changeItemCheckbox()
236
+ }
224
237
  resolve(list.value)
225
238
  }
226
239
  } catch (error) {
@@ -242,7 +255,8 @@ const openDetails = (row: any) => {
242
255
  taskId: row.taskId,
243
256
  batchId: batchId.value,
244
257
  },
245
- row
258
+ row,
259
+ list.value
246
260
  )
247
261
  }
248
262
  const everyStatus = (item: any) => {
@@ -260,14 +274,14 @@ const everyStatus = (item: any) => {
260
274
  }
261
275
  const selectedAll: any = ref(false)
262
276
  const changeSelectAll = (v: any) => {
263
- list.value.forEach((item) => {
277
+ list.value.forEach((item: any) => {
264
278
  item.selected = everyStatus(item) && selectedAll.value
265
279
  })
266
280
  }
267
281
  const changeItemCheckbox = () => {
268
282
  const status = list.value
269
- .filter((item) => everyStatus(item))
270
- .every((item) => item.selected)
283
+ .filter((item: any) => everyStatus(item))
284
+ .every((item: any) => item.selected)
271
285
  selectedAll.value = status
272
286
  }
273
287
  const deleteSelectItem = () => {
@@ -325,31 +339,18 @@ const selectImg = async (type: number) => {
325
339
  }
326
340
  const submitBtn = computed(() => {
327
341
  const flag = list.value
328
- .filter((item) => item.selected)
329
- .every((item) => {
330
- console.log(item.invoiceStatus, 'invoiceStatus')
331
- console.log(item.taskStatus, 'taskStatus')
332
- console.log(item.realStatus, 'realStatus')
342
+ .filter((item: any) => item.selected)
343
+ .every((item: any) => {
333
344
  return everyStatus(item)
334
- // item.invoiceStatus === 'unused' &&
335
- // (item.taskStatus === 'ocr_success' ||
336
- // item.taskStatus === 'repeat' ||
337
- // item.taskStatus === 'finish' ||
338
- // item.invoiceStatus === 'invalid' ||
339
- // item.invoiceStatus === 'invalid' ||
340
- // item.invoiceStatus === 'used' ||
341
- // item.realStatus === 'noNeed' ||
342
- // item.realStatus === 'notCheck' ||
343
- // item.realStatus === 'checked' ||
344
- // item.realStatus === 'checkFail')
345
345
  })
346
- return flag && list.value.filter((item) => item.selected).length > 0
346
+ return flag && list.value.filter((item: any) => item.selected).length > 0
347
347
  })
348
348
  const selectedLength = computed(() => {
349
- return list.value.filter((item) => item.selected && everyStatus(item)).length
349
+ return list.value.filter((item: any) => item.selected && everyStatus(item))
350
+ .length
350
351
  })
351
352
  const selectedLengthCount = computed(() => {
352
- return list.value.filter((item) => everyStatus(item)).length
353
+ return list.value.filter((item: any) => everyStatus(item)).length
353
354
  })
354
355
  const openErrImg = (url: any) => {
355
356
  showImagePreview([url])
@@ -364,7 +365,35 @@ const ok = () => {
364
365
  return
365
366
  }
366
367
  // showDeleteBtn.value = false
367
- const selectData = list.value.filter((item) => item.selected)
368
+ const selectData = list.value
369
+ .filter((item: any) => item.selected)
370
+ .map((item: any) => {
371
+ let obj: any = { ...item }
372
+ // 验真状态 1,绿色 2,黄色, 3,红色
373
+ // 绿色: 验真成功或(无需验真且金额校验成功)或无需验证
374
+ // 黄色: (未验真或验真异常)且金额校验失败
375
+ // 剩下的场景都为红色
376
+ if (
377
+ item.realStatus === 'checked' ||
378
+ (item.realStatus === 'noNeed' && !item.manualModify)
379
+ ) {
380
+ obj.verifyTruth = 1
381
+ } else if (item.manualModify) {
382
+ obj.verifyTruth = 2
383
+ } else {
384
+ obj.verifyTruth = 3
385
+ }
386
+ // 验重状态 1,绿色 2,黄色, 3,红色
387
+ if (item.invoiceStatus === 'unused') {
388
+ obj.checkWeight = 1
389
+ } else if (item.invoiceStatus === 'invalid') {
390
+ obj.checkWeight = 2
391
+ } else {
392
+ obj.checkWeight = 3
393
+ }
394
+ return obj
395
+ })
396
+ console.log(selectData, 'selectData')
368
397
  emit('ok', selectData, batchId.value)
369
398
  }
370
399
  const timeID: any = ref(null)
@@ -484,6 +513,10 @@ onMounted(() => {
484
513
  gap: 7px;
485
514
  margin-top: 8px;
486
515
  margin-bottom: 15px;
516
+ .manual {
517
+ background-color: #facd91;
518
+ color: #c77458;
519
+ }
487
520
  span {
488
521
  padding: 2px 4px;
489
522
  font-size: 12px;
@@ -0,0 +1,70 @@
1
+ export const data = {
2
+ angle: '0',
3
+ attachmentLink:
4
+ 'https://filegw.nuonuo.com/U1ylUBa5YlUHWOpta7hoLSC6QX9pEcu8Kcy-JnCnHTGn6PYXKNdZosH32iDDaLWGzhkY0N88rGOaS4w2krdeCw.png',
5
+ description: '增值税专用发票',
6
+ invoiceType: 'vat_special_invoice',
7
+ invoiceRootType: '专票',
8
+ position: '[0,0,2048,0,0,928,2048,928]',
9
+ checkCode: '',
10
+ clerk: '江海珍',
11
+ electronicMark: '0',
12
+ electronicNumber: '',
13
+ invoiceCode: '3300231130',
14
+ invoiceDate: '2023年07月17日',
15
+ invoiceNum: '08148218',
16
+ itemNames: '*住宿服务*住宿费',
17
+ noTaxAmountTotal: '212.87',
18
+ note: '',
19
+ payee: '王宝存',
20
+ payerAccount: '中信银行上海虹桥商务区支行8110201012601374925',
21
+ payerContact: '上海市闵行区沪青平公路277号5楼13301773703',
22
+ payerId: '91310112MA7BGUJL15',
23
+ payerName: '上海半小妖科技有限公司',
24
+ priceTaxTotalFigure: '215.00',
25
+ printCode: '0814821',
26
+ printNum: '0814821',
27
+ producerStamp: '浙江',
28
+ recheck: '金芬兰',
29
+ seal: '1',
30
+ sellerAccount: '温州银行股份有限公司营业部 745000120190095098',
31
+ sellerContact: '温州市瓯海娄桥古岸头村(不作拆迁凭证)0577-88812123',
32
+ sellerId: '913303046845118206',
33
+ sellerName: '温州蔬农招待所有限公司',
34
+ serviceName: '住宿服务',
35
+ taxAmountTotal: '2.13',
36
+ title: '浙江增值税专用发票',
37
+ totalCn: '贰佰壹拾伍圆整',
38
+ vatInvoicePage: '发票联',
39
+ nuonuoFileId: '3232300437-7d12c82340b84855a04e1f6d9d059c2f',
40
+ deatis: [
41
+ {
42
+ itemExTaxAmount: '212.87',
43
+ itemExTaxPrice: '',
44
+ itemName: '*住宿服务*住宿费',
45
+ itemQuantity: '',
46
+ itemSpec: '',
47
+ itemTaxAmount: '2.13',
48
+ itemTaxRate: '1%',
49
+ itemUtil: '',
50
+ id: 1345,
51
+ code: '1691272134678020096',
52
+ tenantCode: 'af9390a20cccc90fd174e5706f302769',
53
+ brandCode: '1645512743732000029',
54
+ createdAt: 1692065673000,
55
+ updatedAt: 1692065673000,
56
+ invoiceTableCode: '1691272117275852800',
57
+ },
58
+ ],
59
+ status: 'entered',
60
+ invoiceStatus: 'unused',
61
+ realStatus: 'checked',
62
+ fileUrlKey:
63
+ 'https://invoice-sit-1251881907.cos.ap-nanjing.myqcloud.com/f8c6ff4af53241269c39dbd70f59cf6a.png?sign=q-sign-algorithm%3Dsha1%26q-ak%3DAKIDwtmcPMRRMAe9d4Sju00wq7KQPKAvZ6Yt%26q-sign-time%3D1693876509%3B1693876689%26q-key-time%3D1693876509%3B1693876689%26q-header-list%3Dhost%26q-url-param-list%3D%26q-signature%3Dd49201f59ae86085771f52b2df0b13f40556e848',
64
+ invoiceCompanyType: '公司',
65
+ taxRate: '1%',
66
+ taskStatus: 'repeat',
67
+ taskId: '1698867126367944704',
68
+ invoiceConsumptionType: 'accommodation',
69
+ invoiceConsumptionTypeInfo: '住宿费',
70
+ }
@@ -10,6 +10,7 @@
10
10
  <InvoiceList
11
11
  :multiple="multiple"
12
12
  :listId="listId"
13
+ :catchList="catchList"
13
14
  @ok="submit"
14
15
  @edit="edit">
15
16
  </InvoiceList>
@@ -31,6 +32,8 @@ import Cookies from 'js-cookie'
31
32
  // import eruda from 'eruda'
32
33
  // eruda.init()
33
34
  import VConsole from 'vconsole'
35
+ // import { data as dataObj } from './const'
36
+
34
37
  const setLog = () => {
35
38
  if (getUrlParams(window.location.href).log == 1) {
36
39
  const vConsole = new VConsole()
@@ -55,6 +58,7 @@ const { token, multiple, Base_URL, OCR_AuthDomain, batchId } = defineProps({
55
58
  },
56
59
  batchId: {
57
60
  required: false,
61
+
58
62
  },
59
63
  })
60
64
 
@@ -76,13 +80,16 @@ const activePage = ref(0)
76
80
  const listId: any = ref(null)
77
81
  const ids = ref({})
78
82
  const invoiceData = ref({})
83
+ // invoiceData.value = dataObj
79
84
  const uploadSuccess = (id: any) => {
80
85
  listId.value = id
81
86
  activePage.value = 2
82
87
  }
83
- const edit = (id: object, row: any) => {
88
+ const catchList: any = ref(null)
89
+ const edit = (id: object, row: any, list: any) => {
84
90
  ids.value = id
85
91
  invoiceData.value = row
92
+ catchList.value = list
86
93
  activePage.value = 3
87
94
  }
88
95
  const saveSuccess = (id: object) => {
@@ -103,6 +110,7 @@ onMounted(() => {
103
110
  .box {
104
111
  width: 100%;
105
112
  min-height: 100%;
113
+ height: 100%;
106
114
  }
107
115
  }
108
116
  </style>
@@ -65,7 +65,6 @@ const handleClickText = async (text: any) => {
65
65
  if (props.batchId) {
66
66
  fd.append('batchId', props.batchId)
67
67
  }
68
- console.log(props.batchId,'props.batchIdprops.batchIdprops.batchId')
69
68
  showLoading.value = true
70
69
  try {
71
70
  const res: any = await __uploadInvoice(fd)