Browse Source

学生-实习情况

Xiao_123 2 months ago
parent
commit
4c5fbd9d68

+ 33 - 0
src/api/recruit/enterprise/student.js

@@ -0,0 +1,33 @@
+import request from '@/config/axios'
+
+// 获取实习学生记录分页
+export const getStudentPage = async (params) => {
+  return await request.get({
+    url: '/app-api/menduner/system/recruit/student/page',
+    params
+  })
+}
+
+// 获得学生实习记录状态统计
+export const getRecordStatusCount = async (params) => {
+  return await request.get({
+    url: '/app-api/menduner/system/recruit/student/get/record-status/count',
+    params
+  })
+}
+
+// 保存实习证书
+export const saveCertificate = async (data) => {
+  return await request.post({
+    url: '/app-api/menduner/system/recruit/student/save/evaluate',
+    data
+  })
+}
+
+// 保存推荐信
+export const saveRecommend = async (data) => {
+  return await request.post({
+    url: '/app-api/menduner/system/recruit/student/upload/recommendation-letter',
+    data
+  })
+}

+ 13 - 3
src/views/recruit/enterprise/student/InternshipSituation/CertificateForm.vue

@@ -17,9 +17,19 @@ import { ref } from 'vue'
 const CtFormRef = ref()
 const formItems = ref({
   options: [
+    {
+      type: 'textarea',
+      key: 'evaluate',
+      value: null,
+      rows: 5,
+      counter: 60,
+      label: '点评内容 *',
+      outlined: true,
+      rules: [v => !!v || '点评不能为空']
+    },
     {
       slotName: 'uploadFile',
-      key: 'url',
+      key: 'certificate',
       value: '',
       truthValue: '',
       label: '点击上传附件 *',
@@ -39,7 +49,7 @@ const openFileInput = () => {
 
 // 上传附件
 const handleUploadResume = async (url, title, filename) => {
-  const obj = formItems.value.options.find(e => e.key === 'url')
+  const obj = formItems.value.options.find(e => e.key === 'certificate')
   obj.value = filename
   obj.truthValue = url
 }
@@ -47,7 +57,7 @@ const handleUploadResume = async (url, title, filename) => {
 const getQuery = () => {
 	let obj = {}
 	formItems.value.options.forEach(e => {
-		obj[e.key] = e.truthValue
+		obj[e.key] = e.truthValue || e.value
 	})
 	return obj
 }

+ 108 - 53
src/views/recruit/enterprise/student/InternshipSituation/index.vue

@@ -1,19 +1,19 @@
 <template>
 	<v-card class="card-box pa-3">
-		<div class="d-flex">
+		<div class="d-flex justify-space-between align-center">
 			<!-- 统计 -->
 			<div class="d-flex align-center statistics">
-				<div v-for="val in statistics" :key="val.key" class="statistics-card pa-5">
+				<div v-for="(val, index) in statistics" :key="index" class="statistics-card pa-5">
 					<div class="color-666">{{ val.label }}</div>
 					<div class="">
-						<span class="value font-weight-bold color-primary">{{ val.value }}</span>
+						<span class="value font-weight-bold color-primary">{{ val.count }}</span>
 						<span class="color-999 font-size-14">人</span>
 					</div>
 				</div>
 			</div>
 			<!-- 时间范围筛选 -->
 			<div class="d-flex align-center ml-7">
-        <span class="color-666">自定义日期</span>
+        <span class="color-666">实习日期</span>
         <div class="ml-5">
           <date-picker 
 						v-model="date"
@@ -30,10 +30,8 @@
       </div>
 		</div>
 
-		<div class="d-flex mt-5">
-			<v-treeview :items="items" color="primary" style="width: 25%; border-right: 1px solid #eee;" class="pr-5"></v-treeview>
+		<div class="mt-5">
 			<CtTable
-				class="ml-5"
         :items="tableData"
         :headers="headers"
         :loading="loading"
@@ -44,115 +42,172 @@
         :page-info="query"
         itemKey="id"
         @pageHandleChange="handleChangePage"
-				style="flex: 1;"
       >
         <template #studentName="{ item }">
           <div class="d-flex align-center">
-            <v-avatar size="40" :image="getUserAvatar(item.studentHeadImg, item.sex)"></v-avatar>
-            <span class="ml-3">{{ item?.studentName }}</span>
+            <v-avatar size="40" :image="getUserAvatar(item?.person?.avatar, item?.person?.sex)"></v-avatar>
+            <span class="ml-3">{{ item?.person?.name }}</span>
           </div>
         </template>
         <template #actions="{ item }">
-          <v-btn color="primary" variant="text" @click="handleUploadLetter(item)">上传推荐信</v-btn>
-          <v-btn color="#00897B" variant="text" @click="handleIssueCertificate(item)">颁发实习证书</v-btn>
+          <v-btn v-if="!item?.recommendationLetter" color="primary" variant="text" @click="handleUploadLetter(item.id)">上传推荐信</v-btn>
+          <v-btn v-if="!item?.evaluate" color="#00897B" variant="text" @click="handleIssueCertificate(item.id)">颁发实习证书</v-btn>
         </template>
       </CtTable>
 		</div>
 	</v-card>
 
 	<!-- 上传推荐信 -->
-	<CtDialog :visible="showLitterDialog" :widthType="2" titleClass="text-h6" title="上传推荐信" @close="showLitterDialog = false;" @submit="handleSubmitLetter">
+	<CtDialog :visible="showLitterDialog" :widthType="2" titleClass="text-h6" title="上传推荐信" @close="handleLetterClose" @submit="handleSubmitLetter">
     <UploadRecommendationLetterForm ref="RecommendationLetterRef" />
   </CtDialog>
 
 	<!-- 颁发实习证书 -->
-	<CtDialog :visible="showCertificateDialog" :widthType="2" titleClass="text-h6" title="颁发实习证书" @close="showCertificateDialog = false;" @submit="handleSubmitCertificate">
+	<CtDialog :visible="showCertificateDialog" :widthType="2" titleClass="text-h6" title="颁发实习证书" @close="handleCertificateClose" @submit="handleSubmitCertificate">
     <IssueCertificateForm ref="IssueCertificateFormRef" />
   </CtDialog>
 </template>
 
 <script setup>
 defineOptions({ name: 'enterpriseStudentInternshipSituation' })
-import { ref } from 'vue'
+import { ref, onMounted } from 'vue'
 import { getUserAvatar } from '@/utils/avatar'
 import DatePicker from '@/components/FormUI/datePicker'
 import UploadRecommendationLetterForm from './RecommendationLetterForm'
 import IssueCertificateForm from './CertificateForm'
+import { getStudentPage, getRecordStatusCount, saveRecommend, saveCertificate } from '@/api/recruit/enterprise/student'
+import { getDict } from '@/hooks/web/useDictionaries'
+import { dealDictObjData } from '@/utils/position'
+import { formatName } from '@/utils/getText'
+import { timesTampChange } from '@/utils/date'
+import { convertTimestampsToDayRange } from '@/utils/date'
+import Snackbar from '@/plugins/snackbar'
 
 const date = ref(null)
-const statistics = ref([
-	{ label: '实习中', value: 2, key: 'studentPracticeApplyCount' },
-	{ label: '实习结束', value: 6, key: 'studentPracticeSuccessCount' },
-	{ label: '等待实习', value: 10, key: 'studentPracticeWaitCount' }
-])
-
-const items = ref([
-  { id: 1, title: '运营部' },
-  { id: 2, title: '开发部' },
-  { id: 3, title: '行政部' },
-  { id: 4, title: '市场部' }
-])
+const statistics = ref([])
+
 const loading = ref(false)
 const total = ref(0)
 const query = ref({
 	pageNo: 1,
-	pageSize: 10
+	pageSize: 10,
+	startTime: null
 })
-const tableData = ref([
-	{
-		id: 1,
-		studentHeadImg: 'https://minio.menduner.com/dev/4cd324c459e055c34f8467089bd8c3013e2a95dee6f3883cbdacf100b9a50d52.jpeg',
-		studentName: '张三',
-		sex: '1',
-		schoolDepartmentName: '计算机学院',
-		majorName: '计算机科学与技术'
-	},
-	{
-		id: 2,
-		studentHeadImg: 'https://minio.menduner.com/dev/4cd324c459e055c34f8467089bd8c3013e2a95dee6f3883cbdacf100b9a50d52.jpeg',
-		studentName: '张三',
-		sex: '2',
-		schoolDepartmentName: '计算机学院',
-		majorName: '计算机科学与技术'
-	}
-])
+const tableData = ref([])
+
 const headers = [
   { title: '学生姓名', key: 'studentName', sortable: false },
-  { title: '所属院系', key: 'schoolDepartmentName', sortable: false },
-  { title: '所属专业', key: 'majorName', sortable: false },
+  { title: '就读学校', key: 'student.schoolName', sortable: false },
+  { title: '所属院系', key: 'student.schoolDepartmentName', sortable: false },
+  { title: '所属专业', key: 'student.majorName', sortable: false },
+  { title: '应聘职位', key: 'job.name', sortable: false, value: item => formatName(item.job.name) },
+  { title: '到岗日期', key: 'startTime', sortable: false, value: item => timesTampChange(item.startTime, 'Y-M-D') },
+  { title: '结束日期', key: 'endTime', sortable: false, value: item => timesTampChange(item.endTime, 'Y-M-D') },
+  { title: '创建日期', key: 'createTime', sortable: false, value: item => timesTampChange(item.createTime) },
   { title: '操作', key: 'actions', sortable: false }
 ]
 
+// 学生列表
+const getList = async () => {
+	loading.value = true
+	try {
+		const result = await getStudentPage(query.value)
+		tableData.value = result?.list.map(e => {
+			e.enterprise = dealDictObjData({}, e.enterprise)
+			e.job = dealDictObjData({}, e.job)
+			return e
+		})
+		total.value = result?.total || 0
+	} finally {
+		loading.value = false
+	}
+}
+
+// 数值统计
+const getStatistics = async () => {
+	try {
+		const data = await getRecordStatusCount({ type: '-1' })
+		statistics.value.forEach(e => {
+			const obj = data.find(val => val.key === e.value)
+			e.count = obj ? obj.value : 0
+		})
+	} catch {}
+}
+
+onMounted(async () => {
+	const { data } = await getDict('student_practice_status')
+	statistics.value = data
+	getStatistics()
+	getList()
+})
+
 const handleChangePage = (val) => {
 	query.value.pageNo = val
+	getList()
 }
 
 // 时间范围选择
 const handleChangeDate = (time) => {
-	console.log(time, 'range')
+	if (time && time.length) {
+		query.value.startTime = convertTimestampsToDayRange(time)
+	} else {
+		query.value.startTime = []
+	}
+	query.value.pageNo = 1
+	getList()
 }
 
 // 上传推荐信
+const recordId = ref(null)
 const showLitterDialog = ref(false)
 const RecommendationLetterRef = ref(null)
-const handleUploadLetter = () => {
+const handleUploadLetter = (id) => {
+	recordId.value = id
 	showLitterDialog.value = true
 }
+const handleLetterClose = () => {
+	recordId.value = null
+	showLitterDialog.value = false
+}
 const handleSubmitLetter = async () => {
 	const { valid } = await RecommendationLetterRef.value.CtFormRef.formRef.validate()
 	if (!valid) return
 	const query = RecommendationLetterRef.value.getQuery()
-	console.log(query, '推荐信-form')
+
+	try {
+		await saveRecommend({ id: recordId.value, recommendationLetter: query.url })
+		Snackbar.success('上传成功')
+		getList()
+		handleLetterClose()
+	} catch {
+		handleLetterClose()
+	}
 }
 
 // 颁发实习证书
 const showCertificateDialog = ref(false)
 const IssueCertificateFormRef = ref(null)
-const handleIssueCertificate = () => {
+const handleIssueCertificate = (id) => {
+	recordId.value = id
 	showCertificateDialog.value = true
 }
-
+const handleCertificateClose = () => {
+	recordId.value = null
+	showCertificateDialog.value = false
+}
 const handleSubmitCertificate = async () => {
+	const { valid } = await IssueCertificateFormRef.value.CtFormRef.formRef.validate()
+	if (!valid) return
+	const query = IssueCertificateFormRef.value.getQuery()
+
+	try {
+		await saveCertificate({ id: recordId.value, ...query })
+		Snackbar.success('上传成功')
+		getList()
+		handleCertificateClose()
+	} catch {
+		handleCertificateClose()
+	}
   
 }
 </script>