Browse Source

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

lifanagju_citu 11 months ago
parent
commit
c873be8663

+ 2 - 2
package-lock.json

@@ -12,6 +12,7 @@
         "@vuepic/vue-datepicker": "^8.7.0",
         "axios": "^1.6.8",
         "js-cookie": "^3.0.5",
+        "lodash": "^4.17.21",
         "nprogress": "^0.2.0",
         "pinia": "^2.1.7",
         "pinia-plugin-persistedstate": "^3.2.1",
@@ -2220,8 +2221,7 @@
     "node_modules/lodash": {
       "version": "4.17.21",
       "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
-      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
-      "dev": true
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
     },
     "node_modules/lodash.merge": {
       "version": "4.6.2",

+ 1 - 0
package.json

@@ -13,6 +13,7 @@
     "@vuepic/vue-datepicker": "^8.7.0",
     "axios": "^1.6.8",
     "js-cookie": "^3.0.5",
+    "lodash": "^4.17.21",
     "nprogress": "^0.2.0",
     "pinia": "^2.1.7",
     "pinia-plugin-persistedstate": "^3.2.1",

+ 22 - 0
src/api/resume.js

@@ -171,3 +171,25 @@ export const enterpriseSearchByName = async (params) => {
     params
   })
 }
+
+// 保存附件
+export const savePersonResumeCv = async (data) => {
+  return await request.post({
+    url: '/app-api/menduner/system/person/resume/save/person/cv',
+    data
+  })
+}
+
+// 删除附件
+export const deletePersonResumeCv = async (id) => {
+  return await request.delete({
+    url: '/app-api/menduner/system/person/resume/remove/person/cv?id=' + id
+  })
+}
+
+// 获取附件列表
+export const getPersonResumeCv = async () => {
+  return await request.get({
+    url: '/app-api/menduner/system/person/resume/get/person/cv'
+  })
+}

+ 1 - 1
src/components/CtForm/index.vue

@@ -79,7 +79,7 @@ import radioGroupUI from './../FormUI/radioGroup'
 import checkboxUI from './../FormUI/checkbox'
 import textareaUI from './../FormUI/textArea'
 import DatePicker from '@/components/DatePicker'
-import { ref, defineEmits } from 'vue'
+import { ref, defineEmits, defineExpose } from 'vue'
 const emit = defineEmits(['change', 'inputUpdateAutocomplete'])// 定义一个或多个自定义事件
 const props = defineProps({items: Object})
 const valid = ref(false)

+ 2 - 1
src/components/DatePicker/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div style="width: 100%">
     <VueDatePicker
-      locale="zh-CN"
+      :locale="getCurrentLocaleLang() || 'zh-CN'"
       :disabled="options?.disabled || false"
       :range="options?.range || false"
       :model-type="options?.format || 'yyyy.MM.dd'"
@@ -24,6 +24,7 @@
 <script setup>
 defineOptions({ name: 'date-picker'})
 import { computed } from 'vue';
+import { getCurrentLocaleLang } from '@/utils/lang'
 
 const props = defineProps({
   width: {

+ 2 - 2
src/components/Enterprise/details.vue

@@ -12,7 +12,7 @@
         </div>
       </div>
       <div class="float-right d-flex">
-        <div class="tools-box text-center">
+        <div class="tools-box text-center" v-if="info.jobAdvertisedCount">
           <div class="tools-box-number">{{ info.jobAdvertisedCount }}</div>
           <div class="tools-box-text">职位在招</div>
         </div>
@@ -29,7 +29,7 @@
     <div class="mt-3">
       <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#f3f3f3" @update:model-value="handleTabClick">
         <v-tab :value="1">公司简介</v-tab>
-        <v-tab :value="2">在招职位({{ info.jobAdvertisedCount }})</v-tab>
+        <v-tab :value="2">在招职位{{ info.jobAdvertisedCount ? `(${info.jobAdvertisedCount})` : '' }}</v-tab>
       </v-tabs>
       <div class="d-flex" v-if="Object.keys(info).length">
         <div class="content-left">

+ 3 - 3
src/config/axios/service.js

@@ -4,7 +4,7 @@ import Confirm from '@/plugins/confirm'
 import qs from 'qs'
 import { config } from '@/config/axios/config'
 import { getCurrentLocaleLang } from '@/utils/lang'
-import { getToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth'
+import { getToken, getRefreshToken, removeToken, setToken } from '@/utils/auth'
 // import { getToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth'
 import errorCode from './errorCode'
 
@@ -53,7 +53,7 @@ service.interceptors.request.use(
       }
     })
     if (getToken() && !isToken) {
-      ;(config).headers.Authorization = 'Bearer ' + getToken() // 让每个请求携带自定义token
+      (config).headers.Authorization = 'Bearer ' + getToken() // 让每个请求携带自定义token
     }
     // 设置租户
     if (tenantEnable && tenantEnable === 'true') {
@@ -190,7 +190,7 @@ service.interceptors.response.use(
 )
 
 const refreshToken = async () => {
-  axios.defaults.headers.common['tenant-id'] = getTenantId()
+  axios.defaults.headers.common['tenant-id'] = import.meta.env.VITE_TENANTCODE
   return await axios.post(base_url + '/system/auth/refresh-token?refreshToken=' + getRefreshToken())
 }
 const handleAuthorized = () => {

+ 6 - 2
src/locales/en.js

@@ -15,7 +15,9 @@ export default {
     delMsg: 'Delete successful',
     uploadPictures: 'upload pictures',
     uploadSucMsg: 'Upload successful',
-    uploadErrMsg: 'Upload failed'
+    uploadErrMsg: 'Upload failed',
+    fileSizeExceed: 'The file size cannot exceed 10M',
+    fileFormatIncorrect: 'The uploaded file format is incorrect'
   },
   sys: {
     api: {
@@ -104,6 +106,8 @@ export default {
     firstWorkTime: 'First Work Time',
     dataDefaultPrompt: 'Please fill in your ',
     expand: 'Expand',
-    retract: 'Retract'
+    retract: 'Retract',
+    deleteAttachment: 'Are you sure to delete this attachment?',
+    uploadFiveCopies: 'Upload up to 5 copies'
   }
 }

+ 6 - 2
src/locales/zh-CN.js

@@ -15,7 +15,9 @@ export default {
     delMsg: '删除成功',
     uploadPictures: '上传图片',
     uploadSucMsg: '上传成功',
-    uploadErrMsg: '上传失败'
+    uploadErrMsg: '上传失败',
+    fileSizeExceed: '文件大小不能超过10M',
+    fileFormatIncorrect: '上传的文件格式不正确'
   },
   sys: {
     api: {
@@ -104,6 +106,8 @@ export default {
     firstWorkTime: '首次工作时间',
     dataDefaultPrompt: '请填写您的',
     expand: '展开',
-    retract: '收起'
+    retract: '收起',
+    deleteAttachment: '是否确认删除此附件?',
+    uploadFiveCopies: '最多上传5份'
   }
 }

+ 3 - 1
src/store/user.js

@@ -1,5 +1,5 @@
 import { defineStore } from 'pinia'
-import { setToken, removeToken } from '@/utils/auth'
+import { setToken, removeToken, setRefreshToken } from '@/utils/auth'
 import { smsLogin, passwordLogin, getBaseInfo, passwordLoginOfEnterprise, smsLoginOfEnterprise } from '@/api/common/index'
 import { logout } from '@/api/common/index'
 import { getUserInfo } from '@/api/personal/user'
@@ -22,6 +22,7 @@ export const useUserStore = defineStore('user',
           const loginApi = data.type === 330 ? smsLoginOfEnterprise : smsLogin
           loginApi(data).then(res => {
             setToken(res.accessToken)
+            setRefreshToken(res.refreshToken)
             this.accountInfo = res
             localStorage.setItem('accountInfo', JSON.stringify(res))
             localStorage.setItem('expiresTime', res.expiresTime) // token过期时间
@@ -37,6 +38,7 @@ export const useUserStore = defineStore('user',
           const loginApi = data.type === 330 ? passwordLoginOfEnterprise : passwordLogin
           loginApi(data).then(res => {
             setToken(res.accessToken)
+            setRefreshToken(res.refreshToken)
             this.accountInfo = res
             localStorage.setItem('accountInfo', JSON.stringify(res))
             localStorage.setItem('expiresTime', res.expiresTime) // token过期时间

+ 8 - 2
src/utils/auth.js

@@ -10,13 +10,19 @@ export const setToken = (token) => {
 
 // 清除token
 export const removeToken = () => {
-  return localStorage.removeItem('ACCESS_TOKEN')
+  localStorage.removeItem('ACCESS_TOKEN')
+  localStorage.removeItem('REFRESH_TOKEN')
 }
-// 
+// 获取刷新token
 export const getRefreshToken = () => {
   return localStorage.getItem('REFRESH_TOKEN')
 }
 
+// 设置刷新token
+export const setRefreshToken = (refreshToken) => {
+  return localStorage.setItem('REFRESH_TOKEN', refreshToken)
+}
+
 // 租户ID
 export const getTenantId = () => {
   return localStorage.getItem('tenantId')

+ 83 - 14
src/views/PersonalCenter/dynamic/right.vue

@@ -13,38 +13,107 @@
       <div>
         <span class="title">附件简历</span>
         <span class="more-text">最多上传5份</span>
-        <span class="upload--text">上传</span>
+        <span class="upload--text cursor-pointer" @click="openFileInput">
+          上传
+          <input
+            type="file"
+            ref="fileInput"
+            accept=".pdf, .doc, .docx"
+            style="display: none;"
+            @change="handleUploadFile"
+          />
+        </span>
       </div>
-      <div class="d-flex attachment-item my-2 cursor-pointer" v-for="(k, i) in attachmentList" :key="i">
+      <div class="d-flex attachment-item my-2 cursor-pointer" v-for="k in attachmentList" :key="k.id">
         <v-icon color="primary">mdi-file-account</v-icon>
-        <div class="file-name ellipsis ml-2">{{ k.file_name }}</div>
-        <v-icon class="ml-8" color="error">mdi-trash-can-outline</v-icon>
+        <div class="file-name ellipsis ml-2">{{ k.title }}</div>
+        <!-- <v-icon class="ml-8 mr-2" color="primary" @click="handleDownload(k)">mdi-download-box-outline</v-icon> -->
+        <v-icon class="ml-8" color="error" @click="handleDelete(k)">mdi-trash-can-outline</v-icon>
       </div>
-      <div class="border-bottom-dashed my-3"></div>
-      <div class="last-update">最后更新于2024-05-23 12:20:25</div>
     </div>
   </div>
 </template>
 
 <script setup>
 defineOptions({ name: 'personal-center-right'})
+import { ref } from 'vue'
+import { uploadFile } from '@/api/common'
+import { getPersonResumeCv, savePersonResumeCv, deletePersonResumeCv } from '@/api/resume'
+import { useI18n } from '@/hooks/web/useI18n'
+import Snackbar from '@/plugins/snackbar'
+import Confirm from '@/plugins/confirm'
 
+const { t } = useI18n()
 const resumeList = [
   { icon: 'mdi-upload', title: '置顶简历', desc: '增加更多曝光度' },
   { icon: 'mdi-refresh', title: '刷新简历', desc: '提升简历活跃度' }
 ]
-const attachmentList = [
-  { file_name: '陈芊芊-Web前端开发工程师-15425236412' },
-  { file_name: '陈芊芊-Web前端开发工程师-15425236412' },
-  { file_name: '陈芊芊-Web前端开发工程师-15425236412' },
-  { file_name: '陈芊芊-Web前端开发工程师-15425236412' },
-  { file_name: '陈芊芊-Web前端开发工程师-15425236412' }
-]
+
+// 获取附件
+const attachmentList = ref([])
+const getList = async () => {
+  const data = await getPersonResumeCv()
+  attachmentList.value = data
+}
+getList()
+
+// 选择文件
+const fileInput = ref()
+const clicked = ref(false)
+const openFileInput = () => {
+  if (attachmentList.value.length >= 5) return Snackbar.warning(t('resume.uploadFiveCopies'))
+  if (clicked.value) return
+  clicked.value = true
+  fileInput.value.click()
+  clicked.value = false
+}
+
+// 上传附件
+const typeList = ['pdf', 'doc', 'docx']
+const handleUploadFile = async (e) => {
+  const file = e.target.files[0]
+  const size = file.size
+  if (size / (1024*1024) > 10) {
+    Snackbar.warning(t('common.fileSizeExceed'))
+    return
+  }
+  const arr = file.name.split('.')
+  if (typeList.indexOf(arr[arr.length - 1]) < 0) {
+    Snackbar.warning(t('common.fileFormatIncorrect'))
+    return
+  }
+  const formData = new FormData()
+  formData.append('file', file)
+  const { data } = await uploadFile(formData)
+  if (!data) return
+  Snackbar.success(t('common.uploadSucMsg'))
+  await savePersonResumeCv({ title: file.name, url: data })
+  await getList()
+}
+
+// 删除
+const handleDelete = ({ id }) => {
+  Confirm(t('common.confirmTitle'), t('resume.deleteAttachment')).then(async () => {
+    await deletePersonResumeCv(id)
+    Snackbar.success(t('common.delMsg'))
+    getList()
+  })
+}
+
+// const handleDownload = (k) => {
+//   const link = document.createElement('a')
+//   link.href = k.url
+//   link.setAttribute('download', '')
+//   document.body.appendChild(link)
+//   link.click()
+//   document.body.removeChild(link)
+// }
+
 </script>
 
 <style scoped lang="scss">
 .radius {
-  border-radius: 8px
+  border-radius: 8px;
 }
 .title {
   font-weight: 600;

+ 1 - 1
src/views/enterprise/positionManagement/components/baseInfo.vue

@@ -27,7 +27,7 @@
 <script setup>
 defineOptions({ name: 'position-add-baseInfo'})
 import CtForm from '@/components/CtForm'
-import { reactive, ref } from 'vue'
+import { reactive, ref, defineExpose } from 'vue'
 import textUI from '@/components/FormUI/TextInput'
 import jobTypeCard from '@/components/jobTypeCard'
 

+ 1 - 1
src/views/login/components/passwordPage.vue

@@ -34,7 +34,7 @@
 </template>
 
 <script setup name="passwordPage">
-import { ref, reactive } from 'vue'
+import { ref, reactive, defineExpose } from 'vue'
 defineOptions({ name: 'password-form' })
 const passwordType = ref(false)
 

+ 1 - 1
src/views/recruit/position/components/cityFilter.vue

@@ -12,7 +12,7 @@
 </template>
 <script setup>
 import areaTree from './areaCascader'
-import { reactive, ref } from 'vue'
+import { reactive, ref, defineExpose } from 'vue'
 
 defineOptions({name: 'retrieval-components-cityFilter'})
 const emits = defineEmits(['updateCheckedInput'])

+ 1 - 1
src/views/recruit/position/components/conditionFilter.vue

@@ -19,7 +19,7 @@ import payScope from './conditionFilter/payScope.vue'
 import educationType from './conditionFilter/educationType.vue'
 import scale from './conditionFilter/scale.vue'
 import financingStatus from './conditionFilter/financingStatus.vue'
-import { reactive } from 'vue'
+import { reactive, defineExpose } from 'vue'
 import { useRoute } from 'vue-router'
 defineOptions({name: 'retrieval-components-conditionFilter'})
 const emits = defineEmits(['conditionFilterChange'])