Переглянути джерело

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

lifanagju_citu 2 місяців тому
батько
коміт
b40ac93cd4

+ 16 - 0
src/api/school.js

@@ -31,6 +31,22 @@ export const getSchoolAuditById = async (data) => {
 	})
 }
 
+// 学校信息编辑
+export const schoolBaseInfoEdit = async (data) => {
+	return await request.post({
+		url: '/app-api/flames/school/basic/update',
+		data
+	})
+}
+
+// 院系信息编辑
+export const schoolDepartmentEdit = async (data) => {
+	return await request.post({
+		url: '/app-api/flames/school/department/saves',
+		data
+	})
+}
+
 // 学校组织查询
 export const schoolOrganization = async (data) => {
 	return await request.post({

+ 14 - 27
src/layout/teacher/navBar.vue

@@ -14,14 +14,16 @@
               <template v-slot:activator="{ props }">
                 <div class="d-flex ml-3 pl-2 align-center cursor-pointer" v-bind="props">
                   <v-avatar>
-                    <v-img alt="" :src="getUserAvatar(baseInfo?.avatar, baseInfo?.sex)"></v-img>
+                    <v-img alt="" :src="getUserAvatar(schoolInfo?.school?.headImg, schoolInfo?.school?.teacherSex)"></v-img>
                   </v-avatar>
-                  <div class="ml-2 commonHover">{{ formatName(baseInfo?.name ?? baseInfo?.phone) }}</div>
+                  <div class="ml-2 commonHover">
+                    {{ formatName(schoolInfo?.school?.schoolName) + ' - ' + schoolInfo?.school?.teacherNickname ?? schoolInfo?.school?.phone }}
+                  </div>
                 </div>
               </template>
 
               <v-list>
-                <v-list-item v-for="(item, index) in items" :key="index" @click="item.change">
+                <v-list-item v-for="(item, index) in menuList" :key="index" @click="item.change">
                   <template v-slot:prepend>
                     <v-icon :icon="item.icon"></v-icon>
                   </template>
@@ -30,8 +32,6 @@
               </v-list>
             </v-menu>
           </div>
-          <!-- 消息通知 -->
-          <MessageNotification class="commonHover2" path="/recruit/enterprise/chatTools"></MessageNotification>
         </div>
       </div>
     </v-toolbar>
@@ -39,12 +39,11 @@
 </template>
 
 <script setup>
-import { computed, ref, onMounted } from 'vue'
+import { ref, onMounted } from 'vue'
 import { getToken } from '@/utils/auth'
 import { useUserStore } from '@/store/user'; const userStore = useUserStore()
 import { useRouter } from 'vue-router'; const router = useRouter()
 import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
-import MessageNotification from '../message.vue'
 import { getUserAvatar } from '@/utils/avatar'
 import { formatName } from '@/utils/getText';
 defineOptions({ name: 'teacher-navbar' })
@@ -59,32 +58,28 @@ defineProps({
 const showBall = ref(false)
 
 onMounted(() => {
-  if (getToken(1)) {
+  if (getToken()) {
     showBall.value = true
   }
 })
 
 const handleLogoClick = () => { window.open('/recruitHome') } // 点击logo
 
-// 退出登录、切换求职者
-const handleLogout = async (exit = true) => {
-  if (exit) await userStore.userLogout(2)
-  router.push({ path: '/recruitHome' })
+// 退出登录
+const handleLogout = async () => {
+  await userStore.userLogout(1)
+  router.push({ path: '/flame' })
 }
 
 const menuList = ref([
-  { title: t('setting.editPassword'), icon: 'mdi-shield-lock-open-outline', change: () => router.push({ path: '/recruit/enterprise/staffChangePassword' }) },
   { title: t('setting.logOut'), icon: 'mdi-logout', change: handleLogout }
 ])
-const items = computed(() => {
-  return menuList.value.filter(item => !item.hidden)
-})
 
-// 企业logo、用户基本信息
-let baseInfo = ref(JSON.parse(localStorage.getItem('entBaseInfo')) || {})
+// 学校信息
+let schoolInfo = ref(JSON.parse(localStorage.getItem('schoolInfo')) || {})
 
 userStore.$subscribe((mutation, state) => {
-  if (Object.keys(state.entBaseInfo).length) baseInfo.value = state.entBaseInfo
+  if (Object.keys(state.schoolInfo).length) schoolInfo.value = state.schoolInfo
 })
 
 </script>
@@ -124,12 +119,4 @@ userStore.$subscribe((mutation, state) => {
   color: var(--color-333);
   font-size: 15px;
 }
-.enterprise-septal-line {
-  width: 1px;
-  display: inline-block;
-  height: 20px;
-  vertical-align: middle;
-  background-color: #fff;
-  margin: 0 10px;
-}
 </style>

+ 1 - 1
src/router/modules/components/recruit/teacher.js

@@ -89,7 +89,7 @@ const teacher = [
     component: Layout,
     name: 'teacherCertification',
     meta: {
-      title: '教师认证',
+      title: '账号信息',
       enName: 'Teacher Certification',
       icon: 'mdi-human-male-board'
     },

+ 5 - 12
src/store/user.js

@@ -72,7 +72,7 @@ export const useUserStore = defineStore('user',
             updateEventList(true) // 获取规则配置跟踪列表
             await this.getUserInfos()
             await this.getUserBaseInfos('', { chooseRole: data.chooseRole })
-            if (data?.schoolRegister) await this.getSchoolInfo()
+            if (data?.schoolRegister) await this.getSchoolInfo(true)
             resolve(res)
           }).catch(err => { reject(err) })
         })
@@ -276,40 +276,33 @@ export const useUserStore = defineStore('user',
       },
 
       // 获取学校基本信息
-      async getSchoolInfo () {
+      async getSchoolInfo (isRegister = false) {
         const data = await getSchoolInformation()
-        console.log(data, '学校基本信息========================')
         this.schoolInfo = data
         localStorage.setItem('schoolInfo', data ? JSON.stringify(data) : '{}')
 
-        // 没有注册过,直接跳转到学校注册页面
+        // 注册时执行下方内容
+        if (!isRegister) return
+
         if (!data || !data.school || !Object.keys(data.school).length) {
           console.log('没有注册过,直接跳转到学校注册页面')
           router.push({ path: '/register/schoolIndex' })
         }
-
-        // 审核中,等待审核
         else if (data.school?.authStatus === '0') {
           console.log('审核中,等待审核')
           localStorage.setItem('registerSchoolInfo', JSON.stringify(data))
           router.push({ path: '/register/school/inReview' })
         }
-
-        // 审核通过直接进入首页
         else if (data.school?.authStatus === '1') {
           localStorage.setItem('schoolInfo', JSON.stringify(data))
           console.log('审核通过直接进入老师页面')
           router.push('/recruit/teacher') 
         }
-
-        // 审核不通过,重新填写信息提交
         else if (data.school?.authStatus === '2') {
           console.log('审核不通过,重新填写信息提交')
           localStorage.setItem('registerSchoolInfo', JSON.stringify(data))
           router.push({ path: '/register/school/inReview' })
         }
-
-        // return data
       }
     }
   },

+ 16 - 2
src/views/recruit/teacher/teacherCertification/index.vue

@@ -1,10 +1,24 @@
-<!--  -->
 <template>
-  <div>教师认证</div>
+  <v-card class="card-box pa-3">
+    <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#f7f8fa" class="mb-10">
+      <v-tab :value="0">老师基本信息</v-tab>
+      <v-tab :value="1">学校基本信息</v-tab>
+    </v-tabs>
+
+    <SchoolInfo v-if="tab === 1" />
+    <TeacherInfo v-else />
+
+  </v-card>
 </template>
 
 <script setup>
 defineOptions({name: 'teacher-certification'})
+import { ref } from 'vue'
+import SchoolInfo from './schoolInfo.vue'
+import TeacherInfo from './teacherInfo.vue'
+
+const tab = ref(0)
 </script>
+
 <style lang="scss" scoped>
 </style>

+ 123 - 0
src/views/recruit/teacher/teacherCertification/schoolInfo.vue

@@ -0,0 +1,123 @@
+<template>
+	<div class="d-flex flex-column align-center">
+		<CtForm ref="CtFormRef" :items="formItems" style="width: 900px;margin: 0 auto">
+      <template #photos="{ item }">
+        <div>
+					<p class="color-primary">*请上传学校环境图片(最多可上传9张图片)</p>
+					<p class="mb-3 color-primary">*只支持JPG、JPEG、PNG类型的图片</p>
+					<Imgs v-model="item.value" :showTips="false" limit="9"></Imgs>
+				</div>
+      </template>
+    </CtForm>
+    <v-btn class="buttons my-10" color="primary" @click.stop="handleSubmit">{{ $t('common.save') }}</v-btn>
+	</div>
+
+  <Loading :visible="overlay"></Loading>
+</template>
+
+<script setup>
+defineOptions({ name: 'schoolInfo' })
+import { ref, onMounted } from 'vue'
+import { useI18n } from '@/hooks/web/useI18n'
+import { useUserStore } from '@/store/user'
+import Snackbar from '@/plugins/snackbar'
+import { cloneDeep } from 'lodash'
+import { schoolBaseInfoEdit } from '@/api/school'
+
+const overlay = ref(false)
+const CtFormRef = ref()
+const formItems = ref({
+  options: [
+    {
+      type: 'text',
+      key: 'schoolName',
+      value: '',
+      col: 6,
+      label: '学校名称 *',
+      flexStyle: 'mr-3',
+      rules: [v => !!v || '请输入您所在的学校名称']
+    },
+    {
+      type: 'text',
+      key: 'email',
+      value: '',
+      col: 6,
+      label: '学校邮箱'
+    },
+    {
+      type: 'text',
+      key: 'site',
+      value: '',
+      col: 6,
+      flexStyle: 'mr-3',
+      label: '学校官网'
+    },
+    {
+      type: 'text',
+      key: 'schoolAdderss',
+      value: '',
+      col: 6,
+      label: '学校地址'
+    },
+    {
+      type: 'textarea',
+      key: 'introduce',
+      value: null,
+      counter: 2000,
+      rows: 6,
+      label: '学校简介',
+      outlined: true
+    },
+    {
+      slotName: 'photos',
+      key: 'photos',
+      value: []
+    }
+  ]
+})
+
+const { t } = useI18n()
+const userStore = useUserStore()
+const schoolInfo = ref(localStorage.getItem('schoolInfo') ? JSON.parse(localStorage.getItem('schoolInfo')) : {})
+// 监听store变化
+userStore.$subscribe((mutation, state) => {
+  schoolInfo.value = state.schoolInfo
+})
+
+onMounted(async () => {
+	await userStore.getSchoolInfo()
+
+	formItems.value.options.forEach(item => {
+		item.value = schoolInfo.value?.school[item.key]
+	})
+})
+
+// 保存
+const handleSubmit = async () => {
+  const { valid } = await CtFormRef.value.formRef.validate()
+  if (!valid) return
+
+  overlay.value = true
+
+  let obj = cloneDeep(schoolInfo.value?.school)
+	formItems.value.options.forEach(item => {
+		obj[item.key] = item.value
+	})
+
+  try {
+		await schoolBaseInfoEdit(obj)
+
+		setTimeout(async () => {
+			await userStore.getSchoolInfo()
+			Snackbar.success(t('common.saveMsg'))
+			overlay.value = false
+		}, 1000)
+	} catch {
+		overlay.value = false
+	}
+}
+</script>
+
+<style scoped lang="scss">
+
+</style>

+ 252 - 0
src/views/recruit/teacher/teacherCertification/teacherInfo.vue

@@ -0,0 +1,252 @@
+<template>
+	<div class="d-flex flex-column align-center">
+		<CtForm ref="CtFormRef" :items="formItems" style="width: 700px;">
+			<template #headImg="{ item }">
+        <div style="color: #7a7a7a;">头像</div>
+        <div class="avatarsBox" @mouseover="showIcon = true" @mouseleave="showIcon = false">
+          <v-avatar class="elevation-5" size=80 :image="getUserAvatar(item.value, baseInfo?.sex)"></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>
+			<template #department>
+				<div class="pa-5 mb-3" style="width: 100%; border: 1px dashed #ccc; border-radius: 4px;">
+					<p class="color-999 font-size-14 mb-3">
+						<span class="color-error">*</span>
+						负责院系
+					</p>
+					<div v-for="(k, index) in departmentList" :key="index" class="d-flex align-center mb-5">
+						<TextInput v-model="k.departmentTitle" :item="textItem" />
+						<v-icon v-if="index > 0" class="ml-3 cursor-pointer" @click="handleDeleteDepartment(index)" color="error">mdi-close-circle</v-icon>
+					</div>
+					<v-btn class="mt-3" color="primary" prepend-icon="mdi-plus" size="small" @click="handleAddDepartment">添加院系</v-btn>
+				</div>
+			</template>
+		</CtForm>
+		<v-btn class="buttons my-10" color="primary" @click.stop="handleSubmit">{{ $t('common.save') }}</v-btn>
+	</div>
+
+	<Loading :visible="overlay"></Loading>
+  <ImgCropper :visible="isShowCopper" :image="selectPic" :cropBoxResizable="true" @submit="handleHideCopper" :aspectRatio="1 / 1" @close="isShowCopper = false"></ImgCropper>
+</template>
+
+<script setup>
+defineOptions({ name: 'teacherInfo'})
+import { ref, onMounted } from 'vue'
+import { getUserAvatar } from '@/utils/avatar'
+import { getDict } from '@/hooks/web/useDictionaries'
+import { useI18n } from '@/hooks/web/useI18n'
+import { useUserStore } from '@/store/user'
+import { cloneDeep } from 'lodash'
+import { uploadFile } from '@/api/common'
+import Snackbar from '@/plugins/snackbar'
+import { schoolBaseInfoEdit, schoolDepartmentEdit } from '@/api/school'
+
+const showIcon = ref(false)
+const CtFormRef = ref()
+const departmentList = ref([{ departmentTitle: '' }])
+const textItem = {
+	type: 'text',
+  key: 'departmentTitle',
+	width: 450,
+  label: '院系名称 *',
+	hideDetails: true,
+  rules: [v => !!v || '请输入您负责的院系名称']
+}
+const formItems = ref({
+  options: [
+		{
+      slotName: 'headImg',
+      key: 'headImg',
+      value: '',
+      flexStyle: 'align-center'
+    },
+		{
+      type: 'ifRadio',
+      key: 'teacherSex',
+      value: '1',
+			defaultValue: '1',
+      label: '性别 *',
+      width: 50,
+      dictTypeName: 'menduner_sex',
+			rules: [v => !!v || '请选择您的性别'],
+      items: []
+    },
+    {
+      type: 'text',
+      key: 'teacherNickname',
+      value: '',
+      label: '昵称 *',
+      rules: [v => !!v || '请输入您的昵称']
+    },
+		{
+      type: 'phoneNumber',
+      key: 'phone',
+      value: '',
+      label: '联系电话 *',
+      rules: [v => !!v || '请输入您的联系电话']
+    },
+    {
+			slotName: 'department',
+      key: 'departmentTitle',
+			noParam: true,
+      label: '负责院系 *',
+      rules: [v => !!v || '请填写您在学校负责的院系']
+    }
+  ]
+})
+
+const { t } = useI18n()
+const userStore = useUserStore()
+const schoolInfo = ref(localStorage.getItem('schoolInfo') ? JSON.parse(localStorage.getItem('schoolInfo')) : {})
+// 监听store变化
+userStore.$subscribe((mutation, state) => {
+  schoolInfo.value = state.schoolInfo
+})
+
+onMounted(async () => {
+	await userStore.getSchoolInfo()
+
+	// 获取性别字典数据
+	const sexItem = formItems.value.options.find(e => e.key === 'teacherSex')
+	if (!sexItem || !Object.keys(sexItem).length) return
+	const { data } = await getDict(sexItem.dictTypeName)
+	sexItem.items = data || []
+
+	formItems.value.options.forEach(item => {
+		if (!item.noParam) item.value = schoolInfo.value?.school[item.key] || item.defaultValue
+		else {
+			departmentList.value = schoolInfo.value?.schoolDepartments && schoolInfo.value.schoolDepartments.length ? schoolInfo.value.schoolDepartments : [{ departmentTitle: '' }]
+		}
+	})
+})
+
+// 添加院系
+const handleAddDepartment = () => {
+	departmentList.value.push({ departmentTitle: '' })
+}
+// 删除院系
+const handleDeleteDepartment = (index) => {
+	departmentList.value.splice(index, 1)
+}
+
+// 图片裁剪
+const overlay = ref(false)
+const selectPic = ref('')
+const isShowCopper = ref(false)
+
+// 选择文件
+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 arr = file.name.split('.')
+  const fileType = arr?.length ? arr[arr.length-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
+      formItems.value.options.find(e => e.key === 'headImg').value = data
+    })
+  }
+}
+
+// 保存
+const handleSubmit = async () => {
+  const { valid } = await CtFormRef.value.formRef.validate()
+  if (!valid) return
+
+	const isCheck = departmentList.value.every(item => item.departmentTitle)
+	if (!isCheck) return Snackbar.warning('请将院系信息填写完整')
+
+  overlay.value = true
+
+	let obj = cloneDeep(schoolInfo.value?.school)
+	formItems.value.options.forEach(item => {
+		if (item.noParam) return
+		obj[item.key] = item.value
+	})
+
+	try {
+		await schoolBaseInfoEdit(obj)
+		// 院系编辑
+		await schoolDepartmentEdit(departmentList.value)
+
+		setTimeout(async () => {
+			await userStore.getSchoolInfo()
+			Snackbar.success(t('common.saveMsg'))
+			overlay.value = false
+		}, 1000)
+	} catch {
+		overlay.value = false
+	}
+}
+</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>