navBar.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. <template>
  2. <div>
  3. <v-toolbar
  4. class="banner"
  5. density="compact"
  6. style="padding-left: 0px;height: 50px;font-size: 15px;"
  7. >
  8. <div class="innerBox d-flex justify-space-between" style="min-width: 1240px;">
  9. <div>
  10. <div class="nav-logo mr-3 mt-1 cursor-pointer d-flex align-center" @click="router.push('/recruitHome')">
  11. <v-img src="../../assets/logo.png" aspect-ratio="16/9" contain :width="97" style="height: 40px"></v-img>
  12. <span class="ml-2" style="margin-top: 6px;" :class="{'active-route' : route.path === '/recruitHome'}">首页</span>
  13. </div>
  14. <!-- <div class="nav-city">
  15. <p class="nav-city-box">
  16. <v-icon color="primary">mdi-map-marker</v-icon>
  17. <span class="nav-city-selected">广州</span>
  18. <span class="switchover-city nav-city-selected">[{{ $t('sys.switchCities') }}]</span>
  19. </p>
  20. </div> -->
  21. <div class="menuList">
  22. <div v-for="val in navList" :key="val.name" class="mr-3">
  23. <template v-if="val.children">
  24. <defineListPage v-bind="$attrs" :title="val.title" :list="val.children" @emitClick="handleClick" :closeOnContentClick="true"></defineListPage>
  25. </template>
  26. <template v-else>
  27. <span
  28. class="cursor-pointer menuList-first-title"
  29. :class="{'active-route': menuActive(val)}"
  30. @click.stop="handleClick(val, true)">
  31. {{ val.title }}
  32. </span>
  33. </template>
  34. </div>
  35. </div>
  36. </div>
  37. <div class="d-flex" style="height: 50px;">
  38. <div class="btns d-flex align-center" v-if="!isLogin && showLoginBtn">
  39. <v-btn color="primary" style="color: #fff !important; background-color: #00B760;" to="/login">{{ t('login.loginOrRegister') }}</v-btn>
  40. </div>
  41. <!-- 头像用户名 -->
  42. <div class="d-flex align-center color-333" v-if="isLogin">
  43. <span class="cursor-pointer mr-5 commonHover" v-if="showTeacherLogin" @click="handleSwitchTeacher">切换为老师</span>
  44. <span class="cursor-pointer mr-5 commonHover" v-else @click="handleSwitchEnterprise">我要招聘</span>
  45. <span class="cursor-pointer commonHover" @click="router.push({ path: paths[6] })">
  46. <span>
  47. 现金:{{ userAccount?.balance && userAccount?.balance > 0 ? (userAccount?.balance / 100.0).toFixed(2) : 0 }}
  48. <span style="color: #00000000;">1</span>
  49. 积分:{{ userAccount?.point || 0 }}
  50. </span>
  51. </span>
  52. <v-menu open-on-hover>
  53. <template v-slot:activator="{ props }">
  54. <div class="d-flex ml-8 align-center cursor-pointer" :class="{'active-route': route.path.includes('/recruit/personal/personalCenter'), 'vipBox': vip}" v-bind="props" @click="handleToPersonalCenter">
  55. <div style="position: relative;">
  56. <v-avatar class="avatar">
  57. <v-img alt="" :src="getUserAvatar(baseInfo?.avatar, baseInfo?.sex)" ></v-img>
  58. </v-avatar>
  59. <div v-if="vip" style="position: absolute; left: -9px; bottom: -3px;" @click.stop="router.push('/recruit/personal/personalCenter/memberBenefits/membershipPackage')">
  60. <svg-icon name="huangguan1" size="25"></svg-icon>
  61. </div>
  62. </div>
  63. <div class="ml-3 userName">
  64. {{ baseInfo?.name || userInfo.phone }}
  65. </div>
  66. </div>
  67. </template>
  68. <v-list>
  69. <v-list-item v-for="(item, index) in items" :key="index" @click="item.change">
  70. <template v-slot:prepend>
  71. <v-icon :icon="item.icon"></v-icon>
  72. </template>
  73. <v-list-item-title>{{ item.title }}</v-list-item-title>
  74. </v-list-item>
  75. </v-list>
  76. </v-menu>
  77. </div>
  78. <!-- 语言切换 -->
  79. <!-- <v-menu>
  80. <template v-slot:activator="{ props }">
  81. <v-btn
  82. class="ml-3"
  83. color="primary"
  84. icon="mdi-translate"
  85. size="small"
  86. v-bind="props"
  87. >
  88. </v-btn>
  89. </template>
  90. <v-list density="compact" color="primary">
  91. <v-list-item
  92. v-for="item in localeStore.localeMap"
  93. :key="item.name"
  94. :value="item.lang"
  95. :active="localeStore.currentLocale.lang === item.lang"
  96. @click="handleChangeLocale(item)"
  97. >
  98. <v-list-item-title>{{ item.name }}</v-list-item-title>
  99. </v-list-item>
  100. </v-list>
  101. </v-menu> -->
  102. <div class="d-flex align-center ml-3" v-if="showBall">
  103. <message-notification v-if="showBall" :path="paths[3]" class="commonHover2" :class="{'active-route': routeActive === 3}"></message-notification>
  104. </div>
  105. </div>
  106. </div>
  107. </v-toolbar>
  108. <CtDialog :visible="show" title="请选择要切换的公司账号" :footer="true" widthType="2" @close="show = false" @submit="switchSubmit">
  109. <v-radio-group v-model="radios" :hide-details="true">
  110. <v-radio v-for="item in enterpriseList" :key="item.enterpriseId" color="primary" :label="formatName(item.enterpriseName)" :value="item.enterpriseId"></v-radio>
  111. </v-radio-group>
  112. <div class="text-end pr-5 pt-5">
  113. <span class="color-error cursor-pointer border-bottom-error" @click="newRegistering">{{ btnType ? '都不是我要的?去注册新企业' : '查看申请进度' }}</span>
  114. </div>
  115. </CtDialog>
  116. </div>
  117. </template>
  118. <script setup>
  119. import { computed, onMounted, ref } from 'vue'
  120. import { getToken, getRefreshToken } from '@/utils/auth'
  121. import { useUserStore } from '@/store/user'
  122. // import { useLocaleStore } from '@/store/locale'
  123. import { useI18n } from '@/hooks/web/useI18n'
  124. import CtDialog from '@/components/CtDialog'
  125. import { useRoute } from 'vue-router'; const route = useRoute()
  126. import { useRouter } from 'vue-router'; const router = useRouter()
  127. import { getUserBindEnterpriseList, getUserRegisterEnterpriseApply } from '@/api/personal/user'
  128. import MessageNotification from '../message.vue'
  129. import { getUserAvatar } from '@/utils/avatar'
  130. import defineListPage from '@/views/entrances/navBar/components/defineListPage'
  131. import { formatName } from '@/utils/getText'
  132. import { getSchoolInformation } from '@/api/school'
  133. defineOptions({ name: 'personal-navbar' })
  134. defineProps({
  135. showLoginBtn: {
  136. type: Boolean,
  137. default: true
  138. },
  139. sticky: {
  140. type: Boolean,
  141. default: true
  142. }
  143. })
  144. const showBall = ref(false)
  145. const showTeacherLogin = ref(false)
  146. const isSchool = localStorage.getItem('isSchool') ? true : false
  147. // 获取老师注册信息
  148. const schoolInfo = ref({})
  149. const getSchoolInfo = async () => {
  150. const data = await getSchoolInformation()
  151. if (data && Object.keys(data).length > 0) {
  152. schoolInfo.value = data
  153. showTeacherLogin.value = isSchool ? false : true
  154. }
  155. }
  156. onMounted(async () => {
  157. if (getToken()) {
  158. showBall.value = true
  159. await getSchoolInfo()
  160. }
  161. })
  162. const { t } = useI18n()
  163. // const localeStore = useLocaleStore()
  164. const userStore = useUserStore()
  165. const paths = [ // 有选中样式-路由列表
  166. '/recruitHome', // 0
  167. '/recruit/personal/position', // 1
  168. '/recruit/personal/company', // 2
  169. '/recruit/personal/message', // 3
  170. '/recruit/personal/myWallet', // 4
  171. '/recruit/personal/personalCenter', // 5
  172. '/recruit/personal/personalCenter/wallet', // 6
  173. ]
  174. const navList = ref([
  175. { title: '门墩儿招聘', path: '/recruit/personal/recommend', dealActive: true },
  176. { title: '门墩儿猎头', path: '/headhunting' },
  177. { title: '门墩儿商城', path: '/pointsExchange', isEdit: true },
  178. { title: '火苗儿校企', path: '/flameLogin' },
  179. { title: '了解门墩儿', path: '/about' }
  180. ])
  181. const mode = import.meta.env.VITE_NODE_ENV
  182. console.log(import.meta.env.VITE_NODE_ENV, '当前环境变量===========')
  183. const mall = navList.value.find(item => item.isEdit)
  184. // 区分生产环境展示地址
  185. mall.path = mode === 'production' ? '/pointsExchange' : '/mall'
  186. const handleClick = (e, status) => {
  187. if (!e.path) return
  188. if (status) window.open(e.path)
  189. else router.push(e.path)
  190. }
  191. const routeActive = computed(() => {
  192. const index = paths.findIndex(item => item === route.path)
  193. return index
  194. })
  195. // 左侧菜单选中状态
  196. const position = [
  197. '/recruit/personal/recommend',
  198. '/recruit/personal/position',
  199. '/recruit/personal/company',
  200. '/recruit/personal/jobFair',
  201. '/recruit/personal/company/details',
  202. '/recruit/personal/position/details'
  203. ]
  204. const menuActive = (val) => {
  205. let path
  206. position.forEach(e => {
  207. if (route.path.indexOf(e) !== -1) path = e
  208. })
  209. return val.dealActive ? position.indexOf(path) !== -1 : (route.path === val.path || route.path.includes(val.path))
  210. }
  211. const vip = computed(() => {
  212. return new Date().getTime() < userStore.userInfo?.vipExpireDate
  213. })
  214. const show = ref(false)
  215. const radios = ref(null)
  216. const enterpriseList = ref([])
  217. // 切换为招聘者
  218. const handleSwitchEnterprise = async () => {
  219. await getApplyInfo() // 查询申请信息(注册进度)
  220. const data = await getUserBindEnterpriseList() // 申请通过才有数据,否则空数组
  221. enterpriseList.value = data || []
  222. if (data?.length) {
  223. radios.value = data[0].enterpriseId
  224. show.value = true
  225. } else {
  226. // router.push('recruit/enterpriseRegister')
  227. newRegistering() // 既没有存在的企业 又没有申请数据 》 进入注册页面
  228. }
  229. }
  230. // 切换提交
  231. const switchSubmit = async () => {
  232. localStorage.setItem('enterpriseId', radios.value)
  233. localStorage.setItem('emailLoginInfo', "false") // 先清空企业邮箱信息,避免求职切换招聘有邮箱登录信息遗留导致使用邮箱信息
  234. // 跳转企业路由 且验证是否已完善必填基本信息
  235. window.location.href = '/enterpriseVerification'
  236. }
  237. const btnType = ref(0)
  238. // 查看用户是否有在申请中的数据
  239. const getApplyInfo = async () => {
  240. const data = await getUserRegisterEnterpriseApply()
  241. localStorage.setItem('userApplyInfo', JSON.stringify(data))
  242. const status = data?.status || null // 有数据说明有申请
  243. // 0: 有等待审核的数据或者审核不通过的数据 >查看申请进度
  244. // 1: 没有正在审核的数据 >注册新企业
  245. btnType.value = (status === '0' || status === '2') ? 0 : 1
  246. }
  247. // 注册/查看申请进度
  248. const loading1 = ref(false)
  249. const newRegistering = () => {
  250. loading1.value = true
  251. const path = btnType.value ? '/recruit/entRegister' : '/recruit/entRegister/inReview'
  252. router.replace({ path })
  253. }
  254. // 跳转老师页面
  255. const statusList = [
  256. { status: '0', path: '/register/school/inReview', key: 'registerSchoolInfo' }, // 审核中
  257. { status: '1', path: '/recruit/teacher/studentList/index', key: 'schoolInfo' }, // 审核通过
  258. { status: '2', path: '/register/school/inReview', key: 'registerSchoolInfo' }, // 审核不通过
  259. ]
  260. const handleSwitchTeacher = () => {
  261. const obj = statusList.find(item => item.status === schoolInfo.value?.authStatus)
  262. if (!obj || !schoolInfo.value) return
  263. localStorage.setItem(obj.key, JSON.stringify(schoolInfo.value))
  264. router.push({ path: obj.path })
  265. }
  266. const handleToPersonalCenter = () => {
  267. router.push({ path: paths[5] })
  268. }
  269. // 退出登录
  270. const handleLogout = async () => {
  271. await userStore.userLogout(1)
  272. showBall.value = false
  273. if (route.path === '/recruitHome') location.reload()
  274. else router.push({ path: '/recruitHome' })
  275. }
  276. const items = ref([
  277. { title: t('setting.personalCenter'), icon: 'mdi-account-circle-outline', change: handleToPersonalCenter },
  278. { title: t('setting.editPassword'), icon: 'mdi-shield-lock-open-outline', change: () => router.push({ path: '/recruit/personal/personalCenter/accountSettings' }) },
  279. { title: t('setting.logOut'), icon: 'mdi-logout', change: handleLogout }
  280. ])
  281. let baseInfo = ref(JSON.parse(localStorage.getItem('baseInfo')) || {}) // 人才信息
  282. let userInfo = ref(JSON.parse(localStorage.getItem('userInfo')) || {})
  283. let userAccount = ref(JSON.parse(localStorage.getItem('userAccount')) || {}) // 账户信息
  284. const isLogin = ref(Boolean(getRefreshToken(2)))
  285. const updatePage = () => {
  286. baseInfo.value = JSON.parse(localStorage.getItem('baseInfo')) || {}
  287. userInfo.value = JSON.parse(localStorage.getItem('userInfo')) || {}
  288. userAccount.value = JSON.parse(localStorage.getItem('userAccount')) || {}
  289. isLogin.value = Boolean(getRefreshToken(2))
  290. }
  291. userStore.$subscribe((mutation, state) => {
  292. if (Object.keys(state.baseInfo).length) baseInfo.value = state.baseInfo
  293. if (state.userInfo && Object.keys(state.userInfo).length) userInfo.value = state?.userInfo
  294. if (Object.keys(state.userAccount).length) userAccount.value = state.userAccount
  295. if (state.token) showBall.value = true
  296. updatePage()
  297. })
  298. // 语言切换
  299. // const handleChangeLocale = (item) => {
  300. // localeStore.setCurrentLocale(item)
  301. // location.reload()
  302. // }
  303. </script>
  304. <style lang="scss" scoped>
  305. @import '@/styles/personal/navBar.scss'
  306. </style>