灵活用工项目

recruit.js 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /**
  2. * 临时工招聘:条件展示 / 可用人员映射 / 提交载荷
  3. */
  4. import { formatWorkDurationUnit } from '@/utils/draft.js'
  5. function pick(raw, camel, snake) {
  6. if (!raw || typeof raw !== 'object') return null
  7. if (raw[camel] != null && raw[camel] !== '') return raw[camel]
  8. if (raw[snake] != null && raw[snake] !== '') return raw[snake]
  9. return null
  10. }
  11. /** 同步工种头统计与勾选样式 */
  12. export function syncRecruitGroupMeta(group) {
  13. if (!group) return
  14. const workers = group.workers || []
  15. let selectedCount = 0
  16. for (let i = 0; i < workers.length; i++) {
  17. const w = workers[i]
  18. if (w.selected) selectedCount += 1
  19. w.checkClass = w.selected ? 'recruit-check recruit-check--on' : 'recruit-check'
  20. }
  21. group.selectedCount = selectedCount
  22. group.countText = `${selectedCount}/${group.needCount || 0}`
  23. group.chevronText = group.expanded ? '▼' : '▶'
  24. }
  25. /**
  26. * 搜索条件
  27. * 需求人数:工种*人数(如 搬运*10人,分拣*5人)
  28. * 工作天数:工种*{x}天/月(月结用月,其余用天)
  29. */
  30. export function buildRecruitCriteria(workerGroups = []) {
  31. const groups = Array.isArray(workerGroups) ? workerGroups.filter((g) => g && g.workType) : []
  32. const criteriaTypes = groups.map((g) => g.workType).filter(Boolean)
  33. const needParts = []
  34. const dayParts = []
  35. const locations = []
  36. groups.forEach((g) => {
  37. const type = String(g.workType || '').trim()
  38. if (!type) return
  39. const count = Number(g.workerCount)
  40. if (g.workerCount != null && g.workerCount !== '' && !Number.isNaN(count) && count > 0) {
  41. needParts.push(`${type}*${count}人`)
  42. }
  43. const days = Number(g.workDays)
  44. if (g.workDays != null && g.workDays !== '' && !Number.isNaN(days)) {
  45. dayParts.push(`${type}*${days}${formatWorkDurationUnit(g.settlementMode)}`)
  46. }
  47. if (g.workLocation && locations.indexOf(g.workLocation) < 0) {
  48. locations.push(g.workLocation)
  49. }
  50. })
  51. return {
  52. jobTypeText: criteriaTypes.join(',') || '—',
  53. needCountText: needParts.length ? needParts.join(',') : '—',
  54. locationText: locations.join(',') || '—',
  55. daysText: dayParts.length ? dayParts.join(',') : '—'
  56. }
  57. }
  58. /** POST available 查询体 */
  59. export function buildAvailableQueries(workerGroups = []) {
  60. const groups = Array.isArray(workerGroups) ? workerGroups : []
  61. const queries = []
  62. const seen = {}
  63. for (let i = 0; i < groups.length; i++) {
  64. const g = groups[i]
  65. const jobType = String((g && g.workType) || '').trim()
  66. if (!jobType || seen[jobType]) continue
  67. seen[jobType] = true
  68. const workLocation = String((g && g.workLocation) || '').trim()
  69. const item = { jobType, job_type: jobType }
  70. if (workLocation) {
  71. item.workLocation = workLocation
  72. item.work_location = workLocation
  73. }
  74. queries.push(item)
  75. }
  76. return queries
  77. }
  78. /**
  79. * 可用列表仅脱敏时:按 worker_id 生成可过校验的明文(MVP Mock)
  80. * 接口若返回 mobile / id_card_no 则优先使用
  81. */
  82. export function resolveWorkerSubmitIdentity(worker = {}) {
  83. const mobilePlain = String(pick(worker, 'mobile', 'mobile') || pick(worker, 'phone', 'phone') || '').trim()
  84. const idCardPlain = String(
  85. pick(worker, 'idCardNo', 'id_card_no') || pick(worker, 'idCard', 'idCard') || ''
  86. ).trim().toUpperCase()
  87. if (/^1[3-9]\d{9}$/.test(mobilePlain) && idCardPlain.length === 18) {
  88. return { mobile: mobilePlain, idCardNo: idCardPlain }
  89. }
  90. const wid = Number(pick(worker, 'workerId', 'worker_id') || 0) || 0
  91. const mobile = /^1[3-9]\d{9}$/.test(mobilePlain)
  92. ? mobilePlain
  93. : `138${String(10000000 + (wid % 10000000)).slice(-8)}`
  94. const idCardNo = idCardPlain.length === 18 ? idCardPlain : buildMockIdCard(wid || 1)
  95. return { mobile, idCardNo }
  96. }
  97. /** 生成通过校验的 18 位身份证(仅 Mock 提交用) */
  98. function buildMockIdCard(seed) {
  99. const n = Math.abs(Number(seed) || 1) % 10000
  100. const base = `11010119900101${String(1000 + n).slice(-3)}`
  101. const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
  102. const codes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
  103. let sum = 0
  104. for (let i = 0; i < 17; i++) sum += Number(base.charAt(i)) * weights[i]
  105. return base + codes[sum % 11]
  106. }
  107. /**
  108. * available 接口 → 弹层工种分组
  109. * @param {object} raw ThirdPartyWorkerGroupResponse
  110. * @param {Array} workerGroups 草稿工种(取需求人数)
  111. */
  112. export function mapAvailableToRecruitGroups(raw = {}, workerGroups = []) {
  113. const needMap = {}
  114. ;(Array.isArray(workerGroups) ? workerGroups : []).forEach((g) => {
  115. if (!g || !g.workType) return
  116. needMap[g.workType] = Number(g.workerCount) > 0 ? Number(g.workerCount) : 0
  117. })
  118. const apiGroups = (raw && (raw.groups || raw.Groups)) || []
  119. const list = Array.isArray(apiGroups) ? apiGroups : []
  120. const matchGroups = list.map((g, gi) => {
  121. const workType = pick(g, 'jobType', 'job_type') || `工种${gi + 1}`
  122. const needCount = needMap[workType] != null ? needMap[workType] : 0
  123. const workersRaw = g.workers || g.Workers || []
  124. const workers = (Array.isArray(workersRaw) ? workersRaw : []).map((w, wi) => {
  125. const name = pick(w, 'realName', 'real_name') || '临时工'
  126. const mobileMask = pick(w, 'mobileMask', 'mobile_mask') || ''
  127. const workerId = pick(w, 'workerId', 'worker_id')
  128. const identity = resolveWorkerSubmitIdentity(w)
  129. const selected = wi < (needCount > 0 ? needCount : workersRaw.length)
  130. return {
  131. msgKey: workerId != null ? `w_${workerId}` : `w_${gi}_${wi}`,
  132. workerId,
  133. name,
  134. avatarText: String(name).charAt(0) || '工',
  135. jobType: pick(w, 'confirmedWorkType', 'confirmed_work_type') || workType,
  136. mobileMask,
  137. mobile: identity.mobile,
  138. idCardNo: identity.idCardNo,
  139. metaText: mobileMask
  140. ? `${workType} · ${mobileMask}`
  141. : workType,
  142. selected,
  143. checkClass: selected ? 'recruit-check recruit-check--on' : 'recruit-check',
  144. raw: w
  145. }
  146. })
  147. const group = {
  148. msgKey: `g_${gi}_${workType}`,
  149. workType,
  150. needCount,
  151. selectedCount: 0,
  152. countText: `0/${needCount}`,
  153. expanded: true,
  154. chevronText: '▼',
  155. workers
  156. }
  157. syncRecruitGroupMeta(group)
  158. return group
  159. })
  160. const matchedTotal = Number(pick(raw, 'totalCount', 'total_count'))
  161. || matchGroups.reduce((n, g) => n + ((g.workers && g.workers.length) || 0), 0)
  162. return {
  163. matchGroups,
  164. matchedTotal,
  165. resultTitle: `匹配结果(${matchedTotal}人)`,
  166. message: (raw && raw.message) || ''
  167. }
  168. }
  169. /** 确认选择 → third-party/add body.workers */
  170. export function buildThirdPartyAddWorkers(matchGroups = []) {
  171. const workers = []
  172. const groups = Array.isArray(matchGroups) ? matchGroups : []
  173. for (let gi = 0; gi < groups.length; gi++) {
  174. const g = groups[gi]
  175. const list = g.workers || []
  176. for (let wi = 0; wi < list.length; wi++) {
  177. const w = list[wi]
  178. if (!w || !w.selected) continue
  179. const identity = resolveWorkerSubmitIdentity(w)
  180. const item = {
  181. real_name: w.name,
  182. realName: w.name,
  183. mobile: identity.mobile,
  184. id_card_no: identity.idCardNo,
  185. idCardNo: identity.idCardNo,
  186. confirmed_work_type: w.jobType || g.workType,
  187. confirmedWorkType: w.jobType || g.workType
  188. }
  189. if (w.workerId != null && w.workerId !== '') {
  190. item.worker_id = Number(w.workerId)
  191. item.workerId = Number(w.workerId)
  192. }
  193. workers.push(item)
  194. }
  195. }
  196. return workers
  197. }