Procházet zdrojové kódy

Merge branch 'dev' of https://git.citupro.com/zhengnaiwen_citu/menduner-admin into dev

lifanagju_citu před 2 měsíci
rodič
revize
f2281c553d

+ 3 - 2
src/views/menduner/system/enterprisePackage/PackageForm.vue

@@ -31,7 +31,7 @@
       <!-- <el-form-item v-if="!hidden" label="搜索人才数" prop="searchCount">
         <el-input-number v-model="formData.searchCount" :min="0" :step="10" />
       </el-form-item> -->
-      <el-form-item v-if="!hidden" label="查看简历数" prop="lookCvCount">
+      <el-form-item label="查看简历数" prop="lookCvCount">
         <el-input-number v-model="formData.lookCvCount" :min="0" :step="10" />
       </el-form-item>
       <el-form-item v-if="!hidden" label="开启人才地图" prop="personMap">
@@ -122,7 +122,8 @@ const formRules = computed(() => {
     return {
       name: baseRules.name,
       publishJobCount: baseRules.publishJobCount,
-      text: baseRules.text
+      text: baseRules.text,
+      lookCvCount: baseRules.lookCvCount
     }
   }
   // 当hidden为false时,返回所有验证规则

+ 197 - 205
src/views/menduner/system/talentMap/components/FormPage.vue

@@ -7,213 +7,204 @@
       v-loading="loading"
       :disabled="readOnly"
     >
-      <el-row>
-        <div class="m-title">基础信息</div>
-      </el-row>
-      <el-row :gutter="10">
-        <el-col :span="12">
-          <el-form-item label="姓名(中)" prop="name_zh">
-            <el-input v-model="formQuery.name_zh" placeholder="请输入中文姓名" />
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="姓名(英)" prop="name_en">
-            <el-input v-model="formQuery.name_en" placeholder="请输入英文姓名" />
-          </el-form-item>
-        </el-col>
-      </el-row>
-      <el-row :gutter="10">
-        <el-col :span="12">
-          <el-form-item label="职位/头衔(中)" prop="title_zh">
-            <el-input v-model="formQuery.title_zh" placeholder="请输入中文职位/头衔" />
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="职位/头衔(英)" prop="title_en">
-            <el-input v-model="formQuery.title_en" placeholder="请输入英文职位/头衔" />
-          </el-form-item>
-        </el-col>
-      </el-row>
-      <el-row :gutter="10">
-        <el-col :span="12">
-          <el-form-item label="生日" prop="birthday">
-            <!-- <el-input v-model="formQuery.birthday" placeholder="请输入出生日期" /> -->
-            <el-date-picker
-              v-model="formQuery.birthday"
-              value-format="YYYY-MM-DD"
-              type="date"
-              placeholder="请选择出生日期"
-              :disabledDate="disabledDates"
-              @change="birthdayChange"
-            />
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="籍贯" prop="native_place">
-            <el-input v-model="formQuery.native_place" placeholder="请输入籍贯" />
-          </el-form-item>
-        </el-col>
-      </el-row>
-      <el-row :gutter="10">
-        <el-col :span="12">
-          <el-form-item label="年龄" prop="age">
-            <el-input v-model="formQuery.age" placeholder="请输入年龄" />
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="电子邮箱" prop="email">
-            <el-input v-model="formQuery.email" placeholder="请输入电子邮箱" />
-          </el-form-item>
-        </el-col>
-      </el-row>
-      <el-row :gutter="10">
-        <el-col :span="24">
-          <el-form-item label="工作地" prop="residence">
-            <el-input v-model="formQuery.residence" placeholder="请输入当前工作地址" />
-          </el-form-item>
-        </el-col>
-      </el-row>
-      <el-row>
-        <div class="m-title">联系方式</div>
-      </el-row>
-      <el-row :gutter="10">
-        <el-col :span="12">
-          <el-form-item label="固定电话" prop="phone">
-            <el-input v-model="formQuery.phone" placeholder="请输入固定电话" />
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="手机号码1" prop="mobile">
-            <el-input v-model="formQuery.mobile[0]" placeholder="请输入手机号码" @change="val => mobileChange(val, 0)" />
-          </el-form-item>
-        </el-col>
-        
-      </el-row>
-      <el-row :gutter="10">
-        <el-col :span="12">
-          <el-form-item label="手机号码2" prop="mobile">
-            <el-input v-model="formQuery.mobile[1]" placeholder="请输入手机号码" @change="val => mobileChange(val, 1)" />
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="手机号码3" prop="mobile">
-            <el-input v-model="formQuery.mobile[2]" placeholder="请输入手机号码" @change="val => mobileChange(val, 2)" />
-          </el-form-item>
-        </el-col>
-      </el-row>
-      <el-row>
-        <div class="m-title">酒店/公司信息</div>
-      </el-row>
-      <el-row :gutter="10">
-        <el-col :span="12">
-          <el-form-item label="酒店/公司名称(中)" prop="hotel_zh">
-            <el-input v-model="formQuery.hotel_zh" placeholder="请输入中文酒店/公司名称" />
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="酒店/公司名称(英)" prop="hotel_en">
-            <el-input v-model="formQuery.hotel_en" placeholder="请输入英文酒店/公司名称" />
-          </el-form-item>
-        </el-col>
-      </el-row>
-      <el-row :gutter="10">
-        <el-col :span="12">
-          <el-form-item label="品牌名称(中)" prop="brand_zh">
-            <el-input v-model="formQuery.brand_zh" placeholder="请输入中文品牌名称" />
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="品牌名称(英)" prop="brand_en">
-            <el-input v-model="formQuery.brand_en" placeholder="请输入英文品牌名称" />
-          </el-form-item>
-        </el-col>
-      </el-row>
-      <el-row :gutter="10">
-        <el-col :span="12">
-          <el-form-item label="隶属关系(中)" prop="affiliation_zh">
-            <el-input v-model="formQuery.affiliation_zh" placeholder="请输入中文隶属关系" />
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="隶属关系(英)" prop="affiliation_en">
-            <el-input v-model="formQuery.affiliation_en" placeholder="请输入英文隶属关系" />
-          </el-form-item>
-        </el-col>
-      </el-row>
-      <el-form-item label="品牌组合" prop="brand_group">
-        <el-input v-model="formQuery.brand_group" placeholder="请输入品牌组合" />
-      </el-form-item>
-      <el-row>
-        <div class="m-title">职业轨迹</div>
-      </el-row>
-
-      <div class="text-right mb-5px">
-        <el-button @click="addCareer" plain type="primary" class="cursor-pointer">
-          <Icon icon="ep:plus" class="mr-5px" /> 新增
-        </el-button>
+      <div class="m-title">
+        <div class="mb-10px">基础信息</div>
+        <el-row :gutter="10">
+          <el-col :span="12">
+            <el-form-item label="姓名(中)" prop="name_zh">
+              <el-input v-model="formQuery.name_zh" placeholder="请输入中文姓名" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="姓名(英)" prop="name_en">
+              <el-input v-model="formQuery.name_en" placeholder="请输入英文姓名" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="职位/头衔(中)" prop="title_zh">
+              <el-input v-model="formQuery.title_zh" placeholder="请输入中文职位/头衔" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="职位/头衔(英)" prop="title_en">
+              <el-input v-model="formQuery.title_en" placeholder="请输入英文职位/头衔" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="生日" prop="birthday">
+              <el-date-picker
+                v-model="formQuery.birthday"
+                value-format="YYYY-MM-DD"
+                type="date"
+                placeholder="请选择出生日期"
+                :disabledDate="disabledDates"
+                @change="birthdayChange"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="籍贯" prop="native_place">
+              <el-input v-model="formQuery.native_place" placeholder="请输入籍贯" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="年龄" prop="age">
+              <el-input v-model="formQuery.age" placeholder="请输入年龄" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="电子邮箱" prop="email">
+              <el-input v-model="formQuery.email" placeholder="请输入电子邮箱" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="工作地" prop="residence">
+              <el-input v-model="formQuery.residence" placeholder="请输入当前工作地址" />
+            </el-form-item>
+          </el-col>
+        </el-row>
       </div>
-      <el-table :data="careerTrajectory" :stripe="true">
-        <el-table-column label="酒店名称" align="center" prop="hotel_zh">
-          <template #default="scope">
-            <el-input v-model="scope.row.hotel_zh" placeholder="请输入酒店名称" />
-          </template>
-        </el-table-column>
-        <el-table-column label="职位名称" align="center" prop="title_zh">
-          <template #default="scope">
-            <el-input v-model="scope.row.title_zh" placeholder="请输入职位名称" />
-          </template>
-        </el-table-column>
-        <el-table-column label="任职时间" align="center" prop="date">
-          <template #default="scope">
-            <el-date-picker
-              v-model="scope.row.date"
-              value-format="YYYY-MM-DD"
-              type="date"
-              :disabledDate="disabledDates"
-              placeholder="任职时间"
-              class="!w-100%"
-            />
-          </template>
-        </el-table-column>
-        <el-table-column label="操作" align="center" width="80">
-          <template #default="scope">
-            <el-button link type="danger" :icon="Delete" @click="removeCareer(scope.$index)" />
-          </template>
-        </el-table-column>
-      </el-table>
-      <el-row>
-        <div class="m-title">地址信息</div>
-      </el-row>
-      <el-form-item label="中文地址" prop="address_zh">
-        <el-input v-model="formQuery.address_zh" placeholder="请输入中文地址" />
-      </el-form-item>
-      <el-form-item label="英文地址" prop="address_en">
-        <el-input v-model="formQuery.address_en" placeholder="请输入英文地址" />
-      </el-form-item>
-      <el-form-item label="邮政编码" prop="postal_code_zh">
-        <el-input v-model="formQuery.postal_code_zh" placeholder="请输入邮政编码" />
-      </el-form-item>
-      <el-row>
-        <div class="m-title">信息补充</div>
-      </el-row>
-      <el-form-item label="人才信息" prop="talent_profile">
-        <el-input v-model="formQuery.talent_profile" type="textarea" :rows="2" placeholder="个人简介、背景、职业发展、专业技能、资质认证、教育背景、获奖情况及荣誉等信息" />
-      </el-form-item>
-      <template v-if="formType === 'edit'">
-        <el-row>
-          <div class="m-title">系统信息</div>
+      
+      <div class="m-title my-10px">
+        <div class="mb-10px">联系方式</div>
+        <el-row :gutter="10">
+          <el-col :span="12">
+            <el-form-item label="固定电话" prop="phone">
+              <el-input v-model="formQuery.phone" placeholder="请输入固定电话" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="手机号码1" prop="mobile">
+              <el-input v-model="formQuery.mobile[0]" placeholder="请输入手机号码" @change="val => mobileChange(val, 0)" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="手机号码2" prop="mobile">
+              <el-input v-model="formQuery.mobile[1]" placeholder="请输入手机号码" @change="val => mobileChange(val, 1)" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="手机号码3" prop="mobile">
+              <el-input v-model="formQuery.mobile[2]" placeholder="请输入手机号码" @change="val => mobileChange(val, 2)" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </div>
+      
+      <div class="m-title">
+        <div class="mb-10px">酒店/公司信息</div>
+        <el-row :gutter="10">
+          <el-col :span="12">
+            <el-form-item label="酒店/公司名称(中)" prop="hotel_zh">
+              <el-input v-model="formQuery.hotel_zh" placeholder="请输入中文酒店/公司名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="酒店/公司名称(英)" prop="hotel_en">
+              <el-input v-model="formQuery.hotel_en" placeholder="请输入英文酒店/公司名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="品牌名称(中)" prop="brand_zh">
+              <el-input v-model="formQuery.brand_zh" placeholder="请输入中文品牌名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="品牌名称(英)" prop="brand_en">
+              <el-input v-model="formQuery.brand_en" placeholder="请输入英文品牌名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="隶属关系(中)" prop="affiliation_zh">
+              <el-input v-model="formQuery.affiliation_zh" placeholder="请输入中文隶属关系" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="隶属关系(英)" prop="affiliation_en">
+              <el-input v-model="formQuery.affiliation_en" placeholder="请输入英文隶属关系" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="品牌组合" prop="brand_group">
+              <el-input v-model="formQuery.brand_group" placeholder="请输入品牌组合" />
+            </el-form-item>
+          </el-col>
         </el-row>
-        <el-form-item label="状态" v-if="itemData.status">
-          <el-tag :type="itemData.status === 'active' ? 'success' : 'danger'">
-            {{ itemData.status === 'active' ? '已启用' : '已禁用' }}
-          </el-tag>
+      </div>
+      
+      <div class="m-title my-10px">
+        <div>职业轨迹</div>
+        <div class="text-right mb-5px">
+          <el-button @click="addCareer" plain type="primary" class="cursor-pointer">
+            <Icon icon="ep:plus" class="mr-5px" /> 新增
+          </el-button>
+        </div>
+        <el-table :data="careerTrajectory" :stripe="true">
+          <el-table-column label="酒店名称" align="center" prop="hotel_zh">
+            <template #default="scope">
+              <el-input v-model="scope.row.hotel_zh" placeholder="请输入酒店名称" />
+            </template>
+          </el-table-column>
+          <el-table-column label="职位名称" align="center" prop="title_zh">
+            <template #default="scope">
+              <el-input v-model="scope.row.title_zh" placeholder="请输入职位名称" />
+            </template>
+          </el-table-column>
+          <el-table-column label="任职时间" align="center" prop="date">
+            <template #default="scope">
+              <el-date-picker
+                v-model="scope.row.date"
+                value-format="YYYY-MM-DD"
+                type="date"
+                :disabledDate="disabledDates"
+                placeholder="任职时间"
+                class="!w-100%"
+              />
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" align="center" width="80">
+            <template #default="scope">
+              <el-button link type="danger" :icon="Delete" @click="removeCareer(scope.$index)" />
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      
+      <div class="m-title">
+        <div class="mb-10px">地址信息</div>
+        <el-form-item label="中文地址" prop="address_zh">
+          <el-input v-model="formQuery.address_zh" placeholder="请输入中文地址" />
+        </el-form-item>
+        <el-form-item label="英文地址" prop="address_en">
+          <el-input v-model="formQuery.address_en" placeholder="请输入英文地址" />
         </el-form-item>
-        <el-form-item label="创建时间">
-          <el-tag v-if="itemData?.created_at" type="primary" effect="light">{{ itemData?.created_at }}</el-tag>
+        <el-form-item label="邮政编码" prop="postal_code_zh">
+          <el-input v-model="formQuery.postal_code_zh" placeholder="请输入邮政编码" />
         </el-form-item>
-        <el-form-item label="更新时间">
-          <el-tag v-if="itemData?.updated_at" type="primary" effect="light">{{ itemData?.updated_at }}</el-tag>
+      </div>
+      
+      <div class="m-title my-10px">
+        <div class="mb-10px">信息补充</div>
+        <el-form-item label="人才信息" prop="talent_profile">
+          <el-input v-model="formQuery.talent_profile" type="textarea" :rows="2" placeholder="个人简介、背景、职业发展、专业技能、资质认证、教育背景、获奖情况及荣誉等信息" />
         </el-form-item>
+      </div>
+      
+      <template v-if="formType === 'edit'">
+        <div class="m-title">
+          <div class="mb-10px">系统信息</div>
+          <el-form-item label="状态" v-if="itemData.status">
+            <el-tag :type="itemData.status === 'active' ? 'success' : 'danger'">
+              {{ itemData.status === 'active' ? '已启用' : '已禁用' }}
+            </el-tag>
+          </el-form-item>
+          <el-form-item label="创建时间">
+            <el-tag v-if="itemData?.created_at" type="primary" effect="light">{{ itemData?.created_at }}</el-tag>
+          </el-form-item>
+          <el-form-item label="更新时间">
+            <el-tag v-if="itemData?.updated_at" type="primary" effect="light">{{ itemData?.updated_at }}</el-tag>
+          </el-form-item>
+        </div>
       </template>
     </el-form>
 	</div>
@@ -371,13 +362,14 @@ defineExpose({
 .formBox {
   flex: 1;
   max-height: 70vh;
-  padding: 12px;
+  // padding: 12px;
   overflow: auto;
-  background-color: #f5f7f9;
+  // background-color: #f5f7f9;
   .m-title {
-    margin: 12px 8px;
+    padding: 12px 8px;
     font-size: 13px;
     color: #999;
+    background-color: #f5f7f9;
   }
   .trajectoryBox {
     margin: 10px 20px;

+ 134 - 0
src/views/menduner/system/talentMap/maintenance/examine/index.vue

@@ -0,0 +1,134 @@
+<template>
+  <!-- 搜索工作栏 -->
+  <ContentWrap>
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="40px"
+    >
+      <el-form-item label="来源" prop="task_type">
+        <el-select
+          v-model="queryParams.task_type"
+          placeholder="请选择来源"
+          clearable
+          class="!w-200px"
+          @change="handleQuery"
+        >
+          <el-option v-for="(val, index) in taskType" :label="val.label" :value="val.value" :key="index" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery"><Icon icon="ep:search" /> 搜索</el-button>
+        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list" :stripe="true">
+      <el-table-column label="姓名" align="center" prop="" />
+      <el-table-column label="职位" align="center" prop="" />
+      <el-table-column label="酒店" align="center" prop="" />
+      <el-table-column label="联系电话" align="center" prop="" :formatter="dateFormatter" />
+      <el-table-column label="邮箱" align="center" prop="" :formatter="dateFormatter" />
+      <el-table-column label="操作" align="center" fixed="right" min-width="110">
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="handleStore(scope.row)"
+          >入库</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.page"
+      v-model:limit="queryParams.per_page"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+	<!-- 入库 -->
+  <StorePage ref="StorePageRef" @refresh="handleQuery" />
+</template>
+
+<script setup>
+defineOptions({ name: 'TalentMapMaintenanceExamineIndex' })
+import { dateFormatter } from '@/utils/formatTime'
+import StorePage from '@/views/menduner/system/talentMap/maintenance/gather/components/store.vue'
+import { talentGatherApi } from '@/api/menduner/system/talentMap/gather'
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const loading = ref(false) // 列表的加载中
+const list = ref([]) // 列表的数据
+const total = ref(0) // 列表的总页数
+const queryParams = reactive({
+  page: 1,
+  per_page: 10,
+  task_status: undefined // 来源
+})
+const queryFormRef = ref() // 搜索的表单
+const taskType = [
+  { label: '名片', value: '名片' },
+  { label: '简历', value: '简历' },
+  { label: '门墩儿新任命', value: '新任命' },
+  { label: '门墩儿招聘', value: '招聘' },
+  { label: '杂项', value: '杂项' }
+]
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    list.value = []
+    const data = await talentGatherApi.getTaskList(queryParams)
+    list.value = data.tasks ?? []
+    total.value = data.pagination.total ?? 0
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.page = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+// 入库
+const StorePageRef = ref(null)
+const handleStore = ({ task_type, parse_result, id, task_source }, isDetail = false) => {
+  const txt = isDetail ? '暂无详情查看' : '暂无解析数据可入库'
+  const isRecruitment = Boolean(task_type === '招聘')
+  if (isRecruitment) {
+    if (!task_source?.data) return message.warning(txt)
+  } else {
+    if (!parse_result) return message.warning(txt)
+  }
+
+  const list = isRecruitment ? { results: task_source?.data || [] } : parse_result // 解析的人才列表
+  StorePageRef.value.open(task_type, list, id, isDetail)
+}
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
+
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 0 - 393
src/views/menduner/system/talentMap/maintenance/gather/businessCard/index.vue

@@ -1,393 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="名称" prop="name">
-        <el-input v-model="queryParams.name" placeholder="请输入名称" clearable @keyup.enter="handleQuery" class="!w-180px" />
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery('search')"><Icon icon="ep:search" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-        <el-button type="primary" plain @click="handleAdd">
-          <Icon icon="ep:plus" class="mr-5px" /> 新增解析
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true">
-      <el-table-column label="中文名称" align="center" prop="name_zh" fixed="left" />
-      <el-table-column label="英文名称" align="center" prop="name_en" />
-      <el-table-column label="职位" align="center" prop="title_zh" />
-      <el-table-column label="酒店/公司" align="center" prop="hotel_zh" />
-      <el-table-column label="手机号码" align="center" prop="mobile" />
-      <el-table-column label="固定电话" align="center" prop="phone" />
-      <el-table-column label="状态" align="center" prop="status" width="80">
-        <template #default="scope">
-          <el-tag type="success" v-if="scope.row.status === 'active'">已启用</el-tag>
-          <el-tag type="danger" v-else>已禁用</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="创建日期" align="center" prop="created_at" :formatter="dateFormatter" />
-      <el-table-column label="操作" align="center" fixed="right" min-width="110">
-        <template #default="scope">
-          <el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button>
-          <el-button link type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
-          <el-button link :type="scope.row.status === 'active' ? 'warning': 'success'" @click="handleDisable(scope.row)">
-            {{ scope.row.status === 'active' ? '禁用' : '启用'}}
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <!-- 上传 -->
-    <Dialog title="名片解析" v-model="openUploadImg" width="500" @close="handleCancel">
-      <UploadImg
-        v-model="filePath"
-        :limit="1"
-        :uploadSuccessTip="false"
-        @handle-change="uploadChange"
-        height="150px" width="150px" style="margin: 20px auto; width: 150px;"
-      >
-        <template #tip>{{ filePath ? '' : '请上传名片' }}</template>
-      </UploadImg>
-      <template #footer>
-        <el-button @click="handleAnalysis" type="success" :disabled="analysisLoading" :loading="analysisLoading">解 析</el-button>
-        <el-button @click="handleCancel">取 消</el-button>
-      </template>
-    </Dialog>
-
-    <!-- 解析回显 -->
-    <Dialog title="名片解析" v-model="showAnalysisTable" width="80%">
-      <div class="analysisInfoBox">
-        <div class="image">
-          <el-image v-if="filePath" class="!w-100%" :src="filePath" />
-          <div v-else>
-            <UploadImg
-              v-model="filePath"
-              :limit="1"
-              :uploadSuccessTip="false"
-              drag
-              buttonUpload
-              @handle-change="uploadChange"
-              height="32px" width="104px"
-              style="margin: 0 auto; width: 104px;margin-top: 40%;"
-            >
-              <template #tip>{{ filePath ? '' : '请上传名片' }}</template>
-            </UploadImg>
-          </div>
-        </div>
-        <FormPage ref="FormPageRef" :formType="formType" :itemData="itemData" />
-      </div>
-      <template #footer>
-        <el-button @click="handleSave" type="success" :disabled="analysisLoading">保 存</el-button>
-        <el-button @click="showAnalysisTable = false">取 消</el-button>
-      </template>
-    </Dialog>
-  </ContentWrap>
-
-  <MergeForm ref="mergeFormRef" @refresh="getList" />
-</template>
-
-<script setup>
-import { dateFormatter } from '@/utils/formatTime'
-import { talentLabelingApi } from '@/api/menduner/system/talentMap/labeling'
-import { Delete, Plus } from '@element-plus/icons-vue'
-import MergeForm from '@/views/menduner/system/talentMap/components/merge.vue'
-import FormPage from '@/views/menduner/system/talentMap/components/FormPage.vue'
-
-/** 人才采集 名片解析 */
-defineOptions({ name: 'TalentMapCard' })
-
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(false) // 列表的加载中
-const list = ref([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  name: undefined
-})
-const queryFormRef = ref() // 搜索的表单
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    list.value = []
-    const data = await talentLabelingApi.getCardList()
-    list.value = data ? data.reverse() : []
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 搜索按钮操作 */
-const handleQuery = (type) => {
-  // if (type === 'search') {
-  //   message.warning('搜索正在建设中...')
-  //   return
-  // }
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery()
-}
-
-// const dealData = (item) => {
-  // itemData.value = { ...item }
-  // careerTrajectory.value = item?.career_path ? JSON.parse(JSON.stringify(item.career_path)) : []
-  // if (!careerTrajectory.value?.length) careerTrajectory.value = [{ hotel_zh: null,  title_zh: null, date: null }]
-  // Object.keys(formQuery.value).forEach(key => {
-  //   formQuery.value[key] = item[key] || null
-  // })
-// }
-
-/** 删除按钮操作 */
-const handleDelete = async (id) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await talentLabelingApi.deleteBusinessCard(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    setTimeout(async () => {
-      await getList()
-    }, 0)
-  } catch {}
-}
-
-/** 编辑 */
-const { push } = useRouter()
-const handleEdit = async (item) => {
-  formType.value = 'edit'
-  // dealData(item)
-  itemData.value = item
-  file.value = null
-  filePath.value = null
-  try {
-    if (!item?.image_path) {
-      showAnalysisTable.value = true
-      return
-    }
-    const data = await talentLabelingApi.getBusinessCardImage(item.image_path)
-    if (!data?.type) return
-
-    file.value = new File([data], item.image_path, { type: data.type })
-    filePath.value = URL.createObjectURL(data)
-  } catch (error) {
-    console.log('打印->getBusinessCardImage', error)
-  } finally {
-    showAnalysisTable.value = true
-  }
-}
-
-/** 禁用按钮操作 */
-const handleDisable = async (item) => {
-  if (!item?.id) return message.warning('操作失败,请稍后再试')
-  try {
-    // 禁用的二次确认
-    const status = item.status === 'active' ? 'inactive' : 'active'
-    const text = status === 'inactive' ? '禁用' : '启用'
-    
-    await message.delConfirm(`是否${text}该名片?`)
-    // 发起禁用
-    await talentLabelingApi.updateBusinessCardStatus({
-      status,
-    }, item.id)
-    message.success(`${text}成功`)
-    // 刷新列表
-    await getList()
-  } catch {}
-}
-
-// const formQuery = ref({
-//   name_zh: undefined,
-//   name_en: undefined,
-//   title_zh: undefined,
-//   title_en: undefined,
-//   mobile: undefined,
-//   phone: undefined,
-//   email: undefined,
-//   hotel_zh: undefined,
-//   hotel_en: undefined,
-//   brand_zh: undefined,
-//   brand_en: undefined,
-//   affiliation_zh: undefined,
-//   affiliation_en: undefined,
-//   brand_group: undefined,
-//   address_zh: undefined,
-//   address_en: undefined,
-//   postal_code_zh: undefined,
-//   postal_code_en: undefined,
-//   birthday: undefined,
-//   residence: undefined,
-// })
-// const careerTrajectory = ref([{ company_name: null,  position: null, current_date: null }])
-
-const file = ref(null)
-const uploadFile = ref(null)
-const uploadChange = (raw) => {
-  file.value = raw
-}
-
-// const addCareer = () => {
-//   careerTrajectory.value = careerTrajectory.value || []
-//   careerTrajectory.value.push({ company_name: null,  position: null, current_date: null })
-// }
-// const removeCareer = (index) => {
-//   if (careerTrajectory.value.length <= 1) return
-//   careerTrajectory.value.splice(index, 1)
-// }
-
-// 更新
-const showAnalysisTable = ref(false)
-const formLoading = ref(false)
-const formType = ref('')
-const FormPageRef = ref(null)
-const mergeFormRef = ref() // 合并表单 Ref
-const handleSave = async () => {
-  const params = FormPageRef.value.formQuery
-  // 数组转为字符串保存
-  if (Array.isArray(params?.mobile)) {
-    params.mobile = params.mobile.filter(i => Boolean(i)).map(j => String(j).replace(/,|,/g, '')).join(',');
-  }
-  console.log(params, 'handleSubmit')
-  try {
-    formLoading.value = true
-    // formQuery.value.career_path = careerTrajectory
-    // Object.assign(itemData.value, formQuery.value)
-
-    let result = {}
-
-    if (formType.value === 'create') {
-      uploadFile.value.append('card_data', JSON.stringify(params))
-      result = await talentLabelingApi.createBusinessCard(uploadFile.value)
-      message.success('新增成功')
-      showAnalysisTable.value = false
-      // 刷新列表
-      getList()
-
-      if (result.code === 202 || result.message.includes('疑似重复')) {
-        if (!result.data?.main_card?.id) return
-        
-        await message.confirm('发现与当前名片的疑似重复数据,去处理')
-        mergeFormRef.value.open(result.data?.main_card?.id)
-      }
-    } else {
-      await talentLabelingApi.updateBusinessCard(params, itemData.value.id)
-      message.success('更新成功')
-      showAnalysisTable.value = false
-      // 刷新列表
-      getList()
-    }
-  } catch (error) {
-    console.log('更新失败', error)
-  } finally {
-    uploadFile.value = null
-    formLoading.value = false
-  }
-}
-
-// 关闭解析弹窗
-const handleCancel = () => {
-  // console.log('关闭解析弹窗')
-  openUploadImg.value = false
-  analysisLoading.value = false
-}
-
-// 解析中
-const analysisLoading = ref(false)
-const filePath = ref(null)
-const itemData = ref({})
-const handleAnalysis = async () => {
-  if (!filePath.value) {
-    message.warning('请先上传名片!')
-    return
-  }
-  try {
-    analysisLoading.value = true
-    // 开始解析
-    uploadFile.value = new FormData()
-    uploadFile.value.append('image', file.value)
-    message.warning('正在解析...')
-
-    const index = createAnalysisNum.value
-    const res = await talentLabelingApi.businessCardParse(uploadFile.value)
-
-    if (index !== createAnalysisNum.value || !openUploadImg.value) return // 不是最新的名片解析数据(用户在解析完成前已重新上传)或用户已取消解析
-    // dealData(res?.data || res)
-    itemData.value = res?.data || res
-    openUploadImg.value = false
-    showAnalysisTable.value = true
-    message.success('名片解析成功')
-  } catch (error) {
-    console.log('解析失败', error)
-    uploadFile.value = null
-  } finally {
-    analysisLoading.value = false
-  }
-}
-
-// 新增
-const openUploadImg = ref(false)
-const createAnalysisNum = ref(0)
-const handleAdd = () => {
-  formType.value = 'create'
-  file.value = null
-  filePath.value = null
-  analysisLoading.value = false
-  createAnalysisNum.value++
-  // careerTrajectory.value = [{ company_name: null,  position: null, current_date: null }]
-  openUploadImg.value = true
-}
-
-/** 初始化 **/
-onMounted(() => {
-  getList()
-})
-</script>
-
-<style lang="scss" scoped>
-.analysisInfoBox {
-  display: flex;
-  .image {
-    width: 45%;
-    max-height: 70vh;
-    padding-right: 12px;
-    overflow: auto;
-  }
-  .formBox {
-    flex: 1;
-    max-height: 70vh;
-    padding: 12px;
-    overflow: auto;
-    background-color: #f5f7f9;
-    .m-title {
-      margin: 12px 8px;
-      font-size: 13px;
-      // font-weight: bold;
-      color: #999;
-    }
-    .trajectoryBox {
-      margin: 10px 20px;
-      padding-top: 20px;
-      padding-bottom: 5px;
-      border-radius: 5px;
-      background-color: #fff;
-    }
-  }
-}
-</style>

+ 13 - 15
src/views/menduner/system/talentMap/maintenance/gather/components/store.vue

@@ -1,5 +1,11 @@
 <template>
-  <Dialog title="人才入库" v-model="dialogVisible" :modalClose="false" width="90%" @close="dialogVisible = false">
+  <Dialog
+    :title="readOnly ? '解析详情' : '人才入库'"
+    v-model="dialogVisible"
+    :modalClose="false"
+    width="90%"
+    @close="dialogVisible = false"
+  >
     <div class="analysisInfoBox">
       <div class="analysisFile !w-50%">
           <!-- 门墩儿人才库 -->
@@ -49,7 +55,7 @@
               >
                 <div class="flex items-center">
                   <el-checkbox v-if="!readOnly" v-model="val.checked" @click.stop />
-                  <span class="ml-5px">{{ val.name || `人才${index + 1}` }}</span>
+                  <span class="ml-5px">{{ val.name }}</span>
                   <Icon icon="ep:view" class="ml-5px" /> 
                 </div>
               </el-tag>
@@ -221,18 +227,10 @@ const open = async (task_type, parse_result, task_id, isDetail) => {
   resetData()
   source.value = task_type
   const dataList = results.map((e, index) => {
-    if (source.value === '招聘') {
-      Object.assign(e, {
-        error: null,
-        filename: '',
-        index,
-        minio_path: '',
-        object_key: '',
-        success: true,
-      })
-    }
-    // 切换标签
-    tagList.value.push({ name: e.data.name_zh, checked: true })
+    tagList.value.push({
+      name: e.data?.name_zh || `人才${ index + 1 }`,
+      checked: true
+    })
     return e
   })
   originData.value = cloneDeep(dataList)
@@ -280,7 +278,7 @@ const handleStore = async () => {
   }
   
   if (!results?.length) return message.warning(`未选择人才,请选择要入库的人才!`)
-  await message.confirm('是否对当前选中的人才进行入库?')
+  // await message.confirm('是否对当前选中的人才进行入库?')
 
   const loading = ElLoading.service({
     lock: true,

+ 3 - 6
src/views/menduner/system/talentMap/maintenance/gather/index.vue

@@ -57,7 +57,7 @@
             type="success"
             @click="handleAnalysis(scope.row)"
           >解析</el-button>
-          <el-button v-else type="warning" link @click="handleStore(scope.row, true)">详情</el-button>
+          <el-button v-else type="success" link @click="handleStore(scope.row, true)">解析结果</el-button>
           <el-button
             v-if="scope.row.task_status === '成功'"
             link
@@ -260,7 +260,7 @@ const handleStore = ({ task_type, parse_result, id, task_source }, isDetail = fa
     if (!parse_result) return message.warning(txt)
   }
 
-const list = isRecruitment ? { results: JSON.parse(task_source.data) } : parse_result // 解析的人才列表
+  const list = isRecruitment ? { results: task_source?.data || [] } : parse_result // 解析的人才列表
   StorePageRef.value.open(task_type, list, id, isDetail)
 }
 
@@ -283,12 +283,9 @@ const handleAnalysis = async ({ task_type, id, task_source, task_name }) => {
   if (task_type === '杂项') params.process_type = 'table'
   if (task_type === '新任命') params.publish_time = task_source.publish_time
   try {
-    const { results } = await talentGatherApi.taskAnalysis(params)
+    await talentGatherApi.taskAnalysis(params)
     message.success('解析成功')
     handleQuery()
-
-    StorePageRef.value.open(task_type, { results }, id)
-
   } finally {
     loading.close()
   }

+ 0 - 794
src/views/menduner/system/talentMap/maintenance/gather/index1.vue

@@ -1,794 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="名称" prop="name">
-        <el-input v-model="queryParams.name" placeholder="请输入名称" clearable @keyup.enter="handleQuery" class="!w-180px" />
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery('search')"><Icon icon="ep:search" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-        <el-button type="primary" plain @click="handleAdd">
-          <Icon icon="ep:plus" class="mr-5px" /> 新增人才
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true">
-      <el-table-column label="中文名称" align="center" prop="name_zh" fixed="left" />
-      <el-table-column label="英文名称" align="center" prop="name_en" />
-      <el-table-column label="职位" align="center" prop="title_zh" />
-      <el-table-column label="酒店/公司" align="center" prop="hotel_zh" />
-      <el-table-column label="手机号码" align="center" prop="mobile" />
-      <el-table-column label="固定电话" align="center" prop="phone" />
-      <el-table-column label="状态" align="center" prop="status" width="80">
-        <template #default="scope">
-          <el-tag type="success" v-if="scope.row.status === 'active'">已启用</el-tag>
-          <el-tag type="danger" v-else>已禁用</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="创建日期" align="center" prop="created_at" :formatter="dateFormatter" />
-      <el-table-column label="操作" align="center" fixed="right" min-width="110">
-        <template #default="scope">
-          <!-- <el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button> -->
-          <el-button link type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
-          <el-button link :type="scope.row.status === 'active' ? 'warning': 'success'" @click="handleDisable(scope.row)">
-            {{ scope.row.status === 'active' ? '禁用' : '启用'}}
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <!-- 选择来源 -->
-    <Dialog title="新增" v-model="openSelect" width="550" @close="openSelect = false">
-			<el-radio-group v-model="radioValue" size="large" class="radioBox">
-				<el-radio
-					v-for="item in radioList"
-					:key="item.value"
-					:value="item.value"
-				>
-					{{ item.label }}
-				</el-radio>
-			</el-radio-group>
-      <template #footer>
-        <el-button type="primary" @click="handleSelect">确 认</el-button>
-        <el-button @click="openSelect = false">取 消</el-button>
-      </template>
-    </Dialog>
-
-    <!-- 人员搜索 -->
-    <Dialog :title="radioObject.menduner" v-model="openSearch" :modalClose="false" width="1200" @close="openSearch = false">
-      <Search ref="SearchRef" />
-      <template #footer>
-        <el-button type="primary" @click="handleSubmit">确 认</el-button>
-        <el-button @click="openSearch = false">取 消</el-button>
-      </template>
-    </Dialog>
-
-    <!-- 解析文件上传 -->
-    <Dialog :title="radioObject[radioValue]" v-model="dialog_upload" :modalClose="false" :width="DialogWidth" @close="handleCancel">
-      <div>
-        <!-- 简历解析 -->
-        <template v-if="radioValue === 'file'">
-          <el-upload
-            ref="uploadRef"
-            v-model:file-list="fileList"
-            :action="uploadUrl"
-            :auto-upload="false"
-            :data="fileData"
-            :limit="1"
-            :on-change="handleChange"
-            :on-error="submitFormError"
-            :on-exceed="handleExceed"
-            :on-success="submitFormSuccess"
-            :http-request="httpRequest"
-            accept=".pdf, doc, .docx"
-            drag
-            class="flex-1"
-          >
-            <i class="el-icon-upload"></i>
-            <div class="el-upload__text">上传附件, 将文件拖到此处,或 <em>点击上传</em></div>
-            <template #tip>
-              <div class="el-upload__tip color-red">
-                提示:仅允许导入 pdf、doc、docx 格式文件!
-              </div>
-            </template>
-          </el-upload>
-        </template>
-        <!-- 名片解析 -->
-        <template v-if="radioValue === 'card'">
-          <UploadImg
-            v-model="cardImgUrl"
-            :limit="1"
-            :uploadSuccessTip="false"
-            @handle-change="cardUploadChange"
-            height="150px" width="150px" style="margin: 20px auto; width: 150px;"
-          >
-            <template #tip>{{ cardImgUrl ? '' : '请上传名片' }}</template>
-          </UploadImg>
-        </template>
-        <template v-if="radioValue === 'picture'">
-          <UploadImg
-            v-model="cardImgUrl"
-            :limit="1"
-            :uploadSuccessTip="false"
-            @handle-change="cardUploadChange"
-            height="150px" width="150px" style="margin: 20px auto; width: 150px;"
-          >
-            <template #tip>{{ cardImgUrl ? '' : '请上传图片' }}</template>
-          </UploadImg>
-        </template>
-      </div>
-      <template #footer>
-        <el-button @click="handleAnalysis" type="success" :disabled="analysisLoading" :loading="analysisLoading">解 析</el-button>
-        <el-button @click="handleCancel">取 消</el-button>
-      </template>
-    </Dialog>
-
-    <!-- 解析回显 -->
-    <Dialog :title="radioObject[radioValue]" v-model="dialog_analysisInfo" :modalClose="false" width="90%" @close="dialog_analysisInfo = false">
-      <div class="analysisInfoBox">
-        <div class="analysisFile" :style="{'width': showFormPage || tagList?.length > 0 ? '50%' :'100%'}">
-          <!-- 门墩儿人才库 -->
-          <template v-if="radioValue === 'menduner'">
-            <el-tabs v-model="activeName" type="border-card">
-              <el-tab-pane label="基本信息" name="info">
-                <el-card shadow="never">
-                  <Info :id="id" :user-id="userId" />
-                  <expExtend :user-id="userId" defaultShowAll class="m-t-20px" />
-                </el-card>
-              </el-tab-pane>
-
-              <el-tab-pane label="附件简历" name="Attachment">
-                <Attachment showPreview :user-id="userId" />
-              </el-tab-pane>
-            </el-tabs>
-          </template>
-          <!-- 简历解析 -->
-          <template v-if="radioValue === 'file'">
-            <div v-if="fileUrl" style="position: relative;">
-              <div class="text-right m-b-10px">
-                <el-button v-if="!isEdit" @click="handleText">查看文本信息</el-button>
-                <el-button type="primary" @click="handleResetUpload">重新上传简历</el-button>
-              </div>
-              <IFrame :src="fileUrl" />
-              <el-drawer
-                v-model="drawer"
-                modal-class="drawer"
-                size="75%"
-                direction="ltr"
-                title="简历解析(可复制文本使用)"
-              >
-                <p v-for="(text, index) in resumeTxt" :key="text + index">{{ text }}</p>
-              </el-drawer>
-            </div>
-            <el-upload
-              v-else
-              ref="uploadRef"
-              v-model:file-list="fileList"
-              :action="uploadUrl"
-              :auto-upload="false"
-              :data="data"
-              :limit="1"
-              :on-change="handleChange"
-              :on-error="submitFormError"
-              :on-exceed="handleExceed"
-              :on-success="submitFormSuccess"
-              :http-request="httpRequest"
-              accept=".pdf, doc, .docx"
-              drag
-              class="flex-1"
-            >
-              <i class="el-icon-upload"></i>
-              <div class="el-upload__text">上传附件, 将文件拖到此处,或 <em>点击上传</em></div>
-              <template #tip>
-                <div class="el-upload__tip color-red">
-                  提示:仅允许导入 pdf、doc、docx 格式文件!
-                </div>
-              </template>
-            </el-upload>
-          </template>
-          <!-- 名片解析 -->
-          <template v-if="radioValue === 'card'">
-            <div class="image">
-              <el-image v-if="cardImgUrl" class="!w-100%" :src="cardImgUrl" />
-              <div v-else>
-                <UploadImg
-                  v-model="cardImgUrl"
-                  :limit="1"
-                  :uploadSuccessTip="false"
-                  drag
-                  buttonUpload
-                  @handle-change="cardUploadChange"
-                  height="32px" width="104px"
-                  style="margin: 0 auto; width: 104px;margin-top: 40%;"
-                >
-                  <template #tip>{{ cardImgUrl ? '' : '请上传名片' }}</template>
-                </UploadImg>
-              </div>
-            </div>
-          </template>
-          <!-- 网页解析 -->
-          <template v-if="radioValue === 'web'">
-            <webAnalysis
-              v-if="showWebAnalysis"
-              @analysis="handleWebAnalysis"
-              @reset="handleWebClear(), showFormPage = false, formData = {}"
-            />
-          </template>
-        </div>
-        <div class="flex-1">
-          <div class="tagBox mb-10px" v-if="tagList?.length">
-            <el-tag 
-              type="primary" 
-              round
-              size="large"
-              v-for="(val, index) in tagList"
-              :key="index + val"
-              class="mr-10px cursor-pointer mb-10px"
-              @click="handleTagClick(index)"
-            >
-              {{ val }}
-              <Icon v-if="index === tagCurrentIndex" icon="ep:check" class="ml-5px" /> 
-            </el-tag>
-          </div>
-          <FormPage
-            v-if="showFormPage"
-            ref="FormPageRef"
-            :formType="analysisType"
-            :itemData="formData"
-            />
-          <div v-if="tagList?.length && !tagCurrentIndex" class="!h-100% flex items-center justify-center">请点击上方人才姓名进行查看编辑</div>
-        </div>
-      </div>
-      <template #footer>
-        <el-button @click="handleSave" type="success">保 存</el-button>
-        <el-button @click="dialog_analysisInfo = false, handleWebClear">取 消</el-button>
-      </template>
-    </Dialog>
-  </ContentWrap>
-
-  <MergeForm ref="mergeFormRef" @refresh="getList" />
-</template>
-
-<script setup>
-defineOptions({ name: 'TalentMapStoreIndex' })
-import { dateFormatter } from '@/utils/formatTime'
-import { talentLabelingApi } from '@/api/menduner/system/talentMap/labeling'
-import { TalentMap } from '@/api/menduner/system/talentMap'
-import { Delete, Plus } from '@element-plus/icons-vue'
-import MergeForm from '@/views/menduner/system/talentMap/components/merge.vue'
-import FormPage from '@/views/menduner/system/talentMap/components/FormPage.vue'
-// import uploadDialog from './components/uploadDialog.vue'
-import { timesTampChange, timestampToAge } from '@/utils/transform/date'
-import Search from './components/search.vue'
-import webAnalysis from './components/webAnalysis.vue'
-import { useUpload } from '@/components/UploadFile/src/useUpload'
-import { commonApi } from '@/api/menduner/common'
-import { Base64 } from 'js-base64'
-import Info from '@/views/menduner/system/person/details/components/info.vue'
-import expExtend from '@/views/menduner/system/person/details/components/expExtend.vue'
-import Attachment from '@/views/menduner/system/person/details/components/attachment.vue'
-import { talentWebParsingApi } from '@/api/menduner/system/talentMap/webParsing'
-import { ElLoading } from 'element-plus'
-
-const baseUrl = import.meta.env.VITE_PREVIEW_URL
-const { uploadUrl, httpRequest } = useUpload()
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(false) // 列表的加载中
-const list = ref([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  name: undefined
-})
-const queryFormRef = ref() // 搜索的表单
-const dialog_upload = ref(false)
-
-const loadingMark = ref(null)
-const loadingMarkSetting = (text) => {
-  if (text) {
-    loadingMark.value = ElLoading.service({
-      lock: true,
-      text,
-      background: 'rgba(0, 0, 0, 0.7)',
-    })
-  } else {
-    if (loadingMark.value) loadingMark.value.close()
-    loadingMark.value = null
-  }
-}
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    list.value = []
-    const data = await talentLabelingApi.getCardList()
-    list.value = data ?? []
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 搜索按钮操作 */
-const handleQuery = (type) => {
-  if (type !== 'reset') {
-    message.warning('搜索正在建设中...')
-    return
-  }
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery('reset')
-}
-
-/** 删除按钮操作 */
-const handleDelete = async (id) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await talentLabelingApi.deleteBusinessCard(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    setTimeout(async () => {
-      await getList()
-    }, 0)
-  } catch {}
-}
-
-// 网页解析-人才标签点击
-const handleTagClick = (index) => {
-  if (!webOriginList.value?.[index]) return
-  tagCurrentIndex.value = index
-  const obj = webOriginList.value[index]
-  formData.value = obj ?? {}
-  if (!showFormPage.value) showFormPage.value = true
-}
-
-// 网页解析-信息提取
-const tagCurrentIndex = ref(null)
-const tagList = ref([])
-const originMarkdown = ref(null)
-const webOriginList = ref([])
-const handleWebAnalysis = (list, md) => {
-  webOriginList.value = list
-  tagList.value = list.map(e => e.name_zh)
-  originMarkdown.value = md
-  // 只有单个人才时默认选中
-  if (list?.length === 1) {
-    handleTagClick(0)
-  }
-}
-
-const handleWebClear = () => {
-  tagCurrentIndex.value = null
-  originMarkdown.value = null
-  tagList.value = []
-  webOriginList.value = []
-}
-
-/** 编辑 */
-const { push } = useRouter()
-const handleEdit = async (item) => {
-  analysisType.value = 'edit'
-  formData.value = item
-  radioValue.value = item.type || 'card' // menduner
-  try {
-    if (radioValue.value === 'card') {
-      if (!item?.image_path) {
-        cardUploadRow.value = null
-        cardImgUrl.value = null
-        dialog_analysisInfo.value = true
-        return
-      }
-      const data = await talentLabelingApi.getBusinessCardImage(item.image_path)
-      cardUploadRow.value = data?.type ? new File([data], item.image_path, { type: data.type }) : null
-      cardImgUrl.value = data?.type ? URL.createObjectURL(data) : null
-    }
-  } catch (error) {
-    console.log('打印->getBusinessCardImage', error)
-  } finally {
-    dialog_analysisInfo.value = true
-  }
-}
-
-/** 禁用按钮操作 */
-const handleDisable = async (item) => {
-  if (!item?.id) return message.warning('操作失败,请稍后再试')
-  try {
-    // 禁用的二次确认
-    const status = item.status === 'active' ? 'inactive' : 'active'
-    const text = status === 'inactive' ? '禁用' : '启用'
-    
-    await message.delConfirm(`是否${text}该名片?`)
-    // 发起禁用
-    await talentLabelingApi.updateBusinessCardStatus({
-      status,
-    }, item.id)
-    message.success(`${text}成功`)
-    // 刷新列表
-    await getList()
-  } catch {}
-}
-
-// 更新
-const activeName = ref('info')
-const dialog_analysisInfo = ref(false)
-const analysisType = ref('')
-const showFormPage = ref(true)
-const FormPageRef = ref(null)
-const mergeFormRef = ref() // 合并表单 Ref
-
-const handleSaveWebData = async () => {
-  if (!webOriginList.value || !webOriginList.value?.length) {
-    message.warning('请输入微信公众号链接解析并提取信息核对后再提交保存')
-    return
-  }
-  if (!FormPageRef.value) return message.warning('请点击右侧顶部的人才标签查看核对信息后保存')
-  
-  // 验证webOriginList数组中每个人才的name_zh字段,并处理mobile字段
-  if (webOriginList.value && Array.isArray(webOriginList.value) && webOriginList.value.length > 0) {
-    for (let i = 0; i < webOriginList.value.length; i++) {
-      const talent = webOriginList.value[i]
-      if (!talent.name_zh || talent.name_zh.trim() === '') {
-        return message.warning(`第${i + 1}个人才中的中文姓名未填写,请完善后再进行保存`)
-      }
-      
-      // 处理mobile字段,如果是数组则转换为字符串
-      if (Array.isArray(talent.mobile)) {
-        talent.mobile = talent.mobile.filter(i => Boolean(i)).map(j => String(j).replace(/,|,/g, '')).join(',');
-      }
-    }
-  }
-  
-  console.log(webOriginList.value, '网页解析-新增')
-
-  const loading = ElLoading.service({
-    lock: true,
-    text: '正在保存中...',
-    background: 'rgba(0, 0, 0, 0.7)',
-  })
-  try {
-    const result = await talentWebParsingApi.webParsingTalentCreate({
-      talent_list: webOriginList.value,
-      web_md: originMarkdown.value
-    })
-    console.log(result, 'create-result')
-    message.success('新增成功')
-    dialog_analysisInfo.value = false
-    // 刷新列表
-    getList()
-    if (result.code === 202 || result.message.includes('疑似重复')) {
-      if (!result.data?.main_card?.id) return
-      
-      await message.confirm('发现与当前名片的疑似重复数据,去处理')
-      mergeFormRef.value.open(result.data?.main_card?.id)
-    }
-  } finally {
-    loading.close()
-    handleWebClear()
-  }
-  
-}
-
-const handleSave = async () => {
-  // 网页解析新增调用单独的接口保存
-  if (analysisType.value === 'create' && radioValue.value === 'web') return handleSaveWebData()
-
-  if (!FormPageRef.value) return message.warning('请将表单信息完善后再提交')
-
-  const params = { ...FormPageRef.value.formQuery, type: radioValue.value }
-  if (!params.name_zh) return message.warning('请填写姓名!')
-  
-  // 数组转为字符串保存
-  if (Array.isArray(params?.mobile)) {
-    params.mobile = params.mobile.filter(i => Boolean(i)).map(j => String(j).replace(/,|,/g, '')).join(',');
-  }
-
-  const loading = ElLoading.service({
-    lock: true,
-    text: '正在保存中...',
-    background: 'rgba(0, 0, 0, 0.7)',
-  })
-  try {
-    let result = {}
-
-    if (analysisType.value === 'create') {
-      if (cardFileQuery.value) {
-        cardFileQuery.value.append('card_data', JSON.stringify(params)) // 名片
-        result = await talentLabelingApi.createBusinessCard(cardFileQuery.value)
-      } else {
-        // 结构化数据源 不传递文件
-        result = await talentLabelingApi.createBusinessCardPost(params)
-      }
-      message.success('新增成功')
-      dialog_analysisInfo.value = false
-      // 刷新列表
-      getList()
-
-      if (result.code === 202 || result.message.includes('疑似重复')) {
-        if (!result.data?.main_card?.id) return
-        
-        await message.confirm('发现与当前名片的疑似重复数据,去处理')
-        mergeFormRef.value.open(result.data?.main_card?.id)
-      }
-    } else {
-      await talentLabelingApi.updateBusinessCard(params, formData.value.id)
-      message.success('更新成功')
-      dialog_analysisInfo.value = false
-      // 刷新列表
-      getList()
-    }
-  } catch (error) {
-    console.log('更新失败', error)
-  } finally {
-    cardFileQuery.value = null
-    loading.close()
-    openSearch.value = false
-    handleWebClear()
-  }
-}
-
-// 解析中
-const analysisLoading = ref(false)
-const formData = ref({})
-const handleAnalysis = async () => {
-  // 开始解析
-  analysisLoading.value = true
-  loadingMarkSetting('解析中...')
-  cardFileQuery.value = null
-  formData.value = null
-	const type = radioValue.value
-  try {
-    // if (type === 'menduner') { // 门墩儿人才库
-    // } else 
-    if (type === 'file') { // 简历解析
-      if (!fileUrl.value) return message.warning('获取文件失败,请重新上传!')
-      const data = await commonApi.resumeParser({ fileUrl: fileUrl.value })
-      resumeAnalysisToForm(data) // 简历解析
-    } else if (type === 'card') { // 名片解析
-      if (!cardImgUrl.value) {
-        message.warning('请先上传名片!')
-        return
-      }
-      cardFileQuery.value = new FormData()
-      cardFileQuery.value.append('image', cardUploadRow.value)
-      message.warning('正在解析...')
-  
-      const index = createAnalysisNum.value
-      const res = await talentLabelingApi.businessCardParse(cardFileQuery.value)
-      if (index !== createAnalysisNum.value || !dialog_upload.value) return // 不是最新的名片解析数据(用户在解析完成前已重新上传)或用户已取消解析
-      formData.value = res?.data || res
-      message.success('名片解析成功')
-    }
-    // else if (type === 'web') {}
-
-    dialog_upload.value = false
-    dialog_analysisInfo.value = true
-  } catch (error) {
-    console.log('解析失败', error)
-    cardFileQuery.value = null
-  } finally {
-    analysisLoading.value = false
-    loadingMarkSetting()
-  }
-}
-
-// 监听表单变化,同步更新tagList中对应的数据
-watch(() => FormPageRef.value?.formQuery, (newVal) => {
-  if (tagCurrentIndex.value !== null && webOriginList.value && webOriginList.value.length > tagCurrentIndex.value) {
-    // 保留原始pic_url
-    const { pic_url } = webOriginList.value[tagCurrentIndex.value]
-    webOriginList.value[tagCurrentIndex.value] = { ...newVal, pic_url }
-  }
-}, { deep: true })
-
-// 简历解析
-const fileUrl = ref('') // https://minio.menduner.com/dev/person/229988673960153088/attachment/ee3eb21f45e13ede3557a03d18585ed80c5b4212ac5634e3436e309afaa8fe6a.pdf
-const uploadRef = ref()
-const fileList = ref([])
-const fileData = ref({ path: '' })
-// 文件上传
-const handleChange = async (file) => {
-  fileData.value.path = file.name
-  unref(uploadRef)?.submit()
-  if (!fileList.value.length) return
-
-  const url = fileList.value[0].response.data
-  fileUrl.value = !url.includes('.pdf') ?  `${baseUrl}/onlinePreview?url=${encodeURIComponent(Base64.encode(url))}` : url
-  if (dialog_analysisInfo.value) {
-    if (FormPageRef.value?.changeLoading) FormPageRef.value.changeLoading(true)
-    message.warning('正在解析...')
-    const data = await commonApi.resumeParser({ fileUrl: fileUrl.value })
-    resumeAnalysisToForm(data) // 简历解析
-    if (FormPageRef.value?.changeLoading) FormPageRef.value.changeLoading(false)
-  }
-}
-const submitFormError = () => {
-  message.error('上传失败,请您重新上传!')
-}
-const handleExceed = () => {
-  message.error('最多只能上传一个文件!')
-}
-const submitFormSuccess = (e) => {
-  // 清理
-  // unref(uploadRef)?.clearFiles()
-}
-
-const drawer = ref(false)
-const resumeTxt = ref([])
-// 查看文本信息
-const handleText = () => {
-  drawer.value = true
-}
-// 重新上传简历
-const handleResetUpload = async () => {
-  await message.confirm('是否确定重新上传简历?确定后将清空当前信息')
-  fileUrl.value = ''
-  fileData.value.path = ''
-  fileList.value = []
-  resumeAnalysisToForm('reset') // 简历解析
-}
-
-// 简历解析数据解构赋值
-const resumeAnalysisToForm = (data) => {
-  if (data === 'reset') {
-    // 重置表单
-    resumeTxt.value = ''
-    if (FormPageRef.value?.resetFormData) FormPageRef.value.resetFormData()
-  }
-  formData.value = {
-    name_zh: data?.person?.name || '',
-    email: data?.person?.email || '',
-    mobile: data?.person?.phone || '',
-    birthday: data?.person?.birthday ? timesTampChange(data.person.birthday, 'Y-M-D') : '',
-    age: data?.person?.birthday ? timestampToAge(data.person.birthday) : null,
-    career_path: data?.workList ? data.workList.map(e => {
-      return {
-        hotel_zh: e?.enterpriseName || null,
-        title_zh: e?.positionName || null,
-        date: e?.startTime ? timesTampChange(e.startTime, 'Y-M-D') : null
-      }
-    }) : null,
-    created_at: data?.person?.createTime ? timesTampChange(data.person.createTime, 'Y-M-D') : null,
-    updated_at: data?.person?.updateTime ? timesTampChange(data.person.updateTime, 'Y-M-D') : null,
-  }
-  resumeTxt.value = data?.resume?.rawText?.split('\n') || ''
-  if (FormPageRef.value?.setFormData) FormPageRef.value.setFormData(formData.value)
-}
-
-// 名片解析 
-const createAnalysisNum = ref(0)
-const cardFileQuery = ref(null)
-const cardUploadRow = ref(null)
-const cardImgUrl = ref(null)
-const cardUploadChange = (raw) => {
-  cardUploadRow.value = raw
-}
-
-// 人员搜索
-const openSearch = ref(false)
-const id = ref(null)
-const userId = ref(null)
-// const handleDetail = async ({id: use_id, userId: use_userId}) => {
-//   if (!use_userId || !use_userId)  return message.warning('请先选择人才!')
-//   id.value = use_id; userId.value = use_userId
-//   try {
-//     const data = await TalentMap.getTalentMapDetail(use_userId)
-//     resumeAnalysisToForm(data) // 简历解析
-//     dialog_analysisInfo.value = true
-//   } catch {}
-// }
-
-const SearchRef = ref(null)
-const handleSubmit = async () => {
-  console.log('打印->addList', SearchRef.value.addList)
-  message.success('创建任务成功!')
-  openSearch.value = false
-}
-
-const DialogWidth = ref('500')
-const showWebAnalysis = ref(false)
-// 选择解析方式
-const handleSelect = () => {
-  openSelect.value = false
-  formData.value = null
-  showWebAnalysis.value = false
-  showFormPage.value = true // 默认展示表单
-	const type = radioValue.value
-  if (type === 'card') {
-    createAnalysisNum.value++
-  }
-  if (type === 'menduner') {
-    openSearch.value = true
-    return
-  }
-  if (type === 'web') {
-    showFormPage.value = false // 新任命没有表单
-    showWebAnalysis.value = true
-    dialog_analysisInfo.value = true
-    return
-  }
-  dialog_upload.value = true
-}
-
-// 关闭上传弹窗
-const handleCancel = () => {
-  dialog_upload.value = false
-  analysisLoading.value = false
-}
-
-const openSelect = ref(false)
-const radioObject = { card: '名片', file: '简历', web: '门墩儿新任命', menduner: '门墩儿招聘', picture: '杂项' }
-const radioList = ref(Object.keys(radioObject).map(key => ({ value: key, label: radioObject[key]}) ))
-const defaultValue = radioList.value[0].value // 默认选中
-const radioValue = ref(defaultValue)
-// 新增解析
-const handleAdd = () => {
-  handleWebClear()
-  cardUploadRow.value = null
-  cardImgUrl.value = null
-  fileUrl.value = ''
-  fileData.value.path = ''
-  fileList.value = []
-  analysisLoading.value = false
-  analysisType.value = 'create'
-  radioValue.value = defaultValue // 重置解析类型
-  // 
-  openSelect.value = true
-}
-
-/** 初始化 **/
-onMounted(() => {
-  getList()
-})
-
-</script>
-
-<style lang="scss" scoped>
-.analysisInfoBox {
-  display: flex;
-  .analysisFile {
-    // width: 50%;
-    // max-height: 70vh;
-    padding-right: 12px;
-    overflow: auto;
-  }
-}
-.radioBox {
-	margin: 40px 0;
-}
-
-:deep(.drawer) {
-  position: absolute;
-  .el-drawer {
-    background-color: aliceblue;
-  }
-}
-.tagBox {
-  padding: 12px;
-  border: 1px dashed #409EFF;
-  border-radius: 4px;
-}
-:deep {
-  .el-tag__content {
-    display: flex;
-    align-items: center;
-  }
-}
-</style>

+ 0 - 619
src/views/menduner/system/talentMap/maintenance/gather/index2.vue

@@ -1,619 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="名称" prop="name">
-        <el-input v-model="queryParams.name" placeholder="请输入名称" clearable @keyup.enter="handleQuery" class="!w-180px" />
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery('search')"><Icon icon="ep:search" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-        <el-button type="primary" plain @click="handleAdd">
-          <Icon icon="ep:plus" class="mr-5px" /> 新增人才
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true">
-      <el-table-column label="中文名称" align="center" prop="name_zh" fixed="left" />
-      <el-table-column label="英文名称" align="center" prop="name_en" />
-      <el-table-column label="职位" align="center" prop="title_zh" />
-      <el-table-column label="酒店/公司" align="center" prop="hotel_zh" />
-      <el-table-column label="手机号码" align="center" prop="mobile" />
-      <el-table-column label="固定电话" align="center" prop="phone" />
-      <el-table-column label="状态" align="center" prop="status" width="80">
-        <template #default="scope">
-          <el-tag type="success" v-if="scope.row.status === 'active'">已启用</el-tag>
-          <el-tag type="danger" v-else>已禁用</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="创建日期" align="center" prop="created_at" :formatter="dateFormatter" />
-      <el-table-column label="操作" align="center" fixed="right" min-width="110">
-        <template #default="scope">
-          <el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button>
-          <el-button link type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
-          <el-button link :type="scope.row.status === 'active' ? 'warning': 'success'" @click="handleDisable(scope.row)">
-            {{ scope.row.status === 'active' ? '禁用' : '启用'}}
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <!-- 选择来源 -->
-    <Dialog title="新增" v-model="openSelect" width="500" @close="openSelect = false">
-			<el-radio-group v-model="radioValue" size="large" class="radioBox">
-				<el-radio
-					v-for="item in radioList"
-					:key="item.value"
-					:value="item.value"
-				>
-					{{ item.label }}
-				</el-radio>
-			</el-radio-group>
-      <template #footer>
-        <el-button type="primary" @click="handleSelect">确 认</el-button>
-        <el-button @click="openSelect = false">取 消</el-button>
-      </template>
-    </Dialog>
-
-    <!-- 人员搜索 -->
-    <Dialog :title="radioObject.menduner" v-model="openSearch" width="1200" @close="openSearch = false">
-      <Search @detail="handleDetail" />
-    </Dialog>
-
-    <!-- 解析文件上传 -->
-    <Dialog :title="radioObject[radioValue]" v-model="dialog_upload" :width="DialogWidth" @close="handleCancel">
-      <div>
-        <!-- 门墩儿人才库 -->
-        <!-- <template v-if="radioValue === 'menduner'">
-        </template> -->
-        <!-- 简历解析 -->
-        <template v-if="radioValue === 'file'">
-          <el-upload
-            ref="uploadRef"
-            v-model:file-list="fileList"
-            :action="uploadUrl"
-            :auto-upload="false"
-            :data="data"
-            :limit="1"
-            :on-change="handleChange"
-            :on-error="submitFormError"
-            :on-exceed="handleExceed"
-            :on-success="submitFormSuccess"
-            :http-request="httpRequest"
-            accept=".pdf, doc, .docx"
-            drag
-            class="flex-1"
-          >
-            <i class="el-icon-upload"></i>
-            <div class="el-upload__text">上传附件, 将文件拖到此处,或 <em>点击上传</em></div>
-            <template #tip>
-              <div class="el-upload__tip color-red">
-                提示:仅允许导入 pdf、doc、docx 格式文件!
-              </div>
-            </template>
-          </el-upload>
-        </template>
-        <!-- 名片解析 -->
-        <template v-if="radioValue === 'card'">
-          <UploadImg
-            v-model="cardImgUrl"
-            :limit="1"
-            :uploadSuccessTip="false"
-            @handle-change="cardUploadChange"
-            height="150px" width="150px" style="margin: 20px auto; width: 150px;"
-          >
-            <template #tip>{{ cardImgUrl ? '' : '请上传名片' }}</template>
-          </UploadImg>
-        </template>
-        <!-- 网页解析 -->
-        <!-- <template v-if="radioValue === 'web'"></template> -->
-      </div>
-      <template #footer>
-        <el-button @click="handleAnalysis" type="success" :disabled="analysisLoading" :loading="analysisLoading">解 析</el-button>
-        <el-button @click="handleCancel">取 消</el-button>
-      </template>
-    </Dialog>
-
-    <!-- 解析回显 -->
-    <Dialog :title="radioObject[radioValue]" v-model="dialog_analysisInfo" width="90%">
-      <div class="analysisInfoBox">
-        <div class="analysisFile">
-          <!-- 门墩儿人才库 -->
-          <template v-if="radioValue === 'menduner'">
-            <el-tabs v-model="activeName" type="border-card">
-              <el-tab-pane label="基本信息" name="info">
-                <el-card shadow="never" class="m-b-20px">
-                  <!-- <template #header>
-                    <CardTitle title="人才详情" />
-                  </template> -->
-                  <Info :id="id" :user-id="userId" />
-                  <expExtend :user-id="userId" />
-                </el-card>
-              </el-tab-pane>
-
-              <el-tab-pane label="附件简历" name="Attachment">
-                <Attachment showPreview :user-id="userId" />
-              </el-tab-pane>
-            </el-tabs>
-          </template>
-          <!-- 简历解析 -->
-          <template v-if="radioValue === 'file'">
-            <div v-if="fileUrl" style="position: relative;">
-              <div class="text-right m-b-10px">
-                <el-button v-if="!isEdit" @click="handleText">查看文本信息</el-button>
-                <el-button type="primary" @click="handleResetUpload">重新上传简历</el-button>
-              </div>
-              <IFrame :src="fileUrl" />
-              <el-drawer
-                v-model="drawer"
-                modal-class="drawer"
-                size="75%"
-                direction="ltr"
-                title="简历解析(可复制文本使用)"
-              >
-                <p v-for="(text, index) in resumeTxt" :key="text + index">{{ text }}</p>
-              </el-drawer>
-            </div>
-            <el-upload
-              v-else
-              ref="uploadRef"
-              v-model:file-list="fileList"
-              :action="uploadUrl"
-              :auto-upload="false"
-              :data="data"
-              :limit="1"
-              :on-change="handleChange"
-              :on-error="submitFormError"
-              :on-exceed="handleExceed"
-              :on-success="submitFormSuccess"
-              :http-request="httpRequest"
-              accept=".pdf, doc, .docx"
-              drag
-              class="flex-1"
-            >
-              <i class="el-icon-upload"></i>
-              <div class="el-upload__text">上传附件, 将文件拖到此处,或 <em>点击上传</em></div>
-              <template #tip>
-                <div class="el-upload__tip color-red">
-                  提示:仅允许导入 pdf、doc、docx 格式文件!
-                </div>
-              </template>
-            </el-upload>
-          </template>
-          <!-- 名片解析 -->
-          <template v-if="radioValue === 'card'">
-            <div class="image">
-              <el-image v-if="cardImgUrl" class="!w-100%" :src="cardImgUrl" />
-              <div v-else>
-                <UploadImg
-                  v-model="cardImgUrl"
-                  :limit="1"
-                  :uploadSuccessTip="false"
-                  drag
-                  buttonUpload
-                  @handle-change="cardUploadChange"
-                  height="32px" width="104px"
-                  style="margin: 0 auto; width: 104px;margin-top: 40%;"
-                >
-                  <template #tip>{{ cardImgUrl ? '' : '请上传名片' }}</template>
-                </UploadImg>
-              </div>
-            </div>
-          </template>
-          <!-- 网页解析 -->
-          <template v-if="radioValue === 'web'">
-            <webAnalysis v-if="showWebAnalysis" @analysis="val => formData = val" />
-          </template>
-        </div>
-        <FormPage ref="FormPageRef" :analysisType="analysisType" :itemData="formData" />
-      </div>
-      <template #footer>
-        <el-button @click="handleSave" type="success" :disabled="analysisLoading">保 存</el-button>
-        <el-button @click="dialog_analysisInfo = false">取 消</el-button>
-      </template>
-    </Dialog>
-  </ContentWrap>
-
-  <MergeForm ref="mergeFormRef" @refresh="getList" />
-</template>
-
-<script setup>
-defineOptions({ name: 'TalentMapStoreIndex' })
-import { dateFormatter } from '@/utils/formatTime'
-import { talentLabelingApi } from '@/api/menduner/system/talentMap/labeling'
-import { TalentMap } from '@/api/menduner/system/talentMap'
-import { Delete, Plus } from '@element-plus/icons-vue'
-import MergeForm from '@/views/menduner/system/talentMap/components/merge.vue'
-import FormPage from '@/views/menduner/system/talentMap/components/FormPage.vue'
-// import uploadDialog from './components/uploadDialog.vue'
-import { timesTampChange, timestampToAge } from '@/utils/transform/date'
-import Search from './components/search.vue'
-import webAnalysis from './components/webAnalysis.vue'
-import { useUpload } from '@/components/UploadFile/src/useUpload'
-import { commonApi } from '@/api/menduner/common'
-import { Base64 } from 'js-base64'
-import Info from '@/views/menduner/system/person/details/components/info.vue'
-import expExtend from '@/views/menduner/system/person/details/components/expExtend.vue'
-import Attachment from '@/views/menduner/system/person/details/components/attachment.vue'
-
-const baseUrl = import.meta.env.VITE_PREVIEW_URL
-const { uploadUrl, httpRequest } = useUpload()
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(false) // 列表的加载中
-const list = ref([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  name: undefined
-})
-const queryFormRef = ref() // 搜索的表单
-const dialog_upload = ref(false)
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    list.value = []
-    const data = await talentLabelingApi.getCardList()
-    list.value = data ? data.reverse() : []
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 搜索按钮操作 */
-const handleQuery = (type) => {
-  if (type !== 'reset') {
-    message.warning('搜索正在建设中...')
-    return
-  }
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery('reset')
-}
-
-/** 删除按钮操作 */
-const handleDelete = async (id) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await talentLabelingApi.deleteBusinessCard(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    setTimeout(async () => {
-      await getList()
-    }, 0)
-  } catch {}
-}
-
-/** 编辑 */
-const { push } = useRouter()
-const handleEdit = async (item) => {
-  analysisType.value = 'edit'
-  formData.value = item
-  radioValue.value = item.type || 'card' // menduner
-  try {
-    if (radioValue.value === 'card') {
-      if (!item?.image_path) {
-        cardUploadRow.value = null
-        cardImgUrl.value = null
-        dialog_analysisInfo.value = true
-        return
-      }
-      const data = await talentLabelingApi.getBusinessCardImage(item.image_path)
-      cardUploadRow.value = data?.type ? new File([data], item.image_path, { type: data.type }) : null
-      cardImgUrl.value = data?.type ? URL.createObjectURL(data) : null
-    }
-  } catch (error) {
-    console.log('打印->getBusinessCardImage', error)
-  } finally {
-    dialog_analysisInfo.value = true
-  }
-}
-
-/** 禁用按钮操作 */
-const handleDisable = async (item) => {
-  if (!item?.id) return message.warning('操作失败,请稍后再试')
-  try {
-    // 禁用的二次确认
-    const status = item.status === 'active' ? 'inactive' : 'active'
-    const text = status === 'inactive' ? '禁用' : '启用'
-    
-    await message.delConfirm(`是否${text}该名片?`)
-    // 发起禁用
-    await talentLabelingApi.updateBusinessCardStatus({
-      status,
-    }, item.id)
-    message.success(`${text}成功`)
-    // 刷新列表
-    await getList()
-  } catch {}
-}
-
-// 更新
-const activeName = ref('info')
-const dialog_analysisInfo = ref(false)
-const formLoading = ref(false)
-const analysisType = ref('')
-const FormPageRef = ref(null)
-const mergeFormRef = ref() // 合并表单 Ref
-const handleSave = async () => {
-  const params = { ...FormPageRef.value.formQuery, type: radioValue.value }
-  if (!params.name_zh) return message.warning('请填写姓名!')
-  
-  // 数组转为字符串保存
-  if (Array.isArray(params?.mobile)) {
-    params.mobile = params.mobile.filter(i => Boolean(i)).map(j => String(j).replace(/,|,/g, '')).join(',');
-  }
-
-  console.log(params, 'handleSubmit')
-  try {
-    formLoading.value = true
-    let result = {}
-
-    if (analysisType.value === 'create') {
-      if (cardFileQuery.value) {
-        cardFileQuery.value.append('card_data', JSON.stringify(params)) // 名片
-        result = await talentLabelingApi.createBusinessCard(cardFileQuery.value)
-      } else {
-        // 结构化数据源 不传递文件
-        result = await talentLabelingApi.createBusinessCardPost(params)
-      }
-      message.success('新增成功')
-      dialog_analysisInfo.value = false
-      // 刷新列表
-      getList()
-
-      if (result.code === 202 || result.message.includes('疑似重复')) {
-        if (!result.data?.main_card?.id) return
-        
-        await message.confirm('发现与当前名片的疑似重复数据,去处理')
-        mergeFormRef.value.open(result.data?.main_card?.id)
-      }
-    } else {
-      await talentLabelingApi.updateBusinessCard(params, formData.value.id)
-      message.success('更新成功')
-      dialog_analysisInfo.value = false
-      // 刷新列表
-      getList()
-    }
-  } catch (error) {
-    console.log('更新失败', error)
-  } finally {
-    cardFileQuery.value = null
-    formLoading.value = false
-    openSearch.value = false
-  }
-}
-
-// 解析中
-const analysisLoading = ref(false)
-const formData = ref({})
-const handleAnalysis = async () => {
-  // 开始解析
-  analysisLoading.value = true
-  cardFileQuery.value = null
-  formData.value = null
-	const type = radioValue.value
-  try {
-    // if (type === 'menduner') { // 门墩儿人才库
-    // } else 
-    if (type === 'file') { // 简历解析
-      if (!fileUrl.value) return message.warning('获取文件失败,请重新上传!')
-      const data = await commonApi.resumeParser({ fileUrl: fileUrl.value })
-      resumeAnalysisToForm(data) // 简历解析
-    } else if (type === 'card') { // 名片解析
-      if (!cardImgUrl.value) {
-        message.warning('请先上传名片!')
-        return
-      }
-      cardFileQuery.value = new FormData()
-      cardFileQuery.value.append('image', cardUploadRow.value)
-      message.warning('正在解析...')
-  
-      const index = createAnalysisNum.value
-      const res = await talentLabelingApi.businessCardParse(cardFileQuery.value)
-      if (index !== createAnalysisNum.value || !dialog_upload.value) return // 不是最新的名片解析数据(用户在解析完成前已重新上传)或用户已取消解析
-      formData.value = res?.data || res
-      message.success('名片解析成功')
-    }
-    // else if (type === 'web') {}
-
-    dialog_upload.value = false
-    dialog_analysisInfo.value = true
-  } catch (error) {
-    console.log('解析失败', error)
-    cardFileQuery.value = null
-  } finally {
-    analysisLoading.value = false
-  }
-}
-
-// 简历解析
-const fileUrl = ref('') // https://minio.menduner.com/dev/person/229988673960153088/attachment/ee3eb21f45e13ede3557a03d18585ed80c5b4212ac5634e3436e309afaa8fe6a.pdf
-const uploadRef = ref()
-const fileList = ref([])
-const data = ref({ path: '' })
-// 文件上传
-const handleChange = async (file) => {
-  data.value.path = file.name
-  unref(uploadRef)?.submit()
-  if (!fileList.value.length) return
-
-  const url = fileList.value[0].response.data
-  fileUrl.value = !url.includes('.pdf') ?  `${baseUrl}/onlinePreview?url=${encodeURIComponent(Base64.encode(url))}` : url
-  if (dialog_analysisInfo.value) {
-    if (FormPageRef.value?.changeLoading) FormPageRef.value.changeLoading(true)
-    message.warning('正在解析...')
-    const data = await commonApi.resumeParser({ fileUrl: fileUrl.value })
-    resumeAnalysisToForm(data) // 简历解析
-    if (FormPageRef.value?.changeLoading) FormPageRef.value.changeLoading(false)
-  }
-}
-const submitFormError = () => {
-  message.error('上传失败,请您重新上传!')
-}
-const handleExceed = () => {
-  message.error('最多只能上传一个文件!')
-}
-const submitFormSuccess = (e) => {
-  // 清理
-  // unref(uploadRef)?.clearFiles()
-}
-
-const drawer = ref(false)
-const resumeTxt = ref([])
-// 查看文本信息
-const handleText = () => {
-  drawer.value = true
-}
-// 重新上传简历
-const handleResetUpload = async () => {
-  await message.confirm('是否确定重新上传简历?确定后将清空当前信息')
-  fileUrl.value = ''
-  data.value.path = ''
-  fileList.value = []
-  resumeAnalysisToForm('reset') // 简历解析
-}
-
-// 简历解析数据解构赋值
-const resumeAnalysisToForm = (data) => {
-  if (data === 'reset') {
-    // 重置表单
-    resumeTxt.value = ''
-    if (FormPageRef.value?.resetFormData) FormPageRef.value.resetFormData()
-  }
-  formData.value = {
-    name_zh: data?.person?.name || '',
-    email: data?.person?.email || '',
-    mobile: data?.person?.phone || '',
-    birthday: data?.person?.birthday ? timesTampChange(data.person.birthday, 'Y-M-D') : '',
-    age: data?.person?.birthday ? timestampToAge(data.person.birthday) : null,
-    career_path: data?.workList ? data.workList.map(e => {
-      return {
-        hotel_zh: e?.enterpriseName || null,
-        title_zh: e?.positionName || null,
-        date: e?.startTime ? timesTampChange(e.startTime, 'Y-M-D') : null
-      }
-    }) : null,
-    created_at: data?.person?.createTime ? timesTampChange(data.person.createTime, 'Y-M-D') : null,
-    updated_at: data?.person?.updateTime ? timesTampChange(data.person.updateTime, 'Y-M-D') : null,
-  }
-  resumeTxt.value = data?.resume?.rawText?.split('\n') || ''
-  if (FormPageRef.value?.setFormData) FormPageRef.value.setFormData(formData.value)
-}
-
-// 名片解析 
-const createAnalysisNum = ref(0)
-const cardFileQuery = ref(null)
-const cardUploadRow = ref(null)
-const cardImgUrl = ref(null)
-const cardUploadChange = (raw) => {
-  cardUploadRow.value = raw
-}
-
-// 人员搜索
-const openSearch = ref(false)
-const id = ref(null)
-const userId = ref(null)
-const handleDetail = async ({id: use_id, userId: use_userId}) => {
-  if (!use_userId || !use_userId)  return message.warning('请先选择人才!')
-  id.value = use_id; userId.value = use_userId
-  try {
-    const data = await TalentMap.getTalentMapDetail(use_userId)
-    // 去除id
-    resumeAnalysisToForm(data) // 简历解析
-    dialog_analysisInfo.value = true
-    // message.success(`操作成功`)
-  } catch {}
-}
-
-const DialogWidth = ref('500')
-const showWebAnalysis = ref(false)
-// 选择解析方式
-const handleSelect = () => {
-  openSelect.value = false
-  formData.value = null
-  showWebAnalysis.value = false
-	const type = radioValue.value
-  if (type === 'card') {
-    createAnalysisNum.value++
-  }
-  if (type === 'menduner') {
-    openSearch.value = true
-    return
-  }
-  if (type === 'web') {
-    showWebAnalysis.value = true
-    dialog_analysisInfo.value = true
-    return
-  }
-  dialog_upload.value = true
-}
-
-// 关闭上传弹窗
-const handleCancel = () => {
-  dialog_upload.value = false
-  analysisLoading.value = false
-}
-
-const openSelect = ref(false)
-const radioObject = { card: '名片解析', file: '简历解析', web: '网页解析', menduner: '门墩儿招聘' }
-const radioList = ref(Object.keys(radioObject).map(key => ({ value: key, label: radioObject[key]}) ))
-const defaultValue = radioList.value[0].value // 默认选中
-const radioValue = ref(defaultValue)
-// 新增解析
-const handleAdd = () => {
-  cardUploadRow.value = null
-  cardImgUrl.value = null
-  analysisLoading.value = false
-  analysisType.value = 'create'
-  radioValue.value = defaultValue // 重置解析类型
-	// 
-  openSelect.value = true
-}
-
-/** 初始化 **/
-onMounted(() => {
-  getList()
-})
-
-</script>
-
-<style lang="scss" scoped>
-.analysisInfoBox {
-  display: flex;
-  .analysisFile {
-    width: 50%;
-    max-height: 70vh;
-    padding-right: 12px;
-    overflow: auto;
-  }
-}
-.radioBox {
-	margin: 40px 0;
-}
-
-:deep(.drawer) {
-  position: absolute;
-  .el-drawer {
-    background-color: aliceblue;
-  }
-}
-</style>

+ 0 - 764
src/views/menduner/system/talentMap/maintenance/gather/index3.vue

@@ -1,764 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="名称" prop="name">
-        <el-input v-model="queryParams.name" placeholder="请输入名称" clearable @keyup.enter="handleQuery" class="!w-180px" />
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery('search')"><Icon icon="ep:search" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-        <el-button type="primary" plain @click="handleAdd">
-          <Icon icon="ep:plus" class="mr-5px" /> 新增人才
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true">
-      <el-table-column label="中文名称" align="center" prop="name_zh" fixed="left" />
-      <el-table-column label="英文名称" align="center" prop="name_en" />
-      <el-table-column label="职位" align="center" prop="title_zh" />
-      <el-table-column label="酒店/公司" align="center" prop="hotel_zh" />
-      <el-table-column label="手机号码" align="center" prop="mobile" />
-      <el-table-column label="固定电话" align="center" prop="phone" />
-      <el-table-column label="状态" align="center" prop="status" width="80">
-        <template #default="scope">
-          <el-tag type="success" v-if="scope.row.status === 'active'">已启用</el-tag>
-          <el-tag type="danger" v-else>已禁用</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="创建日期" align="center" prop="created_at" :formatter="dateFormatter" />
-      <el-table-column label="操作" align="center" fixed="right" min-width="110">
-        <template #default="scope">
-          <!-- <el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button> -->
-          <el-button link type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
-          <el-button link :type="scope.row.status === 'active' ? 'warning': 'success'" @click="handleDisable(scope.row)">
-            {{ scope.row.status === 'active' ? '禁用' : '启用'}}
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <!-- 选择来源 -->
-    <Dialog title="新增" v-model="openSelect" width="550" @close="openSelect = false">
-			<el-radio-group v-model="radioValue" size="large" class="radioBox">
-				<el-radio
-					v-for="item in radioList"
-					:key="item.value"
-					:value="item.value"
-				>
-					{{ item.label }}
-				</el-radio>
-			</el-radio-group>
-      <template #footer>
-        <el-button type="primary" @click="handleSelect">确 认</el-button>
-        <el-button @click="openSelect = false">取 消</el-button>
-      </template>
-    </Dialog>
-
-    <!-- 人员搜索 -->
-    <Dialog :title="radioObject.menduner" v-model="openSearch" :modalClose="false" width="1200" @close="openSearch = false">
-      <Search @detail="handleDetail" />
-    </Dialog>
-
-    <!-- 解析文件上传 -->
-    <Dialog :title="radioObject[radioValue]" v-model="dialog_upload" :modalClose="false" :width="DialogWidth" @close="handleCancel">
-      <div>
-        <!-- 简历解析 -->
-        <template v-if="radioValue === 'file'">
-          <el-upload
-            ref="uploadRef"
-            v-model:file-list="fileList"
-            :action="uploadUrl"
-            :auto-upload="false"
-            :data="fileData"
-            :on-change="handleChange"
-            :on-error="submitFormError"
-            :on-exceed="handleExceed"
-            :on-success="submitFormSuccess"
-            :http-request="httpRequest"
-            accept=".pdf, doc, .docx"
-            drag
-            multiple
-            class="flex-1"
-          >
-            <i class="el-icon-upload"></i>
-            <div class="el-upload__text">上传附件, 将文件拖到此处,或 <em>点击上传</em></div>
-            <template #tip>
-              <div class="el-upload__tip color-red">
-                提示:仅允许导入 pdf、doc、docx 格式文件!
-              </div>
-            </template>
-          </el-upload>
-        </template>
-        <!-- 名片解析 -->
-        <template v-if="radioValue === 'card'">
-          <UploadImgs
-            v-model="cardImgUrl"
-            :uploadSuccessTip="false"
-            :limit="100"
-            @handle-change="cardUploadChange"
-            height="150px" width="150px" style="margin: 20px auto; text-align: center;"
-          >
-            <template #tip>{{ cardImgUrl ? '' : '请上传名片' }}</template>
-          </UploadImgs>
-        </template>
-        <template v-if="radioValue === 'picture'">
-          <UploadImgs
-            v-model="cardImgUrl"
-            :uploadSuccessTip="false"
-            :limit="100"
-            @handle-change="cardUploadChange"
-            height="150px" width="150px" style="margin: 20px auto; text-align: center;"
-          >
-            <template #tip>{{ cardImgUrl ? '' : '请上传图片' }}</template>
-          </UploadImgs>
-        </template>
-      </div>
-      <template #footer>
-        <el-button @click="handleAnalysis" type="success" :disabled="analysisLoading" :loading="analysisLoading">解 析</el-button>
-        <el-button @click="handleCancel">取 消</el-button>
-      </template>
-    </Dialog>
-
-    <!-- 解析回显 -->
-    <Dialog :title="radioObject[radioValue]" v-model="dialog_analysisInfo" :modalClose="false" width="90%" @close="dialog_analysisInfo = false">
-      <div class="analysisInfoBox">
-        <div class="analysisFile" :style="{'width': showFormPage || tagList?.length > 0 ? '50%' :'100%'}">
-          <!-- 门墩儿人才库 -->
-          <template v-if="radioValue === 'menduner'">
-            <el-tabs v-model="activeName" type="border-card">
-              <el-tab-pane label="基本信息" name="info">
-                <el-card shadow="never">
-                  <Info :id="id" :user-id="userId" />
-                  <expExtend :user-id="userId" defaultShowAll class="m-t-20px" />
-                </el-card>
-              </el-tab-pane>
-
-              <el-tab-pane label="附件简历" name="Attachment">
-                <Attachment showPreview :user-id="userId" />
-              </el-tab-pane>
-            </el-tabs>
-          </template>
-          <!-- 简历解析 -->
-          <template v-if="radioValue === 'file'">
-            <div v-if="fileUrl" style="position: relative;">
-              <div class="text-right m-b-10px">
-                <el-button v-if="!isEdit" @click="handleText">查看文本信息</el-button>
-                <el-button type="primary" @click="handleResetUpload">重新上传简历</el-button>
-              </div>
-              <IFrame :src="fileUrl" />
-              <el-drawer
-                v-model="drawer"
-                modal-class="drawer"
-                size="75%"
-                direction="ltr"
-                title="简历解析(可复制文本使用)"
-              >
-                <p v-for="(text, index) in resumeTxt" :key="text + index">{{ text }}</p>
-              </el-drawer>
-            </div>
-            <el-upload
-              v-else
-              ref="uploadRef"
-              v-model:file-list="fileList"
-              :action="uploadUrl"
-              :auto-upload="false"
-              :data="fileData"
-              :on-change="handleChange"
-              :on-error="submitFormError"
-              :on-exceed="handleExceed"
-              :on-success="submitFormSuccess"
-              :http-request="httpRequest"
-              accept=".pdf, doc, .docx"
-              drag
-              multiple
-              class="flex-1"
-            >
-              <i class="el-icon-upload"></i>
-              <div class="el-upload__text">上传附件, 将文件拖到此处,或 <em>点击上传</em></div>
-              <template #tip>
-                <div class="el-upload__tip color-red">
-                  提示:仅允许导入 pdf、doc、docx 格式文件!
-                </div>
-              </template>
-            </el-upload>
-          </template>
-          <!-- 名片解析 -->
-          <template v-if="radioValue === 'card'">
-            <div class="image">
-              <el-image v-if="cardImgUrl" class="!w-100%" :src="cardImgUrl" />
-              <div v-else>
-                <UploadImgs
-                  v-model="cardImgUrl"
-                  :uploadSuccessTip="false"
-                  :limit="100"
-                  drag buttonUpload
-                  @handle-change="cardUploadChange"
-                  height="32px" width="104px"
-                  style="margin: 0 auto; width: 104px;margin-top: 40%;"
-                >
-                  <template #tip>{{ cardImgUrl ? '' : '请上传名片' }}</template>
-                </UploadImgs>
-              </div>
-            </div>
-          </template>
-          <!-- 网页解析 -->
-          <template v-if="radioValue === 'web'">
-            <webAnalysis
-              v-if="showWebAnalysis"
-              @analysis="handleWebAnalysis"
-              @reset="handleWebClear(), showFormPage = false, formData = {}"
-            />
-          </template>
-        </div>
-        <div class="flex-1">
-          <div class="tagBox mb-10px" v-if="tagList?.length">
-            <el-tag 
-              type="primary" 
-              round
-              size="large"
-              v-for="(val, index) in tagList"
-              :key="index + val"
-              class="mr-10px cursor-pointer mb-10px"
-              @click="handleTagClick(index)"
-            >
-              {{ val }}
-              <Icon v-if="index === tagCurrentIndex" icon="ep:check" class="ml-5px" /> 
-            </el-tag>
-          </div>
-          <FormPage
-            v-if="showFormPage"
-            ref="FormPageRef"
-            :formType="analysisType"
-            :itemData="formData"
-            />
-          <div v-if="tagList?.length && !tagCurrentIndex" class="!h-100% flex items-center justify-center">请点击上方人才姓名进行查看编辑</div>
-        </div>
-      </div>
-      <template #footer>
-        <el-button @click="handleSave" type="success">保 存</el-button>
-        <el-button @click="dialog_analysisInfo = false, handleWebClear">取 消</el-button>
-      </template>
-    </Dialog>
-  </ContentWrap>
-
-  <MergeForm ref="mergeFormRef" @refresh="getList" />
-</template>
-
-<script setup>
-defineOptions({ name: 'TalentMapStoreIndex' })
-import { dateFormatter } from '@/utils/formatTime'
-import { talentLabelingApi } from '@/api/menduner/system/talentMap/labeling'
-import { TalentMap } from '@/api/menduner/system/talentMap'
-import { Delete, Plus } from '@element-plus/icons-vue'
-import MergeForm from '@/views/menduner/system/talentMap/components/merge.vue'
-import FormPage from '@/views/menduner/system/talentMap/components/FormPage.vue'
-// import uploadDialog from './components/uploadDialog.vue'
-import { timesTampChange, timestampToAge } from '@/utils/transform/date'
-import Search from './components/search.vue'
-import webAnalysis from './components/webAnalysis.vue'
-import { useUpload } from '@/components/UploadFile/src/useUpload'
-import { commonApi } from '@/api/menduner/common'
-import { Base64 } from 'js-base64'
-import Info from '@/views/menduner/system/person/details/components/info.vue'
-import expExtend from '@/views/menduner/system/person/details/components/expExtend.vue'
-import Attachment from '@/views/menduner/system/person/details/components/attachment.vue'
-import { talentWebParsingApi } from '@/api/menduner/system/talentMap/webParsing'
-import { ElLoading } from 'element-plus'
-
-const baseUrl = import.meta.env.VITE_PREVIEW_URL
-const { uploadUrl, httpRequest } = useUpload()
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(false) // 列表的加载中
-const list = ref([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  name: undefined
-})
-const queryFormRef = ref() // 搜索的表单
-const dialog_upload = ref(false)
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    list.value = []
-    const data = await talentLabelingApi.getCardList()
-    list.value = data ?? []
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 搜索按钮操作 */
-const handleQuery = (type) => {
-  if (type !== 'reset') {
-    message.warning('搜索正在建设中...')
-    return
-  }
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery('reset')
-}
-
-/** 删除按钮操作 */
-const handleDelete = async (id) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await talentLabelingApi.deleteBusinessCard(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    setTimeout(async () => {
-      await getList()
-    }, 0)
-  } catch {}
-}
-
-// 网页解析-人才标签点击
-const handleTagClick = (index) => {
-  if (!webOriginList.value?.[index]) return
-  tagCurrentIndex.value = index
-  const obj = webOriginList.value[index]
-  formData.value = obj ?? {}
-  if (!showFormPage.value) showFormPage.value = true
-}
-
-// 网页解析-信息提取
-const tagCurrentIndex = ref(null)
-const tagList = ref([])
-const originMarkdown = ref(null)
-const webOriginList = ref([])
-const handleWebAnalysis = (list, md) => {
-  webOriginList.value = list
-  tagList.value = list.map(e => e.name_zh)
-  originMarkdown.value = md
-  // 只有单个人才时默认选中
-  if (list?.length === 1) {
-    handleTagClick(0)
-  }
-}
-
-const handleWebClear = () => {
-  tagCurrentIndex.value = null
-  originMarkdown.value = null
-  tagList.value = []
-  webOriginList.value = []
-}
-
-/** 编辑 */
-const { push } = useRouter()
-const handleEdit = async (item) => {
-  analysisType.value = 'edit'
-  formData.value = item
-  radioValue.value = item.type || 'card' // menduner
-  try {
-    if (radioValue.value === 'card') {
-      if (!item?.image_path) {
-        cardUploadRow.value = null
-        cardImgUrl.value = null
-        dialog_analysisInfo.value = true
-        return
-      }
-      const data = await talentLabelingApi.getBusinessCardImage(item.image_path)
-      cardUploadRow.value = data?.type ? new File([data], item.image_path, { type: data.type }) : null
-      cardImgUrl.value = data?.type ? URL.createObjectURL(data) : null
-    }
-  } catch (error) {
-    console.log('打印->getBusinessCardImage', error)
-  } finally {
-    dialog_analysisInfo.value = true
-  }
-}
-
-/** 禁用按钮操作 */
-const handleDisable = async (item) => {
-  if (!item?.id) return message.warning('操作失败,请稍后再试')
-  try {
-    // 禁用的二次确认
-    const status = item.status === 'active' ? 'inactive' : 'active'
-    const text = status === 'inactive' ? '禁用' : '启用'
-    
-    await message.delConfirm(`是否${text}该名片?`)
-    // 发起禁用
-    await talentLabelingApi.updateBusinessCardStatus({
-      status,
-    }, item.id)
-    message.success(`${text}成功`)
-    // 刷新列表
-    await getList()
-  } catch {}
-}
-
-// 更新
-const activeName = ref('info')
-const dialog_analysisInfo = ref(false)
-const analysisType = ref('')
-const showFormPage = ref(true)
-const FormPageRef = ref(null)
-const mergeFormRef = ref() // 合并表单 Ref
-
-const handleSaveWebData = async () => {
-  if (!webOriginList.value || !webOriginList.value?.length) {
-    message.warning('请输入微信公众号链接解析并提取信息核对后再提交保存')
-    return
-  }
-  if (!FormPageRef.value) return message.warning('请点击右侧顶部的人才标签查看核对信息后保存')
-  
-  // 验证webOriginList数组中每个人才的name_zh字段,并处理mobile字段
-  if (webOriginList.value && Array.isArray(webOriginList.value) && webOriginList.value.length > 0) {
-    for (let i = 0; i < webOriginList.value.length; i++) {
-      const talent = webOriginList.value[i]
-      if (!talent.name_zh || talent.name_zh.trim() === '') {
-        return message.warning(`第${i + 1}个人才中的中文姓名未填写,请完善后再进行保存`)
-      }
-      
-      // 处理mobile字段,如果是数组则转换为字符串
-      if (Array.isArray(talent.mobile)) {
-        talent.mobile = talent.mobile.filter(i => Boolean(i)).map(j => String(j).replace(/,|,/g, '')).join(',');
-      }
-    }
-  }
-  
-  console.log(webOriginList.value, '网页解析-新增')
-
-  const loading = ElLoading.service({
-    lock: true,
-    text: '正在保存中...',
-    background: 'rgba(0, 0, 0, 0.7)',
-  })
-  try {
-    const result = await talentWebParsingApi.webParsingTalentCreate({
-      talent_list: webOriginList.value,
-      web_md: originMarkdown.value
-    })
-    console.log(result, 'create-result')
-    message.success('新增成功')
-    dialog_analysisInfo.value = false
-    // 刷新列表
-    getList()
-    if (result.code === 202 || result.message.includes('疑似重复')) {
-      if (!result.data?.main_card?.id) return
-      
-      await message.confirm('发现与当前名片的疑似重复数据,去处理')
-      mergeFormRef.value.open(result.data?.main_card?.id)
-    }
-  } finally {
-    loading.close()
-    handleWebClear()
-  }
-  
-}
-
-const handleSave = async () => {
-  // 网页解析新增调用单独的接口保存
-  if (analysisType.value === 'create' && radioValue.value === 'web') return handleSaveWebData()
-
-  if (!FormPageRef.value) return message.warning('请将表单信息完善后再提交')
-
-  const params = { ...FormPageRef.value.formQuery, type: radioValue.value }
-  if (!params.name_zh) return message.warning('请填写姓名!')
-  
-  // 数组转为字符串保存
-  if (Array.isArray(params?.mobile)) {
-    params.mobile = params.mobile.filter(i => Boolean(i)).map(j => String(j).replace(/,|,/g, '')).join(',');
-  }
-
-  const loading = ElLoading.service({
-    lock: true,
-    text: '正在保存中...',
-    background: 'rgba(0, 0, 0, 0.7)',
-  })
-  try {
-    let result = {}
-
-    if (analysisType.value === 'create') {
-      if (cardFileQuery.value) {
-        cardFileQuery.value.append('card_data', JSON.stringify(params)) // 名片
-        result = await talentLabelingApi.createBusinessCard(cardFileQuery.value)
-      } else {
-        // 结构化数据源 不传递文件
-        result = await talentLabelingApi.createBusinessCardPost(params)
-      }
-      message.success('新增成功')
-      dialog_analysisInfo.value = false
-      // 刷新列表
-      getList()
-
-      if (result.code === 202 || result.message.includes('疑似重复')) {
-        if (!result.data?.main_card?.id) return
-        
-        await message.confirm('发现与当前名片的疑似重复数据,去处理')
-        mergeFormRef.value.open(result.data?.main_card?.id)
-      }
-    } else {
-      await talentLabelingApi.updateBusinessCard(params, formData.value.id)
-      message.success('更新成功')
-      dialog_analysisInfo.value = false
-      // 刷新列表
-      getList()
-    }
-  } catch (error) {
-    console.log('更新失败', error)
-  } finally {
-    cardFileQuery.value = null
-    loading.close()
-    openSearch.value = false
-    handleWebClear()
-  }
-}
-
-// 解析中
-const analysisLoading = ref(false)
-const formData = ref({})
-const handleAnalysis = async () => {
-  // 开始解析
-  analysisLoading.value = true
-  cardFileQuery.value = null
-  formData.value = null
-	const type = radioValue.value
-  try {
-    // if (type === 'file') { // 简历解析
-    //   if (!fileUrl.value) return message.warning('获取文件失败,请重新上传!')
-    //   const data = await commonApi.resumeParser({ fileUrl: fileUrl.value })
-    //   resumeAnalysisToForm(data) // 简历解析
-    // } else if (type === 'card') { // 名片解析
-    //   if (!cardImgUrl.value) {
-    //     message.warning('请先上传名片!')
-    //     return
-    //   }
-    //   cardFileQuery.value = new FormData()
-    //   cardFileQuery.value.append('image', cardUploadRow.value)
-    //   message.warning('正在解析...')
-  
-    //   const index = createAnalysisNum.value
-    //   const res = await talentLabelingApi.businessCardParse(cardFileQuery.value)
-    //   if (index !== createAnalysisNum.value || !dialog_upload.value) return // 不是最新的名片解析数据(用户在解析完成前已重新上传)或用户已取消解析
-    //   formData.value = res?.data || res
-    //   message.success('名片解析成功')
-    // }
-
-    dialog_upload.value = false
-    message.success('上传成功,请等待解析任务完成,解析完成后点击入库即可完成人才入库!')
-    // dialog_analysisInfo.value = true
-  } catch (error) {
-    console.log('解析失败', error)
-    cardFileQuery.value = null
-  } finally {
-    analysisLoading.value = false
-  }
-}
-
-// 监听表单变化,同步更新tagList中对应的数据
-watch(() => FormPageRef.value?.formQuery, (newVal) => {
-  if (tagCurrentIndex.value !== null && webOriginList.value && webOriginList.value.length > tagCurrentIndex.value) {
-    // 保留原始pic_url
-    const { pic_url } = webOriginList.value[tagCurrentIndex.value]
-    webOriginList.value[tagCurrentIndex.value] = { ...newVal, pic_url }
-  }
-}, { deep: true })
-
-// 简历解析
-const fileUrl = ref('') // https://minio.menduner.com/dev/person/229988673960153088/attachment/ee3eb21f45e13ede3557a03d18585ed80c5b4212ac5634e3436e309afaa8fe6a.pdf
-const uploadRef = ref()
-const fileList = ref([])
-const fileData = ref({ path: '' })
-// 文件上传
-const handleChange = async (file) => {
-  // fileData.value.path = file.name
-  // unref(uploadRef)?.submit()
-  // if (!fileList.value.length) return
-
-  // const url = fileList.value[0].response.data
-  // fileUrl.value = !url.includes('.pdf') ?  `${baseUrl}/onlinePreview?url=${encodeURIComponent(Base64.encode(url))}` : url
-  // if (dialog_analysisInfo.value) {
-  //   if (FormPageRef.value?.changeLoading) FormPageRef.value.changeLoading(true)
-  //   message.warning('正在解析...')
-  //   const data = await commonApi.resumeParser({ fileUrl: fileUrl.value })
-  //   resumeAnalysisToForm(data) // 简历解析
-  //   if (FormPageRef.value?.changeLoading) FormPageRef.value.changeLoading(false)
-  // }
-}
-const submitFormError = () => {
-  message.error('上传失败,请您重新上传!')
-}
-const handleExceed = () => {
-  message.error('最多只能上传一个文件!')
-}
-const submitFormSuccess = (e) => {
-  // 清理
-  // unref(uploadRef)?.clearFiles()
-}
-
-const drawer = ref(false)
-const resumeTxt = ref([])
-// 查看文本信息
-const handleText = () => {
-  drawer.value = true
-}
-// 重新上传简历
-const handleResetUpload = async () => {
-  await message.confirm('是否确定重新上传简历?确定后将清空当前信息')
-  fileUrl.value = ''
-  fileData.value.path = ''
-  fileList.value = []
-  resumeAnalysisToForm('reset') // 简历解析
-}
-
-// 简历解析数据解构赋值
-const resumeAnalysisToForm = (data) => {
-  if (data === 'reset') {
-    // 重置表单
-    resumeTxt.value = ''
-    if (FormPageRef.value?.resetFormData) FormPageRef.value.resetFormData()
-  }
-  formData.value = {
-    name_zh: data?.person?.name || '',
-    email: data?.person?.email || '',
-    mobile: data?.person?.phone || '',
-    birthday: data?.person?.birthday ? timesTampChange(data.person.birthday, 'Y-M-D') : '',
-    age: data?.person?.birthday ? timestampToAge(data.person.birthday) : null,
-    career_path: data?.workList ? data.workList.map(e => {
-      return {
-        hotel_zh: e?.enterpriseName || null,
-        title_zh: e?.positionName || null,
-        date: e?.startTime ? timesTampChange(e.startTime, 'Y-M-D') : null
-      }
-    }) : null,
-    created_at: data?.person?.createTime ? timesTampChange(data.person.createTime, 'Y-M-D') : null,
-    updated_at: data?.person?.updateTime ? timesTampChange(data.person.updateTime, 'Y-M-D') : null,
-  }
-  resumeTxt.value = data?.resume?.rawText?.split('\n') || ''
-  if (FormPageRef.value?.setFormData) FormPageRef.value.setFormData(formData.value)
-}
-
-// 名片解析 
-const createAnalysisNum = ref(0)
-const cardFileQuery = ref(null)
-const cardUploadRow = ref(null)
-const cardImgUrl = ref(null)
-const cardUploadChange = (raw) => {
-  cardUploadRow.value = raw
-}
-
-// 人员搜索
-const openSearch = ref(false)
-const id = ref(null)
-const userId = ref(null)
-const handleDetail = async ({id: use_id, userId: use_userId}) => {
-  if (!use_userId || !use_userId)  return message.warning('请先选择人才!')
-  id.value = use_id; userId.value = use_userId
-  try {
-    const data = await TalentMap.getTalentMapDetail(use_userId)
-    // 去除id
-    resumeAnalysisToForm(data) // 简历解析
-    dialog_analysisInfo.value = true
-    // message.success(`操作成功`)
-  } catch {}
-}
-
-const DialogWidth = ref('500')
-const showWebAnalysis = ref(false)
-// 选择解析方式
-const handleSelect = () => {
-  openSelect.value = false
-  formData.value = null
-  showWebAnalysis.value = false
-  DialogWidth.value = '500'
-	const type = radioValue.value
-  if (type === 'card') {
-    createAnalysisNum.value++
-  DialogWidth.value = '800'
-  }
-  if (type === 'menduner') {
-    openSearch.value = true
-    return
-  }
-  if (type === 'web') {
-    showFormPage.value = false
-    showWebAnalysis.value = true
-    dialog_analysisInfo.value = true
-    return
-  }
-  dialog_upload.value = true
-}
-
-// 关闭上传弹窗
-const handleCancel = () => {
-  dialog_upload.value = false
-  analysisLoading.value = false
-}
-
-const openSelect = ref(false)
-const radioObject = { card: '名片', file: '简历', web: '门墩儿新任命', menduner: '门墩儿招聘', picture: '杂项' }
-const radioList = ref(Object.keys(radioObject).map(key => ({ value: key, label: radioObject[key]}) ))
-const defaultValue = radioList.value[0].value // 默认选中
-const radioValue = ref(defaultValue)
-// 新增解析
-const handleAdd = () => {
-  handleWebClear()
-  cardUploadRow.value = null
-  cardImgUrl.value = null
-  analysisLoading.value = false
-  analysisType.value = 'create'
-  radioValue.value = defaultValue // 重置解析类型
-  // 
-  openSelect.value = true
-}
-
-/** 初始化 **/
-onMounted(() => {
-  getList()
-})
-
-</script>
-
-<style lang="scss" scoped>
-.analysisInfoBox {
-  display: flex;
-  .analysisFile {
-    // width: 50%;
-    // max-height: 70vh;
-    padding-right: 12px;
-    overflow: auto;
-  }
-}
-.radioBox {
-	margin: 40px 0;
-}
-
-:deep(.drawer) {
-  position: absolute;
-  .el-drawer {
-    background-color: aliceblue;
-  }
-}
-.tagBox {
-  padding: 12px;
-  border: 1px dashed #409EFF;
-  border-radius: 4px;
-}
-:deep {
-  .el-tag__content {
-    display: flex;
-    align-items: center;
-  }
-}
-</style>

+ 0 - 194
src/views/menduner/system/talentMap/maintenance/gather/resume/index.vue

@@ -1,194 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="姓名" prop="name">
-        <el-input v-model="queryParams.name" placeholder="请输入姓名" clearable @keyup.enter="handleQuery" class="!w-180px" />
-      </el-form-item>
-      <el-form-item label="联系电话" prop="phone">
-        <el-input v-model="queryParams.phone" placeholder="请输入联系电话" clearable @keyup.enter="handleQuery" class="!w-180px" />
-      </el-form-item>
-      <el-form-item label="邮箱" prop="email">
-        <el-input v-model="queryParams.email" placeholder="请输入邮箱" clearable @keyup.enter="handleQuery" class="!w-180px" />
-      </el-form-item>
-      <el-form-item label="性别" prop="sex">
-        <el-select v-model="queryParams.sex" placeholder="请选择性别" class="!w-180px">
-          <el-option v-for="dict in getIntDictOptions(DICT_TYPE.MENDUNER_SEX)" :key="dict.value" :label="dict.label" :value="dict.value"/>
-        </el-select>
-      </el-form-item>
-      <el-form-item label="学历" prop="eduType">
-        <el-select v-model="queryParams.eduType" placeholder="请选择学历" class="!w-180px">
-          <el-option v-for="dict in getIntDictOptions(DICT_TYPE.MENDUNER_EDUCATION_TYPE)" :key="dict.value" :label="dict.label" :value="dict.value" />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="工作经验" prop="expType">
-        <el-select v-model="queryParams.expType" placeholder="请选择工作经验" class="!w-180px">
-          <el-option v-for="dict in getIntDictOptions(DICT_TYPE.MENDUNER_EXP_TYPE)" :key="dict.value" :label="dict.label" :value="dict.value" />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="所在城市" prop="areaId">
-        <el-cascader v-model="queryParams.areaId" :options="areaTreeData" :props="{ label: 'name', value: 'id', emitPath: false, checkStrictly: true }"  class="!w-180px" />
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-        <el-button type="primary" plain @click="handleAdd">
-          <Icon icon="ep:plus" class="mr-5px" /> 新增人才
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true">
-      <el-table-column label="头像" align="center" prop="avatar" width="70px" fixed="left">
-        <template #default="scope">
-          <el-image v-if="scope.row?.person.avatar" class="h-60px w-60px" :src="scope.row?.person.avatar" lazy preview-teleported :preview-src-list="[scope.row?.person.avatar]" fit="contain" />
-        </template>
-      </el-table-column>
-      <el-table-column label="姓名" align="center" prop="person.name" fixed="left" />
-      <el-table-column label="联系电话" align="center" prop="person.phone" />
-      <el-table-column label="邮箱" align="center" prop="person.email" />
-      <el-table-column label="性别" align="center" prop="person.sexStr">
-        <template #default="scope">
-          <el-tag v-if="scope.row?.person?.sexStr" :type="scope.row?.person?.sexStr === '男' ? 'primary' : 'danger'">
-            {{ scope.row?.person?.sexStr }}
-          </el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="出生日期" align="center" prop="person.birthday" :formatter="dateFormatter2" />
-      <el-table-column label="学历" align="center" prop="person.eduTypeStr" />
-      <el-table-column label="工作经验" align="center" prop="person.expTypeStr" />
-      <el-table-column label="所在城市" align="center" prop="person.areaStr" />
-      <el-table-column label="意向城市" align="center">
-        <template #default="scope">
-          {{ scope.row?.person?.interestedAreaStrList && scope.row?.person?.interestedAreaStrList.length ? scope.row?.person?.interestedAreaStrList.join(',') : '' }}
-        </template>
-      </el-table-column>
-      <el-table-column label="求职意向" align="center">
-        <template #default="scope">
-          {{ scope.row?.person?.jobInterestedList && scope.row?.person?.jobInterestedList.length ? scope.row?.person?.jobInterestedList.join(',') : '' }}
-        </template>
-      </el-table-column>
-      <el-table-column label="首次工作时间" align="center" prop="person.firstWorkTime" :formatter="dateFormatter2" />
-      <el-table-column label="创建时间" align="center" prop="person.createTime" :formatter="dateFormatter" />
-      <el-table-column label="操作" align="center" fixed="right" min-width="110">
-        <template #default="scope">
-          <el-button link type="primary" @click="openDetail(scope.row.person.id)">详情</el-button>
-          <el-button link type="danger" @click="handleDelete(scope.row.person.id)">删除</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </ContentWrap>
-</template>
-
-<script setup>
-import { TalentMap } from '@/api/menduner/system/talentMap'
-import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
-import { getDict } from '@/hooks/web/useDictionaries'
-import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
-
-/** 人才采集 简历解析 */
-defineOptions({ name: 'TalentMap' })
-
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(false) // 列表的加载中
-const list = ref([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  name: undefined,
-  phone: undefined,
-  sex: undefined,
-  email: undefined,
-  eduType: undefined,
-  expType: undefined,
-  areaId: undefined,
-})
-const queryFormRef = ref() // 搜索的表单
-
-// 地区列表
-const areaTreeData = ref([])
-const getDictData = async () => {
-  const { data } = await getDict('areaTreeData', {}, 'areaTreeData')
-  const obj = data.find(e => e.name === '中国')
-  const list = obj?.children ? obj.children.map(e =>{
-    // 市辖区直接显示区
-    const municipality = e.children && e.children.length && e.children[0].name === '市辖区'
-    if (municipality && e.children[0].children?.length) e.children = e.children[0].children
-    return e
-  }) : []
-  areaTreeData.value = list.length ? list : []
-}
-getDictData()
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    const data = await TalentMap.getTalentMapInfoPage(queryParams)
-    list.value = data.list
-    total.value = data.total
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery()
-}
-
-/** 打开用户详情 */
-const { push } = useRouter()
-const openDetail = (id) => {
-  push({ name: 'TalentMapDetail', params: { id } })
-}
-
-/** 删除按钮操作 */
-const handleDelete = async (id) => {
-  if (!id) return message.warning('删除失败,请稍后再试')
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await TalentMap.deleteTalentMap(id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    await getList()
-  } catch {}
-}
-
-const handleAdd = () => {
-  push({ name: 'TalentMapDetail', params: { id: 'add' } })
-}
-
-/** 初始化 **/
-onMounted(() => {
-  getList()
-})
-</script>

+ 0 - 168
src/views/menduner/system/talentMap/maintenance/gather/webPageParsing/index copy.vue

@@ -1,168 +0,0 @@
-<template>
-	<ContentWrap>
-		<el-form
-			class="-mb-15px"
-			:model="queryParams"
-			ref="queryFormRef"
-			:inline="true"
-			label-width="90px"
-		>
-			<el-form-item label="url抓取数据" prop="urls">
-				<el-input
-					v-model="queryParams.urls"
-					class="!w-60vw"
-					type="textarea"
-					:rows="1"
-					placeholder="请输入需要爬取的页面,多个页面请用 ',' 隔开"
-				/>
-			</el-form-item>
-			<el-form-item>
-				<el-button type="primary" plain :loading="loading" @click="handleExecute">执行</el-button>
-			</el-form-item>
-		</el-form>
-	</ContentWrap>
-
-	<ContentWrap v-if="contents.length">
-		<el-row gutter="20">
-			<el-col v-for="(content, index) in contents" :key="index" :span="12">
-				<el-card class="!h-500px" v-loading="!content.data">
-					<template #header>
-						<div class="flex items-center justify-between">
-							<el-text class="flex-1" truncated>{{ content.url }}</el-text>
-							<div class="!w-85px">
-								<Icon icon="ep:view" size="25" class="ml-10px cursor-pointer" color="#409eff" @click="showPage(content)" />
-								<Icon icon="ep:refresh" size="25" class=" ml-18px cursor-pointer" color="#409eff" @click="handleReload(content)" />
-							</div>
-						</div>
-					</template>
-					<div v-if="content.data">
-						<template v-if="typeof content.data === 'string'">{{ content.data }}</template>
-            <el-tabs v-else v-model="content.tab">
-              <el-tab-pane v-for="(v, k) in content.data.data[0]" :key="k" :label="k" :name="k" class="overflow-y-auto !h-360px">
-								<template v-if="k === 'html'">
-									<div class="position-sticky float-right">
-										<el-button
-											type="primary"
-											class="cursor-pointer"
-											@click="content.showHtml = !content.showHtml"
-											:icon="SetUp"
-											circle
-										/>
-									</div>
-                  <pre v-if="!content.showHtml">{{ v }}</pre>
-                  <div v-else v-html="v"></div>
-                </template>
-                <pre v-else>{{ v || '暂无数据' }}</pre>
-							</el-tab-pane>
-            </el-tabs>
-          </div>
-				</el-card>
-			</el-col>
-		</el-row>
-	</ContentWrap>
-
-	<el-drawer
-		v-model="drawer"
-		class="!w-50vw"
-		:with-header="false"
-		:modal="true"
-	>
-		<iframe class="!w-100% !h-[calc(100vh-90px)]" :src="drawerUrl" frameborder="0"></iframe>
-		<el-divider class="!ma-0" />
-		<div class="position-sticky left-20px !h-50px lh-50px">
-			<el-button type="primary" class="!w-100px" @click="drawer = false; drawerUrl = ''">关 闭</el-button>
-		</div>
-	</el-drawer>
-</template>
-
-<script setup>
-/** 人才采集 网页解析 */
-defineOptions({ name: 'WebPageParsing' })
-import FirecrawlApp from '@mendable/firecrawl-js'
-import { SetUp } from '@element-plus/icons-vue'
-
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(false)
-const queryParams = reactive({
-	// urls: 'https://element.eleme.cn/#/zh-CN/component/installation'
-	urls: 'https://mp.weixin.qq.com/s/WeCRR3zN3fPvlGR4t8YFDA'
-})
-const queryFormRef = ref()
-const contents = ref([])
-const drawer = ref(false)
-const drawerUrl = ref('')
-
-const showPage = (content) => {
-	drawer.value = true
-	drawerUrl.value = content.url
-}
-
-const handleReload = async (content) => {
-	content.data = null
-	const res = await handleData(queryParams.urls)
-	content.tab = 0
-	content.data = res
-}
-
-const handleData = async (url) => {
-	try {
-    const app = new FirecrawlApp({ apiKey: 'fc-85c1550c6db64ce4ae8f2d2cd2606e6f' })
-    const crawlResponse = await app.crawlUrl(url, {
-      limit: 100,
-      scrapeOptions: {
-        formats: ['markdown', 'html']
-      }
-    })
-    if (!crawlResponse.success) {
-      throw new Error(`Failed to crawl: ${crawlResponse.error}`)
-    }
-    return crawlResponse
-  } catch (error) {
-    return error.message
-  }
-}
-
-// const handleExecute = async () => {
-// 	if (!queryParams.urls) return
-// 	contents.value = []
-// 	const urls = queryParams.urls.split(',')
-
-// 	const run = async (url) => {
-// 	  contents.value.push({ url, tab: 'markdown', showHtml: false, data: null })
-// 		const res = await handleData(url)
-// 		contents.value[contents.value.length - 1] = { url, tab: 'markdown', showHtml: false, data: res }
-
-// 		if (contents.value.length < urls.length) {
-// 			await run(urls[contents.value.length])
-// 		}
-// 	}
-
-// 	await run(urls[contents.value.length])
-// }
-
-// 执行
-const handleExecute = async () => {
-	if (!queryParams.urls) return
-	contents.value = []
-	const urls = queryParams.urls.split(',').map(url => url.trim()).filter(url => url)
-	if (urls.length === 0) return
-
-	urls.forEach(url => {
-	  contents.value.push({ url, tab: 'markdown', showHtml: false, data: null })
-	})
-
-	const crawlPromises = urls.map(async (url, index) => {
-		const res = await handleData(url)
-		contents.value[index] = { ...contents.value[index], data: res }
-	})
-
-	try {
-		await Promise.all(crawlPromises)
-		console.log('All crawls completed:', contents.value); // 可在此处添加成功回调
-	} catch (error) {
-		console.error('爬取过程中发生错误:', error);
-	}
-}
-</script>

+ 0 - 206
src/views/menduner/system/talentMap/maintenance/gather/webPageParsing/index.vue

@@ -1,206 +0,0 @@
-<template>
-	<ContentWrap>
-		<el-form
-			class="-mb-15px"
-			:model="queryParams"
-			ref="queryFormRef"
-			:inline="true"
-			label-width="110px"
-		>
-			<el-form-item label="微信公众号链接" prop="urls">
-				<el-input
-					v-model="queryParams.urls"
-					class="!w-50vw"
-					type="textarea"
-					:rows="2"
-					placeholder="请输入需要提取的页面"
-				/>
-			</el-form-item>
-			<el-form-item>
-				<el-button type="primary" plain :loading="loading" @click="handleAnalysis">提取</el-button>
-			</el-form-item>
-		</el-form>
-	</ContentWrap>
-
-	<ContentWrap v-if="contents.length">
-		<el-row gutter="20">
-			<el-col v-for="(content, index) in contents" :key="index" :span="12">
-				<el-card class="!h-500px" v-loading="!content.markdown_text">
-					<template #header>
-						<div class="flex items-center justify-between">
-							<el-text class="flex-1" truncated>{{ content.url }}</el-text>
-							<div class="!w-40px">
-								<Icon icon="ep:view" size="25" class="ml-10px cursor-pointer" color="#409eff" @click="showPage(content)" />
-							</div>
-						</div>
-					</template>
-          <div class="overflow-y-auto !h-360px" v-if="content.markdown_text">
-            <pre>{{ content.markdown_text }}</pre>
-          </div>
-					<el-button type="primary" plain class="mt-10px" @click="handleSubmit(content.markdown_text)">解析</el-button>
-				</el-card>
-			</el-col>
-		</el-row>
-	</ContentWrap>
-
-	<el-drawer
-		v-model="drawer"
-		class="!w-50vw"
-		:with-header="false"
-		:modal="true"
-	>
-		<iframe id="iFrame" class="!w-100% !h-[calc(100vh-90px)]" src="" frameborder="0"></iframe>
-		<el-divider class="!ma-0" />
-		<div class="position-sticky left-20px !h-50px lh-50px">
-			<el-button type="primary" class="!w-100px" @click="drawer = false">关 闭</el-button>
-		</div>
-	</el-drawer>
-</template>
-
-<script setup>
-/** 人才采集 网页解析 */
-defineOptions({ name: 'WebPageParsing' })
-import axios from 'axios'
-import TurndownService from 'turndown'
-import { talentWebParsingApi } from '@/api/menduner/system/talentMap/webParsing.ts'
-
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(false)
-const queryParams = reactive({
-	urls: 'https://mp.weixin.qq.com/s/vQLWlSB6DzqSewtBLkk_kQ'
-})
-const queryFormRef = ref()
-const contents = ref([])
-const drawer = ref(false)
-
-// 创建转换服务
-const turndownService = new TurndownService({
-  headingStyle: 'atx',
-  codeBlockStyle: 'fenced',
-  bulletListMarker: '-'
-})
-
-// 添加自定义规则处理微信公众号特有内容
-turndownService.addRule('wechatImages', {
-  filter: 'img',
-  replacement: (content, node) => {
-    const alt = node.getAttribute('alt') || ''
-    const src = node.getAttribute('src') || node.getAttribute('data-src') || ''
-    return `![${alt}](${src})`
-  }
-})
-
-// 提取主要内容并转换为Markdown
-const wechatHtmlToMarkdown = (html) => {
-  // 创建一个临时DOM解析器
-  const parser = new DOMParser()
-  const doc = parser.parseFromString(html, 'text/html')
-  
-  // 提取正文内容 - 微信公众号文章通常在id="js_content"的div中
-  const content = doc.querySelector('#js_content') || doc.body
-  
-  // 移除不需要的元素
-  const elementsToRemove = [
-    'script', 'style', 'iframe', 'button', 
-    '.qr_code', '.rich_media_extra', '.copyright'
-  ]
-  
-  elementsToRemove.forEach(selector => {
-    content.querySelectorAll(selector).forEach(el => el.remove())
-  })
-  
-  // 转换为Markdown
-  return turndownService.turndown(content.innerHTML)
-}
-
-// 转换为markdown格式
-const handleConvert = (res) => {
-	if (!res.data) return
-	const result = wechatHtmlToMarkdown(res.data)
-	if (!result) return message.warning('转换失败')
-	return result
-}
-
-// 查看原网页
-const showPage = (res) => {
-  if (res.data) {
-    drawer.value = true
-    let html = res.data
-    html = html.replace(/data-src/g, 'src') // 将 data-src 转化为 src
-      .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/g, '') // 移除HTML内容中所有的<script>标签,这样可以避免在iframe中执行潜在的不受信任的脚本。
-      .replace(/https/g, 'http') // 将HTML内容中所有的https替换为http,可能是为了避免在HTTPS环境下加载非HTTPS资源导致浏览器警告
-    
-    nextTick(() => {
-      const iframe = document.getElementById('iFrame')
-      if (!iframe) return
-      const doc = iframe.contentDocument || iframe.document
-      // 设置 iframe 中请求不发送 referrer,以绕过图片防盗链
-      const htmlArr = html.split('</head>')
-      const html_src_add = htmlArr[0] + '<meta name="referrer" content="never"></head>' + htmlArr[1]
-      doc.open()
-      doc.write(html_src_add)
-      doc.close()
-      // 通过延时获取文档高度赋值Iframe去除滚动条,根据实际情况增加延时时间
-      setTimeout(() => {
-        const jsContent = doc.getElementById('js_content')
-        if (jsContent) {
-          jsContent.style.visibility = 'visible'
-          jsContent.style.opacity = 1
-        }
-      }, 100)
-    })
-  }
-}
-
-// 解析
-const handleAnalysis = async () => {
-	if (!queryParams.urls) return
-	contents.value = []
-	loading.value = true
-
-	const urlArr = queryParams.urls.split(',').map(url => url.trim()).filter(Boolean)
-
-	const isWeChatUrl = urlArr.every(url => url.includes('https://mp.weixin.qq.com'))
-	if (!isWeChatUrl) {
-		message.warning('请输入微信公众文章链接')
-		return
-	}
-
-	const base_url = import.meta.env.VITE_NODE_BASE_URL
-
-	contents.value = []
-	axios.post(`${base_url}/process-urls`, { urlArr }, { timeout: 60000 }).then(res => {
-		if (!res?.data || !res?.data?.contents || !res?.data?.contents.length) return
-		const list = res?.data?.contents
-		list.forEach(e => {
-			contents.value.push({
-				...e,
-				markdown_text: handleConvert(e)
-			})
-		})
-
-	}).catch(err => {
-		console.log(err, 'error');
-		message.error(err.message)
-	}).finally(_ => {
-		loading.value = false
-	})
-}
-
-const handleSubmit = async (markdown_text) => {
-	try {
-		const data = await talentWebParsingApi.saveMarkdownContent({ markdown_text })
-		console.log(data, markdown_text, 'submit')
-	} catch {}
-}
-</script>
-
-<style scoped>
-.iframe-container {
-  position: relative;
-  width: 100%;
-  height: calc(100vh - 90px);
-}
-</style>

+ 0 - 144
src/views/menduner/system/talentMap/maintenance/gatherDetails/components/search.vue

@@ -1,144 +0,0 @@
-<template>
-    <!-- 搜索工作栏 -->
-  <ContentWrap>
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="姓名" prop="name">
-        <el-input v-model="queryParams.name" placeholder="请输入姓名" clearable @keyup.enter="handleQuery" class="!w-130px" />
-      </el-form-item>
-      <el-form-item label="联系电话" prop="phone">
-        <el-input v-model="queryParams.phone" placeholder="请输入联系电话" clearable @keyup.enter="handleQuery" class="!w-130px" />
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery" type="primary"><Icon icon="ep:search" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <div v-if="!list?.length" class="tip">
-    <div>{{ tipContent }}</div>
-  </div>
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true">
-      <!-- <el-table-column label="用户头像" align="center" prop="avatar" width="90px" fixed="left">
-        <template #default="scope">
-          <el-image v-if="scope.row.person?.avatar" class="h-80px w-80px" :src="scope.row.person?.avatar" lazy preview-teleported :preview-src-list="[scope.row.person?.avatar]" fit="contain" />
-        </template>
-      </el-table-column> -->
-      <el-table-column label="姓名" align="center" prop="person.name" fixed="left" />
-      <el-table-column label="联系电话" align="center" prop="user.phone" width="120px" />
-      <el-table-column label="求职状态" align="center" prop="person.jobStatus" width="130px">
-        <template #default="scope">
-          <dict-tag v-if="scope.row.person?.jobStatus" :type="DICT_TYPE.MENDUNER_JOB_SEEK_STATUS" :value="scope.row.person?.jobStatus" />
-        </template>
-      </el-table-column>
-      <el-table-column label="学历" align="center" prop="person.eduType">
-        <template #default="scope">
-          <dict-tag v-if="scope.row.person?.eduType" :type="DICT_TYPE.MENDUNER_EDUCATION_TYPE" :value="scope.row.person?.eduType" />
-        </template>
-      </el-table-column>
-      <el-table-column label="工作经验" align="center" prop="person.expType">
-        <template #default="scope">
-          <dict-tag v-if="scope.row.person?.expType" :type="DICT_TYPE.MENDUNER_EXP_TYPE" :value="scope.row.person?.expType" />
-        </template>
-      </el-table-column>
-      <el-table-column label="操作" align="center" fixed="right" min-width="60">
-        <template #default="scope">
-          <el-button link type="primary" @click="handleDetail(scope.row)">{{ detailButTxt || '详情'}}</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      layout="total, prev, pager, next"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </ContentWrap>
-</template>
-
-<script setup>
-defineOptions({ name: 'TalentMapSearch' })
-import { TalentMap } from '@/api/menduner/system/talentMap'
-import { PersonInfoApi } from '@/api/menduner/system/person'
-import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
-
-const emit = defineEmits(['detail'])
-const props = defineProps({
-  detailButTxt: String,
-  searchName: String
-})
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(false) // 列表的加载中
-const list = ref([]) // 列表的数据
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  name: '',
-  phone: undefined
-})
-const queryFormRef = ref() // 搜索的表单
-
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    const data = await PersonInfoApi.getPersonInfoPage(queryParams)
-    list.value = data?.list || []
-    total.value = data?.total || 0
-  } finally {
-    loading.value = false
-  }
-}
-
-const tipContent = '输入人才姓名、电话查询后可使用门墩儿人才库数据填充表单!'
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  if (!queryParams.name && !queryParams.phone) {
-    message.warning('请输入后查询 !')
-    return
-  }
-  getList()
-}
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery()
-}
-
-const handleDetail = (row) => {
-  emit('detail', row.user.id)
-}
-
-/** 初始化 **/
-// onMounted(async () => {
-//   setTimeout(() => {
-//     if (props.searchName) {
-//       queryParams.name = props.searchName
-//     }
-//     getList()
-//   }, 1000)
-// })
-</script>
-
-<style lang="scss" scoped>
-.tip {
-  text-align: center;
-  margin-bottom: 10px;
-  color: #e6a23c;
-}
-</style>

+ 0 - 148
src/views/menduner/system/talentMap/maintenance/gatherDetails/components/webAnalysis.vue

@@ -1,148 +0,0 @@
-<template>
-	<ContentWrap>
-		<el-form
-			class="-mb-15px"
-			:model="queryParams"
-			ref="queryFormRef"
-			:inline="true"
-			label-width="90px"
-		>
-			<el-form-item label="url抓取数据" prop="urls">
-				<el-input
-					v-model="queryParams.urls"
-					class="!w-420px"
-					type="textarea"
-					:rows="1"
-					placeholder="请输入需要爬取的页面,多个页面请用 ',' 隔开"
-				/>
-			</el-form-item>
-			<el-form-item>
-				<el-button type="primary" plain :loading="loading" @click="handleExecute">执行</el-button>
-			</el-form-item>
-		</el-form>
-	</ContentWrap>
-
-	<ContentWrap v-if="contents.length">
-		<el-row gutter="20">
-			<el-col v-for="(content, index) in contents" :key="index" :span="12">
-				<el-card class="!h-500px" v-loading="!content.data">
-					<template #header>
-						<div class="flex items-center justify-between">
-							<el-text class="flex-1" truncated>{{ content.url }}</el-text>
-							<div class="!w-85px">
-								<Icon icon="ep:view" size="25" class="ml-10px cursor-pointer" color="#409eff" @click="showPage(content)" />
-								<Icon icon="ep:refresh" size="25" class=" ml-18px cursor-pointer" color="#409eff" @click="handleReload(content)" />
-							</div>
-						</div>
-					</template>
-					<div v-if="content.data">
-						<template v-if="typeof content.data === 'string'">{{ content.data }}</template>
-            <el-tabs v-else v-model="content.tab">
-              <el-tab-pane v-for="(v, k) in content.data.data[0]" :key="k" :label="k" :name="k" class="overflow-y-auto !h-360px">
-								<template v-if="k === 'html'">
-									<div class="position-sticky float-right">
-										<el-button
-											type="primary"
-											class="cursor-pointer"
-											@click="content.showHtml = !content.showHtml"
-											:icon="SetUp"
-											circle
-										/>
-									</div>
-                  <pre v-if="!content.showHtml">{{ v }}</pre>
-                  <div v-else v-html="v"></div>
-                </template>
-                <pre v-else>{{ v || '暂无数据' }}</pre>
-							</el-tab-pane>
-            </el-tabs>
-          </div>
-				</el-card>
-			</el-col>
-		</el-row>
-	</ContentWrap>
-
-	<el-drawer
-		v-model="drawer"
-		class="!w-50vw"
-		:with-header="false"
-		:modal="true"
-	>
-		<iframe class="!w-100% !h-[calc(100vh-90px)]" :src="drawerUrl" frameborder="0"></iframe>
-		<el-divider class="!ma-0" />
-		<div class="position-sticky left-20px !h-50px lh-50px">
-			<el-button type="primary" class="!w-100px" @click="drawer = false; drawerUrl = ''">关 闭</el-button>
-		</div>
-	</el-drawer>
-</template>
-
-<script setup>
-/** 人才采集 网页解析 */
-import FirecrawlApp from '@mendable/firecrawl-js'
-import { SetUp } from '@element-plus/icons-vue'
-
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-
-const loading = ref(false)
-const queryParams = reactive({
-	urls: 'https://mp.weixin.qq.com/s/WeCRR3zN3fPvlGR4t8YFDA'
-})
-const queryFormRef = ref()
-const contents = ref([])
-const drawer = ref(false)
-const drawerUrl = ref('')
-
-const showPage = (content) => {
-	drawer.value = true
-	drawerUrl.value = content.url
-}
-
-const handleReload = async (content) => {
-	content.data = null
-	const res = await handleData(queryParams.urls)
-	content.tab = 0
-	content.data = res
-}
-
-const handleData = async (url) => {
-	try {
-    const app = new FirecrawlApp({ apiKey: 'fc-85c1550c6db64ce4ae8f2d2cd2606e6f' })
-    const crawlResponse = await app.crawlUrl(url, {
-      limit: 100,
-      scrapeOptions: {
-        formats: ['markdown', 'html']
-      }
-    })
-    if (!crawlResponse.success) {
-      throw new Error(`Failed to crawl: ${crawlResponse.error}`)
-    }
-    return crawlResponse
-  } catch (error) {
-    return error.message
-  }
-}
-
-// 执行
-const handleExecute = async () => {
-	if (!queryParams.urls) return
-	contents.value = []
-	const urls = queryParams.urls.split(',').map(url => url.trim()).filter(url => url)
-	if (urls.length === 0) return
-
-	urls.forEach(url => {
-	  contents.value.push({ url, tab: 'markdown', showHtml: false, data: null })
-	})
-
-	const crawlPromises = urls.map(async (url, index) => {
-		const res = await handleData(url)
-		contents.value[index] = { ...contents.value[index], data: res }
-	})
-
-	try {
-		await Promise.all(crawlPromises)
-		console.log('All crawls completed:', contents.value); // 可在此处添加成功回调
-	} catch (error) {
-		console.error('爬取过程中发生错误:', error);
-	}
-}
-</script>

+ 0 - 368
src/views/menduner/system/talentMap/maintenance/gatherDetails/index.vue

@@ -1,368 +0,0 @@
-<template>
-  <div v-loading="saveLoading">
-    <el-row :gutter="10">
-      <el-col :span="12">
-        <ContentWrap>
-          <div>
-            <!-- 简历解析 -->
-            <template v-if="dataType === 'file'">
-              <div v-if="fileUrl" style="position: relative;">
-                <div class="text-right m-b-10px">
-                  <el-button v-if="!isEdit" @click="handleText">查看文本信息</el-button>
-                  <el-button type="primary" @click="handleResetUpload">重新上传简历</el-button>
-                </div>
-                <IFrame :src="fileUrl" />
-                <el-drawer
-                  v-model="drawer"
-                  modal-class="drawer"
-                  size="75%"
-                  direction="ltr"
-                  title="简历解析(可复制文本使用)"
-                >
-                  <p v-for="(text, index) in resumeTxt" :key="text + index">{{ text }}</p>
-                </el-drawer>
-              </div>
-              <el-upload
-                v-else
-                ref="uploadRef"
-                v-model:file-list="fileList"
-                :action="uploadUrl"
-                :auto-upload="false"
-                :data="data"
-                :limit="1"
-                :on-change="handleChange"
-                :on-error="submitFormError"
-                :on-exceed="handleExceed"
-                :on-success="submitFormSuccess"
-                :http-request="httpRequest"
-                accept=".pdf, doc, .docx"
-                drag
-                class="flex-1"
-              >
-                <i class="el-icon-upload"></i>
-                <div class="el-upload__text">上传附件, 将文件拖到此处,或 <em>点击上传</em></div>
-                <template #tip>
-                  <div class="el-upload__tip color-red">
-                    提示:仅允许导入 pdf、doc、docx 格式文件!
-                  </div>
-                </template>
-              </el-upload>
-            </template>
-            <!-- 名片解析 -->
-            <template v-else-if="dataType === 'card'">
-              <div class="image">
-                <el-image v-if="cardImgUrl" class="!w-100%" :src="cardImgUrl" />
-                <div v-else>
-                  <UploadImg
-                    v-model="cardImgUrl"
-                    :limit="1"
-                    :uploadSuccessTip="false"
-                    drag
-                    buttonUpload
-                    @handle-change="cardUploadChange"
-                    height="32px" width="104px"
-                    style="margin: 0 auto; width: 104px;margin-top: 40%;"
-                  >
-                    <template #tip>{{ cardImgUrl ? '' : '请上传名片' }}</template>
-                  </UploadImg>
-                </div>
-              </div>
-            </template>
-            <!-- 网页解析 -->
-            <template v-else-if="dataType === 'web'">
-              <webAnalysis />
-            </template>
-            <!-- 门墩儿人才库 -->
-            <template v-else>
-              <Search @detail="handleDetail" :detailButTxt="detailButTxt" :searchName="formData?.name_zh || formData?.name_en || ''" />
-            </template>
-          </div>
-        </ContentWrap>
-      </el-col>
-
-      <el-col :span="12">
-        <ContentWrap>
-          <div ref="content" :style="{'height': height, 'overflow-y': 'auto'}">
-            <FormPage ref="FormPageRef" :itemData="formData" />
-            <div class="text-center m-t-30px">
-              <el-affix position="bottom" :offset="20">
-                <el-button type="primary" @click="handleSave" size="large" class="!w-120px" :disabled="analysisLoading">保 存</el-button>
-              </el-affix>
-            </div>
-          </div>
-        </ContentWrap>
-      </el-col>
-    </el-row>
-  </div>
-
-  <MergeForm ref="mergeFormRef" @refresh="null" />
-</template>
-
-<script setup>
-defineOptions({ name: 'TalentMapGatherDetails'})
-import { dateFormatter } from '@/utils/formatTime'
-import { talentLabelingApi } from '@/api/menduner/system/talentMap/labeling'
-import { TalentMap } from '@/api/menduner/system/talentMap'
-import MergeForm from '@/views/menduner/system/talentMap/components/merge.vue'
-import FormPage from '@/views/menduner/system/talentMap/components/FormPage.vue'
-import { timesTampChange, timestampToAge } from '@/utils/transform/date'
-import { useUpload } from '@/components/UploadFile/src/useUpload'
-import { commonApi } from '@/api/menduner/common'
-import { Base64 } from 'js-base64'
-import Search from './components/search.vue'
-import webAnalysis from './components/webAnalysis.vue'
-
-const { back } = useRouter()
-const loading = ref(false)
-const activeName = ref('resumeAnalysis')
-const saveLoading = ref(false)
-const { uploadUrl, httpRequest } = useUpload()
-
-/** 初始化 */
-// const InfoRef = ref()
-const content = ref()
-const message = useMessage() // 消息弹窗
-const height = ref(0)
-const route = useRoute()
-const { id } = route.params
-const dataType = ref(route?.query?.type) // 查询参数
-const baseUrl = import.meta.env.VITE_PREVIEW_URL
-const isEdit = ref(false)
-
-// 更新
-const formLoading = ref(false)
-const FormPageRef = ref(null)
-const mergeFormRef = ref() // 合并表单 Ref
-const handleSave = async () => {
-  const params = { ...FormPageRef.value.formQuery, type: dataType.value }
-  if (!params.name_zh) return message.warning('请填写姓名!')
-  
-  // 数组转为字符串保存
-  if (Array.isArray(params?.mobile)) {
-    params.mobile = params.mobile.filter(i => Boolean(i)).map(j => String(j).replace(/,|,/g, '')).join(',');
-  }
-
-  console.log(params, 'handleSubmit')
-  try {
-    formLoading.value = true
-    let result = {}
-
-    if (analysisType.value === 'create') {
-      if (cardFileQuery.value) {
-        cardFileQuery.value.append('card_data', JSON.stringify(params)) // 名片
-        result = await talentLabelingApi.createBusinessCard(cardFileQuery.value)
-      } else {
-        // 结构化数据源 不传递文件
-        result = await talentLabelingApi.createBusinessCardPost(params)
-      }
-      message.success('新增成功')
-      back()
-
-      if (result.code === 202 || result.message.includes('疑似重复')) {
-        if (!result.data?.main_card?.id) return
-        
-        await message.confirm('发现与当前名片的疑似重复数据,去处理')
-        mergeFormRef.value.open(result.data?.main_card?.id)
-      }
-    } else {
-      await talentLabelingApi.updateBusinessCard(params, formData.value.id)
-      message.success('更新成功')
-      back()
-    }
-  } catch (error) {
-    console.log('更新失败', error)
-  } finally {
-    cardFileQuery.value = null
-    formLoading.value = false
-  }
-}
-
-// 简历解析
-const fileUrl = ref('') // https://minio.menduner.com/dev/person/229988673960153088/attachment/ee3eb21f45e13ede3557a03d18585ed80c5b4212ac5634e3436e309afaa8fe6a.pdf
-const uploadRef = ref()
-const fileList = ref([])
-const data = ref({ path: '' })
-// 文件上传
-const handleChange = async (file) => {
-  data.value.path = file.name
-  unref(uploadRef)?.submit()
-  if (!fileList.value.length) return
-
-  const url = fileList.value[0].response.data
-  fileUrl.value = !url.includes('.pdf') ?  `${baseUrl}/onlinePreview?url=${encodeURIComponent(Base64.encode(url))}` : url
-  //
-  if (FormPageRef.value?.changeLoading) FormPageRef.value.changeLoading(true)
-  message.warning('正在解析...')
-  const data = await commonApi.resumeParser({ fileUrl: fileUrl.value })
-  resumeAnalysisToForm(data) // 简历解析
-  if (FormPageRef.value?.changeLoading) FormPageRef.value.changeLoading(false)
-}
-const submitFormError = () => {
-  message.error('上传失败,请您重新上传!')
-}
-const handleExceed = () => {
-  message.error('最多只能上传一个文件!')
-}
-const submitFormSuccess = (e) => {
-  // 清理
-  // unref(uploadRef)?.clearFiles()
-}
-
-const drawer = ref(false)
-const resumeTxt = ref([])
-// 查看文本信息
-const handleText = () => {
-  drawer.value = true
-}
-// 重新上传简历
-const handleResetUpload = async () => {
-  await message.confirm('是否确定重新上传简历?确定后将清空当前信息')
-  fileUrl.value = ''
-  data.value.path = ''
-  fileList.value = []
-  resumeAnalysisToForm('reset') // 简历解析
-}
-
-const detailButTxt = '应用'
-// 搜索-查看详情
-const handleDetail = async (userId) => {
-  if (!userId) return message.warning('请先选择人才!')
-  try {
-    const data = await TalentMap.getTalentMapDetail(userId)
-    // 去除id
-    resumeAnalysisToForm(data) // 简历解析
-    message.success(`${detailButTxt}成功`)
-  } catch {}
-}
-
-// 简历解析数据解构赋值
-const resumeAnalysisToForm = (data) => {
-  if (data === 'reset') {
-    // 重置表单
-    resumeTxt.value = ''
-    if (FormPageRef.value?.resetFormData) FormPageRef.value.resetFormData()
-  }
-  formData.value = {
-    name_zh: data?.person?.name || '',
-    email: data?.person?.email || '',
-    mobile: data?.person?.phone || '',
-    birthday: data?.person?.birthday ? timesTampChange(data.person.birthday, 'Y-M-D') : '',
-    age: data?.person?.birthday ? timestampToAge(data.person.birthday) : null,
-    career_path: data?.workList ? data.workList.map(e => {
-      return {
-        hotel_zh: e?.enterpriseName || null,
-        title_zh: e?.positionName || null,
-        date: e?.startTime ? timesTampChange(e.startTime, 'Y-M-D') : null
-      }
-    }) : null,
-    created_at: data?.person?.createTime ? timesTampChange(data.person.createTime, 'Y-M-D') : null,
-    updated_at: data?.person?.updateTime ? timesTampChange(data.person.updateTime, 'Y-M-D') : null,
-  }
-  resumeTxt.value = data?.resume?.rawText?.split('\n') || ''
-  if (FormPageRef.value?.setFormData) FormPageRef.value.setFormData(formData.value)
-}
-
-// 名片解析 
-const createAnalysisNum = ref(0)
-const cardFileQuery = ref(null)
-const cardUploadRow = ref(null)
-const cardImgUrl = ref(null)
-const cardUploadChange = (raw) => {
-  cardUploadRow.value = raw
-}
-
-/** 编辑 */
-const handleEdit = async (item) => {
-  formData.value = item
-  dataType.value = item.type || 'menduner'
-  try {
-    if (dataType.value === 'card') {
-      if (!item?.image_path) {
-        cardUploadRow.value = null
-        cardImgUrl.value = null
-        return
-      }
-      const data = await talentLabelingApi.getBusinessCardImage(item.image_path)
-      cardUploadRow.value = data?.type ? new File([data], item.image_path, { type: data.type }) : null
-      cardImgUrl.value = data?.type ? URL.createObjectURL(data) : null
-    }
-  } catch (error) {
-    console.log('打印->getBusinessCardImage', error)
-  } finally {
-    if (FormPageRef.value?.setFormData) FormPageRef.value.setFormData(formData.value)
-  }
-}
-
-// 解析中
-const analysisLoading = ref(false)
-const formData = ref({})
-const handleAnalysis = async () => {
-  // 开始解析
-  analysisLoading.value = true
-  cardFileQuery.value = null
-  formData.value = null
-	const type = dataType.value
-  try {
-    // if (type === 'menduner') { // 门墩儿人才库
-    // } else 
-    if (type === 'file') { // 简历解析
-      if (!fileUrl.value) return message.warning('获取文件失败,请重新上传!')
-      const data = await commonApi.resumeParser({ fileUrl: fileUrl.value })
-      resumeAnalysisToForm(data) // 简历解析
-    } else if (type === 'card') { // 名片解析
-      if (!cardImgUrl.value) {
-        message.warning('请先上传名片!')
-        return
-      }
-      cardFileQuery.value = new FormData()
-      cardFileQuery.value.append('image', cardUploadRow.value)
-      message.warning('正在解析...')
-  
-      const res = await talentLabelingApi.businessCardParse(cardFileQuery.value)
-      formData.value = res?.data || res
-      message.success('名片解析成功')
-    }
-    // else if (type === 'web') {}
-
-    if (FormPageRef.value?.setFormData) FormPageRef.value.setFormData(formData.value)
-  } catch (error) {
-    console.log('解析失败', error)
-    cardFileQuery.value = null
-  } finally {
-    analysisLoading.value = false
-  }
-}
-
-
-onMounted(async () => {
-  height.value = document.documentElement.clientHeight + 'px'
-  if (id && id !== 'add') {
-    // 编辑
-    isEdit.value = true
-    const item = route?.query?.item ? JSON.parse(route.query.item) : null
-    if (item) {
-      handleEdit(item)
-    }
-  } else {
-    // 新增
-    const data = route?.query?.item ? JSON.parse(route.query.data) : null
-    // 处理传递的数据
-    if (route?.query?.type === 'file') {
-      fileUrl.value = route?.query?.fileUrl ? decodeURIComponent(route.query.fileUrl) : null
-      //
-      resumeAnalysisToForm(data) // 简历解析
-    }
-    if (route?.query?.type === 'card') {
-      cardImgUrl.value = route?.query?.cardImgUrl ? decodeURIComponent(route.query.cardImgUrl) : null
-      formData.value = data
-      if (FormPageRef.value?.setFormData) FormPageRef.value.setFormData(formData.value)
-    }
-  }
-})
-
-</script>
-<style lang="scss" scoped>
-:deep(.drawer) {
-  position: absolute;
-}
-</style>

+ 1 - 1
src/views/menduner/system/talentMap/maintenance/labeling/LabelingForm.vue

@@ -24,7 +24,7 @@
 				<div class="!h-100% overflow-y-auto">
 					<el-tabs type="border-card">
 						<el-tab-pane label="人才信息">
-							<FormPage ref="baseInfoRef" :itemData="talentItem" />
+							<FormPage ref="baseInfoRef" formType="edit" :itemData="talentItem" />
 							<div class="text-right mt-12px">
 								<el-button @click="handleSave" type="primary" :disabled="saveLoading">保 存</el-button>
 								<el-button @click="dialogVisible = false">取消</el-button>