Browse Source

学生详情

lifanagju_citu 2 months ago
parent
commit
d960c78d5e

+ 2 - 1
components.d.ts

@@ -33,6 +33,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']
+    ElTree: typeof import('element-plus/es')['ElTree']
     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']
@@ -46,7 +47,7 @@ declare module 'vue' {
     IndustryTypeCard: typeof import('./src/components/industryTypeCard/index.vue')['default']
     Info: typeof import('./src/components/Enterprise/info.vue')['default']
     InitPay: typeof import('./src/components/personalRecharge/initPay.vue')['default']
-    Item: typeof import('./src/components/PositionLongStrip/item.vue')['default']
+    Item: typeof import('./src/components/Position/item.vue')['default']
     JobTypeCard: typeof import('./src/components/jobTypeCard/index.vue')['default']
     ListGroup: typeof import('./src/components/FormUI/nestedListGroup/components/listGroup.vue')['default']
     Loading: typeof import('./src/components/Loading/index.vue')['default']

+ 24 - 0
src/api/school.js

@@ -63,6 +63,30 @@ export const studentList = async (data) => {
 	})
 }
 
+// 学生详情
+export const stuDetail = async (data) => {
+	return await request.post({
+		url: '/app-api/flames/student/detail',
+		data
+	})
+}
+
+// 实习证书
+export const certificateList = async (data) => {
+	return await request.post({
+		url: '/app-api/flames/student/internship/certificate/list',
+		data
+	})
+}
+
+// 推荐信
+export const recommendationList = async (data) => {
+	return await request.post({
+		url: '/app-api/flames/student/recommendation/list',
+		data
+	})
+}
+
 // 学生实习情况
 export const studentPracticeStatistics = async (data) => {
 	return await request.post({

+ 1 - 0
src/layout/teacher.vue

@@ -44,6 +44,7 @@ const key = computed(() => {
 })
 
 const whiteList = [
+  '/recruit/teacher/studentList/detail',
 ]
 // 查询是否在白名单内,在则不展示面包屑
 const isInWhiteList = (url)=> {

+ 87 - 0
src/views/recruit/teacher/studentList/components/baseInfo.vue

@@ -0,0 +1,87 @@
+<!-- 基本信息 -->
+<template>
+  <div class="d-flex">
+    <!-- 头像 -->
+    <div class="avatarsBox">
+      <v-badge
+        v-if="info?.sex === '1' || info?.sex === '2'"
+        bordered 
+        offset-x="-25" 
+        offset-y="33" 
+        :color="info?.sex ? (info?.sex === '1' ? '#1867c0' : 'error') : 'error'" 
+        :icon="info?.sex ? (info?.sex === '1' ? 'mdi-gender-male' : 'mdi-gender-female') : 'mdi-gender-female'">
+        <v-avatar size=80 :image="getUserAvatar(info?.avatar, info?.sex)"></v-avatar>
+      </v-badge>
+      <v-avatar v-else size=80 :image="getUserAvatar(info?.student?.studentHeadImg, info?.sex)"></v-avatar>
+    </div>
+    <!-- 信息 -->
+    <div style="flex: 1;">
+      <span style="font-size: 20px; font-weight: 600;color: var(--color-666);">{{ info?.student?.studentName }}</span>
+      <div class="d-flex mt-2 listBox">
+        <span>{{ info?.student?.schoolDepartmentName }}</span>
+        <span v-if="info?.student?.schoolDepartmentName && info?.student?.majorName" class="mx-3">|</span>
+        <span>{{ info?.student?.majorName }}</span>
+      </div>
+      <view class="mt-2 listBox">{{ info?.student?.schoolClassName }}</view>
+    </div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({name: 'studentList-student-details-baseInfo'})
+import { ref } from 'vue'
+import { getUserAvatar } from '@/utils/avatar'
+
+const props = defineProps({
+  data: Object
+})
+const info = ref({})
+if (props.data && Object.keys(props.data).length) {
+  info.value = props.data
+  info.value.sex = info.value?.student?.studentSex === '男' ? '1' : info.value?.student?.studentSex === '女' ? '2' : info.value?.student?.sex || null
+}
+</script>
+
+<style lang="scss" scoped>
+.avatarsBox {
+  height: 80px;
+  width: 80px;
+  position: relative;
+  // 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%;
+  }
+}
+.listBox {
+  display: flex;
+  flex-wrap: wrap; /* 允许换行 */
+  width: 100%; /* 设置容器宽度 */
+  // height: 68px;
+  overflow: hidden;
+  color: var(--color-777);
+  div {
+    margin-right: 50px;
+    span {
+      height: 32px;
+      line-height: 32px;
+    }
+    .mdi {
+      font-size: 22px;
+      margin-right: 8px;
+    }
+  }
+}
+</style>

+ 114 - 0
src/views/recruit/teacher/studentList/components/other.vue

@@ -0,0 +1,114 @@
+<!-- 基本信息 -->
+<template>
+  <div>
+    <div class="boxMy">
+      <div class="title-text">基本信息</div>
+      <div class="my">
+        <span>出生年月:</span>
+        <span class="ml">{{ info?.student?.studentBirthday ? info?.student?.studentBirthday.slice(0, 10) : '' }}</span>
+      </div>
+      <div class="my">
+        <span>联系电话:</span>
+        <span class="ml">{{ info?.student?.phone }}</span>
+      </div>
+      <div class="my">
+        <span>就读院系:</span>
+        <span class="ml">{{ info?.schoolDepartment?.departmentTitle }}</span>
+      </div>
+      <div class="my">
+        <span>就读专业:</span>
+        <span class="ml">{{ info?.major?.majorName }}</span>
+      </div>
+      <div class="my">
+        <span>所在班级:</span>
+        <span class="ml">{{ info?.schoolClass?.title }}</span>
+      </div>
+      <div class="my">
+        <span>学号:</span>
+        <span class="ml">{{ info?.student?.studentNo }}</span>
+      </div>
+      <div class="my">
+        <span>紧急联系人:</span>
+        <span class="ml">{{ info?.student?.emergencyContactName }}</span>
+      </div>
+      <div class="my">
+        <span>紧急联系人电话:</span>
+        <span class="ml">{{ info?.student?.emergencyContactPhone }}</span>
+      </div>
+      <div class="my" style="display: flex;align-items: center;">
+        <span>学生简历:</span>
+        <img v-if="checkIsImage(info?.studentBiographicalNotes?.fileUrl)" :src="info?.studentBiographicalNotes?.fileUrl" style="height: 100px;" />
+      </div>
+      <div class="my">
+        <span>录用企业:</span>
+        <span class="ml">{{ info?.student?.enterpeiseName }}</span>
+      </div>
+      <div class="my">
+        <span>录用部门:</span>
+        <span class="ml">{{ info?.student?.jobDept }}</span>
+      </div>
+      <div class="my">
+        <span>录用岗位:</span>
+        <span class="ml">{{ info?.student?.enterpriseRecruitJobName }}</span>
+      </div>
+    </div>
+
+    <div class="boxMy">
+      <div class="title-text">实习证书</div>
+      <div class="my" style="display: flex;">
+        <span>点评:</span>
+        <span class="ml">{{ info?.certificate?.comment }}</span>
+      </div>
+      <div class="my">
+        <span>证书:</span>
+        <span v-if="info?.certificate?.comment" class="ml link-text" @click="viewCertificate">点击查看</span>
+      </div>
+      <div class="my">
+        <span>附件:</span>
+        <span v-if="info?.certificate?.fileUrl" class="ml link-text" @click="handlePreview(info?.certificate?.fileUrl)">在线预览</span>
+      </div>
+    </div>
+    <div class="boxMy">
+      <div class="title-text">企业推荐信</div>
+      <div class="my">
+        <span>附件:</span>
+        <span v-if="info?.commendation?.fileUrl" class="ml link-text" @click="handlePreview(info?.commendation?.fileUrl)">在线预览</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({name: 'studentList-student-details-baseInfoOther'})
+import { ref } from 'vue'
+import { checkIsImage } from '@/utils'
+
+const props = defineProps({
+  data: Object
+})
+const info = ref({})
+if (props.data && Object.keys(props.data).length) {
+  info.value = props.data
+}
+</script>
+
+<style lang="scss" scoped>
+.boxMy{
+  margin-top: 32px;
+  .title-text{
+    font-size: 16px;
+    font-weight: 600;
+    color: var(--color-333);
+  }
+  .my{
+    margin-top: 20px;
+    span {
+      margin-right: 20px;
+      color: var(--color-777);
+    }
+  }
+  .ml{
+    color: var(--color-666);
+  }
+}
+</style>

+ 5 - 4
src/views/recruit/teacher/studentList/index.vue

@@ -27,7 +27,7 @@
         @pageHandleChange="handleChangePage"
       >
         <template #studentName="{ item }">
-          <div class="d-flex align-center cursor-pointer" @click="studentDetails(item.id)">
+          <div class="d-flex align-center defaultLink" @click="studentDetails(item.id)">
             <v-avatar size="40" :image="getUserAvatar(item?.person?.avatar, item?.person?.sex)"></v-avatar>
             <span class="ml-3">{{ item?.person?.name }}</span>
           </div>
@@ -49,7 +49,7 @@ import Snackbar from '@/plugins/snackbar'
 import { formatName } from '@/utils/getText'
 import { getUserAvatar } from '@/utils/avatar'
 import { schoolOrganization, studentList } from '@/api/school'
-import { useRouter } from 'vue-router'; const router = useRouter()
+// import { useRouter } from 'vue-router'; const router = useRouter()
 
 const loading = ref(false)
 const query = ref({
@@ -69,7 +69,7 @@ const headers = [
 ]
 
 const tableData = ref([])
-tableData.value = [{ test: 'ces', person: { name: '123' }, id: '1'}]
+tableData.value = [{ test: 'ces', person: { name: '123' }, id: '123'}]
 const total = ref(0)
 // 列表
 const getData = async (isRefresh = false) => {
@@ -117,7 +117,8 @@ const getYuanXiItem = async () => {
 getYuanXiItem()
 
 const studentDetails = (id) => {
-  if (id) router.push(`/recruit/teacher/studentList/detail/${id}`)
+  // if (id) router.push(`/recruit/teacher/studentList/detail/${id}`)
+  if (id) window.open(`/recruit/teacher/studentList/detail/${id}`)
 }
 
 const exportLoading = ref(false)

+ 106 - 2
src/views/recruit/teacher/studentList/studentDetails.vue

@@ -1,10 +1,114 @@
-<!--  -->
+<!-- 学生详情 -->
 <template>
-  <div>学生详情</div>
+  <div v-if="Object.keys(info).length" class="d-flex justify-center mb-8">
+    <div style="width: 940px;background: #fff;" class="px-8 pb-12 pt-3 my-n3 mr-3">
+      <!-- 基本信息 -->
+      <baseInfo class="mt-5" :data="info"></baseInfo>
+      <!-- 基本信息 -->
+      <other :data="info"></other>
+    </div>
+    <div class="operate pa-3">
+      <v-list>
+        <v-list-subheader class="title">操作</v-list-subheader>
+        <v-list-item
+          v-for="(item, i) in operateItems" :key="'操作' + i"
+          color="primary"
+          :prepend-icon="item.icon"
+          :title="item.text"
+          @click="handleClick(item)"
+        >
+        </v-list-item>
+      </v-list>
+    </div>
+  </div>
+  <Loading :visible="loading"></Loading>
 </template>
 
 <script setup>
 defineOptions({name: 'studentList-student-details'})
+import baseInfo from './components/baseInfo.vue'
+import other from './components/other.vue'
+import { ref } from 'vue'
+import { stuDetail, certificateList, recommendationList } from '@/api/school'
+import Snackbar from '@/plugins/snackbar'
+import { useRoute } from 'vue-router'; const route = useRoute()
+
+const operateItems = [
+  { text: '上传推荐信', key:'letter', icon: 'mdi-circle-medium' },
+  { text: '颁发实习证书', key:'certificate', icon: 'mdi-circle-medium' },
+]
+
+// 获取人才详情
+const info = ref({
+  student: {
+    studentName: '莫秋妮',
+    studentSex: '男',
+    studentBirthday: '2000-01-01 00:00:00',
+    schoolDepartmentName: '上海交通大学',
+    majorName: '野生动物与自然保护区管理',
+    schoolClassName: '1班',
+  },
+  studentBiographicalNotes: {
+    fileUrl: 'https://minio.huomiaoer.com/dev/data/a613722d-f0cc-404b-88b7-4f92dbed5264/ba9e680f-6bba-4fb9-8df6-1fd723b286ed.jpg',
+  }
+})
+const loading = ref(false)
+const { id: studentId } = route.params
+
+const getCvDetail = async () => {
+  if (!studentId) {
+    Snackbar.warning('缺少学生id')
+    setTimeout(() => {
+      window.close()
+    }, 2000)
+    return
+  }
+  loading.value = true
+  const data = await stuDetail({ studentId })
+  info.value = data
+  await getInternshipCertificate()
+  await getRecommendation()
+  loading.value = false
+}
+getCvDetail()
+
+// 实习证书
+const getInternshipCertificate = async () => {
+  const query = {
+    size: 999,
+    current: 1,
+    studentId
+  }
+  const data = await certificateList(query)
+  info.value.certificate = data.records.length > 0 ? data.records.reverse()[0].studentInternshipCertificate : {}
+  loading.value = false
+}
+
+// 学生推荐信
+const getRecommendation = async () => {
+  const query = {
+    page: {
+      size: 9999,
+      current: 1
+    },
+    entity: {
+      studentId
+    }
+  }
+  const data = await recommendationList(query)
+  info.value.commendation = data.records.length > 0 ? data.records.reverse()[0].entity : {}
+}
+
+const handleClick = (item) => {
+  console.log('handleClick->item:', item)
+}
+
 </script>
 <style lang="scss" scoped>
+.operate {
+  width: 240px;
+  height: 500px; // 272px
+  position: sticky;
+  top: 60px;
+}
 </style>