Browse Source

feat(工种选择): 添加JobTypePicker组件并集成到相关页面

refactor(store): 新增工种相关状态和方法
style(认证): 更新技能证书字段的显示文本
master
前端-胡立永 4 weeks ago
parent
commit
d995811b56
11 changed files with 1889 additions and 639 deletions
  1. +0
    -5
      api/api.js
  2. +5
    -0
      api/model/config.js
  3. +271
    -0
      components/JobTypePicker-README.md
  4. +504
    -0
      components/JobTypePicker.vue
  5. +106
    -3
      components/screen/screenWork.vue
  6. +255
    -0
      pages/demo/JobTypePickerDemo.vue
  7. +2
    -2
      pages_order/auth/certification.vue
  8. +74
    -15
      pages_order/work/addResume.vue
  9. +41
    -11
      pages_order/work/jobPosting.vue
  10. +29
    -1
      store/store.js
  11. +602
    -602
      yarn.lock

+ 0
- 5
api/api.js View File

@ -57,11 +57,6 @@ const config = {
limit : 500,
showLoading : true,
},
//获取工种列表
commonQueryJobTypeList: {
url: '/api/common/queryJobTypeList',
method: 'GET',
},
//获取工作性质列表
commonQueryJobNatureList: {
url: '/api/common/queryJobNatureList',


+ 5
- 0
api/model/config.js View File

@ -12,6 +12,11 @@ const api = {
url: '/employ/config/queryConfig',
method: 'GET',
},
//获取工种列表
commonQueryJobTypeList: {
url: '/employ/config/queryJobTypeList',
method: 'GET',
},
}
export default api

+ 271
- 0
components/JobTypePicker-README.md View File

@ -0,0 +1,271 @@
# JobTypePicker 工种选择组件
基于 AddressPicker 组件设计的工种选择组件,支持单选、多选和多级树形结构。
## 功能特性
- ✅ **单选模式**:选择单个工种
- ✅ **多选模式**:支持选择多个工种
- ✅ **多级树形结构**:支持一级、二级、三级工种选择
- ✅ **灵活配置**:可配置只选择到二级工种
- ✅ **整个工种选择**:支持选择整个工种类别
- ✅ **数据缓存**:自动缓存已加载的工种数据
- ✅ **响应式设计**:适配不同屏幕尺寸
## 组件属性
| 属性名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| multiple | Boolean | false | 是否支持多选 |
| onlySubType | Boolean | false | 是否只选择到二级工种,不选择三级 |
| showSelectWholeType | Boolean | false | 是否显示"选择整个工种"选项 |
## 事件
| 事件名 | 说明 | 回调参数 |
|--------|------|----------|
| confirm | 确认选择时触发 | result: 选择结果对象 |
## 返回数据结构
```javascript
{
// 选中的工种层级
parentType: Object, // 一级工种对象
subType: Object, // 二级工种对象
detailType: Object, // 三级工种对象
// 多选时的数组
subTypes: Array, // 多选二级工种数组
detailTypes: Array, // 多选三级工种数组
// 最终选中的工种
selectedJobType: Object, // 最终选中的工种对象
// 用于后端的数据
selectedId: String, // 单选时的工种ID
selectedIds: Array, // 多选时的工种ID数组
// 显示文本
fullJobType: String // 完整的工种名称文本
}
```
## 基本用法
### 1. 单选模式
```vue
<template>
<view>
<uv-cell
title="选择工种"
:value="selectedJobType || '请选择工种'"
@click="openJobTypePicker"
isLink
></uv-cell>
<JobTypePicker
ref="jobTypePicker"
@confirm="onJobTypeConfirm"
/>
</view>
</template>
<script>
import JobTypePicker from '@/components/JobTypePicker.vue'
export default {
components: {
JobTypePicker
},
data() {
return {
selectedJobType: ''
}
},
methods: {
openJobTypePicker() {
this.$refs.jobTypePicker.open()
},
onJobTypeConfirm(result) {
this.selectedJobType = result.fullJobType
console.log('选中的工种ID:', result.selectedId)
}
}
}
</script>
```
### 2. 多选模式
```vue
<template>
<view>
<JobTypePicker
ref="multipleJobTypePicker"
:multiple="true"
@confirm="onMultipleJobTypeConfirm"
/>
</view>
</template>
<script>
export default {
methods: {
onMultipleJobTypeConfirm(result) {
console.log('选中的工种:', result.fullJobType)
console.log('选中的工种ID数组:', result.selectedIds)
console.log('选中的工种数量:', result.selectedIds.length)
}
}
}
</script>
```
### 3. 只选择到二级工种
```vue
<template>
<view>
<JobTypePicker
ref="subTypeJobTypePicker"
:onlySubType="true"
@confirm="onSubTypeJobTypeConfirm"
/>
</view>
</template>
<script>
export default {
methods: {
onSubTypeJobTypeConfirm(result) {
// 只会返回到二级工种,不会有三级工种
console.log('选中的二级工种:', result.selectedJobType)
}
}
}
</script>
```
### 4. 带"选择整个工种"选项
```vue
<template>
<view>
<JobTypePicker
ref="wholeJobTypePicker"
:showSelectWholeType="true"
@confirm="onWholeJobTypeConfirm"
/>
</view>
</template>
<script>
export default {
methods: {
onWholeJobTypeConfirm(result) {
// 可以选择整个工种类别
console.log('选中结果:', result)
}
}
}
</script>
```
## 数据源配置
组件依赖 Vuex store 中的工种数据,需要确保以下配置:
### 1. store.js 配置
```javascript
// state 中添加
state: {
jobTypeList: [], // 一级工种列表
jobTypeTreeMap: {} // 多级工种映射
}
// mutations 中添加
mutations: {
setChildJobTypeList(state, { pid, list }) {
state.jobTypeTreeMap[pid] = list
}
}
// actions 中添加
actions: {
async getChildJobTypeList({ state, commit }, pid) {
if(state.jobTypeTreeMap[pid]) {
return Promise.resolve(state.jobTypeTreeMap[pid])
}
return new Promise((resolve, reject) => {
api('commonQueryJobTypeList', {
pageNo: 1,
pageSize: 99999,
pid: pid
}, res => {
if(res.code == 200) {
commit('setChildJobTypeList', { pid, list: res.result.records })
resolve(res.result.records)
} else {
reject(res)
}
})
})
}
}
```
### 2. API 接口
确保 `commonQueryJobTypeList` 接口支持 `pid` 参数来获取子级工种:
```javascript
// api.js
commonQueryJobTypeList: {
url: '/api/common/queryJobTypeList',
method: 'GET'
}
```
## 样式自定义
组件使用了 SCSS 变量,可以通过修改 uni-app 的主题变量来自定义样式:
```scss
// uni.scss
$uni-color: #007aff; // 主题色
```
## 注意事项
1. **数据格式**:工种数据需要包含 `id`、`name` 字段
2. **层级关系**:通过 `pid` 字段建立父子关系
3. **缓存机制**:已加载的工种数据会自动缓存,避免重复请求
4. **错误处理**:当获取子级工种失败时,会自动确认当前选择
5. **性能优化**:使用了 scroll-view 组件,支持大量数据的流畅滚动
## 示例页面
可以参考 `pages/demo/JobTypePickerDemo.vue` 文件查看完整的使用示例。
## 依赖组件
- uv-popup:弹窗组件
- uv-icon:图标组件
- uv-cell:单元格组件(示例中使用)
## 兼容性
- ✅ H5
- ✅ 小程序
- ✅ App
## 更新日志
### v1.0.0
- 初始版本
- 支持单选、多选、多级树形结构
- 支持数据缓存和错误处理

+ 504
- 0
components/JobTypePicker.vue View File

@ -0,0 +1,504 @@
<template>
<uv-popup ref="popup" mode="bottom" :round="30"
:safeAreaInsetBottom="false" @close="handleClose">
<view class="job-type-picker">
<view class="header">
<view class="title">选择工种</view>
<view class="close-btn" @click="close">
<uv-icon name="close" size="40rpx"></uv-icon>
</view>
</view>
<view class="content">
<!-- 左侧一级工种列表 -->
<view class="left-panel">
<scroll-view scroll-y class="scroll-view">
<!-- 插槽自定义内容 -->
<slot name="custom-options"></slot>
<view
class="job-type-item"
:class="{ active: selectedParentType && selectedParentType.id === item.id }"
v-for="item in jobTypeList"
:key="item.id"
@click="selectParentType(item)">
{{ item.name }}
</view>
</scroll-view>
</view>
<!-- 右侧二三级工种列表 -->
<view class="right-panel">
<scroll-view scroll-y class="scroll-view">
<!-- 二级工种 -->
<template v-if="selectedParentType && !selectedSubType">
<!-- 选择整个工种选项 -->
<view
v-if="showSelectWholeType"
class="job-type-item whole-type-item"
@click="selectWholeParentType">
<uv-icon name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
选择整个{{ selectedParentType.name }}
</view>
<view
class="job-type-item"
:class="{
'selected': multiple && isSubTypeSelected(item),
'active': !multiple && selectedSubType && selectedSubType.id === item.id
}"
v-for="item in subTypeList"
:key="item.id"
@click="selectSubType(item)">
{{ item.name }}
<uv-icon v-if="multiple && isSubTypeSelected(item)"
name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
</view>
<!-- 多选时的确认按钮 -->
<view v-if="multiple && selectedSubTypes.length > 0"
class="confirm-btn" @click="confirmMultipleSubTypeSelection">
<button class="confirm-button">确认选择工种 ({{ selectedSubTypes.length }})</button>
</view>
</template>
<!-- 三级工种 -->
<template v-if="selectedSubType">
<view
class="job-type-item back-item"
@click="backToSubType">
<uv-icon name="arrow-left" size="30rpx"></uv-icon>
返回{{ selectedParentType.name }}
</view>
<!-- 选择整个子工种选项 -->
<view
v-if="showSelectWholeType"
class="job-type-item whole-type-item"
@click="selectWholeSubType">
<uv-icon name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
选择整个{{ selectedSubType.name }}
</view>
<view
class="job-type-item"
:class="{
'selected': multiple && isDetailTypeSelected(item),
'active': !multiple && selectedDetailType && selectedDetailType.id === item.id
}"
v-for="item in detailTypeList"
:key="item.id"
@click="selectDetailType(item)">
{{ item.name }}
<uv-icon v-if="multiple && isDetailTypeSelected(item)"
name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
</view>
<!-- 多选时的确认按钮 -->
<view v-if="multiple && selectedDetailTypes.length > 0"
class="confirm-btn" @click="confirmMultipleSelection">
<button class="confirm-button">确认选择 ({{ selectedDetailTypes.length }})</button>
</view>
</template>
</scroll-view>
</view>
</view>
</view>
</uv-popup>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'JobTypePicker',
props: {
//
onlySubType: {
type: Boolean,
default: false
},
//
multiple: {
type: Boolean,
default: false
},
// ""
showSelectWholeType: {
type: Boolean,
default: true
}
},
data() {
return {
selectedParentType: null, //
selectedSubType: null, //
selectedDetailType: null, //
selectedSubTypes: [], //
selectedDetailTypes: [], //
subTypeList: [], //
detailTypeList: [], //
}
},
computed: {
...mapState(['jobTypeList'])
},
methods: {
//
open() {
this.$refs.popup.open()
},
//
close() {
this.$refs.popup.close()
},
//
handleClose() {
this.selectedParentType = null
this.selectedSubType = null
this.selectedDetailType = null
this.selectedSubTypes = []
this.selectedDetailTypes = []
this.subTypeList = []
this.detailTypeList = []
},
//
async selectParentType(parentType) {
this.selectedParentType = parentType
this.selectedSubType = null
this.selectedDetailType = null
this.detailTypeList = []
//
try {
this.subTypeList = await this.$store.dispatch('getChildJobTypeList', parentType.id)
//
if (this.subTypeList.length === 0) {
this.confirm()
}
} catch (error) {
console.error('获取二级工种列表失败:', error)
this.subTypeList = []
//
this.confirm()
}
},
//
async selectSubType(subType) {
if (this.multiple) {
//
const index = this.selectedSubTypes.findIndex(item => item.id === subType.id)
if (index > -1) {
//
this.selectedSubTypes.splice(index, 1)
} else {
//
this.selectedSubTypes.push(subType)
}
} else {
//
this.selectedSubType = subType
this.selectedDetailType = null
//
if (this.onlySubType) {
this.confirm()
return
}
//
try {
this.detailTypeList = await this.$store.dispatch('getChildJobTypeList', subType.id)
//
if (this.detailTypeList.length === 0) {
this.confirm()
}
} catch (error) {
console.error('获取三级工种列表失败:', error)
this.detailTypeList = []
//
this.confirm()
}
}
},
//
selectDetailType(detailType) {
if (this.multiple) {
//
const index = this.selectedDetailTypes.findIndex(item => item.id === detailType.id)
if (index > -1) {
//
this.selectedDetailTypes.splice(index, 1)
} else {
//
this.selectedDetailTypes.push(detailType)
}
} else {
//
this.selectedDetailType = detailType
this.confirm()
}
},
//
isSubTypeSelected(subType) {
return this.selectedSubTypes.some(item => item.id === subType.id)
},
//
isDetailTypeSelected(detailType) {
return this.selectedDetailTypes.some(item => item.id === detailType.id)
},
//
selectWholeParentType() {
this.selectedSubType = null
this.selectedSubTypes = []
this.selectedDetailType = null
this.selectedDetailTypes = []
this.confirm()
},
//
selectWholeSubType() {
this.selectedDetailType = null
this.selectedDetailTypes = []
this.confirm()
},
//
confirmMultipleSubTypeSelection() {
//
this.confirm()
},
//
confirmMultipleSelection() {
this.confirm()
},
//
backToSubType() {
this.selectedSubType = null
this.selectedDetailType = null
this.detailTypeList = []
},
//
confirm() {
const result = {
parentType: this.selectedParentType,
subType: this.selectedSubType,
detailType: this.selectedDetailType,
subTypes: this.selectedSubTypes,
detailTypes: this.selectedDetailTypes
}
//
let fullJobType = ''
let selectedId = '' // ID
let selectedIds = [] // ID
if (this.selectedParentType) {
fullJobType += this.selectedParentType.name
}
//
if (this.multiple && this.selectedSubTypes.length > 0) {
const subTypeNames = this.selectedSubTypes.map(item => item.name).join(',')
fullJobType = subTypeNames
result.selectedJobType = this.selectedParentType //
result.selectedSubTypes = this.selectedSubTypes
// ID
selectedIds = this.selectedSubTypes.map(item => item.id)
result.selectedIds = selectedIds
} else if (this.selectedSubType) {
fullJobType += this.selectedSubType.name
//
if (this.multiple && this.selectedDetailTypes.length > 0) {
const detailTypeNames = this.selectedDetailTypes.map(item => item.name).join(',')
fullJobType += detailTypeNames
result.selectedJobType = this.selectedSubType //
result.selectedDetailTypes = this.selectedDetailTypes
// ID
selectedIds = this.selectedDetailTypes.map(item => item.id)
result.selectedIds = selectedIds
} else if (this.selectedDetailType) {
//
fullJobType += this.selectedDetailType.name
result.selectedJobType = this.selectedDetailType
selectedId = this.selectedDetailType.id
} else {
//
result.selectedJobType = this.selectedSubType
selectedId = this.selectedSubType.id
}
} else {
//
result.selectedJobType = this.selectedParentType
selectedId = this.selectedParentType.id
}
result.fullJobType = fullJobType
result.selectedId = selectedId // ID
result.selectedIds = selectedIds // ID
this.$emit('confirm', result)
this.close()
}
}
}
</script>
<style scoped lang="scss">
.job-type-picker {
height: 70vh;
background: #fff;
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1px solid #eee;
.title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.close-btn {
padding: 10rpx;
}
}
.content {
display: flex;
height: calc(70vh - 160rpx);
.left-panel {
width: 240rpx;
border-right: 1px solid #eee;
background: #f8f8f8;
}
.right-panel {
flex: 1;
}
.scroll-view {
height: 100%;
}
.job-type-item {
padding: 30rpx 20rpx;
font-size: 28rpx;
color: #333;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
&.active {
background: #fff;
color: $uni-color;
font-weight: bold;
position: relative;
&::after {
content: '';
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 6rpx;
background: $uni-color;
}
}
&.back-item {
display: flex;
align-items: center;
color: $uni-color;
background: #f8f8f8;
uv-icon {
margin-right: 10rpx;
}
}
&.whole-type-item {
display: flex;
align-items: center;
color: #3796F8;
background: rgba(#3796F8, 0.1);
font-weight: bold;
uv-icon {
margin-right: 10rpx;
}
}
&.selected {
background: rgba($uni-color, 0.1);
color: $uni-color;
font-weight: bold;
position: relative;
&::after {
content: '';
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 6rpx;
background: $uni-color;
}
}
}
.left-panel {
.select-all-item {
display: flex;
align-items: center;
color: #3796F8;
background: rgba(#3796F8, 0.1);
font-weight: bold;
padding: 30rpx 20rpx;
font-size: 28rpx;
border-bottom: 1px solid #f0f0f0;
uv-icon {
margin-right: 10rpx;
}
}
}
.confirm-btn {
position: sticky;
bottom: 0;
background: #fff;
padding: 20rpx;
border-top: 1px solid #eee;
.confirm-button {
width: 100%;
background: $uni-color;
color: #fff;
border: none;
border-radius: 10rpx;
padding: 20rpx;
font-size: 28rpx;
}
}
}
}
</style>

+ 106
- 3
components/screen/screenWork.vue View File

@ -15,7 +15,7 @@
</uv-drop-down-item>
<uv-drop-down-item
name="typeId" type="2"
name="typeId" type="1"
:label="dropItem('typeId').label"
:value="dropItem('typeId').value">
</uv-drop-down-item>
@ -61,6 +61,15 @@
<uv-icon name="arrow-right" size="30rpx"></uv-icon>
</view>
</view>
<!-- 工种选择使用JobTypePicker组件 -->
<view v-else-if="item.type === 'typeId'" class="job-type-selector" @click="openJobTypePickerInPopup">
<view class="selected-job-type">
{{ popupSelectedJobType || '请选择工种' }}
</view>
<view class="arrow">
<uv-icon name="arrow-right" size="30rpx"></uv-icon>
</view>
</view>
<!-- 其他选项使用tagList -->
<view v-else class="tagList">
<view
@ -89,6 +98,16 @@
</view>
</template>
</AddressPicker>
<!-- 工种选择组件 -->
<JobTypePicker ref="jobTypePicker" @confirm="onJobTypeConfirm">
<template #custom-options>
<view class="job-type-item select-all-item" @click="selectAllJobTypes">
<uv-icon name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
选择全部工种
</view>
</template>
</JobTypePicker>
</view>
</template>
@ -97,9 +116,11 @@
mapState,
} from 'vuex'
import AddressPicker from '@/components/AddressPicker.vue'
import JobTypePicker from '@/components/JobTypePicker.vue'
export default {
components: {
AddressPicker
AddressPicker,
JobTypePicker
},
props : {
sign : {
@ -181,6 +202,7 @@
},
],
popupSelectedAddress: '', //
popupSelectedJobType: '', //
}
},
computed : {
@ -278,6 +300,12 @@
return
}
// 使JobTypePicker
if(name === 'typeId'){
this.$refs.jobTypePicker.open()
return
}
//
if(type == 1){
this.$refs.popup.open()
@ -290,7 +318,6 @@
.child.findIndex(item => item.label == find.label && item
.value == find.value);
this[this.activeName].activeIndex = findIndex;
} else {
this[this.activeName].activeIndex = 0;
}
@ -342,6 +369,13 @@
label: this.popupSelectedAddress,
value: this.popupSelectedAddress == '全部地区' ? 'all' : this.areaId.value // 使ID
})
} else if(n.type === 'typeId' && this.popupSelectedJobType) {
// 使JobTypePicker
this.result.push({
name: n.type,
label: this.popupSelectedJobType,
value: this.popupSelectedJobType == '全部工种' ? 'all' : this.typeId.value // 使ID
})
} else {
// 使tag
let t = n.tag[n.index]
@ -365,6 +399,12 @@
this.$refs.addressPicker.open()
},
//
openJobTypePickerInPopup() {
this.activeName = 'typeId'
this.$refs.jobTypePicker.open()
},
//
onAddressConfirm(addressResult) {
//
@ -407,6 +447,49 @@
//
uni.$emit(`${this.sign}_CLOSEPOPUP`)
},
//
onJobTypeConfirm(jobTypeResult) {
//
this.popupSelectedJobType = jobTypeResult.fullJobType
// typeId
this.typeId.label = jobTypeResult.fullJobType
this.typeId.value = jobTypeResult.selectedId // 使ID
console.log('jobTypeResult', jobTypeResult);
// clickItemresult
this.clickItem({
label: jobTypeResult.fullJobType,
value: jobTypeResult.selectedId, // ID
})
// -
uni.$emit(`${this.sign}_CLOSEPOPUP`)
},
//
selectAllJobTypes() {
//
this.popupSelectedJobType = '全部工种'
// typeId
this.typeId.label = '工种'
this.typeId.value = 'all'
// clickItemresult
this.clickItem({
label: '工种',
value: 'all',
})
//
this.$refs.jobTypePicker.close()
//
uni.$emit(`${this.sign}_CLOSEPOPUP`)
},
}
}
</script>
@ -474,6 +557,26 @@
margin-left: 10rpx;
}
}
.job-type-selector {
display: flex;
justify-content: space-between;
align-items: center;
background: rgba($uni-color, 0.1);
padding: 10rpx 20rpx;
margin: 10rpx;
border-radius: 10rpx;
font-size: 26rpx;
.selected-job-type {
flex: 1;
color: #333;
}
.arrow {
margin-left: 10rpx;
}
}
}
}


+ 255
- 0
pages/demo/JobTypePickerDemo.vue View File

@ -0,0 +1,255 @@
<template>
<view class="page">
<navbar title="工种选择组件示例" leftClick @leftClick="$utils.navigateBack" />
<view class="content">
<view class="demo-section">
<view class="section-title">单选模式</view>
<view class="demo-item">
<uv-cell
title="选择工种(单选)"
rightIconStyle="fontSize: 30rpx;"
:value="singleResult.fullJobType || '请选择工种'"
@click="openSinglePicker"
isLink
></uv-cell>
<view v-if="singleResult.selectedId" class="result-info">
<text>选中ID: {{ singleResult.selectedId }}</text>
<text>选中工种: {{ singleResult.selectedJobType?.name }}</text>
</view>
</view>
</view>
<view class="demo-section">
<view class="section-title">多选模式</view>
<view class="demo-item">
<uv-cell
title="选择工种(多选)"
rightIconStyle="fontSize: 30rpx;"
:value="multipleResult.fullJobType || '请选择工种'"
@click="openMultiplePicker"
isLink
></uv-cell>
<view v-if="multipleResult.selectedIds && multipleResult.selectedIds.length > 0" class="result-info">
<text>选中ID数组: {{ multipleResult.selectedIds.join(',') }}</text>
<text>选中数量: {{ multipleResult.selectedIds.length }}</text>
</view>
</view>
</view>
<view class="demo-section">
<view class="section-title">只选择到二级工种</view>
<view class="demo-item">
<uv-cell
title="选择工种(二级)"
rightIconStyle="fontSize: 30rpx;"
:value="subTypeResult.fullJobType || '请选择工种'"
@click="openSubTypePicker"
isLink
></uv-cell>
<view v-if="subTypeResult.selectedId" class="result-info">
<text>选中ID: {{ subTypeResult.selectedId }}</text>
<text>选中工种: {{ subTypeResult.selectedJobType?.name }}</text>
</view>
</view>
</view>
<view class="demo-section">
<view class="section-title">"选择整个工种"选项</view>
<view class="demo-item">
<uv-cell
title="选择工种(整个工种)"
rightIconStyle="fontSize: 30rpx;"
:value="wholeTypeResult.fullJobType || '请选择工种'"
@click="openWholeTypePicker"
isLink
></uv-cell>
<view v-if="wholeTypeResult.selectedId" class="result-info">
<text>选中ID: {{ wholeTypeResult.selectedId }}</text>
<text>选中工种: {{ wholeTypeResult.selectedJobType?.name }}</text>
</view>
</view>
</view>
<view class="demo-section">
<view class="section-title">使用说明</view>
<view class="usage-info">
<text class="usage-title">组件属性</text>
<text> multiple: 是否支持多选</text>
<text> onlySubType: 是否只选择到二级工种</text>
<text> showSelectWholeType: 是否显示"选择整个工种"选项</text>
<text class="usage-title">返回数据</text>
<text> selectedId: 单选时的工种ID</text>
<text> selectedIds: 多选时的工种ID数组</text>
<text> fullJobType: 完整的工种名称文本</text>
<text> selectedJobType: 选中的工种对象</text>
</view>
</view>
</view>
<!-- 工种选择组件 -->
<JobTypePicker
ref="singlePicker"
@confirm="onSingleConfirm"
/>
<JobTypePicker
ref="multiplePicker"
:multiple="true"
@confirm="onMultipleConfirm"
/>
<JobTypePicker
ref="subTypePicker"
:onlySubType="true"
@confirm="onSubTypeConfirm"
/>
<JobTypePicker
ref="wholeTypePicker"
:showSelectWholeType="true"
@confirm="onWholeTypeConfirm"
/>
</view>
</template>
<script>
import JobTypePicker from '@/components/JobTypePicker.vue'
export default {
components: {
JobTypePicker
},
data() {
return {
singleResult: {}, //
multipleResult: {}, //
subTypeResult: {}, //
wholeTypeResult: {} //
}
},
onLoad() {
//
this.$store.commit('getJobTypeList')
},
methods: {
//
openSinglePicker() {
this.$refs.singlePicker.open()
},
//
openMultiplePicker() {
this.$refs.multiplePicker.open()
},
//
openSubTypePicker() {
this.$refs.subTypePicker.open()
},
//
openWholeTypePicker() {
this.$refs.wholeTypePicker.open()
},
//
onSingleConfirm(result) {
console.log('单选结果:', result)
this.singleResult = result
},
//
onMultipleConfirm(result) {
console.log('多选结果:', result)
this.multipleResult = result
},
//
onSubTypeConfirm(result) {
console.log('二级工种结果:', result)
this.subTypeResult = result
},
//
onWholeTypeConfirm(result) {
console.log('整个工种结果:', result)
this.wholeTypeResult = result
}
}
}
</script>
<style scoped lang="scss">
.page {
background: #f5f5f5;
min-height: 100vh;
}
.content {
padding: 20rpx;
}
.demo-section {
margin-bottom: 40rpx;
background: #fff;
border-radius: 20rpx;
overflow: hidden;
.section-title {
padding: 30rpx 30rpx 20rpx;
font-size: 32rpx;
font-weight: bold;
color: #333;
border-bottom: 1px solid #f0f0f0;
}
.demo-item {
padding: 0;
.result-info {
padding: 20rpx 30rpx;
background: #f8f9fa;
border-top: 1px solid #f0f0f0;
text {
display: block;
font-size: 26rpx;
color: #666;
margin-bottom: 10rpx;
&:last-child {
margin-bottom: 0;
}
}
}
}
.usage-info {
padding: 30rpx;
text {
display: block;
font-size: 26rpx;
color: #666;
line-height: 1.6;
margin-bottom: 10rpx;
&.usage-title {
font-weight: bold;
color: #333;
margin-top: 20rpx;
margin-bottom: 15rpx;
&:first-child {
margin-top: 0;
}
}
&:last-child {
margin-bottom: 0;
}
}
}
}
</style>

+ 2
- 2
pages_order/auth/certification.vue View File

@ -45,7 +45,7 @@
<view class="form-item">
<view class="title">
技能证书
上岗操作证或技能等级证书必填
</view>
</view>
@ -259,7 +259,7 @@
name:'请输入姓名',
cerNo:'请输入身份证号码',
phone:'请输入电话号码',
skillBook:'请上传技能证书',
skillBook:'请上传上岗操作证或技能等级证书',
image:'身份证照片不能为空',
})) {
return


+ 74
- 15
pages_order/work/addResume.vue View File

@ -14,7 +14,14 @@
<view class="title">
{{ item.title }}
</view>
<view class="tagList" v-if="!item.useAddressPicker">
<!-- 工种选择器模式 -->
<view class="job-type-selector" v-if="item.useJobTypePicker" @click="openJobTypePicker">
<view class="selected-job-type">
{{ selectedJobType || form.typeId_dictText || '请选择工种' }}
</view>
<uv-icon name="arrow-right" size="30rpx"></uv-icon>
</view>
<view class="tagList" v-else-if="!item.useAddressPicker">
<view :class="{act : i == item.index}"
@click="clickTag(item, i)" v-for="(t, i) in item.tag"
:key="t.id">
@ -128,15 +135,20 @@
<!-- 地址选择组件 -->
<AddressPicker ref="addressPicker" :multiple="true" :showSelectWholeCity="true" :onlyCity="false" @confirm="onAddressConfirm" />
<!-- 工种选择组件 -->
<JobTypePicker ref="jobTypePicker" @confirm="onJobTypeConfirm" />
</view>
</template>
<script>
import { mapState } from 'vuex'
import AddressPicker from '@/components/AddressPicker.vue'
import JobTypePicker from '@/components/JobTypePicker.vue'
export default {
components: {
AddressPicker
AddressPicker,
JobTypePicker
},
data() {
return {
@ -146,7 +158,7 @@
tag: [],
index: 0,
type : 'typeId',
useJobTypePicker: true, // 使
},
{
title: '您希望从事工作的地区',
@ -196,6 +208,7 @@
},
pickerKey : 'workAge',
selectedAddress: '', //
selectedJobType: '', //
}
},
computed : {
@ -244,8 +257,8 @@
// }
this.list.forEach(n => {
// 使tag
if(!n.useAddressPicker) {
// 使tag
if(!n.useAddressPicker && !n.useJobTypePicker) {
this.form[n.type] = n.tag[n.index].id
}
})
@ -301,18 +314,21 @@
this.form = res.result.records[0]
this.list.forEach((n, i) => {
// ID
if(n.useAddressPicker && this.form[n.type]) {
// ID
this.selectedAddress = this.getAddressTextById(this.form[n.type])
} else {
n.tag.forEach((e, index) => {
if(this.form[n.type] == e.id){
n.index = index
}
})
// ID
if(n.useAddressPicker && this.form[n.type]) {
// ID
this.selectedAddress = this.getAddressTextById(this.form[n.type])
} else if(n.useJobTypePicker && this.form[n.type]) {
// ID
this.selectedJobType = this.getJobTypeTextById(this.form[n.type])
} else {
n.tag.forEach((e, index) => {
if(this.form[n.type] == e.id){
n.index = index
}
})
}
})
}
})
@ -370,6 +386,33 @@
this.form.expectAddress = addressResult.selectedId
}
},
//
openJobTypePicker() {
this.$refs.jobTypePicker.open()
},
//
onJobTypeConfirm(jobTypeResult) {
//
this.selectedJobType = jobTypeResult.selectedJobType.name
// ID
this.form.typeId = jobTypeResult.selectedId
this.form.typeId_dictText = jobTypeResult.selectedJobType.name
},
// ID
getJobTypeTextById(id) {
if (!id) return ''
//
for (let jobType of this.jobTypeList) {
if (jobType.id == id) {
return jobType.name
}
}
//
//
return ''
},
},
}
</script>
@ -423,6 +466,22 @@
color: #333;
}
}
.job-type-selector {
display: flex;
justify-content: space-between;
align-items: center;
background: rgba($uni-color, 0.1);
padding: 20rpx;
margin: 10rpx;
border-radius: 10rpx;
font-size: 26rpx;
.selected-job-type {
flex: 1;
color: #333;
}
}
}
}


+ 41
- 11
pages_order/work/jobPosting.vue View File

@ -37,7 +37,7 @@
title="所属工种"
rightIconStyle="fontSize: 30rpx;"
:value="form.typeId_dictText || '请选择所属工种'"
@click="openPicker('typeId', $refs.jobTypeListPicker)"
@click="openJobTypePicker"
isLink
></uv-cell>
@ -184,10 +184,7 @@
:columns="columns"
@confirm="pickerConfirm"></uv-picker>
<uv-picker ref="jobTypeListPicker"
:columns="[jobTypeList]"
keyName="name"
@confirm="pickerConfirm"></uv-picker>
@ -202,6 +199,9 @@
<!-- 地址选择组件 -->
<AddressPicker ref="addressPicker" onlyCity @confirm="onAddressConfirm" />
<!-- 工种选择组件 -->
<JobTypePicker ref="jobTypePicker" @confirm="onJobTypeConfirm" />
<view class="uni-color-btn"
@click="submit">
发布
@ -213,9 +213,11 @@
<script>
import { mapState } from 'vuex'
import AddressPicker from '@/components/AddressPicker.vue'
import JobTypePicker from '@/components/JobTypePicker.vue'
export default {
components: {
AddressPicker
AddressPicker,
JobTypePicker
},
data() {
return {
@ -302,11 +304,10 @@
})
}
this.jobTypeList.forEach(type => {
if(type.id == detail.typeId){
detail.typeId_dictText = type.name
}
})
//
if(detail.typeId) {
detail.typeId_dictText = this.getJobTypeTextById(detail.typeId)
}
delete detail.createBy
delete detail.createTime
@ -449,6 +450,35 @@
// ID
this.form.areaId = addressResult.selectedId
},
//
openJobTypePicker() {
this.$refs.jobTypePicker.open()
},
//
onJobTypeConfirm(jobTypeResult) {
//
this.form.typeId_dictText = jobTypeResult.selectedJobType.name
// ID
this.form.typeId = jobTypeResult.selectedId
},
// ID
getJobTypeTextById(id) {
if (!id) return ''
//
for (let jobType of this.jobTypeList) {
if (jobType.id == id) {
return jobType.name
}
}
//
//
return ''
},
}
}
</script>


+ 29
- 1
store/store.js View File

@ -9,7 +9,7 @@ import api from '@/api/api.js'
const store = new Vuex.Store({
state: {
configList: {}, //配置列表
// 角色 true为老板 false为工人
token : '',
role : false,
userInfo : {}, //用户信息
banner : [],//轮播图
@ -17,6 +17,7 @@ const store = new Vuex.Store({
natureList : [],//工作性质
addressList : [],//开放地址
addressTreeMap : {},//多级地址映射,key为父级id,value为子级数组
jobTypeTreeMap : {},//多级工种映射,key为父级id,value为子级数组
UserExtensionInfo : {},//用户扩展信息,包含认证信息、统计信息
},
getters: {
@ -247,6 +248,10 @@ const store = new Vuex.Store({
setChildAddressList(state, { pid, list }){
state.addressTreeMap[pid] = list
},
// 设置子级工种列表到状态中
setChildJobTypeList(state, { pid, list }){
state.jobTypeTreeMap[pid] = list
},
// 检查当前查看次数,在进入简历页面
checkViewCount(state, {
data = {},
@ -301,6 +306,29 @@ const store = new Vuex.Store({
})
})
},
// 获取子级工种列表
async getChildJobTypeList({ state, commit }, pid){
// 如果已经缓存了,直接返回
if(state.jobTypeTreeMap[pid]){
return Promise.resolve(state.jobTypeTreeMap[pid])
}
return new Promise((resolve, reject) => {
api('commonQueryJobTypeList', {
pageNo : 1,
pageSize : 99999,
pid : pid,
}, res => {
if(res.code == 200){
// 存储到映射中
commit('setChildJobTypeList', { pid, list: res.result.records })
resolve(res.result.records)
} else {
reject(res)
}
})
})
},
},
})

+ 602
- 602
yarn.lock
File diff suppressed because it is too large
View File


Loading…
Cancel
Save