特易招,招聘小程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

503 lines
13 KiB

  1. <template>
  2. <uv-popup ref="popup" mode="bottom" :round="30"
  3. :safeAreaInsetBottom="false" @close="handleClose">
  4. <view class="job-type-picker">
  5. <view class="header">
  6. <view class="title">选择工种</view>
  7. <view class="close-btn" @click="close">
  8. <uv-icon name="close" size="40rpx"></uv-icon>
  9. </view>
  10. </view>
  11. <view class="content">
  12. <!-- 左侧一级工种列表 -->
  13. <view class="left-panel">
  14. <scroll-view scroll-y class="scroll-view">
  15. <!-- 插槽自定义内容 -->
  16. <slot name="custom-options"></slot>
  17. <view
  18. class="job-type-item"
  19. :class="{ active: selectedParentType && selectedParentType.id === item.id }"
  20. v-for="item in jobTypeList"
  21. :key="item.id"
  22. @click="selectParentType(item)">
  23. {{ item.name }}
  24. </view>
  25. </scroll-view>
  26. </view>
  27. <!-- 右侧二三级工种列表 -->
  28. <view class="right-panel">
  29. <scroll-view scroll-y class="scroll-view">
  30. <!-- 二级工种 -->
  31. <template v-if="selectedParentType && !selectedSubType">
  32. <!-- 选择整个工种选项 -->
  33. <view
  34. v-if="showSelectWholeType"
  35. class="job-type-item whole-type-item"
  36. @click="selectWholeParentType">
  37. <uv-icon name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
  38. 选择整个{{ selectedParentType.name }}
  39. </view>
  40. <view
  41. class="job-type-item"
  42. :class="{
  43. 'selected': multiple && isSubTypeSelected(item),
  44. 'active': !multiple && selectedSubType && selectedSubType.id === item.id
  45. }"
  46. v-for="item in subTypeList"
  47. :key="item.id"
  48. @click="selectSubType(item)">
  49. {{ item.name }}
  50. <uv-icon v-if="multiple && isSubTypeSelected(item)"
  51. name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
  52. </view>
  53. <!-- 多选时的确认按钮 -->
  54. <view v-if="multiple && selectedSubTypes.length > 0"
  55. class="confirm-btn" @click="confirmMultipleSubTypeSelection">
  56. <button class="confirm-button">确认选择工种 ({{ selectedSubTypes.length }})</button>
  57. </view>
  58. </template>
  59. <!-- 三级工种 -->
  60. <template v-if="selectedSubType">
  61. <view
  62. class="job-type-item back-item"
  63. @click="backToSubType">
  64. <uv-icon name="arrow-left" size="30rpx"></uv-icon>
  65. 返回{{ selectedParentType.name }}
  66. </view>
  67. <!-- 选择整个子工种选项 -->
  68. <view
  69. v-if="showSelectWholeType"
  70. class="job-type-item whole-type-item"
  71. @click="selectWholeSubType">
  72. <uv-icon name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
  73. 选择整个{{ selectedSubType.name }}
  74. </view>
  75. <view
  76. class="job-type-item"
  77. :class="{
  78. 'selected': multiple && isDetailTypeSelected(item),
  79. 'active': !multiple && selectedDetailType && selectedDetailType.id === item.id
  80. }"
  81. v-for="item in detailTypeList"
  82. :key="item.id"
  83. @click="selectDetailType(item)">
  84. {{ item.name }}
  85. <uv-icon v-if="multiple && isDetailTypeSelected(item)"
  86. name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
  87. </view>
  88. <!-- 多选时的确认按钮 -->
  89. <view v-if="multiple && selectedDetailTypes.length > 0"
  90. class="confirm-btn" @click="confirmMultipleSelection">
  91. <button class="confirm-button">确认选择 ({{ selectedDetailTypes.length }})</button>
  92. </view>
  93. </template>
  94. </scroll-view>
  95. </view>
  96. </view>
  97. </view>
  98. </uv-popup>
  99. </template>
  100. <script>
  101. import { mapState } from 'vuex'
  102. export default {
  103. name: 'JobTypePicker',
  104. props: {
  105. // 是否只选择到二级工种,不选择三级
  106. onlySubType: {
  107. type: Boolean,
  108. default: false
  109. },
  110. // 是否支持多选
  111. multiple: {
  112. type: Boolean,
  113. default: false
  114. },
  115. // 是否显示"选择整个工种"选项
  116. showSelectWholeType: {
  117. type: Boolean,
  118. default: true
  119. }
  120. },
  121. data() {
  122. return {
  123. selectedParentType: null, // 选中的一级工种
  124. selectedSubType: null, // 选中的二级工种
  125. selectedDetailType: null, // 选中的三级工种
  126. selectedSubTypes: [], // 多选时选中的二级工种列表
  127. selectedDetailTypes: [], // 多选时选中的三级工种列表
  128. subTypeList: [], // 二级工种列表
  129. detailTypeList: [], // 三级工种列表
  130. }
  131. },
  132. computed: {
  133. ...mapState(['jobTypeList'])
  134. },
  135. methods: {
  136. // 打开弹窗
  137. open() {
  138. this.$refs.popup.open()
  139. },
  140. // 关闭弹窗
  141. close() {
  142. this.$refs.popup.close()
  143. },
  144. // 弹窗关闭时重置状态
  145. handleClose() {
  146. this.selectedParentType = null
  147. this.selectedSubType = null
  148. this.selectedDetailType = null
  149. this.selectedSubTypes = []
  150. this.selectedDetailTypes = []
  151. this.subTypeList = []
  152. this.detailTypeList = []
  153. },
  154. // 选择一级工种
  155. async selectParentType(parentType) {
  156. this.selectedParentType = parentType
  157. this.selectedSubType = null
  158. this.selectedDetailType = null
  159. this.detailTypeList = []
  160. // 获取二级工种列表
  161. try {
  162. this.subTypeList = await this.$store.dispatch('getChildJobTypeList', parentType.id)
  163. // 如果没有下级工种,直接确认选择
  164. if (this.subTypeList.length === 0) {
  165. this.confirm()
  166. }
  167. } catch (error) {
  168. console.error('获取二级工种列表失败:', error)
  169. this.subTypeList = []
  170. // 获取失败时也直接确认
  171. this.confirm()
  172. }
  173. },
  174. // 选择二级工种
  175. async selectSubType(subType) {
  176. if (this.multiple) {
  177. // 多选模式
  178. const index = this.selectedSubTypes.findIndex(item => item.id === subType.id)
  179. if (index > -1) {
  180. // 取消选择
  181. this.selectedSubTypes.splice(index, 1)
  182. } else {
  183. // 添加选择
  184. this.selectedSubTypes.push(subType)
  185. }
  186. } else {
  187. // 单选模式
  188. this.selectedSubType = subType
  189. this.selectedDetailType = null
  190. // 如果只选择到二级工种,直接确认
  191. if (this.onlySubType) {
  192. this.confirm()
  193. return
  194. }
  195. // 获取三级工种列表
  196. try {
  197. this.detailTypeList = await this.$store.dispatch('getChildJobTypeList', subType.id)
  198. // 如果没有下级工种,直接确认
  199. if (this.detailTypeList.length === 0) {
  200. this.confirm()
  201. }
  202. } catch (error) {
  203. console.error('获取三级工种列表失败:', error)
  204. this.detailTypeList = []
  205. // 获取失败时也直接确认
  206. this.confirm()
  207. }
  208. }
  209. },
  210. // 选择三级工种
  211. selectDetailType(detailType) {
  212. if (this.multiple) {
  213. // 多选模式
  214. const index = this.selectedDetailTypes.findIndex(item => item.id === detailType.id)
  215. if (index > -1) {
  216. // 取消选择
  217. this.selectedDetailTypes.splice(index, 1)
  218. } else {
  219. // 添加选择
  220. this.selectedDetailTypes.push(detailType)
  221. }
  222. } else {
  223. // 单选模式
  224. this.selectedDetailType = detailType
  225. this.confirm()
  226. }
  227. },
  228. // 检查二级工种是否被选中(多选模式)
  229. isSubTypeSelected(subType) {
  230. return this.selectedSubTypes.some(item => item.id === subType.id)
  231. },
  232. // 检查三级工种是否被选中(多选模式)
  233. isDetailTypeSelected(detailType) {
  234. return this.selectedDetailTypes.some(item => item.id === detailType.id)
  235. },
  236. // 选择整个一级工种
  237. selectWholeParentType() {
  238. this.selectedSubType = null
  239. this.selectedSubTypes = []
  240. this.selectedDetailType = null
  241. this.selectedDetailTypes = []
  242. this.confirm()
  243. },
  244. // 选择整个二级工种
  245. selectWholeSubType() {
  246. this.selectedDetailType = null
  247. this.selectedDetailTypes = []
  248. this.confirm()
  249. },
  250. // 确认多选二级工种选择
  251. confirmMultipleSubTypeSelection() {
  252. // 直接返回多选二级工种的数据
  253. this.confirm()
  254. },
  255. // 确认多选选择
  256. confirmMultipleSelection() {
  257. this.confirm()
  258. },
  259. // 返回二级工种选择
  260. backToSubType() {
  261. this.selectedSubType = null
  262. this.selectedDetailType = null
  263. this.detailTypeList = []
  264. },
  265. // 确认选择
  266. confirm() {
  267. const result = {
  268. parentType: this.selectedParentType,
  269. subType: this.selectedSubType,
  270. detailType: this.selectedDetailType,
  271. subTypes: this.selectedSubTypes,
  272. detailTypes: this.selectedDetailTypes
  273. }
  274. // 生成完整工种文本
  275. let fullJobType = ''
  276. let selectedId = '' // 用于传给后端的ID
  277. let selectedIds = [] // 多选时的ID数组
  278. if (this.selectedParentType) {
  279. fullJobType += this.selectedParentType.name
  280. }
  281. // 多选二级工种模式
  282. if (this.multiple && this.selectedSubTypes.length > 0) {
  283. const subTypeNames = this.selectedSubTypes.map(item => item.name).join(',')
  284. fullJobType = subTypeNames
  285. result.selectedJobType = this.selectedParentType // 多选二级工种时返回一级工种作为选中工种
  286. result.selectedSubTypes = this.selectedSubTypes
  287. // 返回多选二级工种的ID数组
  288. selectedIds = this.selectedSubTypes.map(item => item.id)
  289. result.selectedIds = selectedIds
  290. } else if (this.selectedSubType) {
  291. fullJobType += this.selectedSubType.name
  292. // 多选三级工种模式
  293. if (this.multiple && this.selectedDetailTypes.length > 0) {
  294. const detailTypeNames = this.selectedDetailTypes.map(item => item.name).join(',')
  295. fullJobType += detailTypeNames
  296. result.selectedJobType = this.selectedSubType // 多选时返回二级工种作为选中工种
  297. result.selectedDetailTypes = this.selectedDetailTypes
  298. // 返回多选三级工种的ID数组
  299. selectedIds = this.selectedDetailTypes.map(item => item.id)
  300. result.selectedIds = selectedIds
  301. } else if (this.selectedDetailType) {
  302. // 单选三级工种模式
  303. fullJobType += this.selectedDetailType.name
  304. result.selectedJobType = this.selectedDetailType
  305. selectedId = this.selectedDetailType.id
  306. } else {
  307. // 选择整个二级工种
  308. result.selectedJobType = this.selectedSubType
  309. selectedId = this.selectedSubType.id
  310. }
  311. } else {
  312. // 选择整个一级工种
  313. result.selectedJobType = this.selectedParentType
  314. selectedId = this.selectedParentType.id
  315. }
  316. result.fullJobType = fullJobType
  317. result.selectedId = selectedId // 单选时的ID
  318. result.selectedIds = selectedIds // 多选时的ID数组
  319. this.$emit('confirm', result)
  320. this.close()
  321. }
  322. }
  323. }
  324. </script>
  325. <style scoped lang="scss">
  326. .job-type-picker {
  327. height: 70vh;
  328. background: #fff;
  329. .header {
  330. display: flex;
  331. justify-content: space-between;
  332. align-items: center;
  333. padding: 30rpx;
  334. border-bottom: 1px solid #eee;
  335. .title {
  336. font-size: 32rpx;
  337. font-weight: bold;
  338. color: #333;
  339. }
  340. .close-btn {
  341. padding: 10rpx;
  342. }
  343. }
  344. .content {
  345. display: flex;
  346. height: calc(70vh - 160rpx);
  347. .left-panel {
  348. width: 240rpx;
  349. border-right: 1px solid #eee;
  350. background: #f8f8f8;
  351. }
  352. .right-panel {
  353. flex: 1;
  354. }
  355. .scroll-view {
  356. height: 100%;
  357. }
  358. .job-type-item {
  359. padding: 30rpx 20rpx;
  360. font-size: 28rpx;
  361. color: #333;
  362. border-bottom: 1px solid #f0f0f0;
  363. &:last-child {
  364. border-bottom: none;
  365. }
  366. &.active {
  367. background: #fff;
  368. color: $uni-color;
  369. font-weight: bold;
  370. position: relative;
  371. &::after {
  372. content: '';
  373. position: absolute;
  374. right: 0;
  375. top: 0;
  376. bottom: 0;
  377. width: 6rpx;
  378. background: $uni-color;
  379. }
  380. }
  381. &.back-item {
  382. display: flex;
  383. align-items: center;
  384. color: $uni-color;
  385. background: #f8f8f8;
  386. uv-icon {
  387. margin-right: 10rpx;
  388. }
  389. }
  390. &.whole-type-item {
  391. display: flex;
  392. align-items: center;
  393. color: #3796F8;
  394. background: rgba(#3796F8, 0.1);
  395. font-weight: bold;
  396. uv-icon {
  397. margin-right: 10rpx;
  398. }
  399. }
  400. &.selected {
  401. background: rgba($uni-color, 0.1);
  402. color: $uni-color;
  403. font-weight: bold;
  404. position: relative;
  405. &::after {
  406. content: '';
  407. position: absolute;
  408. right: 0;
  409. top: 0;
  410. bottom: 0;
  411. width: 6rpx;
  412. background: $uni-color;
  413. }
  414. }
  415. }
  416. .left-panel {
  417. .select-all-item {
  418. display: flex;
  419. align-items: center;
  420. color: #3796F8;
  421. background: rgba(#3796F8, 0.1);
  422. font-weight: bold;
  423. padding: 30rpx 20rpx;
  424. font-size: 28rpx;
  425. border-bottom: 1px solid #f0f0f0;
  426. uv-icon {
  427. margin-right: 10rpx;
  428. }
  429. }
  430. }
  431. .confirm-btn {
  432. position: sticky;
  433. bottom: 0;
  434. background: #fff;
  435. padding: 20rpx;
  436. border-top: 1px solid #eee;
  437. .confirm-button {
  438. width: 100%;
  439. background: $uni-color;
  440. color: #fff;
  441. border: none;
  442. border-radius: 10rpx;
  443. padding: 20rpx;
  444. font-size: 28rpx;
  445. }
  446. }
  447. }
  448. }
  449. </style>