123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- <template>
- <scroll-view scroll-y :scroll-top="scrollTop" class="tabs-swiper-content" @scroll="handleScroll">
- <uv-tabs :current="current" :list="posts" v-bind="tabOptions">
- <template #default="{item, index}">
- <view class="bg-white mx-card fx-col fx-cen-cen px-20 py-10 text-content text-2xs"
- :class="{'active-post': index==current}" @click="current=index">
- <text>{{ item.name }}</text>
- <text>{{ `${item.salaryMin}-${item.salaryMax}/月` }}</text>
- </view>
- </template>
- </uv-tabs>
- <view class="bg-white">
- <view class="fx-row fx-bet-cen p-20">
- <view class="text-main text-lg font-bold">薪资情况</view>
- <mx-subsection v-model="currentChart" :list="chatTypes" width="120px"/>
- </view>
- <mx-echarts v-if="currentChart==0" :option="trendChartData" canvas-id="trend-chart"/>
- <mx-echarts v-else :option="distributeChartData" canvas-id="distribute-chart"/>
- </view>
- <view class="mt-20 bg-white">
- <view class="fx-row fx-bet-cen p-20">
- <view class="text-main text-lg font-bold">收入排行</view>
- <mx-subsection v-model="currentView" :list="viewTypes" width="120px"/>
- </view>
- <view v-show="currentView==0" class="fx-col gap-20 p-20 text-content">
- <view v-for="s in postDetail.industrySalary" class="fx-row fx-bet-cen gap-20">
- <view class="keep-all">{{ s.name }}</view>
- <uv-line dashed/>
- <view class="keep-all">¥{{ s.salary }}</view>
- </view>
- </view>
- <view v-show="currentView==1" class="fx-col gap-20 p-20 text-content">
- <view v-for="s in postDetail.citySalary" class="fx-row fx-bet-cen gap-20">
- <view class="keep-all">{{ s.city }}</view>
- <uv-line dashed/>
- <view class="keep-all">¥{{ s.salary }}</view>
- </view>
- </view>
- </view>
- <view class="mt-20 bg-white">
- <view class="fx-row fx-bet-cen p-20">
- <view class="text-main text-lg font-bold">就业情况</view>
- <mx-subsection v-model="currentPie" :list="pieTypes" width="120px"/>
- </view>
- <mx-echarts v-if="currentPie==0" :option="eduPieData" canvas-id="edu-chart"/>
- <mx-echarts v-else :option="expPieData" canvas-id="exp-chart"/>
- </view>
- <view class="mt-20 bg-white">
- <view class="fx-row fx-bet-cen p-20">
- <view class="text-main text-lg font-bold">招聘需求量</view>
- </view>
- <view class="fx-col gap-20 p-20 text-content">
- <view v-for="s in postDetail.demand" class="fx-row fx-bet-cen gap-20">
- <view class="keep-all">{{ s.city }}</view>
- <uv-line dashed/>
- <view class="keep-all">{{ s.count }}
- <text class="text-tips text-xs">个职位</text>
- </view>
- </view>
- </view>
- </view>
- <view class="mt-20 bg-white">
- <view class="fx-row p-20">
- <uv-icon name="question-circle" label-size="14" label="数据来源说明:"/>
- </view>
- <view class="text-tips text-2xs p-20">
- <view>{{ postDetail.vocationalSource }}</view>
- <view>{{ postDetail.salarySource }}</view>
- </view>
- </view>
- <uv-gap height="20"/>
- </scroll-view>
- </template>
- <script setup>
- import {ref, computed, watch} from 'vue';
- import {useInjectVocationDetailService} from "@/pages/vocation-library/detail/components/useVocationDetailInjection";
- import {sleep} from "@/uni_modules/uv-ui-tools/libs/function";
- import {useCacheStore} from "@/hooks/useCacheStore";
- import {vocationalPostsDetail} from "@/api/webApi/collegemajor";
- const {posts, scrollTop, currentPostName} = useInjectVocationDetailService()
- const {dispatchCache} = useCacheStore()
- const current = ref(0)
- const postDetail = ref({})
- watch(currentPostName, async name => {
- await sleep(500)
- const idx = posts.value.findIndex(p => p.name == name)
- if (idx > -1) current.value = idx
- else current.value = 0
- }, {immediate: true})
- watch(current, async i => {
- // detail要以current为准,因为currentPostName有匹配不上的情况
- const currentPost = posts.value[i]
- const {name} = currentPost || {}
- if (!name) return
- const payload = {postName: name}
- const res = await dispatchCache(vocationalPostsDetail, payload)
- postDetail.value = res.data
- }, {immediate: true})
- const tabOptions = {
- scrollable: true,
- keyName: 'seq',
- lineHeight: 0.5,
- lineWidth: 80,
- lineColor: 'white',
- itemStyle: {height: '60px'}
- }
- const createPieData = (pieData) => {
- // https://echarts.apache.org/examples/zh/editor.html?c=pie-simple
- return {
- title: {
- text: '',
- subtext: '',
- left: 'center'
- },
- tooltip: {
- trigger: 'item',
- formatter: '{b}: {d}%'
- },
- legend: {
- show: false
- },
- series: [
- {
- name: '',
- type: 'pie',
- radius: ['40%', '70%'],
- data: pieData,
- label: {
- show: true, // 启用标签
- formatter: '{b}: {d}%' // 格式化标签
- },
- emphasis: {
- itemStyle: {
- shadowBlur: 10,
- shadowOffsetX: 0,
- shadowColor: 'rgba(0, 0, 0, 0.5)'
- }
- }
- }
- ]
- }
- }
- const currentChart = ref(0)
- const chatTypes = ['按趋势', '按分布']
- const trendChartData = computed(() => {
- // https://echarts.apache.org/examples/zh/editor.html?c=area-basic
- if (!postDetail.value?.experience?.length) return null
- const exp = postDetail.value.experience
- const cols = exp.map(e => e.year)
- const rows = exp.map(e => e.salary)
- return {
- grid: {
- top: '5%',
- bottom: '12%',
- left: '12%'
- },
- xAxis: {
- type: 'category',
- boundaryGap: false,
- data: cols
- },
- yAxis: {
- type: 'value',
- axisLabel: {
- rotate: 45
- }
- },
- series: [
- {
- data: rows,
- type: 'line',
- smooth: true, // 设置为平滑曲线
- areaStyle: {}
- }
- ]
- }
- })
- const distributeChartData = computed(() => {
- if (!postDetail.value?.salary?.length) return null
- const pieData = postDetail.value.salary.map(item => {
- return {
- value: item.ratio,
- name: `${item.min}-${item.max}元/月`,
- }
- })
- return createPieData(pieData)
- })
- const currentView = ref(0)
- const viewTypes = ['按行业', '按地区']
- // view 没有用报表
- const currentPie = ref(0)
- const pieTypes = ['按学历', '按经验']
- const eduPieData = computed(() => {
- if (!postDetail.value?.edu?.length) return null
- const pieData = postDetail.value.edu.map(item => {
- return {
- value: item.ratio,
- name: `${item.edu}${item.ratio}%`,
- }
- })
- return createPieData(pieData)
- })
- const expPieData = computed(() => {
- if (!postDetail.value?.exp?.length) return null
- const pieData = postDetail.value.exp.map(item => {
- return {
- value: item.ratio,
- name: `${item.exp}${item.ratio}%`,
- }
- })
- return createPieData(pieData)
- })
- const handleScroll = (e) => {
- scrollTop.value = e.detail.scrollTop
- }
- </script>
- <style scoped lang="scss">
- .active-post {
- background-color: var(--primary-color) !important;
- color: white !important;
- }
- ::v-deep(.uv-tabs) {
- .uv-tabs__wrapper__nav {
- /* 不能设置__nav的间隔相关属性,会导致uv-tabs内部无法准确计算位置 */
- /* 重写__item的样式是没有关系的 */
- &__item {
- padding: 8px 4px;
- box-sizing: border-box;
- &:first-child {
- padding-left: 8px;
- }
- &:nth-last-child(2) {
- padding-right: 8px;
- }
- }
- }
- }
- </style>
|