new-detail.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. <template>
  2. <div class="app-container jobDetail" >
  3. <el-row :gutter="20">
  4. <el-col :span="6">
  5. <div class="bd-f2">
  6. <p style="border-bottom: 1px solid #f2f2f2" class="pd10 f-333 f14">全部职业</p>
  7. <div class="tree-wrap">
  8. <el-tree
  9. ref="tree"
  10. :data="vocationList"
  11. node-key="code"
  12. :default-checked-keys="[code]"
  13. :default-expanded-keys="[code]"
  14. :props="defaultProps"
  15. :current-node-key="code"
  16. @node-click="handleNodeClick"
  17. :isLeaf="isLeaf"
  18. >
  19. <p class="custom-tree-node" :title="node.label" slot-scope="{node, data}">
  20. <span class="text-ellipsis">{{node.label }}</span>
  21. <span>{{data.children ? data.children.length : ''}}</span>
  22. </p>
  23. </el-tree>
  24. </div>
  25. </div>
  26. </el-col>
  27. <el-col :span="18">
  28. <el-card style="color: #5E5E5E;" ref="navBar">
  29. <el-breadcrumb separator-class="el-icon-arrow-right">
  30. <el-breadcrumb-item :to="{ path: '/index' }">首页</el-breadcrumb-item>
  31. <el-breadcrumb-item :to="{ path: '/new-gaokao/three/Vocation'}">职业库</el-breadcrumb-item>
  32. <el-breadcrumb-item>职业详情</el-breadcrumb-item>
  33. </el-breadcrumb>
  34. </el-card>
  35. <div class="mt20 header-content pd20" >
  36. <p class="f28 f-333">{{vocationDetail.name || ''}}</p>
  37. </div>
  38. <div class="tabs-wrap">
  39. <span class="tabs-item" @click="tabActive = 0" :class="{'bg-primary':tabActive == 0}">职业概况</span>
  40. <span class="tabs-item" @click="tabActive = 1":class="{'bg-primary':tabActive == 1}">就业岗位</span>
  41. </div>
  42. <p class="line"></p>
  43. <div
  44. v-show="loading"
  45. class="loading-div"
  46. v-loading="loading"
  47. :style="{height:windowHeight + 'px'}">
  48. </div>
  49. <div class="content-wrap mt20" >
  50. <!-- 职业概况 -->
  51. <div v-if="Object.keys(vocationDetail).length > 0">
  52. <div v-show="tabActive == 0">
  53. <div class="desc-item">
  54. <p class="format-tit">职业介绍</p>
  55. <div class="text-666" v-html="vocationDetail.description"></div>
  56. </div>
  57. <div class="desc-item">
  58. <p class="format-tit">就业岗位<span class="text-666 f14">({{vocationDetail.postJobs.length || 0}}个)</span></p>
  59. <div class="post-item mb20" v-for="post in vocationDetail.postJobs">
  60. <div class="post-title f16 mb10" style="cursor: pointer" @click="toJob(post)">{{post.name}}</div>
  61. <div class="pb10 text-666 post-text">
  62. <div>{{`${post.salaryMin}元-${post.salaryMax}元/月`}}</div>
  63. <div>热门地区:{{post.hotCity}}</div>
  64. </div>
  65. </div>
  66. </div>
  67. <div class="desc-item">
  68. <p class="format-tit">相关专业<span class="text-666 f14">({{vocationDetail.postMajors.length}}个)</span></p>
  69. <div class="post-item mb20" v-for="post in vocationDetail.postMajors">
  70. <div class="post-title f16 mb10" style="cursor: pointer" @click="toMajorDetail(post)">{{post.name}}</div>
  71. <div class="pb10 text-666 post-text">
  72. <div>国标代码:{{post.code}}</div>
  73. <div>学制:{{post.learnYear}}</div>
  74. <div>男女比例:{{`${post.maleRatio}/${post.femaleRatio}`}}</div>
  75. </div>
  76. </div>
  77. </div>
  78. </div>
  79. </div>
  80. <!-- 就业岗位-->
  81. <div v-if="vocationalPosts.length > 0">
  82. <div v-show="tabActive == 1">
  83. <el-row :gutter="20" type="flex" class="jobTabs">
  84. <el-col class="job-item-wrap" :span="4" v-for="item in vocationalPosts" :key="item.name">
  85. <div class="job-item f14 f-333" :class="{'bg-primary':jobActiveName == item.name}" @click="toActiveJob(item.name)">
  86. <p class="mb10">{{item.name}}</p>
  87. <p>{{`${item.salaryMin}-${item.salaryMax}`}}{{item.salaryUnit}}</p>
  88. </div>
  89. </el-col>
  90. </el-row>
  91. <!-- 岗位详情 -->
  92. <div v-if="Object.keys(jobDetail).length > 0">
  93. <!-- 图表 -->
  94. <div class="desc-item">
  95. <p class="format-tit">薪资情况</p>
  96. <el-row :gutter="10">
  97. <el-col :span="12">
  98. <p class="text-right f-333">按趋势</p>
  99. <mx-chart :options="chartExperience" height="300px"></mx-chart>
  100. </el-col>
  101. <el-col :span="12">
  102. <p class="text-right f-333">按分布</p>
  103. <mx-chart :options="chartSalary" height="300px"></mx-chart>
  104. </el-col>
  105. </el-row>
  106. </div>
  107. <div class="desc-item">
  108. <p class="format-tit">就业形势</p>
  109. <el-row :gutter="10">
  110. <el-col :span="12">
  111. <p class="text-right f-333">按学历</p>
  112. <mx-chart :options="chartEdu" height="300px"></mx-chart>
  113. </el-col>
  114. <el-col :span="12">
  115. <p class="text-right f-333">按经验</p>
  116. <mx-chart :options="chartExp" height="300px"></mx-chart>
  117. </el-col>
  118. </el-row>
  119. </div>
  120. <div class="desc-item">
  121. <p class="format-tit">招聘需求量</p>
  122. <el-row :gutter="40" type="flex" class="flex-wrap">
  123. <el-col :span="12" class="f16" v-for="(item,index) in jobDetail.demand">
  124. <el-row class="format-job-wrap">
  125. <el-col :span="3" class="f18 text-center f-666">{{index + 1}}</el-col>
  126. <el-col :span="15" class="f-333">{{item.city}}</el-col>
  127. <el-col :span="6" class="f-666 text-right f14">{{item.count}}职位</el-col>
  128. </el-row>
  129. </el-col>
  130. </el-row>
  131. </div>
  132. <div class="desc-item">
  133. <p class="format-tit">收入排行-按行业</p>
  134. <el-row :gutter="40" type="flex" class="flex-wrap">
  135. <el-col :span="12" class="f16" v-for="(item,index) in jobDetail.industrySalary">
  136. <el-row class="format-job-wrap">
  137. <el-col :span="3" class="f18 text-center f-666">{{index + 1}}</el-col>
  138. <el-col :span="15" class="f-333">{{item.name}}</el-col>
  139. <el-col :span="6" class="f-666 text-right f14">{{item.salary}}元 / 月</el-col>
  140. </el-row>
  141. </el-col>
  142. </el-row>
  143. </div>
  144. <div class="desc-item">
  145. <p class="format-tit">收入排行-按地区</p>
  146. <el-row :gutter="40" type="flex" class="flex-wrap">
  147. <el-col :span="12" class="f16" v-for="(item,index) in jobDetail.citySalary">
  148. <el-row class="format-job-wrap">
  149. <el-col :span="3" class="f18 text-center f-666">{{index + 1}}</el-col>
  150. <el-col :span="15" class="f-333">{{item.city}}</el-col>
  151. <el-col :span="6" class="f-666 text-right f14">{{item.salary}}元 / 月</el-col>
  152. </el-row>
  153. </el-col>
  154. </el-row>
  155. </div>
  156. </div>
  157. <div v-else>
  158. 暂无数据
  159. </div>
  160. </div>
  161. </div></div>
  162. </el-col>
  163. </el-row>
  164. </div>
  165. </template>
  166. <script>
  167. import MxChart from '@/components/MxChart/index'
  168. import { vocationalPostsDetail,vocationalPosts } from '@/api/webApi/vocation'
  169. import MxVocationTranslateMixin from '@/components/Cache/modules/mx-vocation-translate-mixin'
  170. export default {
  171. name: "Detail",
  172. components: {
  173. MxChart
  174. },
  175. mixins:[MxVocationTranslateMixin],
  176. data(){
  177. return {
  178. loading:false,
  179. code:'',
  180. defaultProps: {
  181. children: 'children',
  182. label: 'name'
  183. },
  184. tabActive: 0,
  185. vocationalPosts: [], // 就业岗位
  186. vocationDetail:{}, // 职业概况
  187. jobActiveName: '', // 激活状态的岗位
  188. jobDetail: {}, // 岗位详情
  189. windowHeight:document.documentElement.clientHeight,
  190. }
  191. },
  192. created() {
  193. this.code = this.$route.query.code
  194. this.getVocationalOverview();
  195. },
  196. computed:{
  197. // 按工资分布
  198. chartSalary() {
  199. if (!this.jobDetail.salary.length) return null
  200. const pieData = this.jobDetail.salary.map(item => {
  201. return {
  202. value: item.ratio,
  203. name: `${item.min}-${item.max}元/月 ${item.ratio}%`,
  204. }
  205. })
  206. const options = {
  207. toolbox: {
  208. show: true,
  209. },
  210. tooltip: {
  211. trigger: 'item'
  212. },
  213. series: [
  214. {
  215. type: 'pie',
  216. radius: [40, 60],
  217. label:{
  218. formatter: '{b}'
  219. },
  220. data: pieData
  221. }
  222. ]
  223. }
  224. return options
  225. },
  226. // 按经验
  227. chartExp() {
  228. if (!this.jobDetail.exp.length) return null
  229. const pieData = this.jobDetail.exp.map(item => {
  230. return {
  231. value: item.ratio,
  232. name: `${item.exp}${item.ratio}%`,
  233. }
  234. })
  235. const options = {
  236. toolbox: {
  237. show: true,
  238. },
  239. series: [
  240. {
  241. name: 'Nightingale Chart',
  242. type: 'pie',
  243. radius: [30, 60],
  244. label:{
  245. formatter: '{b}'
  246. },
  247. data: pieData
  248. }
  249. ]
  250. }
  251. return options
  252. },
  253. // 按教育程度
  254. chartEdu() {
  255. if (!this.jobDetail.edu.length) return null
  256. const pieData = this.jobDetail.edu.map(item => {
  257. return {
  258. value: item.ratio,
  259. name: `${item.edu}${item.ratio}%`,
  260. }
  261. })
  262. const options = {
  263. toolbox: {
  264. show: true,
  265. },
  266. series: [
  267. {
  268. name: 'Nightingale Chart',
  269. type: 'pie',
  270. radius: [30, 60],
  271. label:{
  272. formatter: '{b}'
  273. },
  274. data: pieData
  275. }
  276. ]
  277. }
  278. return options
  279. },
  280. // 按工资趋势
  281. chartExperience() {
  282. if (!this.jobDetail.experience.length) return null
  283. const col = this.jobDetail.experience.map(item => item.year)
  284. const row = this.jobDetail.experience.map(item => item.salary)
  285. const options = {
  286. xAxis: {
  287. data: col,
  288. axisLine: {
  289. lineStyle: {
  290. type: 'dashed'
  291. }
  292. },
  293. axisTick: {
  294. alignWithLabel: true
  295. },
  296. },
  297. yAxis: {
  298. type: 'value',
  299. },
  300. series: [
  301. {
  302. name: 'Email',
  303. type: 'line',
  304. color: '#47C6A2',
  305. stack: 'Total',
  306. label: {
  307. show: true,
  308. position: 'top',
  309. },
  310. smooth: false,
  311. data: row
  312. },
  313. ]
  314. }
  315. return options
  316. }
  317. },
  318. watch:{
  319. tabActive: {
  320. handler(newVal){
  321. // 1 岗位 0 概览
  322. if(newVal == 0) this.getVocationalOverview();
  323. if(newVal == 1) this.getVocationalPosts();
  324. }
  325. },
  326. },
  327. methods:{
  328. toMajorDetail(row) {
  329. console.log('跳转')
  330. console.log(row)
  331. // 跳转
  332. this.$router.push({path:'/career/plan/MajorDetail',query:{code:row.code}})
  333. },
  334. handleNodeClick(data,node) {
  335. if(!node.isLeaf || this.code == node.data.code) return
  336. this.code =node.data.code
  337. this.tabActive = 0
  338. this.getVocationalOverview()
  339. // 跳转
  340. // this.$router.replace({path:'/career/vocation/Detail',query:{code:node.data.code}})
  341. },
  342. toJob(post) {
  343. console.log(post)
  344. this.tabActive = 1
  345. this.toActiveJob(post.name)
  346. },
  347. isLeaf(data,node) {
  348. return node.childCount == 0
  349. },
  350. toActiveJob(name) {
  351. if(this.jobActiveName == name) return
  352. this.jobActiveName = name
  353. // 刷新数据
  354. this.getVocationalPostsDetail()
  355. },
  356. // 就业岗位
  357. getVocationalPosts() {
  358. this.loading = true
  359. const params={
  360. code:this.code
  361. };
  362. vocationalPosts(params).then(res => {
  363. this.vocationalPosts = res.data
  364. if(this.jobActiveName == '') {
  365. this.jobActiveName = res.data[0].name
  366. this.$nextTick(_ => {
  367. this.getVocationalPostsDetail()
  368. })
  369. }
  370. }).finally(_ => {
  371. this.loading = false
  372. })
  373. },
  374. // 就业岗位详情
  375. getVocationalPostsDetail() {
  376. this.loading = true
  377. vocationalPostsDetail({postName:this.jobActiveName }).then(res => {
  378. console.log(res)
  379. this.jobDetail = res.data || {}
  380. }).finally(res => {
  381. this.loading = false
  382. })
  383. },
  384. // 职业概况
  385. getVocationalOverview(){
  386. this.loading = true
  387. const params={
  388. code:this.code
  389. };
  390. vocationalOverview(params).then(res=>{
  391. console.log(res)
  392. this.vocationDetail = res.data || {}
  393. }).finally(_ => {
  394. this.loading = false
  395. })
  396. }
  397. }
  398. }
  399. </script>
  400. <style lang="scss" scoped>
  401. .jobDetail {
  402. .header-content{
  403. top: 0;
  404. left: 0;
  405. width: 100%;
  406. height: 100%;
  407. background: rgba(66, 185, 131, 0.1);
  408. }
  409. .custom-tree-node{
  410. display: flex;
  411. justify-content: space-between;
  412. align-items: center;
  413. width: 100%;
  414. overflow: hidden;
  415. }
  416. .tabs-wrap{
  417. margin-top: 20px;
  418. height: 40px;
  419. .tabs-item{
  420. cursor: pointer;
  421. padding: 0 33px;
  422. border-radius: 4px 4px 0 0;
  423. display: inline-block;
  424. line-height: 40px;
  425. &:hover{
  426. color:#47C6A2;
  427. }
  428. &.bg-primary{
  429. background: #47C6A2 ;
  430. color: white;
  431. }
  432. }
  433. }
  434. .bg-primary{
  435. background: #47C6A2 !important;
  436. color: white;
  437. }
  438. .format-job-wrap {
  439. display: flex;
  440. height: 44px;
  441. line-height: 44px;
  442. border-bottom: 1px solid #f2f2f2;
  443. }
  444. .line{
  445. background: #47C6A2;
  446. height: 1px;
  447. }
  448. .post-text{
  449. display: flex;
  450. div{
  451. min-width: 160px;
  452. &:not(:first-child):before{
  453. content: "";
  454. display: inline-block;
  455. width: 1px;
  456. height: 12px;
  457. background-color: #ccc;
  458. vertical-align: text-top;
  459. margin-right: 60px;
  460. margin-top: 4px;
  461. }
  462. }
  463. }
  464. .loading-div {
  465. width: 100%;
  466. position: absolute;
  467. background: transparent;
  468. left: 0;
  469. }
  470. .post-title{
  471. color: #333;
  472. }
  473. .post-item{
  474. border-bottom: 1px solid #f2f2f2;
  475. color: #333;
  476. }
  477. .format-tit{
  478. border-left: 4px solid #47C6A2;
  479. padding-left: 10px;
  480. margin-bottom: 20px;
  481. font-size: 20px;
  482. }
  483. .desc-item{
  484. margin-bottom: 40px;
  485. }
  486. .text-666{
  487. font-size: 14px;
  488. color: #666666 !important;
  489. }
  490. .flex-wrap{
  491. flex-wrap: wrap;
  492. }
  493. .jobTabs{
  494. flex-wrap: wrap;
  495. .job-item-wrap{
  496. padding: 10px;
  497. margin-bottom: 20px;
  498. .job-item{
  499. cursor: pointer;
  500. border-radius: 4px;
  501. padding: 10px;
  502. background: #f2f2f2;
  503. }
  504. }
  505. }
  506. .tree-wrap{
  507. padding: 10px;
  508. height: calc(100vh - 176px) ;
  509. overflow: hidden;
  510. overflow-y: auto;
  511. }
  512. ::v-deep .el-tree-node.is-current > .el-tree-node__content {
  513. background: rgba(22, 119, 255, 0.1);
  514. color: #47C6A2;
  515. ::v-deep .is-leaf {
  516. color: rgba(0, 0, 0, 0);
  517. }
  518. }
  519. }
  520. </style>