index.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. <template>
  2. <div>
  3. <div v-if="showLogin" class="login-content" :style="{'background-image': 'url(' + webContent.loginBgUrl + ')'}">
  4. <div class="login-content-box pa-10">
  5. <div class="login-content-box-title text-center mt-4">请先登录您的企业账号</div>
  6. <passwordFrom class="mt-10" ref="passRef" placeholder="请输入企业邮箱" :validEmail="true"></passwordFrom>
  7. <v-btn :loading="loading" color="primary" class="white--text mt-5" min-width="340" @click="handleLogin">登录</v-btn>
  8. </div>
  9. </div>
  10. <div v-else class="content py-3" @scroll="handleScroll">
  11. <v-card class="py-3 px-5" :style="{'width': isMobile ? '100%' : '750px'}" style="min-height: calc(100vh - 24px); box-sizing: border-box; margin: 0 auto;">
  12. <div class="d-flex align-center" style="width: 340px; margin: auto;">
  13. <TextInput v-model="textItem.value" :item="textItem" @click="openDrawer" @appendInnerClick="openDrawer"></TextInput>
  14. </div>
  15. <!-- 已发布职位 -->
  16. <div class="text-end mb-1">
  17. <v-menu transition="slide-y-transition">
  18. <template v-slot:activator="{ props, isActive }">
  19. <v-btn color="primary" v-bind="props" variant="tonal" :append-icon="isActive ? 'mdi mdi-menu-up' : 'mdi mdi-menu-down'">
  20. {{ query?.jobId ? select : '选择已发布职位推荐' }}
  21. </v-btn>
  22. </template>
  23. <v-list>
  24. <v-list-item v-for="k in selectItems.items" :key="k.value" color="primary" :active="k.value === query?.jobId">
  25. <v-list-item-title @click="handleActive(k)">{{ k.label }}</v-list-item-title>
  26. </v-list-item>
  27. </v-list>
  28. </v-menu>
  29. <v-icon v-if="query?.jobId" color="primary" size="30" class="ml-3" @click="handleClearJob">mdi-close-circle-outline</v-icon>
  30. </div>
  31. <v-divider></v-divider>
  32. <div v-if="items.length">
  33. <div v-for="(val, index) in items" :key="val.id" @click="handleDetail(val)">
  34. <div class="py-3 d-flex align-center">
  35. <v-avatar size="large">
  36. <v-img :src="getUserAvatar(val.avatar, val.sex)" width="50" height="50"></v-img>
  37. </v-avatar>
  38. <div class="ml-3 d-flex flex-column">
  39. <p class="font-size-20">{{ val.name }}</p>
  40. <p class="color-999 font-size-16">
  41. {{ val.jobStatusName }}
  42. <span v-if="val.jobStatusName && val.expName" class="septal-line"></span>
  43. {{ val.expName }}
  44. <span v-if="val.eduName" class="septal-line"></span>
  45. {{ val.eduName }}
  46. </p>
  47. </div>
  48. </div>
  49. <div class="bg-box" v-if="index !== items.length - 1"></div>
  50. </div>
  51. </div>
  52. <Empty v-else :elevation="false" message="暂无数据"></Empty>
  53. </v-card>
  54. </div>
  55. <Loading :visible="loading"></Loading>
  56. <v-navigation-drawer v-model="screen" location="top" temporary class="px-5" style="border-radius: 0 0 10px 10px;">
  57. <FilterPage ref="filterRef" @close="screen = false" @search="handleSearch"></FilterPage>
  58. </v-navigation-drawer>
  59. </div>
  60. </template>
  61. <script setup>
  62. defineOptions({ name: 'talentRecommendation'})
  63. import { ref, onMounted } from 'vue'
  64. import passwordFrom from '@/views/login/components/passwordPage.vue'
  65. import Snackbar from '@/plugins/snackbar'
  66. import { useUserStore } from '@/store/user'
  67. import { passwordLogin } from '@/api/common'
  68. import { getJobAdvertised, getPersonSearchPage, getPersonRecommendPage } from '@/api/enterprise'
  69. import { dealDictArrayData } from '@/utils/position'
  70. import { getUserAvatar } from '@/utils/avatar'
  71. import { useRouter } from 'vue-router'
  72. import FilterPage from './components/filter.vue'
  73. import { webContentStore } from '@/store/webContent'
  74. const webContent = webContentStore()
  75. const router = useRouter()
  76. const loading = ref(false)
  77. const passRef = ref(null)
  78. const items = ref([])
  79. const total = ref(0)
  80. const query = ref({
  81. pageNo: 1,
  82. pageSize: 20
  83. })
  84. const select = ref('')
  85. const screen1 = ref(false)
  86. const token = ref(localStorage.getItem('ENT_ACCESS_TOKEN'))
  87. const showLogin = ref(token.value ? false : true)
  88. const selectItems = ref({
  89. label: '已发布职位',
  90. placeholder: '请选择要进行推荐的职位',
  91. clearable: true,
  92. width: 600,
  93. items: []
  94. })
  95. const textItem = ref({
  96. type: 'text',
  97. width: 600,
  98. value: '',
  99. label: '输入关键词搜索',
  100. clearable: false,
  101. readonly: true,
  102. appendInnerIcon: 'mdi-magnify'
  103. })
  104. // 已发布职位列表
  105. const getJobList = async () => {
  106. const data = await getJobAdvertised({ status: 0, exTime: 0 })
  107. if (data.length) {
  108. const list = dealDictArrayData([], data)
  109. selectItems.value.items = list.map(e => {
  110. return {
  111. label: `${e.name}_${e.areaName ? e.area?.str : '全国'} ${e.payFrom ? e.payFrom + '-' : ''}${e.payTo ? e.payTo : ''}${e.payName ? '/' + e.payName : ''}`,
  112. value: e.id
  113. }
  114. })
  115. }
  116. }
  117. const handleActive = (k) => {
  118. select.value = k.label
  119. query.value.jobId = k.value
  120. query.value.pageNo = 1
  121. screen1.value = false
  122. getData(true)
  123. }
  124. const handleClearJob = () => {
  125. select.value = ''
  126. delete query.value.jobId
  127. query.value.pageNo = 1
  128. items.value = []
  129. total.value = 0
  130. screen1.value = false
  131. }
  132. // 组件挂载后添加事件监听器
  133. const isMobile = ref(false)
  134. onMounted(async () => {
  135. await webContent.getSystemWebContent()
  136. if (!token.value) {
  137. Snackbar.warning('请先登录')
  138. showLogin.value = true
  139. }
  140. else getJobList()
  141. const userAgent = navigator.userAgent
  142. isMobile.value = /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i.test(userAgent)
  143. })
  144. // 列表
  145. const getData = async (isEmpty) => {
  146. // isEmpty:是否清空列表
  147. loading.value = true
  148. try {
  149. const res = query.value?.jobId ? await getPersonRecommendPage(query.value) : await getPersonSearchPage(query.value)
  150. const list = res.list || []
  151. items.value = list.length ? !isEmpty ? [...items.value, ...dealDictArrayData([], list)] : dealDictArrayData([], list) : []
  152. total.value = res.total
  153. } catch (err) {
  154. loading.value = false
  155. if (err.code === 401) {
  156. items.value = []
  157. total.value = 0
  158. token.value = null
  159. query.value = {
  160. pageNo: 1,
  161. pageSize: 20
  162. }
  163. Snackbar.warning('请先登录')
  164. showLogin.value = true
  165. }
  166. } finally {
  167. loading.value = false
  168. }
  169. }
  170. // 底部加载
  171. const handleScroll = (e) => {
  172. if (!token.value) {
  173. showLogin.value = true
  174. Snackbar.warning('请先登录')
  175. return
  176. }
  177. if (e.srcElement.scrollTop + e.srcElement.clientHeight > e.srcElement.scrollHeight - 10) {
  178. if (items.value.length < total.value) {
  179. query.value.pageNo++
  180. getData()
  181. }
  182. }
  183. }
  184. // 登录
  185. const handleLogin = async () => {
  186. const { valid } = await passRef.value.passwordForm.validate()
  187. if (!valid) return
  188. loading.value = true
  189. try {
  190. const data = await passwordLogin({ ...passRef.value.loginData, account: passRef.value.loginData.phone })
  191. await useUserStore().changeRole({ ...data, type: 'emailLogin', noJump: true })
  192. await getJobList()
  193. if (data?.accessToken) {
  194. token.value = data?.accessToken
  195. showLogin.value = false
  196. }
  197. } catch (err) {
  198. Snackbar.warning(err)
  199. } finally {
  200. loading.value = false
  201. }
  202. }
  203. // 人才详情
  204. const handleDetail = ({ userId, id }) => {
  205. if (!token.value) {
  206. showLogin.value = true
  207. Snackbar.warning('请先登录')
  208. return
  209. }
  210. if (!userId || !id) return
  211. router.push(`/recruit/enterprise/talentRecommendation/details/${userId}?id=${id}`)
  212. }
  213. // 筛选
  214. const filterRef = ref()
  215. const screen = ref(false)
  216. const openDrawer = () => {
  217. if (!token.value) {
  218. showLogin.value = true
  219. Snackbar.warning('请先登录')
  220. return
  221. }
  222. screen.value = true
  223. }
  224. const handleSearch = (val) => {
  225. if (!token.value) {
  226. showLogin.value = true
  227. Snackbar.warning('请先登录')
  228. return
  229. }
  230. screen.value = false
  231. textItem.value.value = val.content
  232. query.value.pageNo = 1
  233. query.value = Object.assign(query.value, val)
  234. getData(true)
  235. }
  236. </script>
  237. <style scoped lang="scss">
  238. .content {
  239. background-color: #f2f4f7;
  240. height: 100vh;
  241. overflow-y: auto;
  242. }
  243. .login-content {
  244. position: relative;
  245. width: 100%;
  246. height: 100vh;
  247. background-size: cover;
  248. &-box {
  249. position: absolute;
  250. top: 50%;
  251. left: 50%;
  252. translate: -50% -50%;
  253. width: 420px;
  254. height: 380px;
  255. background-color: #fff;
  256. border-radius: 10px;
  257. &-title {
  258. color: #4c4c4c;
  259. font-size: 24px;
  260. }
  261. }
  262. }
  263. .bg-box {
  264. height: 10px;
  265. background-color: #f2f4f7;
  266. }
  267. .active {
  268. color: var(--v-primary-base);
  269. border: 2px solid var(--v-primary-base);
  270. }
  271. </style>