Explorar o código

基本信息加上头像

lifanagju_citu hai 5 meses
pai
achega
0c5924dd18

+ 119 - 3
src/plugins/necessaryInfo/components/infoForm.vue

@@ -1,7 +1,30 @@
 <template>
 <template>
   <div style="width: 100%;">
   <div style="width: 100%;">
-    <CtForm ref="formPageRef" :items="items"></CtForm>
+    <CtForm ref="formPageRef" :items="items">
+      <!-- 头像 -->
+      <template #avatar="{ item }">
+        <!-- <div class="d-flex flex-column">
+          <div class="d-flex align-center">
+          </div>
+        </div> -->
+        <div style="color: #7a7a7a; min-width: 52px;">头像:</div>
+        <div class="avatarsBox" @mouseover="showIcon = true" @mouseleave="showIcon = false">
+          <v-avatar class="elevation-5" size=80 :image="getUserAvatar(item.value, male)"></v-avatar>
+          <div v-show="showIcon" @click="openFileInput" v-bind="$attrs" class="mdi mdi-camera-outline">
+            <input
+              type="file"
+              ref="fileInput"
+              accept="image/png, image/jpg, image/jpeg"
+              style="display: none;"
+              @change="handleUploadFile"
+            />
+          </div>
+        </div>
+        <div style="font-size: 14px; color: var(--color-999);">只支持JPG、JPEG、PNG类型的图片,大小不超过20M</div>
+      </template>
+    </CtForm>
   </div>
   </div>
+  <ImgCropper :visible="isShowCopper" :image="selectPic" :cropBoxResizable="true" @submit="handleHideCopper" :aspectRatio="1 / 1" @close="isShowCopper = false"></ImgCropper>
 </template>
 </template>
 
 
 <script setup>
 <script setup>
@@ -10,6 +33,10 @@ defineOptions({name: 'necessaryInfo-InfoForm'})
 import { reactive, ref } from 'vue'
 import { reactive, ref } from 'vue'
 import { checkEmail } from '@/utils/validate'
 import { checkEmail } from '@/utils/validate'
 import { enterpriseSearchByName } from '@/api/recruit/personal/resume'
 import { enterpriseSearchByName } from '@/api/recruit/personal/resume'
+import { getUserAvatar } from '@/utils/avatar'
+import { uploadFile } from '@/api/common'
+import Snackbar from '@/plugins/snackbar'
+import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
 
 
 const props = defineProps({
 const props = defineProps({
   option: {
   option: {
@@ -37,9 +64,21 @@ const getEnterpriseData = async (name) => {
   }
   }
 }
 }
 
 
+// 图片裁剪
+const selectPic = ref('')
+const isShowCopper = ref(false)
+
+const male = ref('1')
+const showIcon = ref(false)
 let positionName = null
 let positionName = null
 const items = ref({
 const items = ref({
   options: [
   options: [
+    {
+      slotName: 'avatar',
+      key: 'avatar',
+      value: '',
+      flexStyle: 'align-center'
+    },
     {
     {
       type: 'text',
       type: 'text',
       key: 'name',
       key: 'name',
@@ -68,7 +107,8 @@ const items = ref({
       outlined: true,
       outlined: true,
       dictTypeName: 'menduner_sex',
       dictTypeName: 'menduner_sex',
       rules: [v => !!v || '请选择性别'],
       rules: [v => !!v || '请选择性别'],
-      items: []
+      items: [],
+      change: val => male.value = val
     },
     },
     {
     {
       type: 'phoneNumber',
       type: 'phoneNumber',
@@ -200,6 +240,56 @@ const items = ref({
   ]
   ]
 })
 })
 
 
+
+// 选择文件
+const fileInput = ref()
+const clicked = ref(false)
+const openFileInput = () => {
+  if (clicked.value) return
+  clicked.value = true
+  fileInput.value.click()
+  clicked.value = false
+}
+
+// 上传头像
+const accept = ['jpg', 'png', 'jpeg']
+const handleUploadFile = async (e) => {
+  const file = e.target.files[0]
+  if (!file) return
+
+  const fileType = file.name.split('.')[1]
+  if (!accept.includes(fileType)) return Snackbar.warning('请上传图片格式文件')
+
+  const size = file.size
+  if (size / (1024*1024) > 20) {
+    Snackbar.warning(t('common.fileSizeExceed'))
+    return
+  }
+
+  const reader = new FileReader()
+  reader.readAsDataURL(file)
+  reader.onload = () => {
+    selectPic.value = String(reader.result)
+    isShowCopper.value = true
+  }
+}
+
+const handleHideCopper = (data) => {
+  isShowCopper.value = false
+  if (data) {
+    const { file } = data
+    if (!file) return
+
+    const formData = new FormData()
+    formData.append('file', file)
+    formData.append('path', 'img')
+    uploadFile(formData).then(async ({ data }) => {
+      if (!data) return
+      items.value.options.find(e => e.key === 'avatar').value = data
+    })
+  }
+}
+
 // 获取字典内容
 // 获取字典内容
 const getDictData = async (dictTypeName, key) => {
 const getDictData = async (dictTypeName, key) => {
   const item = items.value.options.find(e => e.key === key)
   const item = items.value.options.find(e => e.key === key)
@@ -255,6 +345,7 @@ const getQuery = async () => {
     if (Object.prototype.hasOwnProperty.call(e, 'data')) return obj[e.key] = e.data
     if (Object.prototype.hasOwnProperty.call(e, 'data')) return obj[e.key] = e.data
     obj[e.key] = e.value === '' ? null : e.value
     obj[e.key] = e.value === '' ? null : e.value
   })
   })
+  if (!obj.avatar) obj.avatar = getUserAvatar(null, obj.sex)
   query = Object.assign(query, obj)
   query = Object.assign(query, obj)
   dealQuery()
   dealQuery()
   return query
   return query
@@ -263,4 +354,29 @@ const getQuery = async () => {
 defineExpose({
 defineExpose({
   getQuery
   getQuery
 })
 })
-</script>
+</script>
+<style scoped lang="scss">
+.avatarsBox {
+  height: 80px;
+  width: 80px;
+  position: relative;
+  cursor: pointer;
+  margin: 32px;
+  margin-right: 40px;
+  .img {
+    width: 100%;
+    height: 100%;
+  }
+  .mdi {
+    font-size: 42px;
+    color: #fff;
+  }
+  div {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    border-radius: 50%;
+  }
+}
+</style>

+ 4 - 0
src/plugins/necessaryInfo/components/necessaryInfoDialog.vue

@@ -13,6 +13,7 @@
     >
     >
       <infoForm ref="formRef" :option="option"></infoForm>
       <infoForm ref="formRef" :option="option"></infoForm>
     </CtDialog>
     </CtDialog>
+    <Loading :visible="overlay"></Loading>
   </v-app>
   </v-app>
 </template>
 </template>
 
 
@@ -51,6 +52,7 @@ onMounted(() => {
   dialog.value = true
   dialog.value = true
 })
 })
 
 
+const overlay = ref(false)
 const formRef = ref()
 const formRef = ref()
 const simpleInfoSubmit = async () => {
 const simpleInfoSubmit = async () => {
   if (!getToken()) {
   if (!getToken()) {
@@ -62,6 +64,7 @@ const simpleInfoSubmit = async () => {
   try {
   try {
     const obj = await formRef.value.getQuery()
     const obj = await formRef.value.getQuery()
     if (!obj) return
     if (!obj) return
+    overlay.value = true
     await savePersonSimpleInfo(obj)
     await savePersonSimpleInfo(obj)
     Snackbar.success('保存成功')
     Snackbar.success('保存成功')
     // const info = localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {}
     // const info = localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {}
@@ -69,6 +72,7 @@ const simpleInfoSubmit = async () => {
     // localStorage.setItem('necessaryInfoReady', 'ready') //
     // localStorage.setItem('necessaryInfoReady', 'ready') //
     await useUserStore().getUserBaseInfos() // 更新用户信息
     await useUserStore().getUserBaseInfos() // 更新用户信息
     dialog.value = false
     dialog.value = false
+    overlay.value = false
     props.sure()
     props.sure()
   } catch (error) {
   } catch (error) {
     console.error('error', error)
     console.error('error', error)