|
@@ -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>
|