Przeglądaj źródła

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

lifanagju_citu 2 tygodni temu
rodzic
commit
ce4814784a

+ 1 - 0
package.json

@@ -63,6 +63,7 @@
     "qrcode": "^1.5.3",
     "qs": "^6.12.0",
     "steady-xml": "^0.1.0",
+    "turndown": "^7.2.0",
     "url": "^0.11.3",
     "video.js": "^7.21.5",
     "vue": "3.4.21",

+ 15 - 0
pnpm-lock.yaml

@@ -119,6 +119,9 @@ importers:
       steady-xml:
         specifier: ^0.1.0
         version: 0.1.0
+      turndown:
+        specifier: ^7.2.0
+        version: 7.2.0
       url:
         specifier: ^0.11.3
         version: 0.11.3
@@ -1373,6 +1376,9 @@ packages:
   '@mendable/firecrawl-js@1.19.1':
     resolution: {integrity: sha512-rtBnlF6oLJAxhH4YG8P72FernR1TvdI4J7uiSad2hOF7ZtbkzHkuNsa/10KUTPsmeQf8ESxiSQ1p7HMyEXuW9g==}
 
+  '@mixmark-io/domino@2.2.0':
+    resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==}
+
   '@nodelib/fs.scandir@2.1.5':
     resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
     engines: {node: '>= 8'}
@@ -4864,6 +4870,9 @@ packages:
   tslib@2.6.2:
     resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
 
+  turndown@7.2.0:
+    resolution: {integrity: sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==}
+
   type-check@0.4.0:
     resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
     engines: {node: '>= 0.8.0'}
@@ -6538,6 +6547,8 @@ snapshots:
       - debug
       - ws
 
+  '@mixmark-io/domino@2.2.0': {}
+
   '@nodelib/fs.scandir@2.1.5':
     dependencies:
       '@nodelib/fs.stat': 2.0.5
@@ -10413,6 +10424,10 @@ snapshots:
 
   tslib@2.6.2: {}
 
+  turndown@7.2.0:
+    dependencies:
+      '@mixmark-io/domino': 2.2.0
+
   type-check@0.4.0:
     dependencies:
       prelude-ls: 1.2.1

+ 16 - 16
src/views/menduner/system/enterprise/message/details/components/businessInfo.vue

@@ -1,21 +1,21 @@
 <template>
-  <div>
+  <ContentWrap>
     <el-descriptions class="margin-top" :column="2" border>
-    <el-descriptions-item label="企业名称">{{ formatName(info.name) }}</el-descriptions-item>
-    <el-descriptions-item label="统一社会信用代码">{{ info.code }}</el-descriptions-item>
-    <el-descriptions-item label="法定代表人">{{ info.representative }}</el-descriptions-item>
-    <el-descriptions-item label="成立时间">{{ formatDate(info.establishmentTime, 'YYYY-MM-DD') }}</el-descriptions-item>
-    <el-descriptions-item label="注册资金">{{ info.registeredCapital }}</el-descriptions-item>
-    <el-descriptions-item label="企业类型">{{ info.type }}</el-descriptions-item>
-    <el-descriptions-item label="所属地区">{{ info.area }}</el-descriptions-item>
-    <el-descriptions-item label="核准日期">{{ info.approvalTime }}</el-descriptions-item>
-    <el-descriptions-item label="曾用名">{{ info.formerName }}</el-descriptions-item>
-    <el-descriptions-item label="注册机关">{{ info.registrationAuthority }}</el-descriptions-item>
-    <el-descriptions-item label="注册地址">{{ info.address }}</el-descriptions-item>
-    <el-descriptions-item label="经营状态">{{ info.businessStatus }}</el-descriptions-item>
-    <el-descriptions-item label="营业期限">{{ info.businessTerm }}</el-descriptions-item>
-  </el-descriptions>
-  </div>
+      <el-descriptions-item label="企业名称">{{ formatName(info.name) }}</el-descriptions-item>
+      <el-descriptions-item label="统一社会信用代码">{{ info.code }}</el-descriptions-item>
+      <el-descriptions-item label="法定代表人">{{ info.representative }}</el-descriptions-item>
+      <el-descriptions-item label="成立时间">{{ formatDate(info.establishmentTime, 'YYYY-MM-DD') }}</el-descriptions-item>
+      <el-descriptions-item label="注册资金">{{ info.registeredCapital }}</el-descriptions-item>
+      <el-descriptions-item label="企业类型">{{ info.type }}</el-descriptions-item>
+      <el-descriptions-item label="所属地区">{{ info.area }}</el-descriptions-item>
+      <el-descriptions-item label="核准日期">{{ info.approvalTime }}</el-descriptions-item>
+      <el-descriptions-item label="曾用名">{{ info.formerName }}</el-descriptions-item>
+      <el-descriptions-item label="注册机关">{{ info.registrationAuthority }}</el-descriptions-item>
+      <el-descriptions-item label="注册地址">{{ info.address }}</el-descriptions-item>
+      <el-descriptions-item label="经营状态">{{ info.businessStatus }}</el-descriptions-item>
+      <el-descriptions-item label="营业期限">{{ info.businessTerm }}</el-descriptions-item>
+    </el-descriptions>
+  </ContentWrap>
 </template>
 
 <script setup>

+ 29 - 29
src/views/menduner/system/enterprise/message/details/components/info.vue

@@ -1,34 +1,34 @@
 <template>
-  <div>
+  <ContentWrap>
     <el-descriptions class="margin-top" :column="2" border>
-    <el-descriptions-item label="企业LOGO">
-      <el-image v-if="info.logoUrl" style="width: 100px; height: 100px" :src="info.logoUrl" fit="contain" hide-on-click-modal :preview-src-list="[info.logoUrl]"/>
-    </el-descriptions-item>
-    <el-descriptions-item label="企业全称">{{ formatName(info.name) }}</el-descriptions-item>
-    <el-descriptions-item label="企业别名">{{ formatName(info.anotherName) }}</el-descriptions-item>
-    <el-descriptions-item label="企业官网">
-      <el-link :href="info.website" target="_blank" type="primary">{{ info.website }}</el-link>
-    </el-descriptions-item>
-    <el-descriptions-item label="开业时间">{{ formatDate(info.openTime, 'YYYY-MM-DD') }}</el-descriptions-item>
-    <el-descriptions-item label="联系人">{{ info.contact }}</el-descriptions-item>
-    <el-descriptions-item label="联系电话">{{ info.phone }}</el-descriptions-item>
-    <el-descriptions-item label="是否筹备">{{ info.prepare && info.prepare ? '是' : '否' }}</el-descriptions-item>
-    <el-descriptions-item label="企业规模">
-      <dict-tag :type="DICT_TYPE.MENDUNER_SCALE" :value="info.scale" />
-    </el-descriptions-item>
-    <el-descriptions-item label="所在行业">{{ info.industryName }}</el-descriptions-item>
-    <el-descriptions-item label="融资阶段">
-      <dict-tag :type="DICT_TYPE.MENDUNER_FINANCING_STATUS" :value="info.financingStatus" />
-    </el-descriptions-item>
-    <el-descriptions-item label="企业标签">
-      <el-tag type="primary" v-for="k in info.tagList" :key="k" class="m-r-5px">{{ k }}</el-tag>
-    </el-descriptions-item>
-    <el-descriptions-item label="会员">
-      {{ info.vipExpireDate ? info.vipExpireDate > Date.now() ? packageList.find(e => e.id === Number(info.vipFlag))?.text : '会员已过期' : '非会员' }}
-    </el-descriptions-item>
-    <el-descriptions-item label="会员到期时间">{{ formatDate(info.vipExpireDate, 'YYYY-MM-DD') }}</el-descriptions-item>
-  </el-descriptions>
-  </div>
+      <el-descriptions-item label="企业LOGO">
+        <el-image v-if="info.logoUrl" style="width: 100px; height: 100px" :src="info.logoUrl" fit="contain" hide-on-click-modal :preview-src-list="[info.logoUrl]"/>
+      </el-descriptions-item>
+      <el-descriptions-item label="企业全称">{{ formatName(info.name) }}</el-descriptions-item>
+      <el-descriptions-item label="企业别名">{{ formatName(info.anotherName) }}</el-descriptions-item>
+      <el-descriptions-item label="企业官网">
+        <el-link :href="info.website" target="_blank" type="primary">{{ info.website }}</el-link>
+      </el-descriptions-item>
+      <el-descriptions-item label="开业时间">{{ formatDate(info.openTime, 'YYYY-MM-DD') }}</el-descriptions-item>
+      <el-descriptions-item label="联系人">{{ info.contact }}</el-descriptions-item>
+      <el-descriptions-item label="联系电话">{{ info.phone }}</el-descriptions-item>
+      <el-descriptions-item label="是否筹备">{{ info.prepare && info.prepare ? '是' : '否' }}</el-descriptions-item>
+      <el-descriptions-item label="企业规模">
+        <dict-tag :type="DICT_TYPE.MENDUNER_SCALE" :value="info.scale" />
+      </el-descriptions-item>
+      <el-descriptions-item label="所在行业">{{ info.industryName }}</el-descriptions-item>
+      <el-descriptions-item label="融资阶段">
+        <dict-tag :type="DICT_TYPE.MENDUNER_FINANCING_STATUS" :value="info.financingStatus" />
+      </el-descriptions-item>
+      <el-descriptions-item label="企业标签">
+        <el-tag type="primary" v-for="k in info.tagList" :key="k" class="m-r-5px">{{ k }}</el-tag>
+      </el-descriptions-item>
+      <el-descriptions-item label="会员">
+        {{ info.vipExpireDate ? info.vipExpireDate > Date.now() ? packageList.find(e => e.id === Number(info.vipFlag))?.text : '会员已过期' : '非会员' }}
+      </el-descriptions-item>
+      <el-descriptions-item label="会员到期时间">{{ formatDate(info.vipExpireDate, 'YYYY-MM-DD') }}</el-descriptions-item>
+    </el-descriptions>
+  </ContentWrap>
 </template>
 
 <script setup>

+ 92 - 86
src/views/menduner/system/enterprise/message/details/components/job.vue

@@ -1,99 +1,105 @@
 <template>
-  <div class="flex">
-    <div>
-      <el-input
-        v-model="queryParams.name"
-        placeholder="职位名称(回车搜索)"
-        class="!w-240px"
-        clearable
-        @keyup.enter="handleRefresh"
-        @clear="handleRefresh"
-      />
-      <el-select v-model="queryParams.fairId" placeholder="招聘会" class="!w-240px ml-10px" clearable @change="handleRefresh">
-        <el-option
-          v-for="item in jobFairList"
-          :key="item.id"
-          :label="item.title"
-          :value="item.id"
+  <ContentWrap>
+    <div class="flex">
+      <div>
+        <el-input
+          v-model="queryParams.name"
+          placeholder="职位名称(回车搜索)"
+          class="!w-240px"
+          clearable
+          @keyup.enter="handleRefresh"
+          @clear="handleRefresh"
         />
-        <template #footer v-if="jobFairList?.length < +jobFairTotal">
-          <div class="text-center">
-            <el-button type="primary" link @click="handleGetMore">加载更多</el-button>
-          </div>
-        </template>
-      </el-select>
+        <el-select v-model="queryParams.fairId" placeholder="招聘会" class="!w-240px ml-10px" clearable @change="handleRefresh">
+          <el-option
+            v-for="item in jobFairList"
+            :key="item.id"
+            :label="item.title"
+            :value="item.id"
+          />
+          <template #footer v-if="jobFairList?.length < +jobFairTotal">
+            <div class="text-center">
+              <el-button type="primary" link @click="handleGetMore">加载更多</el-button>
+            </div>
+          </template>
+        </el-select>
+      </div>
+      <el-button class="ml-10px" type="success" plain @click="handleRefresh"><Icon icon="ep:refresh" class="mr-5px" /> 刷新</el-button>
     </div>
-    <el-button class="ml-10px" type="success" plain @click="handleRefresh"><Icon icon="ep:refresh" class="mr-5px" /> 刷新</el-button>
-  </div>
-  <el-table v-loading="loading" :data="tableData" :stripe="true" class="m-t-20px" height="calc(100vh - 360px)">
-    <el-table-column label="职位名称" align="center" prop="name" fixed="left">
-      <template #default="{ row }">{{ formatName(row.name) }}</template>
-    </el-table-column>
-    <el-table-column label="发布人" align="center" prop="sendUser.name" />
-    <el-table-column label="招聘会" align="center" prop="jobFairName" />
-    <el-table-column label="职位类型" align="center" prop="positionName" />
-    <el-table-column label="地区" align="center" prop="areaName">
-      <template #default="scope">
-        {{ !scope.row.areaId ? '全国' : scope.row.areaName }}
-      </template>
-    </el-table-column>
-    <el-table-column label="薪资" align="center" prop="payFrom">
-      <template #default="scope">
-        <span v-if="scope.row.payFrom && scope.row.payTo">
-          {{ scope.row.payFrom }} - {{ scope.row.payTo }}/{{ payUnit.find(e => e.value === Number(scope.row.payUnit))?.label }}
-        </span>
-        <span v-else>面议</span>
-      </template>
-    </el-table-column>
-    <el-table-column label="招聘类型" align="center" prop="type">
-      <template #default="scope">
-        <dict-tag :type="DICT_TYPE.MENDUNER_JOB_TYPE" :value="scope.row.type" />
-      </template>
-    </el-table-column>
-    <el-table-column label="要求学历" align="center" prop="eduType">
-      <template #default="scope">
-        <dict-tag v-if="scope.row.eduType" :type="DICT_TYPE.MENDUNER_EDUCATION_TYPE" :value="scope.row.eduType" />
-        <el-tag v-else>学历不限</el-tag>
-      </template>
-    </el-table-column>
-    <el-table-column label="工作经验" align="center" prop="expType">
-      <template #default="scope">
-        <dict-tag v-if="scope.row.expType" :type="DICT_TYPE.MENDUNER_EXP_TYPE" :value="scope.row.expType" />
-        <el-tag v-else>经验不限</el-tag>
-      </template>
-    </el-table-column>
-    <el-table-column label="职位状态" align="center" prop="status">
+  </ContentWrap>
+
+  <ContentWrap>
+    <el-table v-loading="loading" :data="tableData" :stripe="true" height="calc(100vh - 360px)">
+      <el-table-column label="职位名称" align="center" prop="name" fixed="left">
+        <template #default="{ row }">{{ formatName(row.name) }}</template>
+      </el-table-column>
+      <el-table-column label="发布人" align="center" prop="sendUser.name" />
+      <el-table-column label="招聘会" align="center" prop="jobFairName" />
+      <el-table-column label="职位类型" align="center" prop="positionName" />
+      <el-table-column label="地区" align="center" prop="areaName">
         <template #default="scope">
-          <el-tag v-if="scope.row.status === '99'" type="warning">待支付</el-tag>
-          <dict-tag v-else :type="DICT_TYPE.MENDUNER_STATUS" :value="scope.row.status" />
+          {{ !scope.row.areaId ? '全国' : scope.row.areaName }}
         </template>
       </el-table-column>
-    <el-table-column label="刷新时间" align="center" prop="updateTime" width="180px">
-      <template #default="scope">
-        {{ timesTampChange(scope.row.updateTime, 'Y-M-D h:m') }}
-      </template>
-    </el-table-column>
-    <el-table-column label="操作" align="center" fixed="right" min-width="200">
-      <template #default="scope">
-        <el-button link type="primary" @click="openDetail(scope.row)">详情</el-button>
-        <el-button link type="primary" @click="handleActions(scope.row.id, 0)">刷新</el-button>
-        <el-button link type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
-        <el-button v-if="scope.row.status === '1'" link type="success" @click="handleActions(scope.row.id, 1)">开启</el-button>
-        <el-button v-if="scope.row.status === '0'" link type="danger" @click="handleActions(scope.row.id, 2)">关闭</el-button>
-      </template>
-    </el-table-column>
-  </el-table>
-
-  <Pagination
-    :total="total"
-    v-model:page="queryParams.pageNo"
-    v-model:limit="queryParams.pageSize"
-    @pagination="getList"
-  />
+      <el-table-column label="薪资" align="center" prop="payFrom">
+        <template #default="scope">
+          <span v-if="scope.row.payFrom && scope.row.payTo">
+            {{ scope.row.payFrom }} - {{ scope.row.payTo }}/{{ payUnit.find(e => e.value === Number(scope.row.payUnit))?.label }}
+          </span>
+          <span v-else>面议</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="招聘类型" align="center" prop="type">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.MENDUNER_JOB_TYPE" :value="scope.row.type" />
+        </template>
+      </el-table-column>
+      <el-table-column label="要求学历" align="center" prop="eduType">
+        <template #default="scope">
+          <dict-tag v-if="scope.row.eduType" :type="DICT_TYPE.MENDUNER_EDUCATION_TYPE" :value="scope.row.eduType" />
+          <el-tag v-else>学历不限</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="工作经验" align="center" prop="expType">
+        <template #default="scope">
+          <dict-tag v-if="scope.row.expType" :type="DICT_TYPE.MENDUNER_EXP_TYPE" :value="scope.row.expType" />
+          <el-tag v-else>经验不限</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="职位状态" align="center" prop="status">
+          <template #default="scope">
+            <el-tag v-if="scope.row.status === '99'" type="warning">待支付</el-tag>
+            <dict-tag v-else :type="DICT_TYPE.MENDUNER_STATUS" :value="scope.row.status" />
+          </template>
+        </el-table-column>
+      <el-table-column label="刷新时间" align="center" prop="updateTime" width="180px">
+        <template #default="scope">
+          {{ timesTampChange(scope.row.updateTime, 'Y-M-D h:m') }}
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" fixed="right" min-width="200">
+        <template #default="scope">
+          <el-button link type="primary" @click="openDetail(scope.row)">详情</el-button>
+          <el-button link type="primary" @click="handleActions(scope.row.id, 0)">刷新</el-button>
+          <el-button link type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
+          <el-button v-if="scope.row.status === '1'" link type="success" @click="handleActions(scope.row.id, 1)">开启</el-button>
+          <el-button v-if="scope.row.status === '0'" link type="danger" @click="handleActions(scope.row.id, 2)">关闭</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
 
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+  
   <Dialog title="职位详情" v-model="dialogVisible" class="!w-60%">
     <el-descriptions :column="2" border>
       <el-descriptions-item label="职位名称">{{ formatName(itemData.name) }}</el-descriptions-item>
+      <el-descriptions-item label="发布人">{{ itemData.sendUser.name }}</el-descriptions-item>
       <el-descriptions-item label="招聘会">{{ itemData.jobFairName }}</el-descriptions-item>
       <el-descriptions-item label="职位类型">{{ itemData.positionName }}</el-descriptions-item>
       <el-descriptions-item label="地区">{{ !itemData.areaId ? '全国' : itemData.areaName }}</el-descriptions-item>

+ 81 - 77
src/views/menduner/system/enterprise/message/details/components/resume.vue

@@ -1,91 +1,95 @@
 <template>
-  <div class="flex">
-    <div v-if="!fairId">
-			<el-select v-model="queryParams.jobId" placeholder="招聘职位" class="!w-240px ml-10px" clearable @change="handleRefresh">
+  <ContentWrap>
+    <div class="flex">
+      <div v-if="!fairId">
+        <el-select v-model="queryParams.jobId" placeholder="招聘职位" class="!w-240px ml-10px" clearable @change="handleRefresh">
+          <el-option
+            v-for="item in jobList"
+            :key="item.id"
+            :label="formatName(item.name)"
+            :value="item.id"
+          />
+        </el-select>
+        <el-select v-model="queryParams.jobFairId" placeholder="招聘会" class="!w-240px ml-10px" clearable @change="handleRefresh">
+          <el-option
+            v-for="item in jobFairList"
+            :key="item.id"
+            :label="item.title"
+            :value="item.id"
+          />
+          <template #footer v-if="jobFairList?.length < +jobFairTotal">
+            <div class="text-center">
+              <el-button type="primary" link @click="handleGetMore">加载更多</el-button>
+            </div>
+          </template>
+        </el-select>
+      </div>
+      <el-select v-else v-model="queryParams.enterpriseId" placeholder="招聘企业" class="!w-240px ml-10px" clearable @change="handleRefresh">
         <el-option
-          v-for="item in jobList"
-          :key="item.id"
-          :label="formatName(item.name)"
-          :value="item.id"
+          v-for="item in enterpriseList"
+          :key="item.enterpriseId"
+          :label="formatName(item.enterprise.anotherName || item.enterprise.name)"
+          :value="item.enterpriseId"
         />
-      </el-select>
-      <el-select v-model="queryParams.jobFairId" placeholder="招聘会" class="!w-240px ml-10px" clearable @change="handleRefresh">
-        <el-option
-          v-for="item in jobFairList"
-          :key="item.id"
-          :label="item.title"
-          :value="item.id"
-        />
-        <template #footer v-if="jobFairList?.length < +jobFairTotal">
+        <template #footer v-if="enterpriseList?.length < +enterpriseTotal">
           <div class="text-center">
-            <el-button type="primary" link @click="handleGetMore">加载更多</el-button>
+            <el-button type="primary" link @click="handleGetEnterpriseMore">加载更多</el-button>
           </div>
         </template>
       </el-select>
+      <el-button type="success" class="ml-10px" plain @click="handleRefresh"><Icon icon="ep:refresh" class="mr-5px" /> 刷新</el-button>
     </div>
-    <el-select v-else v-model="queryParams.enterpriseId" placeholder="招聘企业" class="!w-240px ml-10px" clearable @change="handleRefresh">
-      <el-option
-        v-for="item in enterpriseList"
-        :key="item.enterpriseId"
-        :label="formatName(item.enterprise.anotherName || item.enterprise.name)"
-        :value="item.enterpriseId"
+  </ContentWrap>
+
+  <ContentWrap>
+    <el-table v-loading="loading" :data="tableData" :stripe="true" height="calc(100vh - 360px)">
+      <el-table-column label="求职者" align="center" prop="person.name" />
+      <el-table-column label="投递职位" align="center" prop="job.name">
+        <template #default="{ row }">{{ formatName(row.job.name) }}</template>
+      </el-table-column>
+      <el-table-column v-if="fairId" label="发布企业" align="center" prop="enterprise.anotherName">
+        <template #default="{ row }">{{ formatName(row.enterprise.anotherName || row.enterprise.name) }}</template>
+      </el-table-column>
+      <el-table-column v-if="isRecruitmentOfficer" label="职位发布人" align="center" prop="contact.name" />
+      <el-table-column label="求职者联系电话" align="center" prop="person.phone" />
+      <el-table-column v-if="!fairId" label="招聘会" align="center" prop="jobFair.title" />
+      <el-table-column label="简历查看状态" align="center" prop="cvRel.status">
+        <template #default="scope">
+          <el-tag :type="scope.row.cvRel.status === '0' ? 'warning' : 'success'">
+            {{ scope.row.cvRel.status === '0' ? '未查看' : '已查看' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="投递时间"
+        align="center"
+        prop="cvRel.createTime"
+        :formatter="dateFormatter"
+        width="180px"
       />
-      <template #footer v-if="enterpriseList?.length < +enterpriseTotal">
-        <div class="text-center">
-          <el-button type="primary" link @click="handleGetEnterpriseMore">加载更多</el-button>
-        </div>
-      </template>
-    </el-select>
-    <el-button type="success" class="ml-10px" plain @click="handleRefresh"><Icon icon="ep:refresh" class="mr-5px" /> 刷新</el-button>
-  </div>
+      <el-table-column label="操作" align="center" fixed="right" min-width="80">
+        <template #default="scope">
+          <el-link
+            v-if="scope.row.cvRel?.url"
+            type="primary"
+            download
+            :href="scope.row.cvRel.url"
+            :underline="false"
+            target="_blank"
+          >
+            附件下载
+          </el-link>
+        </template>
+      </el-table-column>
+    </el-table>
 
-  <el-table v-loading="loading" :data="tableData" :stripe="true" class="m-t-20px" height="calc(100vh - 360px)">
-		<el-table-column label="求职者" align="center" prop="person.name" />
-    <el-table-column label="投递职位" align="center" prop="job.name">
-      <template #default="{ row }">{{ formatName(row.job.name) }}</template>
-    </el-table-column>
-		<el-table-column v-if="fairId" label="发布企业" align="center" prop="enterprise.anotherName">
-      <template #default="{ row }">{{ formatName(row.enterprise.anotherName || row.enterprise.name) }}</template>
-    </el-table-column>
-		<el-table-column v-if="isRecruitmentOfficer" label="职位发布人" align="center" prop="contact.name" />
-    <el-table-column label="求职者联系电话" align="center" prop="person.phone" />
-    <el-table-column v-if="!fairId" label="招聘会" align="center" prop="jobFair.title" />
-    <el-table-column label="简历查看状态" align="center" prop="cvRel.status">
-      <template #default="scope">
-				<el-tag :type="scope.row.cvRel.status === '0' ? 'warning' : 'success'">
-					{{ scope.row.cvRel.status === '0' ? '未查看' : '已查看' }}
-				</el-tag>
-      </template>
-    </el-table-column>
-		<el-table-column
-      label="投递时间"
-      align="center"
-      prop="cvRel.createTime"
-      :formatter="dateFormatter"
-      width="180px"
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
     />
-    <el-table-column label="操作" align="center" fixed="right" min-width="80">
-      <template #default="scope">
-        <el-link
-					v-if="scope.row.cvRel?.url"
-					type="primary"
-					download
-					:href="scope.row.cvRel.url"
-					:underline="false"
-					target="_blank"
-				>
-					附件下载
-				</el-link>
-      </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>

+ 36 - 34
src/views/menduner/system/enterprise/message/details/components/subordinateEnterprises.vue

@@ -1,39 +1,41 @@
 <template>
-  <el-table v-loading="loading" :data="tableData" :stripe="true" height="calc(100vh - 310px)">
-    <el-table-column label="企业全称" align="center" prop="name">
-      <template #default="scope">{{ formatName(scope.row.name) }}</template>
-    </el-table-column>
-    <el-table-column label="企业别称" align="center" prop="anotherName">
-      <template #default="scope">{{ formatName(scope.row.anotherName) }}</template>
-    </el-table-column>
-    <el-table-column label="联系人" align="center" prop="contact" />
-    <el-table-column label="联系电话" align="center" prop="phone" />
-    <el-table-column label="所在行业" align="center" prop="industryName" />
-    <el-table-column label="融资阶段" align="center" prop="financingStatus">
-      <template #default="scope">
-        <dict-tag :type="DICT_TYPE.MENDUNER_FINANCING_STATUS" :value="scope.row.financingStatus" />
-      </template>
-    </el-table-column>
-    <el-table-column label="人员规模" align="center" prop="scale">
-      <template #default="scope">
-        <dict-tag :type="DICT_TYPE.MENDUNER_SCALE" :value="scope.row.scale" />
-      </template>
-    </el-table-column>
-    <el-table-column
-      label="创建时间"
-      align="center"
-      prop="createTime"
-      :formatter="dateFormatter"
-      width="180px"
-    />
-  </el-table>
+  <ContentWrap>
+    <el-table v-loading="loading" :data="tableData" :stripe="true" height="calc(100vh - 310px)">
+      <el-table-column label="企业全称" align="center" prop="name">
+        <template #default="scope">{{ formatName(scope.row.name) }}</template>
+      </el-table-column>
+      <el-table-column label="企业别称" align="center" prop="anotherName">
+        <template #default="scope">{{ formatName(scope.row.anotherName) }}</template>
+      </el-table-column>
+      <el-table-column label="联系人" align="center" prop="contact" />
+      <el-table-column label="联系电话" align="center" prop="phone" />
+      <el-table-column label="所在行业" align="center" prop="industryName" />
+      <el-table-column label="融资阶段" align="center" prop="financingStatus">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.MENDUNER_FINANCING_STATUS" :value="scope.row.financingStatus" />
+        </template>
+      </el-table-column>
+      <el-table-column label="人员规模" align="center" prop="scale">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.MENDUNER_SCALE" :value="scope.row.scale" />
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        :formatter="dateFormatter"
+        width="180px"
+      />
+    </el-table>
 
-  <Pagination
-    :total="total"
-    v-model:page="queryParams.pageNo"
-    v-model:limit="queryParams.pageSize"
-    @pagination="getList"
-  />
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
 </template>
 
 <script setup>

+ 50 - 48
src/views/menduner/system/enterprise/message/details/components/user.vue

@@ -1,53 +1,55 @@
 <template>
-  <el-table v-loading="loading" :data="tableData" :stripe="true" height="calc(100vh - 310px)">
-    <el-table-column label="用户名" align="center" prop="name" />
-    <el-table-column label="联系电话" align="center" prop="phone" />
-    <el-table-column label="邮箱" align="center" prop="email" />
-    <!-- <el-table-column label="性别" align="center" prop="sex">
-      <template #default="scope">
-        <dict-tag v-if="scope.row.sex !== '0'" :type="DICT_TYPE.MENDUNER_SEX" :value="scope.row.sex" />
-        <span v-else></span>
-      </template>
-    </el-table-column> -->
-    <el-table-column label="用户类型" align="center" prop="userType">
-      <template #default="scope">
-        {{ scope.row.userType === '1' ? '管理员': '普通用户' }}
-      </template>
-    </el-table-column>
-    <el-table-column label="帐号状态" align="center" prop="status">
-      <template #default="scope">
-        <dict-tag :type="DICT_TYPE.MENDUNER_STATUS" :value="scope.row.status" />
-      </template>
-    </el-table-column>
-    <el-table-column label="最后登录IP" align="center" prop="loginIp" />
-    <el-table-column
-      label="最后登录时间"
-      align="center"
-      prop="loginDate"
-      :formatter="dateFormatter"
-      width="180px"
-    />
-    <el-table-column
-      label="创建时间"
-      align="center"
-      prop="createTime"
-      :formatter="dateFormatter"
-      width="180px"
-    />
-    <el-table-column label="操作" align="center">
-      <template #default="scope">
-        <el-button v-if="scope.row.status === '0'" link type="danger" @click="handleActions(scope.row.id, 'disabled')">禁用</el-button>
-        <el-button v-if="scope.row.status === '1'" link type="success" @click="handleActions(scope.row.id, 'enable')">启用</el-button>
-      </template>
-    </el-table-column>
-  </el-table>
+  <ContentWrap>
+    <el-table v-loading="loading" :data="tableData" :stripe="true" height="calc(100vh - 310px)">
+      <el-table-column label="用户名" align="center" prop="name" />
+      <el-table-column label="联系电话" align="center" prop="phone" />
+      <el-table-column label="邮箱" align="center" prop="email" />
+      <!-- <el-table-column label="性别" align="center" prop="sex">
+        <template #default="scope">
+          <dict-tag v-if="scope.row.sex !== '0'" :type="DICT_TYPE.MENDUNER_SEX" :value="scope.row.sex" />
+          <span v-else></span>
+        </template>
+      </el-table-column> -->
+      <el-table-column label="用户类型" align="center" prop="userType">
+        <template #default="scope">
+          {{ scope.row.userType === '1' ? '管理员': '普通用户' }}
+        </template>
+      </el-table-column>
+      <el-table-column label="帐号状态" align="center" prop="status">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.MENDUNER_STATUS" :value="scope.row.status" />
+        </template>
+      </el-table-column>
+      <el-table-column label="最后登录IP" align="center" prop="loginIp" />
+      <el-table-column
+        label="最后登录时间"
+        align="center"
+        prop="loginDate"
+        :formatter="dateFormatter"
+        width="180px"
+      />
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        :formatter="dateFormatter"
+        width="180px"
+      />
+      <el-table-column label="操作" align="center">
+        <template #default="scope">
+          <el-button v-if="scope.row.status === '0'" link type="danger" @click="handleActions(scope.row.id, 'disabled')">禁用</el-button>
+          <el-button v-if="scope.row.status === '1'" link type="success" @click="handleActions(scope.row.id, 'enable')">启用</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
 
-  <Pagination
-    :total="total"
-    v-model:page="queryParams.pageNo"
-    v-model:limit="queryParams.pageSize"
-    @pagination="getList"
-  />
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
 </template>
 
 <script setup>

+ 1 - 1
src/views/menduner/system/enterprise/message/details/index.vue

@@ -26,7 +26,7 @@
             <el-tab-pane label="企业详情">
               <Info :id="id"/>
             </el-tab-pane>
-            <el-tab-pane label="企业工商信息" v-hasPermi="['menduner:system:enterprise-business:query']">
+            <el-tab-pane label="工商信息" v-hasPermi="['menduner:system:enterprise-business:query']">
               <Business :id="id" />
             </el-tab-pane>
             <el-tab-pane label="下级企业">

+ 12 - 12
src/views/menduner/system/enterprise/userbind/details/components/account.vue

@@ -1,17 +1,17 @@
 <template>
-  <div>
+  <ContentWrap>
     <el-descriptions class="margin-top" :column="1" border>
-      <!-- <template #extra>
-        <el-button type="primary">Operation</el-button>
-      </template> -->
-    <el-descriptions-item label="用户类型">{{ info.userType === '1' ? '管理员' : '普通用户' }}</el-descriptions-item>
-    <el-descriptions-item label="创建时间">{{ formatDate(info.createTime) }}</el-descriptions-item>
-    <el-descriptions-item label="账户余额">{{ info.balance }}</el-descriptions-item>
-    <el-descriptions-item label="累计支出">{{ info.totalExpense }}</el-descriptions-item>
-    <el-descriptions-item label="累计充值">{{ info.totalRecharge }}</el-descriptions-item>
-    <el-descriptions-item label="冻结金额">{{ info.freezePrice }}</el-descriptions-item>
-  </el-descriptions>
-  </div>
+        <!-- <template #extra>
+          <el-button type="primary">Operation</el-button>
+        </template> -->
+      <el-descriptions-item label="用户类型">{{ info.userType === '1' ? '管理员' : '普通用户' }}</el-descriptions-item>
+      <el-descriptions-item label="创建时间">{{ formatDate(info.createTime) }}</el-descriptions-item>
+      <el-descriptions-item label="账户余额">{{ info.balance }}</el-descriptions-item>
+      <el-descriptions-item label="累计支出">{{ info.totalExpense }}</el-descriptions-item>
+      <el-descriptions-item label="累计充值">{{ info.totalRecharge }}</el-descriptions-item>
+      <el-descriptions-item label="冻结金额">{{ info.freezePrice }}</el-descriptions-item>
+    </el-descriptions>
+  </ContentWrap>
 </template>
 
 <script setup>

+ 35 - 33
src/views/menduner/system/enterprise/userbind/details/components/rechargeOrder.vue

@@ -1,38 +1,40 @@
 <template>
-  <el-table v-loading="loading" :data="tableData" :stripe="true" class="m-t-20px">
-    <el-table-column label="充值点数" align="center" prop="id">
-      <template #default="scope">
-        {{ payCalculation(scope.row.payPrice, true) }}
-      </template>
-    </el-table-column>
-    <el-table-column label="支付金额" align="center" prop="payPrice">
-      <template #default="scope">
-        {{ payCalculation(scope.row.payPrice) }}
-      </template>
-    </el-table-column>
-    <el-table-column label="支付渠道" align="center" prop="payChannelName" />
-    <el-table-column label="支付订单编号" align="center" prop="payOrderId" />
-    <el-table-column label="支付成功的外部订单号" align="center" prop="payOrderChannelOrderNo" />
-    <el-table-column label="是否已支付" align="center" prop="payStatus">
-      <template #default="scope">
-        {{ scope.row.payStatus ? "已支付" : "未支付"}}
-      </template>
-    </el-table-column>
-    <el-table-column
-      label="订单支付时间"
-      align="center"
-      prop="payTime"
-      :formatter="dateFormatter"
-      width="180px"
-    />
-  </el-table>
+  <ContentWrap>
+    <el-table v-loading="loading" :data="tableData" :stripe="true">
+      <el-table-column label="充值点数" align="center" prop="id">
+        <template #default="scope">
+          {{ payCalculation(scope.row.payPrice, true) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="支付金额" align="center" prop="payPrice">
+        <template #default="scope">
+          {{ payCalculation(scope.row.payPrice) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="支付渠道" align="center" prop="payChannelName" />
+      <el-table-column label="支付订单编号" align="center" prop="payOrderId" />
+      <el-table-column label="支付成功的外部订单号" align="center" prop="payOrderChannelOrderNo" />
+      <el-table-column label="是否已支付" align="center" prop="payStatus">
+        <template #default="scope">
+          {{ scope.row.payStatus ? "已支付" : "未支付"}}
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="订单支付时间"
+        align="center"
+        prop="payTime"
+        :formatter="dateFormatter"
+        width="180px"
+      />
+    </el-table>
 
-  <Pagination
-    :total="total"
-    v-model:page="queryParams.pageNo"
-    v-model:limit="queryParams.pageSize"
-    @pagination="getList"
-  />
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
 </template>
 
 <script setup>

+ 25 - 23
src/views/menduner/system/enterprise/userbind/details/components/tradingOrder.vue

@@ -1,28 +1,30 @@
 <template>
-  <el-table v-loading="loading" :data="tableData" :stripe="true" class="m-t-20px">
-    <el-table-column label="商品名称" align="center" prop="spuName" />
-    <el-table-column label="点数" align="center" prop="price" />
-    <el-table-column label="是否已支付" align="center" prop="payStatus">
-      <template #default="scope">
-        {{ scope.row.payStatus ? "已支付" : "未支付"}}
-      </template>
-    </el-table-column>
-    <el-table-column label="支付订单编号" align="center" prop="payOrderId" />
-    <el-table-column
-      label="订单支付时间"
-      align="center"
-      prop="payTime"
-      :formatter="dateFormatter"
-      width="180px"
-    />
-  </el-table>
+  <ContentWrap>
+    <el-table v-loading="loading" :data="tableData" :stripe="true">
+      <el-table-column label="商品名称" align="center" prop="spuName" />
+      <el-table-column label="点数" align="center" prop="price" />
+      <el-table-column label="是否已支付" align="center" prop="payStatus">
+        <template #default="scope">
+          {{ scope.row.payStatus ? "已支付" : "未支付"}}
+        </template>
+      </el-table-column>
+      <el-table-column label="支付订单编号" align="center" prop="payOrderId" />
+      <el-table-column
+        label="订单支付时间"
+        align="center"
+        prop="payTime"
+        :formatter="dateFormatter"
+        width="180px"
+      />
+    </el-table>
 
-  <Pagination
-    :total="total"
-    v-model:page="queryParams.pageNo"
-    v-model:limit="queryParams.pageSize"
-    @pagination="getList"
-  />
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
 </template>
 
 <script setup>

+ 23 - 23
src/views/menduner/system/enterprise/userbind/details/components/user.vue

@@ -1,28 +1,28 @@
 <template>
-  <div>
+  <ContentWrap>
     <el-descriptions class="margin-top" :column="2" border>
-      <!-- <template #extra>
-        <el-button type="primary">Operation</el-button>
-      </template> -->
-    <el-descriptions-item label="用户头像">
-      <el-image v-if="info.avatar" style="width: 100px; height: 100px" :src="info.avatar" fit="contain" hide-on-click-modal :preview-src-list="[info.avatar]"/>
-    </el-descriptions-item>
-    <el-descriptions-item label="用户名称">{{ info.name }}</el-descriptions-item>
-    <el-descriptions-item label="性别">
-      <dict-tag v-if="info.sex !== '0'" :type="DICT_TYPE.MENDUNER_SEX" :value="info.sex" />
-    </el-descriptions-item>
-    <el-descriptions-item label="联系电话">{{ info.phone }}</el-descriptions-item>
-    <el-descriptions-item label="邮箱">{{ info.email }}</el-descriptions-item>
-    <el-descriptions-item label="企业名称">{{ formatName(info.enterpriseName) }}</el-descriptions-item>
-    <el-descriptions-item label="岗位名称">{{ info.postName }}</el-descriptions-item>
-    <el-descriptions-item label="账号状态">
-      <dict-tag :type="DICT_TYPE.MENDUNER_STATUS" :value="info.status" />
-    </el-descriptions-item>
-    <el-descriptions-item label="用户类型">{{ info.userType === '1' ? '管理员' : '普通用户' }}</el-descriptions-item>
-    <el-descriptions-item label="最后登录IP">{{ info.loginIp }}</el-descriptions-item>
-    <el-descriptions-item label="最后登录时间">{{ formatDate(info.loginDate) }}</el-descriptions-item>
-  </el-descriptions>
-  </div>
+        <!-- <template #extra>
+          <el-button type="primary">Operation</el-button>
+        </template> -->
+      <el-descriptions-item label="用户头像">
+        <el-image v-if="info.avatar" style="width: 100px; height: 100px" :src="info.avatar" fit="contain" hide-on-click-modal :preview-src-list="[info.avatar]"/>
+      </el-descriptions-item>
+      <el-descriptions-item label="用户名称">{{ info.name }}</el-descriptions-item>
+      <el-descriptions-item label="性别">
+        <dict-tag v-if="info.sex !== '0'" :type="DICT_TYPE.MENDUNER_SEX" :value="info.sex" />
+      </el-descriptions-item>
+      <el-descriptions-item label="联系电话">{{ info.phone }}</el-descriptions-item>
+      <el-descriptions-item label="邮箱">{{ info.email }}</el-descriptions-item>
+      <el-descriptions-item label="企业名称">{{ formatName(info.enterpriseName) }}</el-descriptions-item>
+      <el-descriptions-item label="岗位名称">{{ info.postName }}</el-descriptions-item>
+      <el-descriptions-item label="账号状态">
+        <dict-tag :type="DICT_TYPE.MENDUNER_STATUS" :value="info.status" />
+      </el-descriptions-item>
+      <el-descriptions-item label="用户类型">{{ info.userType === '1' ? '管理员' : '普通用户' }}</el-descriptions-item>
+      <el-descriptions-item label="最后登录IP">{{ info.loginIp }}</el-descriptions-item>
+      <el-descriptions-item label="最后登录时间">{{ formatDate(info.loginDate) }}</el-descriptions-item>
+    </el-descriptions>
+  </ContentWrap>
 </template>
 
 <script setup>

+ 32 - 2
src/views/menduner/system/jobFair/manage/details/components/InfoSettings.vue

@@ -53,10 +53,23 @@
         </div>
       </el-form-item>
       <el-form-item label="开始时间" prop="startTime">
-        <el-date-picker v-model="formData.startTime" type="date" value-format="x" placeholder="选择开始时间" />
+        <el-date-picker
+          v-model="formData.startTime"
+          type="date"
+          value-format="x"
+          :disabledDate="disabledStartDates"
+          placeholder="选择开始时间"
+        />
       </el-form-item>
       <el-form-item label="结束时间" prop="endTime">
-        <el-date-picker v-model="formData.endTime" type="date" value-format="x" placeholder="选择结束时间" />
+        <el-date-picker 
+          v-model="formData.endTime"
+          type="date"
+          value-format="x"
+          :disabledDate="disabledEndDates"
+          placeholder="选择结束时间"
+          @change="handleEndDate"
+        />
       </el-form-item>
       <el-form-item label="状态" prop="status">
         <el-radio-group v-model="formData.status">
@@ -123,9 +136,26 @@ const formRules = reactive({
   title: [{ required: true, message: '标题不能为空', trigger: 'blur' }],
   startTime: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
   endTime: [{ required: true, message: '结束时间不能为空', trigger: 'blur' }],
+  status: [{ required: true, message: '状态不能为空', trigger: 'change' }],
 })
 const formRef = ref() // 表单 Ref
 
+// 开始时间禁选范围
+const disabledStartDates = (date: any) => {
+  return formData.value.endTime ? date.getTime() > formData.value.endTime : false
+}
+// 结束时间禁选范围
+const disabledEndDates = (date: any) => {
+  return formData.value.startTime ? date.getTime() < formData.value.startTime : false
+}
+
+const handleEndDate = () => {
+  if (!formData.value.startTime) {
+    message.warning('请先选择开始时间')
+    formData.value.endTime = undefined
+  }
+}
+
 onMounted(() => {
   if (props.info && Object.keys(props.info).length > 0) {
 		formLoading.value = true

+ 17 - 581
src/views/menduner/system/talentMap/maintenance/gather/index.vue

@@ -1,591 +1,27 @@
 <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="null">取 消</el-button>
-      </template>
-    </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="80%">
-      <div class="analysisInfoBox">
-        <div class="analysisFile">
-          <!-- 门墩儿人才库 -->
-          <template v-if="radioValue === 'menduner'">
-            <Search @detail="handleDetail" :detailButTxt="detailButTxt" :searchName="formData?.name_zh || formData?.name_en || ''" />
-          </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"/>
-          </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" />
+  <div>
+    <el-tabs v-model="activeName" type="border-card">
+      <el-tab-pane label="简历解析" name="resume">
+        <resumePage/>
+      </el-tab-pane>
+      <el-tab-pane label="名片解析" name="card">
+        <cardPage/>
+      </el-tab-pane>
+      <el-tab-pane label="网页解析" name="webpage">
+        <webPageParsing/>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
 </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'
-
-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 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
-  }
-}
-
-// 解析中
-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 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 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') {
-    dialog_analysisInfo.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()
-})
+import resumePage from './resume/index.vue'
+import cardPage from './businessCard/index.vue'
+import webPageParsing from './webPageParsing/index.vue'
 
+const activeName = ref('resume')
 </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>

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

@@ -0,0 +1,591 @@
+<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="null">取 消</el-button>
+      </template>
+    </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="80%">
+      <div class="analysisInfoBox">
+        <div class="analysisFile">
+          <!-- 门墩儿人才库 -->
+          <template v-if="radioValue === 'menduner'">
+            <Search @detail="handleDetail" :detailButTxt="detailButTxt" :searchName="formData?.name_zh || formData?.name_en || ''" />
+          </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"/>
+          </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'
+
+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 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
+  }
+}
+
+// 解析中
+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 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 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') {
+    dialog_analysisInfo.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>

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

@@ -37,6 +37,7 @@
           <div class="overflow-y-auto !h-360px" v-if="content.data">
             <pre>{{ content.data }}</pre>
           </div>
+					<el-button type="primary" plain class="mt-10px" @click="handleConvert(content)">转换为markdown格式</el-button>
 				</el-card>
 			</el-col>
 		</el-row>
@@ -54,12 +55,26 @@
 			<el-button type="primary" class="!w-100px" @click="drawer = false">关 闭</el-button>
 		</div>
 	</el-drawer>
+
+	<el-drawer
+		v-model="showMarkDown"
+		class="!w-50vw"
+		:with-header="false"
+		:modal="true"
+	>
+		<pre>{{ markdownContent }}</pre>
+		<el-divider class="!ma-0" />
+		<div class="position-sticky left-20px !h-50px lh-50px">
+			<el-button type="primary" class="!w-100px" @click="showMarkDown = false; markdownContent = ''">关 闭</el-button>
+		</div>
+	</el-drawer>
 </template>
 
 <script setup>
 /** 人才采集 网页解析 */
 defineOptions({ name: 'WebPageParsing' })
 import axios from 'axios'
+import TurndownService from 'turndown'
 
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化
@@ -72,6 +87,58 @@ 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 showMarkDown = ref(false)
+const markdownContent = ref(null)
+const handleConvert = (res) => {
+	if (!res.data) return
+	const result = wechatHtmlToMarkdown(res.data)
+	// console.log(result, 'markdown格式')
+	if (!result) return message.warning('转换失败')
+	markdownContent.value = result
+	showMarkDown.value = true
+}
+
 // 查看原网页
 const showPage = (res) => {
   if (res.data) {