index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. <template>
  2. <div :class="{ isMobile: mobile }">
  3. <!-- web -->
  4. <div v-if="!mobile">
  5. <div class="jobList">
  6. <div class="topArea" v-if="!isSeoList">
  7. <!--{{JSON.stringify(typeList)}}-->
  8. </div>
  9. <div class="contentArea">
  10. <div class="tabs">
  11. <div class="tabs-item tabs-item-active">职位</div>
  12. <div class="tabs-item">
  13. <nuxt-link to="/company/list">公司</nuxt-link>
  14. </div>
  15. </div>
  16. <div class="selectArea" v-if="!isSeoList">
  17. <!--职业角色、国内地区筛选-->
  18. <div class="selectContent" v-for="(key, i) in Object.keys(typeList)" :key="i + 'selectContent'">
  19. <div class="content">
  20. <div class="left">
  21. <p>{{ typeList[key].title }}</p>
  22. </div>
  23. <div class="right">
  24. <div class="cell" v-for="(item, ii) in typeList[key].list"
  25. :class="{selected: item.id === selected[key], noneClick: !canSelectCity && key === 'city'}"
  26. @click="changeIndexSeo(key, item)" v-if="ii < 8 || expansion[key]" :key="ii + key + item.id">
  27. <p>{{ item.name }}</p>
  28. </div>
  29. </div>
  30. <div class="more" @click="changeExpansion(key)" v-if="typeList[key].list.length > 8">{{ expansion[key] ? "收起" : "更多" }}</div>
  31. </div>
  32. <div class="smallContent" v-if="key === 'direction' && typeList[key].smallList.length > 0">
  33. <div class="cell" v-for="(item, ii) in typeList[key].smallList" :class="{ selected: item.id === selected.directionSmall }"
  34. @click="changeIndexSeo('directionSmall', item)" :key="ii + key + item.id">
  35. <p>{{ item.name }}</p>
  36. </div>
  37. </div>
  38. </div>
  39. </div>
  40. <!--不包含搜索关键字-->
  41. <div class="breadcrumb" v-if="!page.keyword">
  42. <a v-for="(item, index) in breadcrumbList" :key="'breadcrumb' + index" :href="item.url" :title="item.name">
  43. <p v-if="index !== breadcrumbList.length - 1">
  44. {{ item.name }}<span>&nbsp;>&nbsp;</span>
  45. </p>
  46. <h1 v-else>{{ item.name }}</h1>
  47. </a>
  48. </div>
  49. <!--包含搜索关键字-->
  50. <div class="breadcrumb" v-else>
  51. <h1>为您找到 {{dataList.length}} 个 "{{ page.keyword }}"的兼职信息</h1>
  52. </div>
  53. <!--搜索结果-->
  54. <div class="listArea">
  55. <div class="list" v-loading="loading">
  56. <nuxt-link class="cell" v-for="item in dataList" :key="item.id" :to="`/d/${item.hashId}`" target="_blank" :title="item.name">
  57. <div class="topArea">
  58. <div class="left">{{ item.title }}</div>
  59. <div class="right">{{ item.salaryName }}</div>
  60. </div>
  61. <div class="labelList">
  62. <div class="label" v-for="skill in item.skills || []">
  63. <p style="margin-bottom: 0 !important;">{{ skill.name }}</p>
  64. </div>
  65. <div class="label">
  66. <p>{{ item.experienceName }}</p>
  67. </div>
  68. <div class="label" v-if="item.month">
  69. <p>{{ item.month }}个月</p>
  70. </div>
  71. <div class="label" v-if="item.cityName">
  72. <p>驻场</p>
  73. </div>
  74. <div class="label" v-if="item.cityName">
  75. <p>{{ item.cityName }}</p>
  76. </div>
  77. </div>
  78. <div class="workDesc">{{ item.description }}</div>
  79. <div class="bottomArea">
  80. <a :href="`${siteurl}/company/${item.companyInfo.uid}`" target="_blank" :title="item.name">
  81. <div class="companyInfo" style="width:100%">
  82. <div class="logo">
  83. <img :src="item.companyInfo.logo" alt/>
  84. </div>
  85. <div class="companyName">
  86. {{ item.companyInfo.shortName || item.companyInfo.name }}
  87. </div>
  88. </div>
  89. </a>
  90. <div class="publishTime">{{ item.createdAtFormat }}</div>
  91. </div>
  92. </nuxt-link>
  93. <div v-if="dataList.length === 0" class="noneData">
  94. <p>没有数据</p>
  95. </div>
  96. </div>
  97. </div>
  98. <div class="pagination">
  99. <el-pagination v-if="!isSeoList" background layout="prev, pager, next" :total="page.total" :page-size="page.pageSize" @current-change="pageChange"
  100. :current-page="Number(page.current)"></el-pagination>
  101. <div v-else>
  102. <div class="list">
  103. <nuxt-link v-for="(item, index) in new Array(Math.ceil(page.total / page.size))" :to="`/?page=${index + 1}`" :key="page + index">{{ index + 1
  104. }}
  105. </nuxt-link>
  106. </div>
  107. </div>
  108. </div>
  109. <BottomBanner></BottomBanner>
  110. </div>
  111. </div>
  112. <SeoFooter style :data="footer"/>
  113. </div>
  114. <!-- mobile -->
  115. <div v-else class="jobListMobile">
  116. <div class="topSelect">
  117. <van-dropdown-menu>
  118. <!--方式-->
  119. <van-dropdown-item
  120. v-model="selected['workType']"
  121. :options="typeList['workType'].list"
  122. key="workType"
  123. @change="changeIndex('workType')"
  124. />
  125. <!--地区-->
  126. <van-dropdown-item
  127. v-model="selected['city']"
  128. :options="typeList['city'].list"
  129. key="city"
  130. @change="changeIndex('city')"
  131. :disabled="selected.workType === 1"
  132. />
  133. <!--职业-->
  134. <van-dropdown-item
  135. :title="calcName.text"
  136. key="direction"
  137. ref="directionSelect"
  138. >
  139. <van-tree-select
  140. :items="typeList['direction'].list"
  141. active-id="1123321100"
  142. :main-active-index.sync="selected['directionIndex']"
  143. @click-item="
  144. data => {
  145. changeIndex('directionSmall', data);
  146. }
  147. "
  148. />
  149. </van-dropdown-item>
  150. </van-dropdown-menu>
  151. </div>
  152. <h1>{{ calcH1() }}</h1>
  153. <van-pull-refresh
  154. v-model="refreshing"
  155. @refresh="onRefresh"
  156. class="listArea"
  157. :class="{ noneInWx: !$deviceType.app && !isWxapp}"
  158. >
  159. <div style="text-align: center" v-if="firstLoading">
  160. <van-loading size="24px">加载中...</van-loading>
  161. </div>
  162. <van-list v-else v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad" :immediate-check="false" class="list">
  163. <nuxt-link class="cell" v-for="item in dataList" :key="item.id" :to="`/d/${item.hashId}`" :title="item.name">
  164. <div class="topArea">
  165. <div class="left">{{ item.directionName }}</div>
  166. <div class="right">{{ item.salaryName }}</div>
  167. </div>
  168. <div class="labelList">
  169. <div class="label" v-for="skill in item.skills || []">
  170. <p>{{ skill.name }}</p>
  171. </div>
  172. <div class="label" v-if="item.month">
  173. <p>{{ item.month }}个月</p>
  174. </div>
  175. <div class="label" v-if="item.cityName">
  176. <p>驻场</p>
  177. </div>
  178. <div class="label" v-if="item.cityName">
  179. <p>{{ item.cityName }}</p>
  180. </div>
  181. </div>
  182. <div class="workDesc">{{ item.description }}</div>
  183. <div class="companyInfo" @click.stop="jumpToCompanyInfo(item)">
  184. <div class="logo">
  185. <img :src="item.companyInfo.logo" alt/>
  186. </div>
  187. <div class="companyName">
  188. {{ item.companyInfo.shortName || item.companyInfo.name }}
  189. </div>
  190. </div>
  191. </nuxt-link>
  192. </van-list>
  193. </van-pull-refresh>
  194. <a v-if="!deviceType.app && mobile && !isBaiduxapp && !isWxapp" :class="{'downapp':isShowDownLoad}" :href="downloadhref">下载APP</a>
  195. </div>
  196. </div>
  197. </template>
  198. <script>
  199. import ConnectUs from "@/components/common/connectUs";
  200. import BindMobile from "@/components/common/bindMobile";
  201. import DealSeoData from "@/components/job/dealSeoIndex";
  202. import DealSeoFooter from "@/components/job/dealSeoFooter";
  203. import BottomBanner from "@/components/job/bottomBanner";
  204. import SeoFooter from "@/components/SeoFooter";
  205. import {mapState, mapMutations} from "vuex";
  206. import deviceType from "../../plugins/deviceType";
  207. export default {
  208. name: "JobListSeoIndex",
  209. // layout: "opacity_header",
  210. showCommonFooter: false,
  211. components: {ConnectUs, BindMobile, SeoFooter, BottomBanner},
  212. data() {
  213. return {
  214. expansion: {direction: 0, city: 0},
  215. isShowToast: false,
  216. name: "",
  217. phone: "",
  218. loading: false,
  219. refreshing: false,
  220. firstLoading: false, //移动端加载loading
  221. isLoading: false,
  222. selected: {},
  223. downloadhref: "",
  224. type: "",
  225. isShowDownLoad: false,
  226. isBaiduxapp: false,
  227. isWxapp: true,
  228. siteurl: "",
  229. jobUrl:""
  230. };
  231. },
  232. head() {
  233. const {
  234. title = "",
  235. keyword = "",
  236. description = "",
  237. h1 = "",
  238. canonical = "",
  239. metaLocation
  240. } = this.head || {};
  241. let obj = {
  242. title: title,
  243. meta: [
  244. {
  245. name: "keywords",
  246. content: keyword
  247. },
  248. {
  249. name: "description",
  250. content: description
  251. },
  252. {
  253. name: "h1",
  254. content: h1
  255. }
  256. ],
  257. link: [{rel: "canonical", href: canonical}]
  258. };
  259. if (metaLocation) {
  260. obj.meta.push({name: "location", content: metaLocation});
  261. }
  262. return obj;
  263. },
  264. async asyncData({...params}) {
  265. console.log("*********params:", params);
  266. try {
  267. params.store.commit("updateNoneCommonFooter", true);
  268. } catch (e) {
  269. console.log("updateNoneCommonFooter", e);
  270. }
  271. console.log("asyncdata..............");
  272. let dealDataObj = new DealSeoData(params);
  273. let ans = await dealDataObj.dealData();
  274. let dealSeoFooterObj = new DealSeoFooter(params);
  275. let {footer, selected} = await dealSeoFooterObj.dealData();
  276. if (this) {
  277. this.selected = selected;
  278. }
  279. return {
  280. selected: selected,
  281. ...ans,
  282. footer
  283. };
  284. },
  285. watch: {},
  286. computed: {
  287. ...mapState(["isPC", "isWeixin", "deviceType", "noneCommonFooter"]),
  288. canSelectCity() {
  289. //远程无法选中地区
  290. return this.selected.workType !== 1;
  291. },
  292. calcName() {
  293. const {
  294. direction,
  295. directionSmall,
  296. directionName = "",
  297. directionSmallName = ""
  298. } = this.selected;
  299. let job = directionSmall || direction;
  300. let jobName =
  301. directionSmallName === "全部"
  302. ? directionName
  303. : directionSmallName || directionName;
  304. return {
  305. text: jobName || "全部职业",
  306. value: job
  307. };
  308. }
  309. },
  310. created() {
  311. console.log("created................", this.selected, this.typeList);
  312. // this.firstLoading = true
  313. },
  314. mounted() {
  315. console.log("mounted................", this.selected, this.typeList);
  316. // this.getList()
  317. if (this.mobile) {
  318. document.body.style = "overflow:hidden;";
  319. }
  320. let UA = navigator.userAgent;
  321. console.log("-------------", UA);
  322. this.isWxapp = UA.indexOf('miniProgram') > -1;
  323. console.error(this.isWxapp ? "是微信" : "不是微信");
  324. this.isBaiduxapp = navigator.userAgent.indexOf('swan/') > -1;
  325. console.log(this.$route.params);
  326. var isAndroid = UA.indexOf("Android") > -1 || UA.indexOf("Adr") > -1; //android终端
  327. console.error(isAndroid ? "是安卓" : "不是安卓");
  328. var isiOS = !!UA.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
  329. console.error(isiOS ? "是IOS" : "不是IOS");
  330. if (this.isWxapp) {
  331. this.downloadhref =
  332. "http://a.app.qq.com/o/simple.jsp?pkgname=com.proginn";
  333. } else if (isAndroid) {
  334. this.downloadhref =
  335. "http://inncms.storage.proginn.com/android/proginn-v4.10.0.apk";
  336. } else if (isiOS) {
  337. this.downloadhref =
  338. "https://itunes.apple.com/cn/app/cheng-xu-yuan-ke-zhan/id979914687";
  339. }
  340. console.log("this.downloadhref =", this.downloadhref);
  341. this.type = deviceType.app ? "APP" : "浏览器";
  342. setTimeout(() => {
  343. this.isShowDownLoad = true;
  344. console.log("this.isShowDownload=", this.isShowDownLoad);
  345. }, 500)
  346. let ua = navigator.userAgent;
  347. this.isBaiduxapp = navigator.userAgent.indexOf('swan/') > -1;
  348. console.log(this.$route.params);
  349. // this.isWxapp = this.$route.query.isWxMini && this.$route.query.isWxMini == 1;
  350. this.siteurl = this.$store.state.domainConfig.siteUrl;
  351. this.jobUrl = this.$store.state.domainConfig.jobUrl
  352. },
  353. methods: {
  354. //移动端选择器
  355. changeIndex(key, item) {
  356. //级联选择 特殊处理一下
  357. console.log(this.selected, this.typeList);
  358. if (key === "directionSmall") {
  359. //当右侧选择的不是"全部"选项的时候,将大选项初始化
  360. if (item.id) {
  361. this.selected["direction"] = 0;
  362. this.selected["directionName"] = "";
  363. this.selected["directionSlug"] = "";
  364. this.selected[key] = item.id;
  365. this.selected[key + "Name"] = item.name;
  366. this.selected[key + "Slug"] = item.slug;
  367. } else {
  368. //当右侧选择的是"全部"选项的时候, 将右侧数据初始化,只保留左侧数据
  369. //左侧更改时 直接更改的是索引index,故这里要转换一下
  370. let myItem = this.typeList.direction.list[
  371. this.selected.directionIndex
  372. ];
  373. this.selected["direction"] = myItem.id;
  374. this.selected["directionName"] = myItem.name;
  375. this.selected["directionSlug"] = myItem.slug;
  376. this.selected[key] = 0;
  377. this.selected[key + "Name"] = "";
  378. this.selected[key + "Slug"] = "";
  379. }
  380. }
  381. //如果选中的工作方式是远程,则初始化 城市选择
  382. if (key === "workType" && this.selected.workType === 1) {
  383. this.selected.city = 0;
  384. this.selected.cityName = "";
  385. }
  386. this.page.page = 1;
  387. this.page.total = 0;
  388. this.firstLoading = true;
  389. this.getList();
  390. this.$refs.directionSelect.toggle(false);
  391. },
  392. //web的选择器
  393. changeIndexSeo(key, item) {
  394. console.log("hhhahahahahhhhhhhhhhhhhhhhh");
  395. console.log("key:", key, "item:", item, "selected:", this.selected);
  396. //远程无法选中地区
  397. if (!this.canSelectCity && key === "city") {
  398. return;
  399. }
  400. // 如果选中了远程工作,重置城市选择
  401. if (key === "workType" && item.id === 1) {
  402. this.selected.city = 0;
  403. this.selected.cityName = "";
  404. this.selected.citySlug = "";
  405. }
  406. this.selected[key] = item.id;
  407. this.selected[key + "Slug"] = item.slug;
  408. //大工作分类时,先显示子分类
  409. if (key === "direction") {
  410. if (item.id === 0) {
  411. this.selected["directionSmall"] = item.id;
  412. this.selected["directionSmallSlug"] = item.slug;
  413. } else {
  414. let list = this.typeList.direction.list.filter(
  415. item => item.id === this.selected.direction
  416. )[0];
  417. if (list.children && list.children.length > 1) {
  418. this.selected["directionSmall"] = -1;
  419. this.typeList[key].smallList = [...((list && list.children) || [])];
  420. return;
  421. }
  422. }
  423. }
  424. let {
  425. citySlug,
  426. directionSlug,
  427. directionSmallSlug,
  428. workTypeSlug
  429. } = this.selected;
  430. let url = "/";
  431. if (citySlug) {
  432. url += citySlug + "/";
  433. }
  434. if (directionSmallSlug || directionSlug) {
  435. url += (directionSmallSlug || directionSlug) + "/";
  436. }
  437. //驻场方式 不放到url里面
  438. if (key === "workType") {
  439. this.page.page = 1;
  440. this.page.total = 0;
  441. this.getList();
  442. return;
  443. }
  444. location.href = url;
  445. },
  446. changeExpansion(key) {
  447. this.expansion[key] = !this.expansion[key];
  448. },
  449. getList() {
  450. const {page, selected: {city, direction, directionSmall, workType}} = this;
  451. let p = {cityId: city, ...page};
  452. direction && (p.occupationId = direction); //一级
  453. workType && (p.workType = workType);
  454. directionSmall > 0 && (p.directionId = directionSmall); //耳二级
  455. if (this.isLoading) {
  456. return;
  457. }
  458. this.loading = true;
  459. this.isLoading = true;
  460. this.$axios.post("/api/recruit/search", p).then(res => {
  461. if (Number(res.data.status) === 1) {
  462. let data = res.data.data;
  463. this.page.total = data.total;
  464. if (this.page.page === 1 || !this.mobile) {
  465. this.dataList = [...data.list];
  466. } else {
  467. this.dataList = [...this.dataList, ...data.list];
  468. }
  469. this.page.page += 1;
  470. this.page.current = Number(data.page);
  471. if (this.page.total <= this.dataList.length) {
  472. this.finished = true;
  473. }
  474. }
  475. }).finally(() => {
  476. this.firstLoading = false;
  477. this.refreshing = false;
  478. this.isLoading = false;
  479. this.$nextTick(() => {
  480. this.loading = false;
  481. });
  482. console.log(
  483. "this.finished................................",
  484. this.finished
  485. );
  486. });
  487. },
  488. pageChange(i) {
  489. this.page.page = i;
  490. this.getList();
  491. },
  492. jumpToCompanyInfo(item) {
  493. const {
  494. companyInfo: {uid}
  495. } = item;
  496. window.open(
  497. this.$store.state.domainConfig.baseUrl + `/wo/${uid}`,
  498. `targetCompany${uid}`
  499. );
  500. },
  501. /** 移动端下拉刷新 **/
  502. onRefresh() {
  503. // 清空列表数据
  504. this.finished = false;
  505. console.log("onRefresh");
  506. this.onLoad();
  507. },
  508. onLoad() {
  509. console.log("onLoad");
  510. this.getList();
  511. },
  512. calcH1() {
  513. const {
  514. city,
  515. cityName = "",
  516. direction,
  517. directionName = "",
  518. directionSmall,
  519. directionSmallName = ""
  520. } = this.selected;
  521. let job = directionSmall || direction;
  522. let jobName =
  523. directionSmallName === "全部"
  524. ? directionName
  525. : directionSmallName || directionName;
  526. let title = "兼职招聘";
  527. if (city && job) {
  528. title = `${cityName}${jobName}兼职招聘`;
  529. } else if (city && !job) {
  530. //兼职城市
  531. title = `${cityName}兼职招聘`;
  532. } else if (!city && job) {
  533. //岗位页
  534. title = `${jobName}兼职招聘`;
  535. }
  536. return title;
  537. },
  538. is_weixin() {
  539. }
  540. }
  541. };
  542. </script>
  543. <style scope lang="scss">
  544. @import "../../assets/css/job/index.scss";
  545. </style>
  546. <style lang="scss">
  547. .van-dropdown-menu__title {
  548. color: #666;
  549. }
  550. .downapp {
  551. position: absolute;
  552. bottom: 0;
  553. z-index: 1000;
  554. width: 100vw;
  555. height: 50px;
  556. background: cornflowerblue;
  557. color: white;
  558. font-size: 15px;
  559. text-align: center;
  560. line-height: 50px;
  561. }
  562. .labelList p {
  563. margin-bottom: 0 !important;
  564. }
  565. .cell p{
  566. margin-bottom: 0 !important;
  567. }
  568. </style>