index copy.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. <!-- 样式备份 -->
  2. <template>
  3. <v-card class="card-box pa-5">
  4. <div class="d-flex justify-space-between">
  5. <TextUI v-if="showTextUI" :item="textItem" @enter="handleEnter" @appendInnerClick="handleEnter"></TextUI>
  6. <div></div>
  7. <v-btn color="primary" prependIcon="mdi-filter-multiple-outline" class="half-button" variant="tonal" @click="openDrawer">筛选</v-btn>
  8. </div>
  9. <div v-if="items.length && showTextUI" class="d-flex align-center" style="margin-left: 14px;">
  10. <v-checkbox v-model="selectAll" :label="!selectAll ? '全选' : `已选中${selectList.length}条`" hide-details color="primary" @update:model-value="handleChangeSelectAll"></v-checkbox>
  11. <v-btn class="ml-8" :disabled="!selectAll" color="primary" variant="tonal" size="small">邀请面试</v-btn>
  12. <v-btn class="mx-3" :disabled="!selectAll" color="primary" variant="tonal" size="small">简历回复</v-btn>
  13. <v-btn :disabled="!selectAll" color="primary" variant="tonal" size="small">批量导出</v-btn>
  14. <v-btn class="mx-3" :disabled="!selectAll" color="primary" variant="tonal" size="small">移除</v-btn>
  15. <v-btn :disabled="!selectAll" color="primary" variant="tonal" size="small">移动到回收站</v-btn>
  16. <v-btn class="ml-3" :disabled="!selectAll" color="primary" variant="tonal" size="small">加入黑名单</v-btn>
  17. </div>
  18. <div v-if="items.length" class="mt-3">
  19. <div v-for="val in items" :key="val.id" class="list-item mb-3">
  20. <div class="top">
  21. <v-checkbox class="mr-5" v-model="val.select" color="primary" density="compact" hide-details @update:model-value="handleChangeSelect"></v-checkbox>
  22. <span>应聘/意向职位:{{ val.job.name }}</span>
  23. <span class="mx-10">加入时间:{{ val.createTime }}</span>
  24. <span>人才分类:{{ val.type }}</span>
  25. </div>
  26. <div @click.stop="talentPoolDetails(val)" class="px-5 py-3 d-flex justify-space-between align-center cursor-pointer">
  27. <div class="d-flex">
  28. <v-badge
  29. bordered
  30. offset-x="6"
  31. offset-y="44"
  32. :color="val.sex ? (val.sex === '1' ? '#1867c0' : 'error') : 'error'"
  33. :icon="val.sex ? (val.sex === '1' ? 'mdi-gender-male' : 'mdi-gender-female') : 'mdi-gender-female'">
  34. <v-avatar size="large" :image="val.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
  35. </v-badge>
  36. <div class="ml-5">
  37. <div class="user-name">{{ val.name }}</div>
  38. <div class="mt-2 user-info">
  39. <span v-for="(k, i) in dict" :key="k">
  40. {{ val[k] }}
  41. <span v-if="i !== dict.length - 1" class="mx-3">|</span>
  42. </span>
  43. </div>
  44. </div>
  45. </div>
  46. <div>
  47. <!-- <v-btn color="primary" variant="tonal" @click.stop="{}">和TA聊聊</v-btn>
  48. <v-btn class="ml-3" color="primary" @click.stop="{}">邀请面试</v-btn> -->
  49. <v-btn class="ml-3" color="primary" variant="tonal" @click.stop="handleRemove(val)">移除</v-btn>
  50. </div>
  51. </div>
  52. <div class="d-flex mx-5 bottom cursor-pointer">
  53. <div class="experience" v-if="val.exp.length">
  54. <div class="second-title">工作经验</div>
  55. <v-timeline density="compact" align="start" side="end" truncate-line="both">
  56. <v-timeline-item v-for="(j, i) in val.exp" :key="i" dot-color="primary" size="small">
  57. <div class="timeline-item mt-1">
  58. <div>{{ j.startTime }}-{{ j.endTime }} ({{ j.year }})</div>
  59. <div class="timeline-item-name ellipsis">{{ j.name }}</div>
  60. <div class="timeline-item-name ellipsis">{{ j.jobName }}</div>
  61. </div>
  62. </v-timeline-item>
  63. </v-timeline>
  64. </div>
  65. <div class="edu" v-if="val.edu.length">
  66. <div class="second-title">教育经历</div>
  67. <v-timeline density="compact" align="start" side="end" truncate-line="both">
  68. <v-timeline-item v-for="(j, i) in val.edu" :key="i" dot-color="primary" size="small">
  69. <div class="timeline-item mt-1">
  70. <div>{{ j.startTime }}-{{ j.endTime }}</div>
  71. <div class="timeline-item-name ellipsis">{{ j.name }}</div>
  72. <div class="timeline-item-name ellipsis">{{ j.major }}</div>
  73. </div>
  74. </v-timeline-item>
  75. </v-timeline>
  76. </div>
  77. </div>
  78. </div>
  79. <CtPagination
  80. :total="total"
  81. :page="pageInfo.pageNo"
  82. :limit="pageInfo.pageSize"
  83. @handleChange="handleChangePage"
  84. ></CtPagination>
  85. </div>
  86. <Empty v-else :message="tipsText" :elevation="false" class="mt-15"></Empty>
  87. <v-navigation-drawer v-model="screen" location="right" absolute temporary width="700">
  88. <FilterPage
  89. ref="FilterPageRef"
  90. @confirm="handleConfirm"
  91. @cancel="screen = false"
  92. ></FilterPage>
  93. </v-navigation-drawer>
  94. </v-card>
  95. </template>
  96. <script setup>
  97. // import { useRouter } from 'vue-router'
  98. // const router = useRouter()
  99. defineOptions({ name: 'enterprise-talent-pool'})
  100. import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
  101. import { reactive, ref } from 'vue'
  102. import Snackbar from '@/plugins/snackbar'
  103. import TextUI from '@/components/FormUI/TextInput'
  104. import FilterPage from './components/filter.vue'
  105. import { dealDictArrayData } from '@/utils/position'
  106. import { getTalentPoolPage } from '@/api/recruit/enterprise/talentPool'
  107. import { removeFormTalentPool } from '@/api/recruit/enterprise/personnel'
  108. let query = {}
  109. const showTextUI = ref(false)
  110. const screen = ref(false)
  111. const selectAll = ref(false)
  112. const selectList = ref([])
  113. const items = ref([
  114. {
  115. job: {
  116. name: '客房服务员'
  117. },
  118. createTime: '2026-11-12',
  119. type: '默认分类',
  120. name: '花城',
  121. age: '27岁',
  122. expName: '3年经验',
  123. areaName: '广州',
  124. userId: '1',
  125. id: '1793583467288223745',
  126. sex: '2',
  127. select: false,
  128. eduName: '本科',
  129. payName: '薪资面议',
  130. avatar: 'https://cdn.vuetifyjs.com/images/john.jpg',
  131. exp: [
  132. {
  133. startTime: '2016.05',
  134. endTime: '2018.05',
  135. year: '2年',
  136. name: '广州辞图科技有限公司',
  137. jobName: '前台'
  138. },
  139. {
  140. startTime: '2016.05',
  141. endTime: '2018.05',
  142. year: '2年',
  143. name: '广州辞图科技有限公司',
  144. jobName: '前台'
  145. }
  146. ],
  147. edu: [
  148. {
  149. startTime: '2016.05',
  150. endTime: '2018.05',
  151. name: '广州大学',
  152. major: '酒店管理'
  153. }
  154. ]
  155. },
  156. {
  157. job: {
  158. name: '客房服务员'
  159. },
  160. createTime: '2026-11-12',
  161. type: '默认分类',
  162. name: '花城',
  163. age: '27岁',
  164. expName: '3年经验',
  165. areaName: '广州',
  166. userId: '1',
  167. id: '1793583467288223745',
  168. sex: '2',
  169. select: false,
  170. eduName: '本科',
  171. payName: '薪资面议',
  172. avatar: 'https://cdn.vuetifyjs.com/images/john.jpg',
  173. exp: [
  174. {
  175. startTime: '2016.05',
  176. endTime: '2018.05',
  177. year: '2年',
  178. name: '广州辞图科技有限公司',
  179. jobName: '前台'
  180. },
  181. {
  182. startTime: '2016.05',
  183. endTime: '2018.05',
  184. year: '2年',
  185. name: '广州辞图科技有限公司',
  186. jobName: '前台'
  187. }
  188. ],
  189. edu: [
  190. {
  191. startTime: '2016.05',
  192. endTime: '2018.05',
  193. name: '广州大学',
  194. major: '酒店管理'
  195. }
  196. ]
  197. }
  198. ])
  199. // items.value = [] // 暂定无数据展示
  200. const tipsText = ref('暂无数据')
  201. const total = ref(2)
  202. const pageInfo = reactive({ pageNo: 1, pageSize: 10 })
  203. const textItem = ref({
  204. type: 'text',
  205. width: 600,
  206. value: '',
  207. label: '请输入简历姓名/职位名称',
  208. appendInnerIcon: 'mdi-magnify'
  209. })
  210. const dict = ['age', 'expName', 'areaName', 'eduName', 'payName']
  211. // 获取数据
  212. const getData = async () => {
  213. const obj = { ...pageInfo, ...query }
  214. console.log('obj', obj)
  215. const { list, total: number } = await getTalentPoolPage(pageInfo)
  216. total.value = number
  217. if (showTextUI.value) items.value = list?.length ? dealDictArrayData([], list) : []
  218. }
  219. // getData()
  220. const handleEnter = (e) => {
  221. console.log(e, 'enter')
  222. }
  223. // 移出人才库
  224. const handleRemove = async (item) => {
  225. if (!item.userId) return Snackbar.warning('数据异常')
  226. await removeFormTalentPool(item.userId)
  227. Snackbar.success(t('common.operationSuccessful'))
  228. }
  229. const dealSelect = () => {
  230. selectList.value = items.value.filter(e => e.select).map(k => k.id)
  231. }
  232. // 全选
  233. const handleChangeSelectAll = () => {
  234. items.value.map(k => {
  235. k.select = selectAll.value
  236. return k
  237. })
  238. dealSelect()
  239. }
  240. // 单选
  241. const handleChangeSelect = () => {
  242. const length = items.value.filter(k => k.select).length
  243. selectAll.value = length > 0 ? true : false
  244. dealSelect()
  245. }
  246. const handleChangePage = () => {
  247. // 分页获取新数据后勾选
  248. selectList.value.forEach(e => {
  249. const obj = items.value.find(k => k.id === e)
  250. if (obj) obj.select = true
  251. })
  252. getData()
  253. }
  254. // 筛选
  255. const handleConfirm = (params) => {
  256. screen.value = false
  257. pageInfo.pageNo = 1
  258. query = { ...params }
  259. getData()
  260. }
  261. const FilterPageRef = ref()
  262. const openDrawer = () => {
  263. screen.value = true
  264. if (Object.keys(query).length) FilterPageRef.value?.setValue(query)
  265. else FilterPageRef.value?.resetValue()
  266. }
  267. // 人才详情
  268. const talentPoolDetails = ({ userId, id }) => {
  269. if (!userId || !id) return
  270. window.open(`/recruit/enterprise/talentPool/details/${userId}?id=${id}`)
  271. }
  272. </script>
  273. <style scoped lang="scss">
  274. .list-item {
  275. border: 1px solid #e5e6eb;
  276. }
  277. .top {
  278. display: flex;
  279. background-color: #f7f8fa;
  280. height: 50px;
  281. line-height: 50px;
  282. font-size: 14px;
  283. color: var(--color-666);
  284. padding: 0 20px;
  285. }
  286. .user-name {
  287. font-size: 18px;
  288. font-weight: 700;
  289. color: #555;
  290. }
  291. .user-info {
  292. color: var(--color-666);
  293. font-size: 14px;
  294. font-weight: 500;
  295. }
  296. :deep(.v-timeline-divider__dot--size-small) {
  297. width: 10px !important;
  298. height: 10px !important;
  299. margin-top: 10px !important;
  300. }
  301. :deep(.v-timeline-divider__inner-dot) {
  302. width: 10px !important;
  303. height: 10px !important;
  304. }
  305. .bottom {
  306. display: flex;
  307. justify-content: space-between;
  308. padding-bottom: 12px;
  309. .experience {
  310. width: 54%;
  311. height: 100%;
  312. }
  313. .edu {
  314. width: 40%;
  315. height: 100%;
  316. }
  317. }
  318. .second-title {
  319. color: var(--color-666);
  320. font-size: 15px;
  321. }
  322. .timeline-item {
  323. display: flex;
  324. align-items: center;
  325. justify-content: space-between;
  326. width: 100%;
  327. color: var(--color-666);
  328. font-size: 13px;
  329. .timeline-item-name {
  330. width: 26%;
  331. }
  332. }
  333. :deep(.v-timeline-item__body) {
  334. width: 100%;
  335. }
  336. :deep(.v-timeline--vertical.v-timeline) {
  337. row-gap: 0;
  338. }
  339. </style>