Procházet zdrojové kódy

个人简历-基础信息调整

Xiao_123 před 9 měsíci
rodič
revize
5a9d6c9115

+ 0 - 1
components.d.ts

@@ -27,7 +27,6 @@ declare module 'vue' {
     DatePicker: typeof import('./src/components/DatePicker/index.vue')['default']
     Details: typeof import('./src/components/Enterprise/details.vue')['default']
     Echarts: typeof import('./src/components/Echarts/index.vue')['default']
-    ElCascader: typeof import('element-plus/es')['ElCascader']
     Empty: typeof import('./src/components/Empty/index.vue')['default']
     File: typeof import('./src/components/Upload/file.vue')['default']
     HeadSearch: typeof import('./src/components/headSearch/index.vue')['default']

+ 50 - 0
package-lock.json

@@ -13,6 +13,7 @@
         "axios": "^1.6.8",
         "echarts": "^5.4.3",
         "element-plus": "^2.8.0",
+        "html2canvas": "^1.4.1",
         "js-base64": "^3.7.7",
         "js-cookie": "^3.0.5",
         "lodash": "^4.17.21",
@@ -1418,6 +1419,15 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/base64-arraybuffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+      "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
     "node_modules/base64-js": {
       "version": "1.5.1",
       "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
@@ -1845,6 +1855,15 @@
       "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
       "license": "MIT"
     },
+    "node_modules/css-line-break": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz",
+      "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+      "license": "MIT",
+      "dependencies": {
+        "utrie": "^1.0.2"
+      }
+    },
     "node_modules/css-select": {
       "version": "4.3.0",
       "resolved": "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz",
@@ -3452,6 +3471,19 @@
         "node": ">=18"
       }
     },
+    "node_modules/html2canvas": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
+      "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+      "license": "MIT",
+      "dependencies": {
+        "css-line-break": "^2.1.0",
+        "text-segmentation": "^1.0.3"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
     "node_modules/htmlparser2": {
       "version": "3.10.1",
       "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-3.10.1.tgz",
@@ -6463,6 +6495,15 @@
         "url": "https://opencollective.com/unts"
       }
     },
+    "node_modules/text-segmentation": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz",
+      "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+      "license": "MIT",
+      "dependencies": {
+        "utrie": "^1.0.2"
+      }
+    },
     "node_modules/text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz",
@@ -7160,6 +7201,15 @@
       "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
       "dev": true
     },
+    "node_modules/utrie": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz",
+      "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+      "license": "MIT",
+      "dependencies": {
+        "base64-arraybuffer": "^1.0.2"
+      }
+    },
     "node_modules/vary": {
       "version": "1.1.2",
       "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz",

+ 1 - 0
package.json

@@ -14,6 +14,7 @@
     "axios": "^1.6.8",
     "echarts": "^5.4.3",
     "element-plus": "^2.8.0",
+    "html2canvas": "^1.4.1",
     "js-base64": "^3.7.7",
     "js-cookie": "^3.0.5",
     "lodash": "^4.17.21",

+ 46 - 0
src/views/recruit/personal/position/components/details.vue

@@ -141,6 +141,7 @@
             variant="text"
             @click="shareDialog = false"
           >{{ $t('common.close') }}</v-btn>
+          <!-- <v-btn class="radius button-item float-right ma-2" variant="text" color="indigo" :ripple="false" @click="handleShareClick">下载图片</v-btn> -->
         </div>
       </template>
     </Dialog>
@@ -167,13 +168,58 @@ import selectResumeDialog from './jobDetails/selectResumeDialog'
 import { getToken } from '@/utils/auth'
 import { prologue, defaultText } from '@/hooks/web/useIM'
 import { getUserAvatar } from '@/utils/avatar'
+// import html2canvas from 'html2canvas'
 
 const { t } = useI18n()
 const router = useRouter()
 const { id } = router.currentRoute.value.params
+// provide('jobId', id)
 const delivery = ref(false) // 是否已投递简历
 const loading = ref(false)
 
+// const DPR = () => {
+//   // 获取设备dpi
+//   if (window.devicePixelRatio && window.devicePixelRatio > 1) {
+//     return window.devicePixelRatio * 2
+//   }
+//   // 直接返回高像素比
+//   return 8
+// }
+
+// const downloadBase64 = (content, fileName) => {
+//   const base64ToBlob = function (code) {
+//     let parts = code.split(';base64,')
+//     let contentType = parts[0].split(':')[1]
+//     let raw = window.atob(parts[1])
+//     let rawLength = raw.length
+//     let uInt8Array = new Uint8Array(rawLength)
+//     for (let i = 0; i < rawLength; ++i) {
+//       uInt8Array[i] = raw.charCodeAt(i)
+//     }
+
+//     return new Blob([uInt8Array], {
+//       type: contentType
+//     })
+//   }
+//   let aLink = document.createElement('a')
+//   let blob = base64ToBlob(content)
+//   aLink.download = fileName + '.png'
+//   aLink.href = URL.createObjectURL(blob)
+//   aLink.click()
+//   loading.value = false
+// }
+
+// const handleShareClick = () => {
+//   const dpi = DPR()
+//   html2canvas(document.getElementById('share'), { useCORS: true, scale: dpi }).then(function(canvas) {
+//     shareDialog.value = false
+//     loading.value = true
+//     const base64 = canvas.toDataURL().replace(/^data:image\/(png|jpg);base64,/, '')
+//     const base64img = `data:image/png;base64,${base64}`
+//     downloadBase64(base64img, 'test4')
+//   })
+// }
+
 // 相似职位
 const similarList = ref([])
 const getSimilarPositionList = async () => {

+ 140 - 0
src/views/recruit/personal/position/components/poster.vue

@@ -0,0 +1,140 @@
+<!-- 分享职位 -->
+<template>
+  <div style="background-color: #f0f0f0;" :style="{'padding': '10px'}">
+    <v-card
+      class="py-3 px-5"
+      style="min-height: calc(100vh - 20px); box-sizing: border-box; margin: 0 auto;"
+      :style="{'width': '750px'}"
+    >
+      <div v-if="!Object.keys(info).length">加载失败</div>
+      <div v-else>
+        <div class="d-flex justify-space-between">
+          <h2 class="JobName ellipsis">{{ info.name }}</h2>
+          <span class="salary">{{ info.payFrom }}-{{ info.payTo }}/{{ positionInfo.payName }}</span>
+        </div>
+        <div class="d-flex justify-space-between mt-4">
+          <div class="banner-tags">
+            <div v-for="k in desc" :key="k.mdi" class="mr-3">
+              <v-icon color="var(--color-666)" size="20">{{ k.mdi }}</v-icon>
+              <span class="f-w-600 ml-1">{{ positionInfo[k.value] }}</span>
+            </div>
+          </div>
+          <div class="mt-4" v-if="info?.tagList">
+            <v-chip size="small" class="mr-1 mb-1" color="primary" label v-for="(k, i) in info.tagList" :key="i">{{ k }}</v-chip>
+          </div>
+        </div>
+        <div class="text-end d-flex align-center justify-space-between mt-3">
+          <svg-icon v-if="info.hire" name="pin" size="50"></svg-icon>
+        </div>
+        <div v-if="info.hire" class="mt-3">
+          <v-chip v-if="info.hirePrice" label color="primary">赏金:{{ commissionCalculation(info.hirePrice, 1) }}元</v-chip>
+          <v-chip v-if="info.hirePoint" label color="primary">积分:{{ commissionCalculation(info.hirePoint, 1) }}点</v-chip>
+        </div>
+        <v-divider class="mt-3"></v-divider>
+        <div class="mt-3 mb-1 f-w-600">{{ $t('position.jobResponsibilities') }}</div>
+        <div v-if="info.content" class="requirement" v-html="info.content?.replace(/\n/g, '</br>')"></div>
+        <div v-else>暂无</div>
+        <div class="mt-3 mb-1 f-w-600">{{ $t('position.jobRequirements') }}</div>
+        <div v-if="info.requirement" class="requirement" v-html="info.requirement?.replace(/\n/g, '</br>')"></div>
+        <div v-else>暂无</div>
+        <v-divider class="my-3"></v-divider>
+        <div class="contact">
+          <div class="float-left d-flex align-center">
+            <v-img :src="info.contact?.avatar || 'https://minio.citupro.com/dev/menduner/7.png'" :width="45" style="height: 45px;"></v-img>
+            <div class="ml-2">
+              <div class="contact-name">{{ info.contact?.name }}</div>
+              <div class="contact-info">{{ info.enterprise?.name }} · {{ info.contact?.postNameCn }}</div>
+            </div>
+          </div>
+        </div>
+        <v-divider class="my-3"></v-divider>
+        <div>
+          <h4>{{ $t('position.address') }}</h4>
+          <div class="mt-1">
+            <v-icon size="25" color="primary">mdi-map-marker</v-icon>
+            <span style="color: var(--color-666);font-size: 15px;">{{ info.address }}</span>
+          </div>
+        </div>
+      </div>
+    </v-card>
+
+  </div>
+</template>
+
+<script setup>
+import { commissionCalculation } from '@/utils/position'
+defineOptions({name: 'recruit-personal-shareJob-index'})
+import { ref, inject } from 'vue';
+import { getPositionDetails } from '@/api/position'
+import { dealDictObjData } from '@/utils/position'
+
+// 职位详情
+const jobId = inject('jobId')
+const info = ref({})
+const positionInfo = ref({})
+const getPositionDetail = async () => {
+  const data = await getPositionDetails({ id: jobId })
+  info.value = data
+  positionInfo.value = { ...dealDictObjData({}, info.value), ...info.value }
+}
+if (jobId) getPositionDetail()
+
+const desc = [
+  { mdi: 'mdi-map-marker-outline', value: 'areaName' },
+  { mdi: 'mdi-school-outline', value: 'eduName' },
+  { mdi: 'mdi-clock-time-ten-outline', value: 'expName' }
+]
+</script>
+
+<style lang="scss" scoped>
+.f-w-600 { font-weight: 600; }
+.radius { border-radius: 8px; }
+.salary {
+  color: var(--v-error-base);
+  line-height: 41px;
+  font-weight: 600;
+  height: auto;
+  display: inline-block;
+  vertical-align: sub;
+}
+.JobName {
+  color: #37576c;
+  font-size: 28px;
+  margin-right: 30px;
+  margin-top: 1px;
+  // max-width: 45%;
+  vertical-align: middle;
+  flex: 1;
+}
+.banner-tags { display: flex; flex-wrap: wrap; }
+.requirement {
+  white-space: pre-wrap;
+  word-break: break-all;
+  line-height: 28px;
+  color: var(--color-333);
+  font-size: 15px;
+  text-align: justify;
+  letter-spacing: 0;
+}
+.contact {
+  height: 60px;
+  line-height: 60px;
+  padding: 0 10px;
+}
+.contact-name {
+  font-size: 20px;
+  font-weight: 500;
+  color: var(--color-222);
+  line-height: 28px;
+}
+.contact-info {
+  font-size: 15px;
+  color: var(--color-666);
+  line-height: 21px;
+  margin-top: 8px;
+}
+.button-item {
+  min-width: 110px;
+  height: 36px
+}
+</style>

+ 20 - 46
src/views/recruit/personal/remuse/components/basicInfo.vue

@@ -4,18 +4,15 @@
       <div class="resume-title">{{ $t('resume.basicInfo') }}</div>
       <v-btn v-if="!isEdit" variant="text" color="primary" prepend-icon="mdi-square-edit-outline" @click="isEdit = true">{{ $t('common.edit') }}</v-btn>
     </div>
-    <div class="d-flex align-center">
+    <div class="d-flex align-start mt-5">
       <!-- 头像 -->
       <div class="avatarsBox" @mouseover="showIcon = true" @mouseleave="showIcon = false">
         <v-badge 
           bordered 
-          offset-x="-25" 
-          offset-y="33" 
           :color="baseInfo?.sex ? (baseInfo?.sex === '1' ? '#1867c0' : 'error') : 'error'" 
           :icon="baseInfo?.sex ? (baseInfo?.sex === '1' ? 'mdi-gender-male' : 'mdi-gender-female') : 'mdi-gender-female'">
-          <v-avatar size=80 :image="getUserAvatar(baseInfo?.avatar, baseInfo?.sex)">
-          </v-avatar>
-          <div v-show="showIcon" @click="openFileInput" v-bind="$attrs" class="mdi mdi-camera-outline">
+          <v-img :src="getUserAvatar(baseInfo?.avatar, baseInfo?.sex)" width="130" height="130" style="border-radius: 6px;"></v-img>
+          <div v-show="showIcon" @click="openFileInput" class="mdi mdi-camera-outline camera">
             <input
               type="file"
               ref="fileInput"
@@ -27,9 +24,9 @@
         </v-badge>
       </div>
       <!-- 基础信息 -->
-      <div style="flex: 1;" class="mr-8 mt-5">
+      <div style="flex: 1;" class="mr-8">
         <!-- 编辑 -->
-        <div v-if="isEdit">
+        <div v-if="isEdit" class="ml-5">
           <CtForm ref="CtFormRef" :items="items" style="width: 100%;">
             <template v-slot:phone>
               <v-btn variant="text" class="ml-2" color="primary">{{ $t('common.change') }}</v-btn>
@@ -42,9 +39,9 @@
         </div>
         <!-- 展示 -->
         <div v-else>
-          <span style="font-size: 20px; font-weight: 600;color: var(--color-666);">{{ baseInfo?.name || userInfo.phone }}</span>
+          <span class="ml-50" style="font-size: 20px; font-weight: 600;color: var(--color-666);">{{ baseInfo?.name || userInfo.phone }}</span>
           <div class="mt-3 d-flex">
-            <div class="listBox" :style="{ height: isExpand ? 'auto' : '68px' }">
+            <div class="listBox ml-50">
               <div>
                 <span class="mdi mdi-map-marker-outline"></span>
                 <span>{{ baseInfo?.areaName || $t('common.currentlyUnavailable') }}</span>
@@ -65,10 +62,6 @@
                 <span class="mdi mdi-school-outline"></span>
                 <span>{{ baseInfo?.eduTypeText || $t('common.currentlyUnavailable') }}</span>
               </div>
-              <!-- <div>
-                <span class="mdi mdi-briefcase-outline"></span>
-                <span>{{ baseInfo?.jobTypeText || $t('common.currentlyUnavailable') }}</span>
-              </div> -->
               <div>
                 <span class="mdi mdi-tag-outline"></span>
                 <span>{{ baseInfo?.jobStatusText || $t('common.currentlyUnavailable') }}</span>
@@ -86,12 +79,8 @@
                 <span>{{ baseInfo?.firstWorkTimeText || $t('common.currentlyUnavailable') }}</span>
               </div>
             </div>
-            <div class="ml-3">
-              <v-btn variant="text" color="primary" v-if="isExpand" @click="isExpand = false">{{ $t('resume.retract') }}</v-btn>
-              <v-btn variant="text" color="primary" v-else @click="isExpand = true">{{ $t('resume.expand') }}</v-btn>
-            </div>
           </div>
-          <div class="mt-4">
+          <div class="mt-4 ml-50">
             <span style="font-size: 15px;">个人画像:</span>
             <v-chip size="small" label v-for="(k, i) in welfareList.slice(0, 8)" :key="i" class="mr-2" color="primary">{{ k }}</v-chip>
           </div>
@@ -124,7 +113,6 @@ const userStore = useUserStore()
 const CtFormRef = ref()
 const isEdit = ref(false)
 const showIcon = ref(false)
-const isExpand = ref(false)
 const overlay = ref(false) // 加载中
 const welfareList = ref(['响应', '改变', '诚信', '进取精神', '信任', '卓越'])
 let baseInfo = ref({})
@@ -263,13 +251,13 @@ const items = ref({
       key: 'expType',
       value: null,
       default: null,
-      label: '工作经验 *',
+      label: '工作年限 *',
       col: 6,
       outlined: true,
       itemText: 'label',
       itemValue: 'value',
       dictTypeName: 'menduner_exp_type',
-      rules: [v => !!v || '请选择工作经验'],
+      rules: [v => !!v || '请选择工作年限'],
       items: []
     },
     {
@@ -462,27 +450,22 @@ getDict('areaTreeData', null, 'areaTreeData').then(({ data }) => {
   top: -22px;
   left: 0;
 }
+.ml-50 {
+  margin-left: 50px;
+}
 .avatarsBox {
-  height: 80px;
-  width: 80px;
+  height: 150px;
+  width: 120px;
   position: relative;
   cursor: pointer;
-  margin: 32px;
-  margin-right: 40px;
-  .img {
-    width: 100%;
-    height: 100%;
-  }
-  .mdi {
-    font-size: 42px;
+  margin: 0 32px;
+  .camera {
     color: #fff;
-  }
-  div {
+    font-size: 42px;
     position: absolute;
     top: 50%;
     left: 50%;
     transform: translate(-50%, -50%);
-    border-radius: 50%;
   }
 }
 
@@ -490,11 +473,11 @@ getDict('areaTreeData', null, 'areaTreeData').then(({ data }) => {
   display: flex;
   flex-wrap: wrap; /* 允许换行 */
   width: 100%; /* 设置容器宽度 */
-  // height: 68px;
   overflow: hidden;
   color: var(--color-666);
   div {
-    margin-right: 50px;
+    width: 50%;
+    margin-bottom: 10px;
     span {
       height: 32px;
       line-height: 32px;
@@ -502,16 +485,7 @@ getDict('areaTreeData', null, 'areaTreeData').then(({ data }) => {
     .mdi {
       font-size: 22px;
       margin-right: 8px;
-      // margin-top: 2px;
     }
   }
-  // border: 1px solid red; /* 可视化边界 */
-  // .jobItems {
-  //   font-size: 14px;
-  //   margin-left: 12px;
-  //   cursor: pointer;
-  //   color: var(--color-666);
-  //   font-family: 微软雅黑; 
-  // }
 }
 </style>