lifanagju_citu 2 månader sedan
förälder
incheckning
c4ef57ae9e

+ 161 - 0
src/layout/teacher.vue

@@ -0,0 +1,161 @@
+<template>
+  <div class="parent d-flex flex-column">
+    <Headers class="headers"></Headers>
+    <div class="content d-flex">
+      <side class="content-sticky" v-if="!router.currentRoute.value?.meta?.hideSide"></side>
+      <div class="content-box d-flex flex-column" :style="`width: ${ !isInWhiteList(route.path) ? 'calc(100% - 230px)' : '100%'}`">
+        <div v-if="!isInWhiteList(route.path)" class="breadcrumbs_sticky">
+          <div class=" d-flex align-center justify-space-between">
+            <v-breadcrumbs :items="system.breadcrumbs" elevation="3">
+              <template v-slot:item="{ item }">
+                <span class="text" :class="{active: !item.disabled}" @click="toPath(item)">{{ item.text }}</span>
+              </template>
+            </v-breadcrumbs>
+          </div>
+          <v-divider></v-divider>
+        </div>
+        <div class="box pa-3">
+          <div v-if="!isInWhiteList(route.path)" class="box-content">
+            <router-view :key="key"></router-view>
+          </div>
+          <div v-else class="full">
+            <router-view :key="key"></router-view>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'teacher-layout-index' })
+import Headers from './teacher/navBar.vue'
+import side from './teacher/side.vue'
+import { useRouter, useRoute } from 'vue-router'
+import { watch, computed } from 'vue'
+import { useSystem } from '@/store/system'
+import { useUserStore } from '@/store/user'
+
+const router = useRouter()
+const route = useRoute()
+const system = useSystem()
+const key = computed(() => {
+  return route.path + Math.random()
+})
+
+const whiteList = []
+// 查询是否在白名单内,在则不展示面包屑
+const isInWhiteList = (url)=> {
+  const path = url.split('?')[0]
+  for (const item of whiteList) {
+    if (path.startsWith(item)) {
+      return true
+    }
+  }
+  return false
+}
+
+const user = useUserStore()
+watch(
+  () => route.matched,
+  async (val) => {
+    // getTitle(val, route.fullPath)
+    system.setBreadcrumbs(val, route.fullPath)
+    await user.getEnterpriseInfo(true)
+  },
+  { immediate: true },
+  { deep: true }
+)
+
+const toPath = (item) => {
+  const { disabled, to } = item
+  if (disabled) {
+    event.preventDefault()
+    return
+  }
+  router.push(to)
+}
+</script>
+
+<style lang="scss" scoped>
+$top: 50px;
+.parent {
+  background-color: var(--default-bgc);
+  min-width: 1200px;
+}
+.headers {
+  position: sticky;
+  top: 0;
+  z-index: 999;
+}
+.box {
+  height: 0;
+  flex: 1;
+  height: calc(100vh - 50px);
+  &-content {
+    width: 100%;
+    min-height: 100%;
+    position: relative;
+    overflow-y: auto;
+    overflow-x: hidden;
+  }
+}
+.full {
+  height: calc(100vh - $top - 25px);
+  width: 100%;
+  flex: 1;
+  position: relative;
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+.slider {
+  position: fixed;
+  bottom: 50%;
+  right: 24px;
+  translate: 0 50%;
+  z-index: 999;
+}
+.content {
+  height: 0;
+  flex: 1;
+
+  &-sticky {
+    position: sticky;
+    top: $top;
+    height: calc(100vh - $top);
+  }
+}
+.breadcrumbs_sticky {
+  position: sticky;
+  top: $top;
+  background: #FFF;
+  z-index: var(--zIndex-breadcrumbs);
+  border-left: 1px solid #eaecef;
+}
+.text {
+  color: var(--color-999);
+  font-size: 14px;
+  &.active {
+    color: var(--v-primary-base);
+    cursor: pointer;
+  }
+}
+/* 滚动条样式 */
+::-webkit-scrollbar {
+  -webkit-appearance: none;
+  width: 0px;
+  height: 0px;
+}
+/* 滚动条内的轨道 */
+::-webkit-scrollbar-track {
+  background: rgba(0, 0, 0, 0.1);
+  border-radius: 0;
+}
+/* 滚动条内的滑块 */
+::-webkit-scrollbar-thumb {
+  cursor: pointer;
+  border-radius: 5px;
+  background: rgba(0, 0, 0, 0.15);
+  transition: color 0.2s ease;
+}
+</style>

+ 135 - 0
src/layout/teacher/navBar.vue

@@ -0,0 +1,135 @@
+<template>
+  <div>
+    <v-toolbar class="banner font-size-14 pl-0" density="compact" style="height: 50px;">
+      <div class="innerBox d-flex justify-space-between">
+        <div class="nav-logo" style="cursor: pointer;" @click="handleLogoClick">
+          <v-img src="../../assets/logo.png"  aspect-ratio="16/9" contain :width="97" style="height: 40px"></v-img>
+        </div>
+        
+        <div class="d-flex user-nav align-center">
+          
+          <!-- 头像用户名 -->
+          <div class="d-flex align-center" v-if="showBall">
+            <v-menu open-on-hover>
+              <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-avatar>
+                  <div class="ml-2 commonHover">{{ formatName(baseInfo?.name ?? baseInfo?.phone) }}</div>
+                </div>
+              </template>
+
+              <v-list>
+                <v-list-item v-for="(item, index) in items" :key="index" @click="item.change">
+                  <template v-slot:prepend>
+                    <v-icon :icon="item.icon"></v-icon>
+                  </template>
+                  <v-list-item-title>{{ item.title }}</v-list-item-title>
+                </v-list-item>
+              </v-list>
+            </v-menu>
+          </div>
+          <!-- 消息通知 -->
+          <MessageNotification class="commonHover2" path="/recruit/enterprise/chatTools"></MessageNotification>
+        </div>
+      </div>
+    </v-toolbar>
+  </div>
+</template>
+
+<script setup>
+import { computed, 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' })
+
+defineProps({
+  sticky: {
+    type: Boolean,
+    default: true
+  }
+})
+
+const showBall = ref(false)
+
+onMounted(() => {
+  if (getToken(1)) {
+    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 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')) || {})
+
+userStore.$subscribe((mutation, state) => {
+  if (Object.keys(state.entBaseInfo).length) baseInfo.value = state.entBaseInfo
+})
+
+</script>
+
+<style lang="scss" scoped>
+.banner {
+  width: 100%;
+  height: 50px;
+  z-index: var(--zIndex-nav) !important;
+  color: #fff;
+  background-color: var(--color-d5e6e8);
+  padding-left: 0px;
+  height: 50px;
+  font-size: 15px;
+}
+.hover:hover {
+  cursor: pointer;
+  background: rgba(0, 0, 0, 0.03);
+}
+.innerBox {
+  position: relative;
+  width: 100%;
+  align-items: center;
+  padding: 0 30px;
+}
+.nav-logo {
+  float: left;
+}
+.nav {
+  font-size: 0;
+  float: left;
+  margin-left: 50px;
+  height: 49px;
+  line-height: 49px;
+}
+.user-nav {
+  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>

+ 84 - 0
src/layout/teacher/side.vue

@@ -0,0 +1,84 @@
+<template>
+  <div>
+    <v-list class="side-box" color="primary">
+      <template v-for="(item, index) in list">
+        <template v-if="!item.children.length">
+          <v-list-item
+            :key="`${item.name}_${index}`"
+            active-class="active"
+            color="primary"
+            :href="item.path"
+            :to="item.path"
+            rounded="shaped"
+            :prepend-icon="item.icon"
+            :title="getCurrentLocaleLang() === 'zh_CN' ? item.title : item.enName"
+          >
+          </v-list-item>
+        </template>
+        <v-list-group
+          v-else
+          color="primary"
+          rounded="shaped"
+          :key="`${item.path}_${item.title}`"
+          :prepend-icon="item.icon"
+        >
+          <template v-slot:activator="{ props }">
+            <v-list-item v-bind="props" :title="getCurrentLocaleLang() === 'zh_CN' ? item.title : item.enName"></v-list-item>
+          </template>
+          <v-list-item
+            v-for="(val, i) in item.children"
+            :key="i"
+            color="primary"
+            :href="val.path"
+            style="padding-left: 40px;"
+            :to="val.path"
+            :title="getCurrentLocaleLang() === 'zh_CN' ? val.title : val.enName"
+            rounded="shaped"
+          ></v-list-item>
+        </v-list-group>
+      </template>
+    </v-list>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'teacher-side'})
+import { computed } from 'vue'
+import { getCurrentLocaleLang } from '@/utils/lang.js'
+import routeList from '@/router/modules/components/recruit/teacher'
+
+const list = computed(() => {
+  return getList(routeList)
+})
+
+// console.log(import.meta.env.VITE_NODE_ENV, '当前环境变量============')
+
+const getList = (arr, obj = []) => {
+
+  arr.forEach(element => {
+    if (element.show) return
+    let data = {}
+    data = {
+      title: element?.meta?.title,
+      enName: element?.meta?.enName,
+      icon: element?.meta?.icon,
+      name: element?.name,
+      path: element?.path,
+      children: []
+    }
+    if (element?.children) {
+      getList(element.children, data.children)
+    }
+    obj.push(data)
+  })
+  return obj
+}
+
+</script>
+
+<style scoped lang="scss">
+.side-box {
+  width: 230px;
+  height: 100%;
+}
+</style>

+ 105 - 0
src/router/modules/components/recruit/teacher.js

@@ -0,0 +1,105 @@
+// 企业路由信息
+import Layout from '@/layout/teacher.vue'
+
+const teacher = [
+  {
+    path: '/recruit/teacher',
+    show: true,
+    redirect: '/recruit/teacher/studentList',
+  },
+  {
+    path: '/recruit/teacher/studentList',
+    component: Layout,
+    name: 'studentList',
+    meta: {
+      title: '学生列表',
+      enName: 'Student List',
+      icon: 'mdi-wallet-membership'
+    },
+    children: [
+      {
+        path: '/recruit/teacher/studentList',
+        show: true,
+        component: () => import('@/views/recruit/teacher/studentList/index.vue')
+      },
+      {
+        path: '/recruit/teacher/studentDetails',
+        show: true,
+        meta: {
+          title: '学生详情',
+          enName: 'Student Details'
+        },
+        component: () => import('@/views/recruit/teacher/studentList/studentDetails.vue')
+      },
+    ]
+  },
+  {
+    path: '/recruit/teacher/internshipSituation',
+    component: Layout,
+    name: 'internshipSituation',
+    meta: {
+      title: '实习情况',
+      enName: 'Internship Situation',
+      icon: 'mdi-wallet-membership'
+    },
+    children: [
+      {
+        path: '/recruit/teacher/internshipSituation',
+        show: true,
+        component: () => import('@/views/recruit/teacher/internshipSituation/index.vue')
+      },
+    ]
+  },
+  {
+    path: '/recruit/teacher/internshipReport',
+    component: Layout,
+    name: 'internshipReport',
+    meta: {
+      title: '实习报告',
+      enName: 'Internship Report',
+      icon: 'mdi-wallet-membership'
+    },
+    children: [
+      {
+        path: '/recruit/teacher/internshipReport',
+        show: true,
+        component: () => import('@/views/recruit/teacher/internshipReport/index.vue')
+      },
+    ]
+  },
+  {
+    path: '/recruit/teacher/internshipCompany',
+    component: Layout,
+    name: 'internshipCompany',
+    meta: {
+      title: '实习企业',
+      enName: 'Internship Company',
+      icon: 'mdi-wallet-membership'
+    },
+    children: [
+      {
+        path: '/recruit/teacher/internshipCompany',
+        show: true,
+        component: () => import('@/views/recruit/teacher/internshipCompany/index.vue')
+      },
+    ]
+  },
+  {
+    path: '/recruit/teacher/teacherCertification',
+    component: Layout,
+    name: 'teacherCertification',
+    meta: {
+      title: '教师认证',
+      enName: 'Teacher Certification',
+      icon: 'mdi-wallet-membership'
+    },
+    children: [
+      {
+        path: '/recruit/teacher/teacherCertification',
+        show: true,
+        component: () => import('@/views/recruit/teacher/teacherCertification/index.vue')
+      },
+    ]
+  },
+]
+export default teacher

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

@@ -1,5 +1,6 @@
 
 // 门墩儿招聘
+import teacher from './components/recruit/teacher'
 import enterprise from './components/recruit/enterprise'
 import personal from './components/recruit/personal'
 import Layout from '@/layout'
@@ -290,6 +291,7 @@ setLoginType(personal, 'personal')
 const routeArray = [
   ...recruit,
   ...enterprise,
+  ...teacher,
   ...personal
 ]
 export default routeArray

+ 10 - 0
src/views/recruit/teacher/internshipCompany/index.vue

@@ -0,0 +1,10 @@
+<!--  -->
+<template>
+  <div>实习企业</div>
+</template>
+
+<script setup>
+defineOptions({name: 'internship-company'})
+</script>
+<style lang="scss" scoped>
+</style>

+ 10 - 0
src/views/recruit/teacher/internshipReport/index.vue

@@ -0,0 +1,10 @@
+<!--  -->
+<template>
+  <div>实习报告</div>
+</template>
+
+<script setup>
+defineOptions({name: 'internship-report'})
+</script>
+<style lang="scss" scoped>
+</style>

+ 10 - 0
src/views/recruit/teacher/internshipSituation/index.vue

@@ -0,0 +1,10 @@
+<!--  -->
+<template>
+  <div>实习情况</div>
+</template>
+
+<script setup>
+defineOptions({name: 'studentList-internship-situation'})
+</script>
+<style lang="scss" scoped>
+</style>

+ 10 - 0
src/views/recruit/teacher/studentList/index.vue

@@ -0,0 +1,10 @@
+<!--  -->
+<template>
+  <div>学生列表</div>
+</template>
+
+<script setup>
+defineOptions({name: 'studentList-index'})
+</script>
+<style lang="scss" scoped>
+</style>

+ 10 - 0
src/views/recruit/teacher/studentList/studentDetails.vue

@@ -0,0 +1,10 @@
+<!--  -->
+<template>
+  <div>学生详情</div>
+</template>
+
+<script setup>
+defineOptions({name: 'studentList-student-details'})
+</script>
+<style lang="scss" scoped>
+</style>

+ 10 - 0
src/views/recruit/teacher/teacherCertification/index.vue

@@ -0,0 +1,10 @@
+<!--  -->
+<template>
+  <div>教师认证</div>
+</template>
+
+<script setup>
+defineOptions({name: 'teacher-certification'})
+</script>
+<style lang="scss" scoped>
+</style>