baseInfo.vue 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. <template>
  2. <div class="mt-10">
  3. <CtForm ref="formPageRef" :items="items" style="width: 650px;">
  4. <template #numericalValue>
  5. <div class="font-size-14 color-error my-1">
  6. <div class="d-flex align-center font-size-13">
  7. <div style="color: var(--v-error-base); cursor: pointer; text-decoration: underline;" @click="handleViewRule">
  8. <v-icon size="20" color="error">mdi-help-circle-outline</v-icon>
  9. 全员猎寻岗位规则说明。
  10. </div>
  11. <div class=" ml-5" style="color: var(--v-error-base);">
  12. 全员猎寻岗位分配比例:推荐人占比{{ ratio.recommendRate }}%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 平台占比{{ ratio.headhuntRate }}%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 投递人占比{{ ratio.cvRate }}%
  13. </div>
  14. </div>
  15. <div class="d-flex">
  16. 按全员猎寻岗位分配比例计算后的赏金(M豆):
  17. <span class="calculation ml-3">推荐人{{ calculation('hirePrice', 1, true) }}个</span>
  18. <span class="calculation">平台{{ calculation('hirePrice', 0, true) }}个</span>
  19. <span class="calculation">投递人{{ calculation('hirePrice', 2, true) }}个</span>
  20. </div>
  21. </div>
  22. </template>
  23. <template #positionId="{ item }">
  24. <v-menu :close-delay="1" :open-delay="0" v-bind="$attrs">
  25. <template v-slot:activator="{ props }">
  26. <textUI
  27. :modelValue="item.value"
  28. :item="item"
  29. v-bind="props"
  30. style="position: relative;"
  31. ></textUI>
  32. </template>
  33. <jobTypeCard class="jobTypeCardBox" :select="[query.positionId].filter(Boolean)" :isSingle="true" @handleJobClick="handleJobClickItem"></jobTypeCard>
  34. </v-menu>
  35. <v-btn v-if="showTemplateBtn" class="ml-3 half-button" color="primary" style="margin-top: 2px;" @click="useJobTemplate(item)">职位模板</v-btn>
  36. </template>
  37. <template #expireTime="{ item }">
  38. <div>
  39. <v-checkbox-btn
  40. v-model="soFar"
  41. color="primary"
  42. label="长期有效"
  43. class="ml-2"
  44. :disabled="false"
  45. :style="`line-height: ${item.dense === 'default' ? 56 : item.dense === 'comfortable' ? 48 : 40 }px;`"
  46. style="width: 110px;"
  47. hide-details
  48. @update:modelValue="v => handleSoFarChange(v, item)"
  49. ></v-checkbox-btn>
  50. </div>
  51. </template>
  52. </CtForm>
  53. <CtDialog :visible="show" :widthType="1" titleClass="text-h6" title="全员猎寻岗位规则说明" :footer="false" @close="show = false">
  54. <RulePage />
  55. </CtDialog>
  56. </div>
  57. </template>
  58. <script setup>
  59. defineOptions({ name: 'position-add-baseInfo'})
  60. import CtForm from '@/components/CtForm'
  61. import { reactive, ref, watch } from 'vue'
  62. import textUI from '@/components/FormUI/TextInput'
  63. import jobTypeCard from '@/components/jobTypeCard'
  64. import RulePage from './rule.vue'
  65. import { commissionCalculation } from '@/utils/position'
  66. import { getPublicRatio, getRecruitPositionDetails } from '@/api/recruit/enterprise/position'
  67. const props = defineProps({
  68. itemData: Object
  69. })
  70. const showTemplateBtn = ref(true)
  71. const show = ref(false)
  72. const ratio = ref({})
  73. // 按分配比例计算金额积分
  74. const calculation = (key, type) => {
  75. const value = items.value.options.find(e => e.key === key).value
  76. return commissionCalculation(value, type)
  77. }
  78. const getValue = (key) => {
  79. return items.value.options.find(e => e.key === key)
  80. }
  81. const formPageRef = ref()
  82. let query = reactive({})
  83. const items = ref({
  84. options: [
  85. {
  86. type: 'number',
  87. key: 'hirePrice',
  88. value: null,
  89. label: '请填写赏金 * (1赏金=1M豆)',
  90. suffix: '点',
  91. hideDetails: true,
  92. change: val => hirePriceChange(val, 'hirePrice')
  93. },
  94. {
  95. slotName: 'numericalValue',
  96. noParam: true,
  97. },
  98. {
  99. slotName: 'positionId',
  100. key: 'positionId',
  101. value: '',
  102. labelKey: 'positionName',
  103. label: '职位类型 *',
  104. noParam: true,
  105. flexStyle: 'mt-5',
  106. readonly: true,
  107. rules: [v => !!v || '请选择职位类型']
  108. },
  109. {
  110. type: 'text',
  111. key: 'name',
  112. value: '',
  113. label: '职位名称 *',
  114. rules: [v => !!v || '请填写职位名称']
  115. },
  116. {
  117. type: 'datePicker',
  118. key: 'expireTime',
  119. value: null,
  120. format: 'YYYY-MM-DD',
  121. disabledDate: true,
  122. labelWidth: 120,
  123. label: '到期时间 *',
  124. slotName: 'expireTime',
  125. },
  126. {
  127. type: 'wangEditor',
  128. key: 'content',
  129. value: '',
  130. label: '岗位职责 *',
  131. maxLength: 5000,
  132. rules: '请填写岗位职责'
  133. },
  134. {
  135. type: 'wangEditor',
  136. key: 'requirement',
  137. value: '',
  138. label: '岗位要求 *',
  139. maxLength: 5000,
  140. rules: '请填写岗位要求'
  141. }
  142. ]
  143. })
  144. // 获取众聘分配比例
  145. const getRatio = async () => {
  146. const data = await getPublicRatio()
  147. ratio.value = data
  148. }
  149. getRatio()
  150. // 编辑回显
  151. watch(
  152. () => props.itemData,
  153. (val) => {
  154. if (!Object.keys(val).length) return
  155. items.value.options.forEach(e => {
  156. if (e.labelKey) {
  157. query[e.key] = val[e.key]
  158. e.value = val[e.labelKey]
  159. return
  160. }
  161. if (e.noParam) return
  162. if (e.key === 'expireTime' && !val[e.key]) return handleSoFarChange(true, e)
  163. if (e.key === 'hirePrice') e.value = val[e.key] / 100
  164. else e.value = val[e.key]
  165. e.change && e.change(e.value)
  166. })
  167. },
  168. { immediate: true },
  169. { deep: true }
  170. )
  171. // 职位类型
  172. const handleJobClickItem = (list, name) => {
  173. const positionId = getValue('positionId')
  174. if (!list.length) {
  175. delete query.positionId
  176. positionId.value = ''
  177. return
  178. }
  179. showTemplateBtn.value = true
  180. query.positionId = list[0]
  181. positionId.value = name
  182. }
  183. // 职位模板
  184. import Confirm from '@/plugins/confirm'
  185. import Snackbar from '@/plugins/snackbar'
  186. import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
  187. const useJobTemplate = async () => {
  188. if (!query.positionId) return Snackbar.warning('请先选择职位类型')
  189. // 获取职位模板内容-赋值
  190. const res = await getRecruitPositionDetails(query.positionId)
  191. if (!res || !res.content || !res.requirement) {
  192. Snackbar.warning('此职位类型没有可使用的模板!')
  193. showTemplateBtn.value = false
  194. return
  195. }
  196. const content = items.value.options.find(e => e.key === 'content')
  197. const requirement = items.value.options.find(e => e.key === 'requirement')
  198. if ((content && content.value) || (requirement && requirement.value)) {
  199. // 弹窗提示
  200. Confirm(t('common.confirmTitle'), '您确定要放弃目前岗位描述的内容吗?').then(() => {
  201. content.value = res.content
  202. requirement.value = res.requirement
  203. Snackbar.success('模板填充完成!')
  204. })
  205. } else {
  206. // 无内容点击默认填充
  207. if (content) content.value = res.content
  208. if (requirement) requirement.value = res.requirement
  209. Snackbar.success('模板填充完成!')
  210. }
  211. }
  212. // 众聘规则查看
  213. const handleViewRule = () => {
  214. show.value = true
  215. }
  216. // 众聘规则查看
  217. const hirePriceChange = (value, key) => {
  218. let calcCost = value-0
  219. if (calcCost < 10 ) calcCost = 10
  220. // else {
  221. // calcCost = parseInt(calcCost/10)*10
  222. // }
  223. const obj = items.value.options.find(k => k.key === key)
  224. if (obj) {
  225. obj.value = calcCost
  226. }
  227. }
  228. const soFar = ref(false)
  229. // 长期有效
  230. const handleSoFarChange = (bool, item) => {
  231. soFar.value = bool
  232. item.value = null
  233. item.disabled = bool ? true : false
  234. item.label = bool ? '到期时间' : '到期时间 *'
  235. }
  236. const getQuery = async () => {
  237. const { valid } = await formPageRef.value.formRef.validate()
  238. if (!valid) return
  239. const obj = {
  240. soFar: soFar.value,
  241. hire: true
  242. }
  243. items.value.options.forEach(e => {
  244. if (e.noParam || e.value === null) return
  245. if (e.key === 'hirePrice') obj[e.key] = e.value * 100
  246. else obj[e.key] = e.value
  247. })
  248. if (!obj.content) {
  249. Snackbar.warning('请填写岗位职责')
  250. return 'failed'
  251. }
  252. if (!obj.requirement) {
  253. Snackbar.warning('请填写岗位要求')
  254. return 'failed'
  255. }
  256. if (!obj.expireTime && !soFar.value) {
  257. Snackbar.warning('请填写到期时间')
  258. return 'failed'
  259. }
  260. query = Object.assign(query, obj)
  261. return query
  262. }
  263. defineExpose({
  264. formPageRef,
  265. getQuery
  266. })
  267. </script>
  268. <style scoped lang="scss">
  269. .jobTypeCardBox {
  270. position: absolute;
  271. top: -22px;
  272. left: 0;
  273. }
  274. .calculation {
  275. display: block;
  276. width: 115px;
  277. }
  278. </style>