Jelajahi Sumber

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

lifanagju_citu 4 bulan lalu
induk
melakukan
5eb5c9c9e7
22 mengubah file dengan 745 tambahan dan 106 penghapusan
  1. 5 0
      components.d.ts
  2. 19 10
      src/components/Enterprise/hotPromoted.vue
  3. 524 0
      src/components/PreviewImg/previewImage.vue
  4. 104 0
      src/directives/previewImageDirective.js
  5. 2 0
      src/main.js
  6. 20 1
      src/utils/index.js
  7. 1 1
      src/views/recruit/entRegister/register.vue
  8. 13 1
      src/views/recruit/enterprise/entInfoSetting/index.vue
  9. 1 1
      src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/basicInfo.vue
  10. 2 5
      src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/businessInformation.vue
  11. 3 12
      src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/enterpriseAlbum.vue
  12. 1 1
      src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/enterpriseLabel.vue
  13. 1 1
      src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/enterpriseLogo.vue
  14. 1 1
      src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/welfareLabel.vue
  15. 7 1
      src/views/recruit/personal/PersonalCenter/jobFeedback/components/companyCollection.vue
  16. 1 1
      src/views/recruit/personal/PersonalCenter/jobFeedback/components/interested.vue
  17. 7 1
      src/views/recruit/personal/PersonalCenter/jobFeedback/components/positionCollection.vue
  18. 4 4
      src/views/recruit/personal/companyDetail/components/introduction.vue
  19. 1 13
      src/views/recruit/personal/home/components/hotPromotedPositions.vue
  20. 1 1
      src/views/recruit/personal/home/components/popularEnterprises.vue
  21. 23 49
      src/views/recruit/personal/position/components/details.vue
  22. 4 2
      src/views/recruit/personal/position/components/poster.vue

+ 5 - 0
components.d.ts

@@ -32,6 +32,7 @@ declare module 'vue' {
     Echarts: typeof import('./src/components/Echarts/index.vue')['default']
     ElCascader: typeof import('element-plus/es')['ElCascader']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
+    ElImage: typeof import('element-plus/es')['ElImage']
     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']
@@ -53,6 +54,7 @@ declare module 'vue' {
     NestedListGroup: typeof import('./src/components/FormUI/nestedListGroup/index.vue')['default']
     Pay: typeof import('./src/components/pay/index.vue')['default']
     PersonalRecharge: typeof import('./src/components/personalRecharge/index.vue')['default']
+    PreviewImage: typeof import('./src/components/PreviewImg/previewImage.vue')['default']
     PreviewImg: typeof import('./src/components/PreviewImg/index.vue')['default']
     ProgressBar: typeof import('./src/components/ProgressBar/index.vue')['default']
     QrCode: typeof import('./src/components/QrCode/index.vue')['default']
@@ -74,4 +76,7 @@ declare module 'vue' {
     VerifySlide: typeof import('./src/components/Verifition/Verify/VerifySlide.vue')['default']
     WangEditor: typeof import('./src/components/FormUI/wangEditor/index.vue')['default']
   }
+  export interface ComponentCustomProperties {
+    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
+  }
 }

+ 19 - 10
src/components/Enterprise/hotPromoted.vue

@@ -3,7 +3,7 @@
     <div class="sub-li" v-for="(item, index) in list" :key="index">
       <div v-if="item">
         <!-- 公司信息 -->
-        <div class="company-info-top align-center" @click.stop="jumpToEnterpriseDetail(item.enterprise.id, false)">
+        <div class="company-info-top align-center" :class="{'elevation-5': item.active}" @click.stop="jumpToEnterpriseDetail(item.enterprise.id, false)" @mouseenter="item.active = true" @mouseleave="item.active = false">
           <div class="float-left">
             <v-img :src="item?.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" alt="" width="77" height="77" style="border-radius: 4px;"/>
           </div>
@@ -25,7 +25,7 @@
         </div>
         <!-- 职位列表 -->
         <!-- :class="{'company-job-item-hover': k.active}" -->
-        <ul class="company-job-list">
+        <ul class="company-job-list pt-3">
           <li v-for="(k, i) in item.jobList" :key="i" @mouseenter="k.active = true" @mouseleave="k.active = false" @click="handleClickPosition(k)">
             <v-card :elevation="k.active ? 5 : 0" class="company-job-item cursor-pointer mb-3">
               <div class="mb-2 d-flex">
@@ -47,8 +47,8 @@
             </v-card>
           </li>
         </ul>
-        <div class="moreBtn d-flex align-center justify-center" @click.stop="handleMoreEnterprise(item)" @mouseenter="item.active = true" @mouseleave="item.active = false">
-          <span :style="{'border-bottom': item.active ? '1px solid #fff' : 'none'}">{{ $t('position.moreBtn') }}</span>
+        <div class="moreBtn d-flex align-center justify-center" @click.stop="handleMoreEnterprise(item)">
+          <span>{{ $t('position.moreBtn') }}</span>
           <v-icon>mdi-menu-right</v-icon>
         </div>
       </div>
@@ -122,7 +122,7 @@ const handleMoreEnterprise = (item) => {
   min-width: calc((100% - 24px) / 3);
   max-width: calc((100% - 24px) / 3);
   margin: 0 12px 12px 0;
-  height: 360px + 12px;
+  height: 370px;
   border-radius: 12px;
   padding: 0;
   overflow: hidden;
@@ -146,13 +146,14 @@ const handleMoreEnterprise = (item) => {
 }
 .company-info-top {
   display: flex;
-  height: 110px;
-  padding: 16px 20px;
+  height: 90px;
+  margin: 12px 12px 0 12px;
+  padding: 0 12px;
   overflow: hidden;
   border-bottom: 1px solid #EBEBEB;
-  &:hover {
-    background-color: #f2f4f7;
-  }
+  // &:hover {
+  //   background-color: #f2f4f7;
+  // }
 }
 .welfareTag {
   color: #CEC149;
@@ -227,5 +228,13 @@ ul li {
   cursor: pointer;
   font-size: 14px;
   background: linear-gradient(to right, #12ebb0, #427daa);
+  &:hover {
+    // color: #cec149;
+    font-size: 16px;
+    span {
+      font-weight: 700;
+      text-decoration: underline;
+    }
+  }
 }
 </style>

+ 524 - 0
src/components/PreviewImg/previewImage.vue

@@ -0,0 +1,524 @@
+<template>
+  <transition name="viewer-fade">
+    <div
+      ref="wrapper"
+      :tabindex="-1"
+      class="el-image-viewer__wrapper"
+      :style="{'z-index': zIndex}"
+    >
+      <div class="el-image-viewer__mask"  @click.self="hideOnClickModal && hide()"></div>
+      <!-- CLOSE -->
+      <span class="el-image-viewer__btn el-image-viewer__close" @click="hide">
+        <i class="el-icon">
+          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
+            <path
+              fill="currentColor"
+              d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"
+            ></path>
+          </svg>
+        </i>
+      </span>
+      <!-- ARROW -->
+      <template v-if="!isSingle">
+        <span
+          class="el-image-viewer__btn el-image-viewer__prev"
+          :class="{ 'is-disabled': !infinite && isFirst }"
+          @click="prev"
+        >
+          <i class="el-icon">
+            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
+              <path
+                fill="currentColor"
+                d="M609.408 149.376 277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0 30.592 30.592 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.592 30.592 0 0 0 0-42.688 29.12 29.12 0 0 0-41.728 0z"
+              ></path>
+            </svg>
+          </i>
+        </span>
+        <span
+          class="el-image-viewer__btn el-image-viewer__next"
+          :class="{ 'is-disabled': !infinite && isLast }"
+          @click="next"
+        >
+          <i class="el-icon">
+            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
+              <path
+                fill="currentColor"
+                d="M340.864 149.312a30.592 30.592 0 0 0 0 42.752L652.736 512 340.864 831.872a30.592 30.592 0 0 0 0 42.752 29.12 29.12 0 0 0 41.728 0L714.24 534.336a32 32 0 0 0 0-44.672L382.592 149.376a29.12 29.12 0 0 0-41.728 0z"
+              ></path>
+            </svg>
+          </i>
+        </span>
+      </template>
+      <!-- ACTIONS -->
+      <div
+        class="el-image-viewer__btn el-image-viewer__actions"
+        v-if="!isVideo(urlList[index])"
+      >
+        <div class="el-image-viewer__actions__inner">
+          <i class="el-icon" @click="handleActions('zoomOut')"
+            ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
+              <path
+                fill="currentColor"
+                d="m795.904 750.72 124.992 124.928a32 32 0 0 1-45.248 45.248L750.656 795.904a416 416 0 1 1 45.248-45.248zM480 832a352 352 0 1 0 0-704 352 352 0 0 0 0 704M352 448h256a32 32 0 0 1 0 64H352a32 32 0 0 1 0-64"
+              ></path></svg
+          ></i>
+          <i class="el-icon" @click="handleActions('zoomIn')"
+            ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
+              <path
+                fill="currentColor"
+                d="m795.904 750.72 124.992 124.928a32 32 0 0 1-45.248 45.248L750.656 795.904a416 416 0 1 1 45.248-45.248zM480 832a352 352 0 1 0 0-704 352 352 0 0 0 0 704m-32-384v-96a32 32 0 0 1 64 0v96h96a32 32 0 0 1 0 64h-96v96a32 32 0 0 1-64 0v-96h-96a32 32 0 0 1 0-64z"
+              ></path></svg
+          ></i>
+          <i class="el-image-viewer__actions__divider"></i>
+ 
+          <i class="el-icon" @click="toggleMode"
+            ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
+              <path
+                fill="currentColor"
+                d="m160 96.064 192 .192a32 32 0 0 1 0 64l-192-.192V352a32 32 0 0 1-64 0V96h64zm0 831.872V928H96V672a32 32 0 1 1 64 0v191.936l192-.192a32 32 0 1 1 0 64zM864 96.064V96h64v256a32 32 0 1 1-64 0V160.064l-192 .192a32 32 0 1 1 0-64l192-.192zm0 831.872-192-.192a32 32 0 0 1 0-64l192 .192V672a32 32 0 1 1 64 0v256h-64z"
+              ></path></svg
+          ></i>
+          <i class="el-image-viewer__actions__divider"></i>
+          <i class="el-icon" @click="handleActions('anticlocelise')"
+            ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
+              <path
+                fill="currentColor"
+                d="M289.088 296.704h92.992a32 32 0 0 1 0 64H232.96a32 32 0 0 1-32-32V179.712a32 32 0 0 1 64 0v50.56a384 384 0 0 1 643.84 282.88 384 384 0 0 1-383.936 384 384 384 0 0 1-384-384h64a320 320 0 1 0 640 0 320 320 0 0 0-555.712-216.448z"
+              ></path></svg
+          ></i>
+          <i class="el-icon" @click="handleActions('clocelise')"
+            ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
+              <path
+                fill="currentColor"
+                d="M784.512 230.272v-50.56a32 32 0 1 1 64 0v149.056a32 32 0 0 1-32 32H667.52a32 32 0 1 1 0-64h92.992A320 320 0 1 0 524.8 833.152a320 320 0 0 0 320-320h64a384 384 0 0 1-384 384 384 384 0 0 1-384-384 384 384 0 0 1 643.712-282.88z"
+              ></path></svg
+          ></i>
+					<i class="el-icon" @click="handleDownload">
+						<svg data-v-d2e47025="" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
+							<path fill="currentColor" d="M160 832h704a32 32 0 1 1 0 64H160a32 32 0 1 1 0-64m384-253.696 236.288-236.352 45.248 45.248L508.8 704 192 387.2l45.248-45.248L480 584.704V128h64z"></path>
+						</svg>
+					</i>
+        </div>
+      </div>
+      <!-- CANVAS -->
+      <div class="el-image-viewer__canvas">
+        <template v-for="(url, i) in urlList" :key="i">
+          <img
+            v-if="i == index && !isVideo(url)"
+            ref="media"
+            :src="url"
+            :style="mediaStyle"
+            class="el-image-viewer__img"
+            @load="handleMediaLoad"
+            @error="handleMediaError"
+            @mousedown="handleMouseDown"
+          />
+          <video
+						v-if="i == index && isVideo(url)"
+            ref="media"
+            :src="url"
+						:controls="true"
+						:autoplay="true"
+            :style="mediaStyle"
+            class="el-image-viewer__img"
+            @load="handleMediaLoad"
+            @error="handleMediaError"
+            @mousedown="handleMouseDown"
+          >
+          </video>
+        </template>
+      </div>
+    </div>
+  </transition>
+</template>
+ 
+<script>
+import { computed, ref, onMounted, watch, nextTick } from 'vue'
+import { downloadImgVideo, downloadBase64 } from '@/utils'
+ 
+const EVENT_CODE = {
+  tab: 'Tab',
+  enter: 'Enter',
+  space: 'Space',
+  left: 'ArrowLeft', // 37
+  up: 'ArrowUp', // 38
+  right: 'ArrowRight', // 39
+  down: 'ArrowDown', // 40
+  esc: 'Escape',
+  delete: 'Delete',
+  backspace: 'Backspace'
+}
+ 
+const isFirefox = function () {
+  return !!window.navigator.userAgent.match(/firefox/i)
+}
+ 
+const rafThrottle = function (fn) {
+  let locked = false
+  return function (...args) {
+    if (locked) return
+    locked = true
+    window.requestAnimationFrame(() => {
+      fn.apply(this, args)
+      locked = false
+    })
+  }
+}
+ 
+const Mode = {
+  CONTAIN: {
+    name: 'contain',
+    icon: 'el-icon-full-screen'
+  },
+  ORIGINAL: {
+    name: 'original',
+    icon: 'el-icon-c-scale-to-original'
+  }
+}
+ 
+const mousewheelEventName = isFirefox() ? 'DOMMouseScroll' : 'mousewheel'
+const CLOSE_EVENT = 'close'
+const SWITCH_EVENT = 'switch'
+ 
+export default {
+  name: 'ElMediaViewer',
+  props: {
+    urlList: {
+      type: Array,
+      default: () => []
+    },
+    zIndex: {
+      type: Number,
+      default: 2016
+    },
+    initialIndex: {
+      type: Number,
+      default: 0
+    },
+    infinite: {
+      type: Boolean,
+      default: true
+    },
+    hideOnClickModal: {
+      type: Boolean,
+      default: true
+    },
+		// 下载文件名
+		fileName: {
+			type: String,
+			default: ''
+		}
+  },
+  emits: [CLOSE_EVENT, SWITCH_EVENT],
+  setup(props, { emit }) {
+    let _keyDownHandler = null
+    let _mouseWheelHandler = null
+    let _dragHandler = null
+ 
+    const loading = ref(true)
+    const index = ref(props.initialIndex)
+    const wrapper = ref(null)
+    const media = ref(null)
+    const mode = ref(Mode.CONTAIN)
+    const transform = ref({
+      scale: 1,
+      deg: 0,
+      offsetX: 0,
+      offsetY: 0,
+      enableTransition: false
+    })
+		const isVideo = computed(() => (url) => {
+			return /\.(mp4|webm|ogg)$/i.test(url)
+		})
+ 
+    // 处理 video 有video 时 字段
+    const isSingle = computed(() => {
+      // const { urlList } = props
+      // urlList.forEach((item) => {
+      //   if (!item.type) {
+      //     item.type = item.response.type
+      //     if (item.response.thumbnailUrl) {
+      //       item.videoUrl = item.response.thumbnailUrl
+      //     }
+      //   }
+      // })
+      // return urlList.length <= 1
+			return false
+    })
+ 
+    const isFirst = computed(() => {
+      return index.value === 0
+    })
+ 
+    const isLast = computed(() => {
+      return index.value === props.urlList.length - 1
+    })
+ 
+    const currentMedia = computed(() => {
+      return props.urlList[index.value]
+    })
+ 
+    const isImage = computed(() => {
+      const currentUrl = props.urlList[index.value]
+      return currentUrl.endsWith('.jpg') || currentUrl.endsWith('.png')
+    })
+ 
+    const mediaStyle = computed(() => {
+      const { scale, deg, offsetX, offsetY, enableTransition } = transform.value
+      const style = {
+        transform: `scale(${scale}) rotate(${deg}deg)`,
+        transition: enableTransition ? 'transform .3s' : '',
+        marginLeft: `${offsetX}px`,
+        marginTop: `${offsetY}px`
+      }
+      if (mode.value.name === Mode.CONTAIN.name) {
+        style.maxWidth = style.maxHeight = '100%'
+      }
+      return style
+    })
+ 
+    function hide() {
+      deviceSupportUninstall()
+      emit(CLOSE_EVENT)
+    }
+ 
+    function deviceSupportInstall() {
+      _keyDownHandler = rafThrottle((e) => {
+        switch (e.code) {
+          // ESC
+          case EVENT_CODE.esc:
+            hide()
+            break
+          // SPACE
+          case EVENT_CODE.space:
+            toggleMode()
+            break
+          // LEFT_ARROW
+          case EVENT_CODE.left:
+            prev()
+            break
+          // UP_ARROW
+          case EVENT_CODE.up:
+            handleActions('zoomIn')
+            break
+          // RIGHT_ARROW
+          case EVENT_CODE.right:
+            next()
+            break
+          // DOWN_ARROW
+          case EVENT_CODE.down:
+            handleActions('zoomOut')
+            break
+        }
+      })
+ 
+      _mouseWheelHandler = rafThrottle((e) => {
+        const delta = e.wheelDelta ? e.wheelDelta : -e.detail
+        if (delta > 0) {
+          handleActions('zoomIn', {
+            zoomRate: 0.015,
+            enableTransition: false
+          })
+        } else {
+          handleActions('zoomOut', {
+            zoomRate: 0.015,
+            enableTransition: false
+          })
+        }
+      })
+ 
+      document.addEventListener('keydown', _keyDownHandler, false)
+      document.addEventListener(mousewheelEventName, _mouseWheelHandler, false)
+    }
+ 
+    function deviceSupportUninstall() {
+      document.removeEventListener('keydown', _keyDownHandler, false)
+      document.removeEventListener(
+        mousewheelEventName,
+        _mouseWheelHandler,
+        false
+      )
+      _keyDownHandler = null
+      _mouseWheelHandler = null
+    }
+ 
+    function handleMediaLoad() {
+      loading.value = false
+    }
+ 
+    function handleMediaError(e) {
+      loading.value = false
+    }
+ 
+    function handleMouseDown(e) {
+      if (loading.value || e.button !== 0) return
+ 
+      const { offsetX, offsetY } = transform.value
+      const startX = e.pageX
+      const startY = e.pageY
+ 
+      const divLeft = wrapper.value.clientLeft
+      const divRight = wrapper.value.clientLeft + wrapper.value.clientWidth
+      const divTop = wrapper.value.clientTop
+      const divBottom = wrapper.value.clientTop + wrapper.value.clientHeight
+ 
+      _dragHandler = rafThrottle((ev) => {
+        transform.value = {
+          ...transform.value,
+          offsetX: offsetX + ev.pageX - startX,
+          offsetY: offsetY + ev.pageY - startY
+        }
+      })
+      document.addEventListener('mousemove', _dragHandler, false)
+      document.addEventListener(
+        'mouseup',
+        (e) => {
+          const mouseX = e.pageX
+          const mouseY = e.pageY
+          if (
+            mouseX < divLeft ||
+            mouseX > divRight ||
+            mouseY < divTop ||
+            mouseY > divBottom
+          ) {
+            reset()
+          }
+          document.removeEventListener('mousemove', _dragHandler, false)
+        },
+        false
+      )
+ 
+      e.preventDefault()
+    }
+ 
+    function reset() {
+      transform.value = {
+        scale: 1,
+        deg: 0,
+        offsetX: 0,
+        offsetY: 0,
+        enableTransition: false
+      }
+    }
+ 
+    function toggleMode() {
+      if (loading.value) return
+ 
+      const modeNames = Object.keys(Mode)
+      const modeValues = Object.values(Mode)
+      const currentMode = mode.value.name
+      const index = modeValues.findIndex((i) => i.name === currentMode)
+      const nextIndex = (index + 1) % modeNames.length
+      mode.value = Mode[modeNames[nextIndex]]
+      reset()
+    }
+ 
+    function prev() {
+      if (isFirst.value && !props.infinite) return
+      const len = props.urlList.length
+      index.value = (index.value - 1 + len) % len
+    }
+ 
+    function next() {
+      if (isLast.value && !props.infinite) return
+      const len = props.urlList.length
+      index.value = (index.value + 1) % len
+    }
+ 
+    function handleActions(action, options = {}) {
+      if (loading.value) return
+      const { zoomRate, rotateDeg, enableTransition } = {
+        zoomRate: 0.2,
+        rotateDeg: 90,
+        enableTransition: true,
+        ...options
+      }
+      switch (action) {
+        case 'zoomOut':
+          if (transform.value.scale > 0.2) {
+            transform.value.scale = parseFloat(
+              (transform.value.scale - zoomRate).toFixed(3)
+            )
+          }
+          break
+        case 'zoomIn':
+          transform.value.scale = parseFloat(
+            (transform.value.scale + zoomRate).toFixed(3)
+          )
+          break
+        case 'clocelise':
+          transform.value.deg += rotateDeg
+          break
+        case 'anticlocelise':
+          transform.value.deg -= rotateDeg
+          break
+      }
+      transform.value.enableTransition = enableTransition
+    }
+
+		function handleDownload () {
+			const url = props.urlList[index.value]
+			const isOnlineImage = url.startsWith('http://') || url.startsWith('https://') // 判断是否为在线地址
+			const date = new Date()
+			const filename = props.fileName || `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`
+			isOnlineImage ? downloadImgVideo(url, filename) : downloadBase64(url, filename)
+		}
+ 
+    watch(currentMedia, () => {
+      nextTick(() => {
+        const $media = media.value
+        if (!$media.complete) {
+          loading.value = true
+        }
+      })
+    })
+ 
+    watch(index, (val) => {
+      reset()
+      emit(SWITCH_EVENT, val)
+    })
+ 
+    onMounted(() => {
+      deviceSupportInstall()
+      wrapper.value?.focus?.()
+    })
+ 
+    return {
+      index,
+      wrapper,
+      media,
+      isSingle,
+      isFirst,
+      isLast,
+      currentMedia,
+      isImage,
+      isVideo,
+      mediaStyle,
+      mode,
+      handleActions,
+      prev,
+      next,
+      hide,
+      toggleMode,
+      handleMediaLoad,
+      handleMediaError,
+			handleDownload,
+      handleMouseDown
+    }
+  }
+}
+</script>
+ 
+<style lang="scss" scoped>
+// .el-icon {
+//   z-index: 200;
+// }
+// .el-image-viewer__btn {
+//   overflow: hidden;
+//   border-radius: 100px;
+//   opacity: 1;
+//   text-align: center;
+//   line-height: 44px;
+//   background-color: rgba($color: #0d0d0d, $alpha: 0.5);
+// }
+</style>

+ 104 - 0
src/directives/previewImageDirective.js

@@ -0,0 +1,104 @@
+import { h, render } from 'vue';
+import { ElImageViewer } from 'element-plus';
+import { downloadImgVideo } from '@/utils'
+
+export default function (app) {
+	app.directive('previewImage', {
+		mounted(el, binding) {
+			const previewBox = document.createElement('div');
+			previewBox.classList.add('preview-box');
+			let vnode;
+
+			let downloadIndex
+
+			// 判断是否为视频文件
+			const isVideo = (url) => /\.(mp4|webm|ogg)$/i.test(url);
+
+			// 创建混合媒体预览组件
+			const createMixedMediaViewer = (urls, initialIndex = 0) => {
+				const mediaList = urls.map(url => ({
+					url,
+					isVideo: isVideo(url)
+				}));
+
+				const _el = h(ElImageViewer, {
+					urlList: urls,
+					initialIndex,
+					hideOnClickModal: true,
+					onClose: () => {
+						render(null, previewBox);
+						document.body.removeChild(previewBox);
+					},
+					onSwitch: (index) => {
+						downloadIndex = index
+						const elAll = document.querySelectorAll('.el-image-viewer__img');
+						const el = elAll[index];
+					
+						if (!isVideo(el.src) || el.tagName !== 'VIDEO') { // 当前是 IMG
+							elAll.forEach((item, i) => {
+								if (item.tagName === 'VIDEO') {
+									item.style.display = 'none'
+								} 
+							})
+						} else {
+							el.style.display = 'block'
+						}
+
+						if (isVideo(el.src) && el.tagName === 'IMG') {
+							const currentMedia = mediaList[index];
+							const canvasElement = document.querySelector('.el-image-viewer__canvas');
+							// 如果是视频,替换img标签为video标签
+							setTimeout(() => {
+								if (canvasElement) {
+									const videoElement = document.createElement('video');
+									videoElement.src = currentMedia.url;
+									videoElement.controls = true;
+									videoElement.autoplay = true;
+									videoElement.className = el.className;
+									videoElement.style.maxWidth = '100%';
+									videoElement.style.maxHeight = '100%';
+									canvasElement.appendChild(videoElement);
+									el.remove()
+								}
+							}, 0);
+						}
+					}
+				});
+				return _el
+			};
+
+			el.addEventListener('click', () => {
+				el.style.cursor = 'pointer';
+				const urls = Array.isArray(binding.value[0]) ? binding.value[0] : [binding.value[0]];
+				const initialIndex = binding.value[1] || 0;
+				downloadIndex = initialIndex
+				vnode = createMixedMediaViewer(urls, initialIndex);
+				render(vnode, previewBox);
+				document.body.appendChild(previewBox);
+
+
+				// 添加下载按钮
+				const action_btn = previewBox.children[0].children[4].children[0]
+				const divider = document.createElement('i')
+				divider.className = 'el-image-viewer__actions__divider'
+				action_btn.appendChild(divider)
+
+				const download_btn = document.createElement('i')
+				download_btn.className = 'el-icon'
+				download_btn.innerHTML = `<svg data-v-d2e47025="" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
+					<path fill="currentColor" d="M160 832h704a32 32 0 1 1 0 64H160a32 32 0 1 1 0-64m384-253.696 236.288-236.352 45.248 45.248L508.8 704 192 387.2l45.248-45.248L480 584.704V128h64z"></path>
+				</svg>`
+
+				const date = new Date()
+				download_btn.addEventListener('click', () => {
+					const fileName = binding.value[2] || `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`
+					downloadImgVideo(urls[downloadIndex], fileName)
+				})
+				action_btn.appendChild(download_btn)
+			});
+		},
+		unmounted(el) {
+			el.removeEventListener('click', () => {});
+		}
+	});
+}

+ 2 - 0
src/main.js

@@ -32,6 +32,7 @@ import VueLuckyCanvas from '@lucky-canvas/vue'
 import router from './router'
 
 import { ellipsisTooltip } from '@/components/CtTooltip/index.js'
+// import imageDirective from '@/directives/previewImageDirective'
 
 import './permission'
 
@@ -47,6 +48,7 @@ app.use(pinia)
 app.use(router)
 app.use(Clipboard)
 app.use(VueLuckyCanvas)
+// imageDirective(app)
 
 
 // app.config.globalProperties.$echarts = echarts

+ 20 - 1
src/utils/index.js

@@ -73,7 +73,26 @@ export const base64ToBlob = (code) => {
   })
 }
 
-export const downloadBase64 = (content, fileName) => {
+import axios from 'axios'
+export const downloadImgVideo = async (content, fileName) => {
+  try {
+    const response = await axios.get(content, { responseType: 'blob' });
+    const suffix = content.split('.').pop()
+    const url = window.URL.createObjectURL(new Blob([response.data]));
+    const link = document.createElement('a');
+    link.style.display = 'none';
+    link.href = url;
+    link.download = fileName + `.${suffix}`
+    document.body.appendChild(link);
+    link.click();
+    window.URL.revokeObjectURL(url);
+  } catch (error) {
+    console.error('Error downloading file:', error);
+  }
+}
+
+
+export const downloadBase64 = async (content, fileName) => {
   let aLink = document.createElement('a')
   let blob = base64ToBlob(content)
   aLink.download = fileName + '.png'

+ 1 - 1
src/views/recruit/entRegister/register.vue

@@ -70,7 +70,7 @@
         </v-btn>
       </div>
     </v-card>
-    <PreviewImg v-if="showPreview" :current="current" :list="[licenseUrl]" @close="showPreview = !showPreview"></PreviewImg>
+    <PreviewImage v-if="showPreview" :initialIndex="current" :urlList="[licenseUrl]" @close="showPreview = !showPreview" />
 
     <Loading :visible="loading"></Loading>
     

+ 13 - 1
src/views/recruit/enterprise/entInfoSetting/index.vue

@@ -31,11 +31,14 @@
               ref="componentRef"
               :is="item.path"
               @complete="handleComplete"
+              @preview="handlePreview"
             />
           </div>
         </template>
       </div>
     </v-card>
+
+    <PreviewImage v-if="showPreview" :initialIndex="initialIndex" :urlList="urlsList" @close="showPreview = !showPreview" />
   </div>
 </template>
 
@@ -56,9 +59,9 @@ import { useI18n } from '@/hooks/web/useI18n'
 
 const componentRef = ref()
 
+const showPreview = ref(false)
 const route = useRoute()
 const { t } = useI18n()
-// tab
 const tab = ref(1)
 const tabList = [
   { label: t('enterprise.infoSetting.basicInfo'), value: 1, path: basicInfo, id: 'basicInfo' },
@@ -77,6 +80,15 @@ const status = ref([
   { id: 'businessInformation', status: false }
 ])
 
+// 图片预览
+const urlsList = ref([])
+const initialIndex = ref(0)
+const handlePreview = (urls, index) => {
+  urlsList.value = urls
+  initialIndex.value = index || 0
+  showPreview.value = true
+}
+
 watch(() => route?.query?.tabKey, (newVal) => {
   if (newVal) tab.value = newVal - 0
 }, { deep: true, immediate: true })

+ 1 - 1
src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/basicInfo.vue

@@ -34,7 +34,7 @@ import { useI18n } from '@/hooks/web/useI18n'
 import industryTypeCard from '@/components/industryTypeCard'
 import Snackbar from '@/plugins/snackbar'
 import { useUserStore } from '@/store/user'
-const emit = defineEmits(['complete'])
+const emit = defineEmits(['complete', 'preview'])
 
 const { t } = useI18n()
 const CtFormRef = ref()

+ 2 - 5
src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/businessInformation.vue

@@ -16,7 +16,7 @@
               tips="上传图片" 
               :value="licenseUrl"
               :showSnackbar="false" 
-              @imgClick="showPreview = !showPreview" 
+              @imgClick="emit('preview', [licenseUrl])" 
               :showCursor="true" 
               @success="handleUploadImg" 
               @delete="handleDeleteImg"
@@ -26,7 +26,6 @@
       </template>
     </CtForm>
     <Loading :visible="loading"></Loading>
-    <PreviewImg v-if="showPreview" :current="current" :list="[licenseUrl]" @close="showPreview = !showPreview"></PreviewImg>
     <div class="text-center">
       <v-btn color="primary" class="buttons mt-3 mb-10" @click="handleSave">{{ $t('common.save') }}</v-btn>
     </div>
@@ -42,12 +41,10 @@ import { useI18n } from '@/hooks/web/useI18n'
 import { getBusinessLicenseOCR } from '@/api/common'
 import Confirm from '@/plugins/confirm'
 // import { getDict } from '@/hooks/web/useDictionaries'
-const emit = defineEmits(['complete'])
+const emit = defineEmits(['complete', 'preview'])
 
 defineOptions({name: 'informationSettingsComponents-businessInformation'})
 const { t } = useI18n()
-const showPreview = ref(false)
-const current = ref(0)
 let licenseUrl = ref('')
 // 图片预览
 const loading = ref(false)

+ 3 - 12
src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/enterpriseAlbum.vue

@@ -27,15 +27,14 @@
       </v-btn>
     </div>
     <div class="imgItem" v-for="(item, index) in imgList" :key="index">
-      <v-img v-if="checkIsImage(item)" width="100%" height="100%" :src="item" @click="handleClick(index)"></v-img>
-      <video v-else class="videos-radius mr-3" :src="item" controls height="172" width="172" preload="preload" @click="handleClick(index)"></video>
+      <v-img v-if="checkIsImage(item)" width="100%" height="100%" :src="item" @click="emit('preview', imgList, index)"></v-img>
+      <video v-else class="videos-radius mr-3" :src="item" controls height="172" width="172" preload="preload" @click="emit('preview', imgList, index)"></video>
       <div class="operate">
         <span></span>
         <span class="mdi mdi-trash-can-outline" @click="handleDelete(item)"></span>
       </div>
     </div>
   </div>
-  <PreviewImg v-if="showPreview" :current="current" :list="imgList" @close="showPreview = !showPreview"></PreviewImg>
 </template>
 
 <script setup>
@@ -49,7 +48,7 @@ import Snackbar from '@/plugins/snackbar'
 import Confirm from '@/plugins/confirm'
 import cloneDeep from 'lodash/cloneDeep'
 
-const emit = defineEmits(['complete'])
+const emit = defineEmits(['complete', 'preview'])
 
 const { t } = useI18n()
 const imgList = ref([])
@@ -68,14 +67,6 @@ const getInfo = async () => {
 }
 getInfo()
 
-// 预览
-const showPreview = ref(false)
-const current = ref(0)
-const handleClick = (index) => {
-  showPreview.value = !showPreview.value
-  current.value = index
-}
-
 // 删除
 const handleDelete = async (url) => {
   const index = imgList.value.indexOf(url)

+ 1 - 1
src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/enterpriseLabel.vue

@@ -48,7 +48,7 @@ import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
 import { getEnterpriseBaseInfo, updateEnterpriseTag, getTagTreeDataApi } from '@/api/enterprise'
 import { ref } from 'vue';
 defineOptions({name: 'informationSettingsComponents-enterpriseLabel'})
-const emit = defineEmits(['complete'])
+const emit = defineEmits(['complete', 'preview'])
 
 const customTag = ref(false)
 const limitNum = ref(10)

+ 1 - 1
src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/enterpriseLogo.vue

@@ -49,7 +49,7 @@ import { uploadFile } from '@/api/common'
 import { useI18n } from '@/hooks/web/useI18n'
 import { updateEnterpriseLogo, getEnterpriseBaseInfo } from '@/api/enterprise'
 import Snackbar from '@/plugins/snackbar'
-const emit = defineEmits(['complete'])
+const emit = defineEmits(['complete', 'preview'])
 
 const { t } = useI18n()
 let squareImageUrl = ref('')

+ 1 - 1
src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/welfareLabel.vue

@@ -48,7 +48,7 @@ import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
 import { getEnterpriseBaseInfo, updateEnterpriseWelfare, getTagTreeDataApi } from '@/api/enterprise'
 import { ref } from 'vue';
 defineOptions({name: 'informationSettingsComponents-welfareLabel'})
-const emit = defineEmits(['complete'])
+const emit = defineEmits(['complete', 'preview'])
 
 const customTag = ref(false)
 const limitNum = ref(10)

+ 7 - 1
src/views/recruit/personal/PersonalCenter/jobFeedback/components/companyCollection.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <div v-if="items.length">
-      <LongCompany :list="items" @refresh="getPositionList"></LongCompany>
+      <LongCompany :list="items" @refresh="handleCancelCollect"></LongCompany>
       <CtPagination
         :total="total"
         :page="page.pageNo"
@@ -20,6 +20,7 @@ import { ref } from 'vue'
 import { dealDictArrayData } from '@/utils/position'
 import { getSubscribeEnterprise } from '@/api/position'
 
+const emit = defineEmits(['cancelCollect'])
 const total = ref(0)
 const items = ref([])
 const page = ref({
@@ -36,6 +37,11 @@ const getPositionList = async () => {
 }
 getPositionList()
 
+const handleCancelCollect = () => {
+  emit('cancelCollect', 1)
+  getPositionList()
+}
+
 const handleChangePage = (index) => {
   page.value.pageNo = index
   getPositionList()

+ 1 - 1
src/views/recruit/personal/PersonalCenter/jobFeedback/components/interested.vue

@@ -6,7 +6,7 @@
       <v-tab :value="1">{{ subscribeCount > 0 ? `${$t('position.companyCollection')}(${subscribeCount})` : $t('position.companyCollection') }}</v-tab>
     </v-tabs>
     <div class="mt-3">
-      <component :is="tabVal === 0 ? positionCollection : companyCollection"></component>
+      <component :is="tabVal === 0 ? positionCollection : companyCollection" @cancelCollect="val => val ? getSubscribeCount() : getFavoriteCount()"></component>
     </div>
   </div>
 </template>

+ 7 - 1
src/views/recruit/personal/PersonalCenter/jobFeedback/components/positionCollection.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <div v-if="items.length">
-      <LongStrip :items="items" :showCancelBtn="true" @refresh="getPositionList"></LongStrip>
+      <LongStrip :items="items" :showCancelBtn="true" @refresh="handleCancelCollect"></LongStrip>
       <CtPagination
         :total="total"
         :page="page.pageNo"
@@ -20,6 +20,7 @@ import { ref } from 'vue'
 import { dealDictObjData } from '@/utils/position'
 import { getJobFavoriteList } from '@/api/position'
 
+const emit = defineEmits(['cancelCollect'])
 const total = ref(0)
 const items = ref([])
 const page = ref({
@@ -41,6 +42,11 @@ const getPositionList = async () => {
 }
 getPositionList()
 
+const handleCancelCollect = () => {
+  emit('cancelCollect', 0)
+  getPositionList()
+}
+
 const handleChangePage = (index) => {
   page.value.pageNo = index
   getPositionList()

+ 4 - 4
src/views/recruit/personal/companyDetail/components/introduction.vue

@@ -16,16 +16,16 @@
     <div>
       <h4>公司相册</h4>
       <v-slide-group v-if="props.info.enterprise.albumList" :show-arrows="true" class="mt-3 img-box cursor-pointer">
-        <v-slide-group-item v-for="(val, i) in props.info.enterprise.albumList" :key="val">
+        <v-slide-group-item v-for="(val, i) in props.info.enterprise.albumList" :key="i">
           <div>
-            <v-img v-if="checkIsImage(val)" class="mr-3" width="200" height="115" :src="val" cover rounded @click="handleClick(i)"></v-img>
-            <video v-else class="videos-radius mr-3" :src="val" controls height="118" width="200" preload="preload" @click="handleClick(i)"></video>
+            <v-img v-if="checkIsImage(val)" class="mr-3" width="200" height="115" :src="val" cover rounded @click.stop="handleClick(i)"></v-img>
+            <video v-else class="videos-radius mr-3" :src="val" controls height="118" width="200" preload="preload" @click.stop="handleClick(i)"></video>
           </div>
         </v-slide-group-item>
       </v-slide-group>
       <div v-else>暂无</div>
     </div>
-    <PreviewImg v-if="showPreview" :current="current" :list="props.info.enterprise.albumList" @close="showPreview = !showPreview"></PreviewImg>
+    <PreviewImage v-if="showPreview" :initialIndex="current" :urlList="props.info.enterprise.albumList" @close="showPreview = !showPreview" />
   </div>
 </template>
 

+ 1 - 13
src/views/recruit/personal/home/components/hotPromotedPositions.vue

@@ -16,21 +16,9 @@
         <PositionCard v-if="items.filter(Boolean) && items.length" :isOpenWindow="false" :items="items" :tab="tab" @position="handlePosition"></PositionCard>
         <Empty v-else class="mb-3" :elevation="false"></Empty>
       </v-window-item>
-      <!-- <v-window-item :value="1">
-        <PositionCard v-if="items.filter(Boolean) && items.length" :items="items" :tab="tab" @position="handlePosition"></PositionCard>
-        <Empty v-else class="mb-3" :elevation="false"></Empty>
-      </v-window-item>
-      <v-window-item :value="2">
-        <PositionCard v-if="items.filter(Boolean) && items.length" :isOpenWindow="false" :items="items" :tab="tab" @position="handlePosition"></PositionCard>
-        <Empty v-else class="mb-3" :elevation="false"></Empty>
-      </v-window-item>
-      <v-window-item :value="3">
-        <PositionCard v-if="items.filter(Boolean) && items.length" :isOpenWindow="false" :items="items" :tab="tab" @position="handlePosition"></PositionCard>
-        <Empty v-else class="mb-3" :elevation="false"></Empty>
-      </v-window-item> -->
     </v-window>
     <div class="text-center mt-5" style="border-top: 1px solid #ccc; padding-top: 30px;">
-      <v-btn class="buttons btnColor" @click.stop="handleToMore">{{ $t('position.moreBtn') }}</v-btn>
+      <v-btn class="buttons btnColor" elevation="5" @click.stop="handleToMore">{{ $t('position.moreBtn') }}</v-btn>
     </div>
   </div>
 </template>

+ 1 - 1
src/views/recruit/personal/home/components/popularEnterprises.vue

@@ -9,7 +9,7 @@
     <HotPromoted v-if="items.length" class="mt-5" :items="items"></HotPromoted>
     <Empty v-else :elevation="false" class="mt-3" message="暂无精选企业"></Empty>
     <div v-if="items.length" class="text-center">
-      <v-btn class="buttons btnColor" color="primary" @click.stop="handleToMore">{{ $t('enterprise.moreBtn') }}</v-btn>
+      <v-btn class="buttons btnColor" elevation="5" color="primary" @click.stop="handleToMore">{{ $t('enterprise.moreBtn') }}</v-btn>
     </div>
   </div>
 </template>

+ 23 - 49
src/views/recruit/personal/position/components/details.vue

@@ -8,22 +8,24 @@
         </div>
         <v-btn v-if="showContentRight" color="primary" variant="text" size="large" @click.stop="handleReturn" prepend-icon="mdi-chevron-triple-left">返回上一页</v-btn>
       </div>
-      <div class="text-end">
-        <span v-if="!info.payFrom && !info.payTo" class="salary font-size-20">面议</span>
-        <span v-else class="salary font-size-20">{{ info.payFrom ? info.payFrom + '-' : ''}}{{ info.payTo }}{{ positionInfo.payName ? '/' + positionInfo.payName : '' }}</span>
-      </div>
-      <div class="refresh-time text-end">{{ timesTampChange(info.updateTime) }} {{ $t('common.refresh') }} <v-icon color="primary" size="20">mdi-circle-medium</v-icon></div>
-      <div class="banner-tags mt-4">
-        <span v-for="k in desc" :key="k.mdi">
-          <span v-if="positionInfo[k.value] || k.value === 'areaName'" class="mr-10">
-            <v-icon color="var(--color-666)" size="20">{{ k.mdi }}</v-icon>
-            <span class="ml-1">
-              {{ k.value === 'areaName' ? !positionInfo.areaId ? '全国' : positionInfo.area?.str : positionInfo[k.value] }}
-              <!-- {{ (k.value === 'areaName' && !positionInfo.areaId) ? '全国' : positionInfo[k.value] }} -->
+      <div class="d-flex mt-1 justify-space-between align-center">
+        <div class="banner-tags">
+          <span v-for="k in desc" :key="k.mdi">
+            <span v-if="positionInfo[k.value] || k.value === 'areaName'" class="mr-10">
+              <v-icon color="var(--color-666)" size="20">{{ k.mdi }}</v-icon>
+              <span class="ml-1">
+                {{ k.value === 'areaName' ? !positionInfo.areaId ? '全国' : positionInfo.area?.str : positionInfo[k.value] }}
+                <!-- {{ (k.value === 'areaName' && !positionInfo.areaId) ? '全国' : positionInfo[k.value] }} -->
+              </span>
             </span>
           </span>
-        </span>
+        </div>
+        <div>
+          <span v-if="!info.payFrom && !info.payTo" class="salary font-size-20">面议</span>
+          <span v-else class="salary font-size-20">{{ info.payFrom ? info.payFrom + '-' : ''}}{{ info.payTo }}{{ positionInfo.payName ? '/' + positionInfo.payName : '' }}</span>
+        </div>
       </div>
+      <div class="refresh-time text-end">{{ timesTampChange(info.updateTime) }} {{ $t('common.refresh') }} <v-icon color="primary" size="20">mdi-circle-medium</v-icon></div>
       <div class="banner-tools my-4">
         <v-chip size="small" label v-for="(k, i) in info.tagList" :key="i" class="mr-1" color="primary">{{ k }}</v-chip>
       </div>
@@ -126,34 +128,11 @@
     <!-- 选择简历 -->
     <selectResumeDialog v-model="showResume" :list="resumeList" @submit="handleSubmit" @close="handleClose"></selectResumeDialog>
 
-    <!-- 职位分享 -->
-    <Dialog
-      :visible="shareDialog" :widthType="2" :footer="false" titleClass="text-h6"
-      :title="$t('position.rewardsShared')"
-      @close="shareDialog = false"
-    >
-      <div>
-        <div class="mb-3 text-center">微信分享:保存图片分享给好友</div>
-        <div class="d-flex align-center flex-column">
-          <v-img :src="previewSrc" width="200" height="250"></v-img>
-          <div class="mt-5">
-            <v-btn color="primary" variant="outlined" prepend-icon="mdi-eye-outline" @click="showPreview = true" style="width: 133px">预 览</v-btn>
-            <v-btn class="ml-3" color="primary" variant="outlined" prepend-icon="mdi-arrow-down-bold-box-outline" @click="handleDownloadImage" style="width: 133px">保存到本地</v-btn>
-          </div>
-        </div>
-      </div>
-      <template #footer>
-        <v-divider></v-divider>
-        <div>
-          <v-btn class="float-right ma-2" color="primary" variant="text" @click="shareDialog = false">{{ $t('common.close') }}</v-btn>
-        </div>
-      </template>
-    </Dialog>
-    <PreviewImg v-if="showPreview" :list="[previewSrc]" @close="showPreview = false" @download="handleDownloadImage"   :isImage="true"></PreviewImg>
+    <!-- 图片预览 -->
+    <PreviewImage v-if="showPreview" :urlList="[previewSrc]" :fileName="fileName" @close="showPreview = !showPreview" />
 
     <Loading :visible="loading"></Loading>
     <div v-if="Object.keys(info).length && Object.keys(positionInfo).length" style="position: absolute; left: -9999px; bottom: 0">
-      
       <PosterPage :id="id" :info="info" :positionInfo="positionInfo" ref="share"></PosterPage>
     </div>
 
@@ -164,7 +143,7 @@
 
 <script setup>
 defineOptions({ name: 'position-details' })
-import { ref } from 'vue'
+import { ref, computed } from 'vue'
 import { useRouter } from 'vue-router'
 import Snackbar from '@/plugins/snackbar'
 import html2canvas from 'html2canvas'
@@ -174,7 +153,6 @@ import PosterPage from './poster.vue'
 import selectResumeDialog from './jobDetails/selectResumeDialog'
 import similarPositions from '@/components/Position/similarPositions.vue'
 import EnterpriseInfo from '@/components/Enterprise/info.vue'
-import Dialog from '@/components/CtDialog'
 import loginPage from '@/views/common/loginDialog.vue'
 import {
   getPositionDetails,
@@ -187,7 +165,7 @@ import {
 } from '@/api/position'
 import { getPersonResumeCv, savePersonResumeCv } from '@/api/recruit/personal/resume'
 
-import { downloadBase64, DPR } from '@/utils'
+import { DPR } from '@/utils'
 import { timesTampChange } from '@/utils/date'
 import { dealDictObjData, dealDictArrayData, commissionCalculation } from '@/utils/position'
 import { getToken } from '@/utils/auth'
@@ -298,15 +276,12 @@ const generateAndDownloadImage = async () => {
   }
 }
 
-// 保存图片到本地
-const handleDownloadImage = () => {
+// 职位详情分享图片下载文件名
+const fileName = computed(() => {
   const { name, areaName, payFrom, payTo } = info.value
   const salary = payFrom && payTo ? `${payFrom ? '_' + payFrom + '-' : ''}${payTo}` : '-面议'
-  downloadBase64(previewSrc.value, `${name}${areaName ? '_' + areaName : ''}${salary}${positionInfo.value.payName ? '-' + positionInfo.value.payName : ''}`)
-  setTimeout(() => {
-    Snackbar.success('下载成功')
-  }, 500);
-}
+  return `${name}${areaName ? '_' + areaName : ''}${salary}${positionInfo.value.payName ? '-' + positionInfo.value.payName : ''}`
+})
 
 // 相似职位
 const similarList = ref([])
@@ -352,7 +327,6 @@ const getCollectionStatus = async () => {
 getCollectionStatus()
 
 // 分享有礼
-const shareDialog = ref(false)
 const handleShare = async () => {
   nextFunc.value = handleShare // 登录成功或强制填写个人信息成功后回调
   if (!getToken()) {

+ 4 - 2
src/views/recruit/personal/position/components/poster.vue

@@ -7,8 +7,6 @@
         <!-- 职位名称+薪资 -->
         <div class="d-flex justify-space-between mx-5">
           <h2 class="JobName ellipsis">{{ formatName(info.name) }}</h2>
-          <span v-if="!info.payFrom && !info.payTo" class="salary">面议</span>
-          <span v-else class="salary">{{ info.payFrom ? info.payFrom + '-' : '' }}{{ info.payTo }}{{ positionInfo.payName ? '/' + positionInfo.payName : '' }}</span>
         </div>
         <!-- 地区、工作经验、学历 -->
         <div class="d-flex justify-space-between mx-5">
@@ -21,6 +19,10 @@
               <span v-if="i !== desc.length - 1 && (positionInfo[k.value] || k.value === 'areaName')" class="septal-line"></span>
             </div>
           </div>
+          <div>
+            <span v-if="!info.payFrom && !info.payTo" class="salary">面议</span>
+            <span v-else class="salary">{{ info.payFrom ? info.payFrom + '-' : '' }}{{ info.payTo }}{{ positionInfo.payName ? '/' + positionInfo.payName : '' }}</span>
+          </div>
         </div>
         <!-- 企业信息 -->
         <div class="radius pa-3 mx-5 mt-5 d-flex align-center" style="background-color: #5baea6;">