zhengnaiwen_citu 3 ヶ月 前
コミット
478eeaaea3

+ 26 - 1
src/api/dataOrigin.js

@@ -25,5 +25,30 @@ export function datasourceParse (data) {
 }
 // 数据源 快捷解析
 export function deleteDatasource (data) {
-  return http.post('/datasource/delete', data)
+  return http.formData('/datasource/delete', data)
+}
+
+// 非结构化数据源 名片解析
+export function businessCardParse (data) {
+  return http.upload('/parse/business-card-parse', data)
+}
+
+// 非结构化数据源 更新名片
+export function updateBusinessCard (data) {
+  return http.put('/parse/business-cards', data)
+}
+
+// 非结构化数据源 获取名片列表
+export function getBusinessCard (data) {
+  return http.get('/parse/get-business-cards', data)
+}
+
+// 非结构化数据源 更新名片状态
+export function updateBusinessCardStatus (data, id) {
+  return http.put(`/parse/update-business-cards/${id}/status`, data)
+}
+
+// 非结构化数据源 获取名片图片
+export function getBusinessCardImage (path) {
+  return http.defaultBlob(`/business-cards/image/${path}`, undefined, 'get')
 }

+ 15 - 6
src/components/List/table.vue

@@ -284,11 +284,8 @@ export default {
     }
   },
   watch: {
-    options: {
-      handler () {
-        this.handleChangeOptions()
-      },
-      deep: true
+    sortChange () {
+      this.handleChangeOptions()
     },
     headers (val) {
       this.headersFinish()
@@ -339,6 +336,9 @@ export default {
         }
         return e
       })
+    },
+    sortChange () {
+      return this.options.sortDesc.toString() + this.options.sortBy.toString()
     }
   },
   data () {
@@ -350,7 +350,16 @@ export default {
       selectHeader: [], // 选择头部
       selectItems: [], // 过滤展示头部
       backupHeaders: [],
-      options: {},
+      options: {
+        page: 1,
+        itemsPerPage: -1,
+        sortBy: [],
+        sortDesc: [],
+        groupBy: [],
+        groupDesc: [],
+        mustSort: false,
+        multiSort: false
+      },
       // sortBy: [],
       // sortDesc: [],
       headerCountShow: this.defaultHeaderShow

+ 2 - 2
src/components/MForm/index.vue

@@ -283,8 +283,8 @@ export default {
       default: () => ({})
     },
     items: {
-      type: Object,
-      default: () => {},
+      type: Array,
+      default: () => [],
       required: true
     }
   },

+ 10 - 1
src/utils/request.js

@@ -69,7 +69,7 @@ const http = {
   },
   put (url, params) {
     return service.put(url, {
-      params: params
+      ...params
     })
   },
   del (url, params) {
@@ -101,6 +101,15 @@ const http = {
       },
       responseType: 'blob'
     })
+  },
+  defaultBlob (url, params, method = 'post', config = {
+    timeout: 10000,
+    headers: {
+      'Content-Type': 'application/json'
+    },
+    responseType: 'blob'
+  }) {
+    return service[method](url, params, config)
   }
 }
 export default http

+ 102 - 47
src/views/dataOrigin/unstructuredData/manualCollection/components/imageImportEdit.vue

@@ -1,9 +1,9 @@
 <template>
-  <m-dialog title="名片解析" :visible.sync="show" showDrawer :footer="false">
-    <div class="fullBox box-2">
+  <m-dialog title="名片解析" :visible.sync="show" :showDrawer="id !== null" :footer="false" @close="handleClose">
+    <div class="fullBox box-1" :class="{ 'box-2': id }">
       <v-card class="upload-card d-flex flex-column align-center justify-center overflow-hidden" elevation="5">
         <template v-if="file">
-          <div class="fullBox overflow-auto">
+          <div class="fullBox overflow-auto text-center">
             <div class="change d-flex align-center justify-end pr-3 pt-3">
               <UploadBtn
                 :loading="loading"
@@ -12,25 +12,15 @@
                 class="white--text"
                 @change="handleImport"
               >
-                <v-icon
-                  left
-                  dark
-                >
-                  mdi-cloud-upload
-                </v-icon>
+                <v-icon left dark>mdi-cloud-upload</v-icon>
                 更换名片
               </UploadBtn>
               <v-btn color="primary" rounded class="buttons white--text ml-2" @click="handleAnalysis">
-                <v-icon
-                  left
-                  dark
-                >
-                  mdi-file-arrow-left-right
-                </v-icon>
+                <v-icon left dark>mdi-file-arrow-left-right</v-icon>
                 解析
               </v-btn>
             </div>
-            <img width="100%" :src="previewUrl" />
+            <img width="100%" :src="previewUrl" style="max-width: 700px;" />
           </div>
         </template>
         <template v-else>
@@ -42,10 +32,7 @@
               class="ma-2 white--text"
               @change="handleImport"
             >
-              <v-icon
-                left
-                dark
-              >
+              <v-icon left dark>
                 mdi-cloud-upload
               </v-icon>
               点击上传
@@ -56,11 +43,11 @@
           </div>
         </template>
       </v-card>
-      <MCard class="show-card d-flex flex-column" title="名片解析" v-loading="loading">
+      <MCard v-if="id" class="show-card d-flex flex-column" title="名片解析" v-loading="loading">
         <template #title>
-          <v-btn color="primary" class="buttons" rounded>
-            <v-icon left>mdi-send</v-icon>
-            提交
+          <v-btn color="primary" class="buttons" rounded @click="handleUpdate">
+            <v-icon left>mdi-update</v-icon>
+            更新
           </v-btn>
         </template>
         <div class="fullBox overflow-auto">
@@ -138,6 +125,11 @@ import UploadBtn from '@/components/UploadBtn'
 import MDialog from '@/components/Dialog'
 import MCard from '@/components/MCard'
 import MForm from '@/components/MForm'
+import {
+  businessCardParse,
+  getBusinessCardImage,
+  updateBusinessCardStatus
+} from '@/api/dataOrigin'
 export default {
   name: 'imageImportEdit',
   components: {
@@ -156,39 +148,68 @@ export default {
       previewUrl: null,
       formItems: [
         { slotName: 'baseInfo' },
-        { label: '姓名(中)', key: 'name', type: 'text', outlined: true, dense: true, col: 6 },
-        { label: '姓名(英)', key: 'name', type: 'text', outlined: true, dense: true, col: 6 },
-        { label: '职位(中)', key: 'post', type: 'text', outlined: true, dense: true, col: 6 },
-        { label: '职位(英)', key: 'post', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '中文姓名', key: 'name_zh', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '英文姓名', key: 'name_en', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '中文职位/头衔', key: 'title_zh', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '英文职位/头衔', key: 'title_en', type: 'text', outlined: true, dense: true, col: 6 },
         { slotName: 'relationship' },
-        { label: '手机', key: 'post', type: 'text', outlined: true, dense: true, col: 4 },
-        { label: '电话', key: 'post', type: 'text', outlined: true, dense: true, col: 4 },
-        { label: '邮箱', key: 'post', type: 'text', outlined: true, dense: true, col: 4 },
+        { label: '手机号码', key: 'mobile', type: 'text', outlined: true, dense: true, col: 4 },
+        { label: '固定电话', key: 'phone', type: 'text', outlined: true, dense: true, col: 4 },
+        { label: '电子邮箱', key: 'email', type: 'text', outlined: true, dense: true, col: 4 },
         { slotName: 'companyInfo' },
-        { label: '酒店名称(中)', key: 'post', type: 'text', outlined: true, dense: true, col: 6 },
-        { label: '酒店名称(英)', key: 'post', type: 'text', outlined: true, dense: true, col: 6 },
-        { label: '品牌名称(中)', key: 'post', type: 'text', outlined: true, dense: true, col: 6 },
-        { label: '品牌名称(英)', key: 'post', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '中文酒店/公司名称', key: 'hotel_zh', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '英文酒店/公司名称', key: 'hotel_en', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '中文品牌名称', key: 'brand_zh', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '英文品牌名称', key: 'brand_en', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '中文隶属关系', key: 'affiliation_zh', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '英文隶属关系', key: 'affiliation_en', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '品牌组合', key: 'brand_group', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '职业轨迹', key: 'career_path', type: 'text', outlined: true, dense: true, col: 6 },
         { slotName: 'addressInfo' },
-        { label: '详细地址(中)', key: 'post', type: 'text', outlined: true, dense: true, col: 6 },
-        { label: '详细地址(英)', key: 'post', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '中文地址', key: 'address_zh', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '英文地址', key: 'address_en', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '中文邮政编码', key: 'postal_code_zh', type: 'text', outlined: true, dense: true, col: 6 },
+        { label: '英文邮政编码', key: 'postal_code_en', type: 'text', outlined: true, dense: true, col: 6 },
         { slotName: 'systemInfo' }
       ],
-      formQuery: {}
+      formQuery: {},
+      id: null
     }
   },
   methods: {
-    open (item) {
+    async open (item) {
       this.show = true
       this.loading = false
+      this.formQuery = this.formItems.reduce((res, val) => {
+        if (val.key) {
+          res[val.key] = null
+        }
+        return res
+      }, {})
       if (!item) {
         this.file = null
         this.previewUrl = null
+        this.id = null
         return
       }
+      this.id = item.id
+      Object.keys(this.formQuery).forEach(key => {
+        this.formQuery[key] = item[key] || null
+      })
       // 获取文件内容
-      this.file = null
-      this.handlePreview(this.file)
+      if (!item.image_path) {
+        this.file = null
+        this.previewUrl = null
+        return
+      }
+      this.file = item.image_path
+      try {
+        const { data } = await getBusinessCardImage(item.image_path)
+        console.log(data)
+        this.handlePreview(this.file)
+      } catch (error) {
+        this.$snackbar.error(error)
+      }
     },
     async handleImport (file) {
       this.loading = true
@@ -206,11 +227,40 @@ export default {
       }
       reader.readAsDataURL(file)
     },
-    handleAnalysis () {
+    async handleAnalysis () {
       this.linearLoading = true
-      setTimeout(() => {
+      const query = new FormData()
+      query.append('image', this.file)
+      try {
+        const { data } = await businessCardParse(query)
+        this.id = data.id
+        Object.keys(this.formQuery).forEach(key => {
+          this.formQuery[key] = data[key] || null
+        })
+        this.$snackbar.success('名片解析成功')
+      } catch (error) {
+        this.$snackbar.error(error)
+      } finally {
         this.linearLoading = false
-      }, 2000)
+      }
+    },
+    async handleUpdate () {
+      if (!this.id) {
+        this.$snackbar.error('ID获取异常')
+        return
+      }
+      try {
+        await updateBusinessCardStatus(this.formQuery, this.id)
+        this.$snackbar.success('更新成功')
+      } catch (error) {
+        this.$snackbar.error(error)
+      }
+    },
+    handleClose () {
+      if (!this.id) {
+        return
+      }
+      this.$emit('refresh')
     }
   }
 }
@@ -220,6 +270,7 @@ export default {
 .fullBox {
   width: 100%;
   height: 100%;
+  min-height: 400px;
 }
 .upload-card {
   // position: relative;
@@ -229,11 +280,15 @@ export default {
   }
 
 }
-.box-2 {
+.box-1 {
   display: grid;
-  grid-template-columns: 1fr 1fr;
-  grid-gap: 15px;
+  grid-template-columns: 1fr;
+  &.box-2 {
+    grid-template-columns: 1fr 1fr;
+    grid-gap: 15px;
+  }
 }
+
 .infoBox {
   .label {
     width: 100px;

+ 38 - 20
src/views/dataOrigin/unstructuredData/manualCollection/dynamic/imageImport.vue

@@ -14,7 +14,7 @@
       @sort="handleSort"
     >
       <template #status="{ item }">
-        <v-chip small :color="item.status === 1 ? 'success' : 'error'">{{ item.status === 1 ? '已启用' : '已禁用'}}</v-chip>
+        <v-chip small :color="item.status === 'active' ? 'success' : 'error'">{{ item.status === 'active' ? '已启用' : '已禁用'}}</v-chip>
       </template>
       <template #navBtn>
         <v-btn class="buttons" rounded elevation="5" color="primary" @click="handleAdd">
@@ -24,13 +24,13 @@
       </template>
       <template #actions="{ item }">
         <v-btn color="primary" text @click="handleEdit(item)">编辑</v-btn>
-        <v-btn :color="item.status === 1 ? 'warning' : 'success'" text @click="handleStatus(item)">
-          {{ item.status === 1 ? '禁用' : '启用'}}
+        <v-btn :color="item.status === 'active' ? 'warning' : 'success'" text @click="handleStatus(item)">
+          {{ item.status === 'active' ? '禁用' : '启用'}}
         </v-btn>
-        <v-btn color="error" text @click="handleDelete(item)">删除</v-btn>
+        <!-- <v-btn color="error" text @click="handleDelete(item)">删除</v-btn> -->
       </template>
     </m-table>
-    <ImageImportEdit ref="imageImportEditRefs"></ImageImportEdit>
+    <ImageImportEdit ref="imageImportEditRefs" @refresh="init()"></ImageImportEdit>
   </div>
 </template>
 
@@ -38,6 +38,10 @@
 import MFilter from '@/components/Filter'
 import MTable from '@/components/List/table.vue'
 import ImageImportEdit from '../components/imageImportEdit'
+import {
+  getBusinessCard,
+  updateBusinessCardStatus
+} from '@/api/dataOrigin'
 export default {
   name: 'image-import',
   components: { MFilter, MTable, ImageImportEdit },
@@ -55,22 +59,15 @@ export default {
         name: null
       },
       headers: [
-        { text: '姓名', align: 'start', value: 'name' },
-        { text: '职位', align: 'start', value: 'source' },
-        { text: '酒店/公司', align: 'start', value: 'company' },
-        { text: '创建日期', align: 'start', value: 'createDate' },
+        { text: '姓名(中)', align: 'start', value: 'name_zh' },
+        { text: '姓名(英)', align: 'start', value: 'name_en' },
+        { text: '职位', align: 'start', value: 'title_en' },
+        { text: '酒店/公司', align: 'start', value: 'hotel_zh' },
+        { text: '创建日期', align: 'start', value: 'created_at' },
         { text: '状态', align: 'start', value: 'status' },
         { text: '操作', align: 'start', value: 'actions' }
       ],
-      items: [
-        {
-          name: '张三',
-          source: '总经理',
-          company: 'UrCove by HYATT 上海静安',
-          createDate: '2025/03/31 10:06',
-          status: 1
-        }
-      ],
+      items: [],
       orders: [],
       pageInfo: {
         size: 10,
@@ -83,7 +80,17 @@ export default {
     this.init()
   },
   methods: {
-    async init () {},
+    async init () {
+      this.loading = true
+      try {
+        const { data } = await getBusinessCard()
+        this.items = data
+      } catch (error) {
+        this.$snackbar.error(error)
+      } finally {
+        this.loading = false
+      }
+    },
     handleSearch (val) {
       Object.assign(this.queryData, val)
       this.pageInfo.current = 1
@@ -96,7 +103,18 @@ export default {
       this.$refs.imageImportEditRefs.open(item)
     },
     async handleStatus (item) {
-      item.status = item.status === 1 ? 0 : 1
+      try {
+        if (item.status === 'active') {
+          await this.$confirm('提示', '是否禁用该名片?')
+        }
+        await updateBusinessCardStatus({
+          status: item.status === 'active' ? 'inactive' : 'active'
+        }, item.id)
+        this.$snackbar.success('更新成功')
+        this.init()
+      } catch (error) {
+        this.$snackbar.error(error)
+      }
     },
     handleDelete (item) {
       this.$confirm('提示', `是否删除 [ ${item.name} ]`)