Browse Source

学生-实习企业

Xiao_123 2 months ago
parent
commit
0dad4f4bb8

+ 1 - 0
components.d.ts

@@ -30,6 +30,7 @@ declare module 'vue' {
     CtTextField: typeof import('./src/components/CtVuetify/CtTextField/index.vue')['default']
     DatePicker: typeof import('./src/components/DatePicker/index.vue')['default']
     Echarts: typeof import('./src/components/Echarts/index.vue')['default']
+    ElCascader: typeof import('element-plus/es')['ElCascader']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     Empty: typeof import('./src/components/Empty/index.vue')['default']
     File: typeof import('./src/components/Upload/file.vue')['default']

+ 0 - 5
src/router/modules/recruit.js

@@ -228,11 +228,6 @@ const recruit = [
       component: () => import('@/views/recruit/personal/jobFair/details/enterprises.vue'),
       name: 'jobFairEnterprises'
     },
-    {
-      path: '/recruit/personal/jobFair/position/:id',
-      component: () => import('@/views/recruit/personal/jobFair/details/position.vue'),
-      name: 'jobFairPosition'
-    },
     {
       path: '/recruit/personal/jobFair/entPosition/:id',
       component: () => import('@/views/recruit/personal/jobFair/details/entJobCard.vue'),

+ 25 - 0
src/store/personCenter.js

@@ -0,0 +1,25 @@
+import { defineStore } from 'pinia'
+
+export const usePersonCenterStore = defineStore('personCenter',
+	{
+		state: () => ({
+			initialIndex: 0,
+			urlsList: []
+		}),
+		actions: {
+			setPreviewData (arr, index) {
+				this.urlsList = arr
+				this.initialIndex = index
+			},
+			clearPreviewData () {
+				this.urlsList = []
+				this.initialIndex = 0
+			}
+		}
+	},
+	{
+		persist: true,
+		devtools: true
+	}
+)
+

+ 21 - 1
src/views/recruit/personal/PersonalCenter/index.vue

@@ -44,14 +44,17 @@
       <router-view></router-view>
     </v-card>
   </div>
+
+  <PreviewImage v-if="showPreview" :initialIndex="initialIndex" :urlList="urlsList" @close="handleClosePreview" />
 </template>
 
 <script setup>
 defineOptions({ name: 'person-center'})
-import { computed } from 'vue'
+import { computed, ref } from 'vue'
 import { getCurrentLocaleLang } from '@/utils/lang.js'
 import personCenterRoute from '@/router/modules/components/recruit/personCenter'
 import { useUserStore } from '@/store/user'
+import { usePersonCenterStore } from '@/store/personCenter'
 
 const userStore = useUserStore()
 const info = localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {}
@@ -89,6 +92,23 @@ const getList = (arr, obj = []) => {
   return obj
 }
 
+const personCenterStore = usePersonCenterStore()
+const showPreview = ref(false)
+const initialIndex = ref(0)
+const urlsList = ref([])
+personCenterStore.$subscribe((mutation, state) => {
+  if (state.urlsList.length > 0) {
+    urlsList.value = state.urlsList
+    initialIndex.value = state.initialIndex
+    showPreview.value = true
+  }
+})
+
+const handleClosePreview = () => {
+  personCenterStore.clearPreviewData()
+  showPreview.value = false
+}
+
 // 更新账户信息
 const updateAccountInfo = async () => {
   await userStore.getUserAccountInfo()

+ 59 - 2
src/views/recruit/personal/PersonalCenter/student/InternshipReport/index.vue

@@ -1,11 +1,68 @@
 <template>
-	<div>xxx</div>
+	<!-- 筛选 -->
+	<div>筛选</div>
+	<v-divider class="my-3"></v-divider>
+	<!-- 实习报告 -->
+	<div>
+		<div v-for="item in items" :key="item.date" class="mb-3">
+			<div class="title-date">{{ item.date }}</div>
+			<div class="d-flex flex-wrap">
+				<img 
+					v-for="(src, index) in item.arr" 
+					:key="index" 
+					:src="src" 
+					@click="handlePreview(item.arr, index)" 
+					class="cursor-pointer" 
+					style="width: 200px; height: 250px;"
+				/>
+			</div>
+		</div>
+	</div>
 </template>
 
 <script setup>
 defineOptions({ name: 'InternshipReport' })
+import { ref } from 'vue'
+import { usePersonCenterStore } from '@/store/personCenter'
+
+const items = ref([
+	{
+		date: '2025-02-21',
+		arr: [
+			'https://minio.citupro.com/dev/huomiao/report.png',
+			'https://minio.citupro.com/dev/huomiao/report1.png',
+			'https://minio.citupro.com/dev/huomiao/report2.png'
+		]
+	},
+	{
+		date: '2025-02-25',
+		arr: [
+			'https://minio.citupro.com/dev/huomiao/report.png',
+			'https://minio.citupro.com/dev/huomiao/report1.png',
+			'https://minio.citupro.com/dev/huomiao/report2.png'
+		]
+	},
+	{
+		date: '2025-03-01',
+		arr: [
+			'https://minio.citupro.com/dev/huomiao/report.png',
+			'https://minio.citupro.com/dev/huomiao/report1.png',
+			'https://minio.citupro.com/dev/huomiao/report2.png'
+		]
+	}
+])
+
+// 预览
+const personCenterStore = usePersonCenterStore()
+const handlePreview = (arr, index) => {
+	personCenterStore.setPreviewData(arr, index)
+}
 </script>
 
 <style scoped lang="scss">
-
+.title-date {
+	border-left: 5px solid var(--v-primary-base);
+	padding-left: 12px;
+	line-height: 17px;
+}
 </style>

+ 3 - 3
src/views/recruit/personal/PersonalCenter/student/information/index.vue

@@ -1,11 +1,11 @@
 <template>
-  <div style="padding: 20px 30px">
+  <div class="px-3">
     <div class="resume-header mb-3">
-      <div class="resume-title">学生信息认证</div>
+      <div class="resume-title">学生信息编辑</div>
     </div>
     <div class="d-flex flex-column align-center pt-5">
       <CtForm ref="CtFormRef" :items="items" style="width: 900px;"></CtForm>
-      <v-btn class="buttons mt-5" color="primary" @click.stop="handleSubmit">{{ $t('common.save') }}</v-btn>
+      <v-btn class="buttons mt-5 elevation-5" color="primary" @click.stop="handleSubmit">{{ $t('common.save') }}</v-btn>
     </div>
   </div>
 

+ 51 - 8
src/views/recruit/personal/PersonalCenter/student/intershipCompany/index.vue

@@ -1,24 +1,67 @@
 <template>
-	<div>
-		<v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#f7f8fa">
-      <v-tab v-for="(k, index) in ['等待中', '进行中', '已结束']" :key="index" :value="index">{{ k }}</v-tab>
-    </v-tabs>
-	</div>
+	<v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#f7f8fa">
+    <v-tab v-for="(k, index) in tabList" :key="index" :value="k.value">{{ k.label }}</v-tab>
+  </v-tabs>
+
+	<ItemPage v-if="data[tabList[tab].key]?.length" :tab="tab" class="mt-3" :items="data[tabList[tab].key]" />
+	<Empty v-else :elevation="false" />
 </template>
 
 <script setup>
 defineOptions({ name: 'PersonalCenterStudentInternshipCompany'})
 import { ref } from 'vue'
 import { practiceProcess } from '@/api/recruit/personal/personalCenter/student.js'
+import ItemPage from './item.vue'
 
 const tab = ref(0)
+const tabList = ref([
+	{ label: '等待中', value: 0, key: 'waitProcess' },
+	{ label: '进行中', value: 1, key: 'process' },
+	{ label: '已结束', value: 2, key: 'endProcess' },
+])
 const studentInfo = ref(localStorage.getItem('studentInfo') ? JSON.parse(localStorage.getItem('studentInfo')) : {})
 
+const data = ref({
+	waitProcess: [
+		{
+			job: {
+				name: '前端开发',
+				payFrom: 10000,
+				payTo: 20000,
+				payUnit: '月',
+				area: {
+					"str": "呼和浩特市-和林格尔县"
+				},
+				id: '1864521193404727297',
+				areaId: 150123,
+				eduType: null,
+				expType: null,
+				status: '0',
+				updateTime: 1740972578585
+			},
+			enterprise: {
+				id: '1',
+				anotherName: '门墩儿',
+				name: '门墩儿科技有限公司',
+				logoUrl: 'https://minio.citupro.com/dev/menduner/company-avatar.png',
+				industryName: '互联网',
+				scaleName: '10000-50000人',
+			},
+			entity: {
+				jobJoinDate: 1740972578585,
+				internshipEndDate: 1740972578585,
+				percentage: '50',
+			}
+		}
+	],
+	process: [],
+	endProcess: []
+})
 const getList = async () => {
-	const data = await practiceProcess({ studentId: studentInfo.value.userId })
-	console.log(data, 'list')
+	data = await practiceProcess({ studentId: studentInfo.value.userId })
+	// console.log(data, 'list')
 }
-getList()
+// getList()
 </script>
 
 <style scoped lang="scss">

+ 153 - 0
src/views/recruit/personal/PersonalCenter/student/intershipCompany/item.vue

@@ -0,0 +1,153 @@
+<template>
+  <div>
+    <div class="position-item mb-3 job-closed elevation-2" style="position: relative;" 
+      v-for="(val, i) in props.items" :key="i" @mouseenter="val.active = true" @mouseleave="val.active = false"
+    >
+			<!-- 职位、企业信息 -->
+      <div class="info-content" >
+        <div class="job-info">
+          <div class="job-name ellipsis" :class="{'cursor-pointer': val.job.status === '0'}" v-ellipse-tooltip>
+            <span class="mr-3" :class="{'info-name': val.job.status === '0'}" @click.stop="handleToPositionDetails(val)">{{ formatName(val.job.name) }}</span>
+            <span>
+              [{{ !val.job.areaId ? '全国' : val.job.area?.str }}]
+            </span>
+          </div>
+          <div class="job-other">
+            <span v-if="!val.job.payFrom && !val.job.payTo" class="salary color-primary">面议</span>
+            <span v-else class="salary color-primary">{{ val.job.payFrom ? val.job.payFrom + '-' : ''}}{{ val.job.payTo }}{{ val.job.payUnit ? '/' + val.job.payUnit : '' }}</span>
+            <v-chip v-if="val.job?.expName" class="mx-3" color="primary" label size="small">{{ val.job.expName }}</v-chip>
+            <v-chip v-if="val.job?.eduName" color="primary" label size="small">{{ val.job.eduName }}</v-chip>
+          </div>
+        </div>
+        <div class="company-info ml-3" style="flex: 1;">
+          <div style="height: 50px; width: 50px;">
+            <v-img width="50" height="50" :src="val.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/7.png'" rounded contain></v-img>
+          </div>
+          <div class="ml-3">
+            <div class="cursor-pointer info-name" v-ellipse-tooltip @click.stop="jumpToEnterpriseDetail(val.enterprise.id)">{{ formatName(val.enterprise.anotherName || val.enterprise.name) }}</div>
+            <div class="mt-3 ellipsis color-666 font-size-13" style="max-width: 260px;">
+              <span v-for="(k, i) in desc" :key="k">
+                {{ val.enterprise[k] }}
+                <span v-if="i !== desc.length - 1 && val.enterprise[k] && val.enterprise[desc[i + 1]]" class="septal-line"></span>
+              </span>
+            </div>
+          </div>
+        </div>
+      </div>
+			<div class="mx-5" style="border-bottom: 1px dashed #e0e0e0"></div>
+			<!-- 实习范围 -->
+			<div class="d-flex justify-space-between px-5 py-3">
+				<div class="color-666 font-size-15">
+					<p>实习时间:{{ timesTampChange(val.entity.jobJoinDate, 'Y-M-D') }} 至 {{ timesTampChange(val.entity.internshipEndDate, 'Y-M-D') }}</p>
+				</div>
+				<div class="text-end" v-if="tab === 0">
+					<v-btn size="small" color="primary" @click="handleToReport(val)">实习报告</v-btn>
+				</div>
+			</div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'PersonalCenterStudentInternshipCompanyItem' })
+import { useRouter } from 'vue-router'
+import { formatName } from '@/utils/getText'
+import { jumpToEnterpriseDetail } from '@/utils/position'
+import { timesTampChange } from '@/utils/date'
+
+const props = defineProps({
+  items: {
+    type: Array,
+    default: () => []
+  },
+	tab: Number
+})
+
+const router = useRouter()
+
+const desc = ['industryName', 'scaleName']
+
+// 职位详情
+const handleToPositionDetails = (item) => {
+  if (item.job.status === '1') return
+  let path = `/recruit/personal/position/details/${item.job.id}`
+  if (item.cvRel?.jobFairId) path += `?jobFairId=${item.cvRel.jobFairId}`
+  router.push(path)
+}
+
+// 实习报告
+const handleToReport = (val) => {
+	console.log(val, 'report')
+	router.push(`/recruit/personal/personalCenter/student/internshipReport?id=${val.enterprise.id}`)
+}
+</script>
+
+<style scoped lang="scss">
+.position-item {
+  height: 144px;
+  background-color: #fff;
+  border-radius: 12px;
+  &:hover {
+    box-shadow: 0px 3px 5px -1px var(--v-shadow-key-umbra-opacity, rgba(0, 0, 0, 0.2)), 0px 5px 8px 0px var(--v-shadow-key-penumbra-opacity, rgba(0, 0, 0, 0.14)), 0px 1px 14px 0px var(--v-shadow-key-ambient-opacity, rgba(0, 0, 0, 0.12)) !important;
+  }
+  .info-header {
+    height: 48px;
+    background: linear-gradient(90deg,#f5fcfc,#fcfbfa);
+    border-radius: 12px;
+    .img-box {
+      padding: 12px 24px;
+      .name {
+        color: var(--color-222);
+        font-weight: 400;
+        font-size: 13px;
+        .gray {
+          color: var(--color-666);
+        }
+      }
+    }
+    .header-btn {
+      padding: 10px 10px 0 0;
+      float: right;
+      .v-btn {
+        z-index: 1;
+      }
+    }
+  }
+  .info-content {
+    display: flex;
+    padding: 16px 24px;
+    justify-content: space-between;
+    .job-info {
+      width: 430px;
+      min-width: 430px;
+      max-width: 430px;
+      font-weight: 500;
+      font-size: 16px;
+      .job-name {
+        width: 100%;
+        height: 22px;
+        line-height: 22px;
+        color: #0E100F;
+        margin-bottom: 12px;
+      }
+      .job-other {
+        color: var(--v-error-base);
+        height: 22px;
+        line-height: 22px;
+      }
+    }
+    .company-info {
+      display: flex;
+      align-items: center
+    }
+    .interview-info {
+      color: var(--color-333);
+      font-size: 15px;
+    }
+  }
+}
+
+.info-name:hover {
+  color: var(--v-primary-base);
+}
+</style>