index.vue 18 KB

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