baseInfo.vue 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. <template>
  2. <div>
  3. <CtForm ref="formPageRef" :items="items" style="width: 650px;">
  4. <template #bizId="{ item }">
  5. <div>
  6. <v-checkbox-btn
  7. v-model="jobFairCheckbox"
  8. color="primary"
  9. label="设置为招聘会职位"
  10. class="ml-2"
  11. :disabled="false"
  12. :style="`line-height: ${item.dense === 'default' ? 56 : item.dense === 'comfortable' ? 48 : 40 }px;`"
  13. style="width: 174px;"
  14. hide-details
  15. @update:modelValue="v => jobFairCheckboxChange(v, item)"
  16. ></v-checkbox-btn>
  17. </div>
  18. </template>
  19. <template #positionId="{ item }">
  20. <v-menu :close-delay="1" :open-delay="0" v-bind="$attrs">
  21. <template v-slot:activator="{ props }">
  22. <textUI
  23. :modelValue="item.value"
  24. :item="item"
  25. v-bind="props"
  26. style="position: relative;"
  27. ></textUI>
  28. </template>
  29. <jobTypeCard class="jobTypeCardBox" :select="[query.positionId].filter(Boolean)" :isSingle="true" @handleJobClick="handleJobClickItem"></jobTypeCard>
  30. </v-menu>
  31. <v-btn v-if="showTemplateBtn" class="ml-3 half-button" color="primary" style="margin-top: 2px;" @click="useJobTemplate(item)">职位模板</v-btn>
  32. </template>
  33. <template #expireTime="{ item }">
  34. <div>
  35. <v-checkbox-btn
  36. v-model="soFar"
  37. color="primary"
  38. label="长期有效"
  39. class="ml-2"
  40. :disabled="false"
  41. :style="`line-height: ${item.dense === 'default' ? 56 : item.dense === 'comfortable' ? 48 : 40 }px;`"
  42. style="width: 110px;"
  43. hide-details
  44. @update:modelValue="v => handleSoFarChange(v, item)"
  45. ></v-checkbox-btn>
  46. </div>
  47. </template>
  48. </CtForm>
  49. </div>
  50. </template>
  51. <script setup>
  52. defineOptions({ name: 'position-add-baseInfo'})
  53. import CtForm from '@/components/CtForm'
  54. import { reactive, ref, watch } from 'vue'
  55. import textUI from '@/components/FormUI/TextInput'
  56. import jobTypeCard from '@/components/jobTypeCard'
  57. import { getRecruitPositionDetails } from '@/api/recruit/enterprise/position'
  58. import Confirm from '@/plugins/confirm'
  59. import Snackbar from '@/plugins/snackbar'
  60. import { useI18n } from '@/hooks/web/useI18n';
  61. import { getJobFairWhiteList } from '@/api/recruit/enterprise/jobFair'
  62. const { t } = useI18n()
  63. const props = defineProps({
  64. itemData: Object,
  65. isFair: {
  66. type: Boolean,
  67. default: false
  68. }
  69. })
  70. const getValue = (key) => {
  71. return items.value.options.find(e => e.key === key)
  72. }
  73. const showTemplateBtn = ref(true)
  74. const formPageRef = ref()
  75. let query = reactive({})
  76. const items = ref({
  77. options: [
  78. {
  79. slotName: 'bizId',
  80. type: 'autocomplete',
  81. key: 'bizId',
  82. value: null,
  83. label: '招聘会',
  84. itemText: 'title',
  85. itemValue: 'id',
  86. disabled: true,
  87. items: [],
  88. },
  89. {
  90. slotName: 'positionId',
  91. key: 'positionId',
  92. value: '',
  93. flexStyle: 'mt-5',
  94. labelKey: 'positionName',
  95. label: '职位类型 *',
  96. noParam: true,
  97. readonly: true,
  98. rules: [v => !!v || '请选择职位类型']
  99. },
  100. {
  101. type: 'text',
  102. key: 'name',
  103. value: '',
  104. label: '职位名称 *',
  105. rules: [v => !!v || '请填写职位名称']
  106. },
  107. {
  108. type: 'datePicker',
  109. key: 'expireTime',
  110. value: null,
  111. format: 'YYYY-MM-DD',
  112. label: '到期时间 *',
  113. disabledDate: true,
  114. dayAfter: true,
  115. labelWidth: 120,
  116. slotName: 'expireTime',
  117. },
  118. {
  119. type: 'wangEditor',
  120. key: 'content',
  121. value: '',
  122. label: '岗位职责 *',
  123. maxLength: 5000,
  124. rules: '请填写岗位职责'
  125. },
  126. {
  127. type: 'wangEditor',
  128. key: 'requirement',
  129. value: '',
  130. label: '岗位要求 *',
  131. maxLength: 5000,
  132. rules: '请填写岗位要求'
  133. }
  134. ]
  135. })
  136. // 获取企业已加入的招聘会列表
  137. const jobFairLoaded = ref(false)
  138. const jobFairWhiteList = ref([])
  139. const getJobFairData = async () => {
  140. const data = await getJobFairWhiteList()
  141. jobFairWhiteList.value = data?.length ? data : []
  142. jobFairLoaded.value = true
  143. // 没有加入任何招聘会则不展示招聘会
  144. if (!jobFairWhiteList.value?.length) items.value.options = items.value.options.filter(e => e.key !== 'bizId')
  145. }
  146. getJobFairData()
  147. const jobFairValid = async () => {
  148. if (props.isFair) {
  149. // 招聘会内编辑职位,不需要展示选择招聘会
  150. items.value.options = items.value.options.filter(e => e.key !== 'bizId')
  151. return
  152. }
  153. // 加载招聘会白名单列表
  154. if (!jobFairLoaded.value) await getJobFairData()
  155. const bizIdObj = items.value.options.find(e => e.key === 'bizId') // 招聘会下拉框
  156. if (!bizIdObj) return
  157. bizIdObj.items = jobFairWhiteList.value // 下拉框内容赋值
  158. if (bizIdObj.value) {
  159. const index = jobFairWhiteList.value.findIndex(e => e.id === props.itemData.bizId)
  160. if (index === -1) bizIdObj.value = null // 招聘会已经关闭 或者已被移除招聘会白名单
  161. jobFairCheckboxChange(Boolean(bizIdObj.value), bizIdObj) // 招聘会回显
  162. }
  163. }
  164. jobFairValid()
  165. // 设置为招聘会职位
  166. // 现有逻辑:设置为招聘会职位后,职位管理编辑再修改为去掉选中招聘会不会改变数据的jobFairIds字段(用于职位列表判断是否展示招聘会标识),只有去招聘会移除才会改变jobFairIds字段。
  167. const jobFairCheckbox = ref(false)
  168. const jobFairCheckboxChange = (bool, item) => {
  169. item.value = bool ? (item?.value || null) : null
  170. jobFairCheckbox.value = bool
  171. item.disabled = bool ? false : true
  172. item.label = bool ? '招聘会 *' : '招聘会'
  173. }
  174. // 长期有效
  175. const soFar = ref(false)
  176. const handleSoFarChange = (bool, item) => {
  177. soFar.value = bool
  178. item.value = null
  179. item.disabled = bool ? true : false
  180. item.label = bool ? '到期时间' : '到期时间 *'
  181. }
  182. // 编辑回显
  183. watch(
  184. () => props.itemData,
  185. (val) => {
  186. if (!Object.keys(val).length) return
  187. items.value.options.forEach(e => {
  188. if (e.labelKey) {
  189. query[e.key] = val[e.key]
  190. e.value = val[e.labelKey]
  191. return
  192. }
  193. if (e.noParam) return
  194. if (e.key === 'expireTime' && !val[e.key]) return handleSoFarChange(true, e)
  195. if (e.key === 'bizId' && val.source === '0') return // 非招聘会职位
  196. e.value = val[e.key]
  197. })
  198. // jobFairValid() // 招聘会回显
  199. },
  200. { immediate: true },
  201. { deep: true }
  202. )
  203. // 职位类型
  204. const handleJobClickItem = (list, name) => {
  205. const positionId = getValue('positionId')
  206. if (!list.length) {
  207. delete query.positionId
  208. positionId.value = ''
  209. return
  210. }
  211. showTemplateBtn.value = true
  212. query.positionId = list[0]
  213. positionId.value = name
  214. }
  215. const useJobTemplate = async () => {
  216. if (!query.positionId) return Snackbar.warning('请先选择职位类型')
  217. // 获取职位模板内容-赋值
  218. const res = await getRecruitPositionDetails(query.positionId)
  219. if (!res || !res.content || !res.requirement) {
  220. Snackbar.warning('此职位类型没有可使用的模板!')
  221. showTemplateBtn.value = false
  222. return
  223. }
  224. const content = items.value.options.find(e => e.key === 'content')
  225. const requirement = items.value.options.find(e => e.key === 'requirement')
  226. if ((content && content.value) || (requirement && requirement.value)) {
  227. // 弹窗提示
  228. Confirm(t('common.confirmTitle'), '您确定要放弃目前岗位描述的内容吗?').then(() => {
  229. content.value = res.content
  230. requirement.value = res.requirement
  231. Snackbar.success('模板填充完成!')
  232. })
  233. } else {
  234. // 无内容点击默认填充
  235. if (content) content.value = res.content
  236. if (requirement) requirement.value = res.requirement
  237. Snackbar.success('模板填充完成!')
  238. }
  239. }
  240. const getQuery = async () => {
  241. const { valid } = await formPageRef.value.formRef.validate()
  242. if (!valid) return
  243. const obj = {
  244. hirePrice: 0,
  245. soFar: soFar.value,
  246. hire: false
  247. }
  248. items.value.options.forEach(e => {
  249. if (e.noParam || e.value === null) return
  250. else obj[e.key] = e.value
  251. })
  252. if (jobFairCheckbox.value && !obj.bizId && jobFairWhiteList.value?.length) {
  253. Snackbar.warning('请选择招聘会')
  254. return 'failed'
  255. }
  256. if (!obj.content) {
  257. Snackbar.warning('请填写岗位职责')
  258. return 'failed'
  259. }
  260. if (!obj.requirement) {
  261. Snackbar.warning('请填写岗位要求')
  262. return 'failed'
  263. }
  264. if (!obj.expireTime && !soFar.value) {
  265. Snackbar.warning('请填写到期时间')
  266. return 'failed'
  267. }
  268. obj.source = jobFairCheckbox.value ? '2' : '0' // 职位来源(0职位管理|1众聘职位|2招聘会)
  269. if (obj.source === '0') obj.bizId = null
  270. Object.assign(query, obj)
  271. return query
  272. }
  273. defineExpose({
  274. formPageRef,
  275. getQuery
  276. })
  277. </script>
  278. <style scoped lang="scss">
  279. .jobTypeCardBox {
  280. position: absolute;
  281. top: -22px;
  282. left: 0;
  283. }
  284. .calculation {
  285. display: block;
  286. width: 120px;
  287. }
  288. </style>