index.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <template>
  2. <view style="padding: 15px 15px 70px 15px;">
  3. <baseInfo v-if="cvData?.person" :data="cvData?.person" />
  4. <view v-if="skillExp?.length">
  5. <view class="gap-line"></view>
  6. <view class="title-line">职业技能</view>
  7. <view class="tags">
  8. <view
  9. v-for="skill in skillExp"
  10. :key="skill.title"
  11. class="tag"
  12. >
  13. {{ skill.title }}
  14. </view>
  15. </view>
  16. </view>
  17. <view v-if="cvData?.person?.advantage">
  18. <view class="gap-line"></view>
  19. <view class="title-line">个人优势</view>
  20. <advantage :data="cvData?.person?.advantage" />
  21. </view>
  22. <view v-if="cvData?.interestedList?.length">
  23. <view class="gap-line"></view>
  24. <view class="title-line">求职意向</view>
  25. <jobIntention :data="dealJobData(cvData?.interestedList || [])" />
  26. </view>
  27. <view v-if="cvData?.eduList?.length">
  28. <view class="gap-line"></view>
  29. <view class="title-line">教育经历</view>
  30. <eduExp :data="cvData?.eduList" />
  31. </view>
  32. <view v-if="cvData?.workList?.length">
  33. <view class="gap-line"></view>
  34. <view class="title-line">工作经历</view>
  35. <workExp :data="cvData?.workList" />
  36. </view>
  37. <view v-if="cvData?.trainList?.length">
  38. <view class="gap-line"></view>
  39. <view class="title-line">培训经历</view>
  40. <trainingExperience :data="cvData?.trainList" />
  41. </view>
  42. <view class="bottom-sticky">
  43. <view class="bottom-content">
  44. <!-- <button class="bottom-content-tool shareButtonCss" open-type="share">
  45. <uni-icons type="redo-filled" size="24" color="#00B760" style="line-height: 24px;"/>
  46. <span style="color:#00B760;font-weight:bold;line-height: 22px;">分享</span>
  47. </button> -->
  48. <button class="btnStyle bgButtons ss-m-l-15" type="primary" plain="true" @tap="handleSend">立即沟通</button>
  49. <button class="buttons btnStyle" type="primary" @click="handleInvite">邀请面试</button>
  50. </view>
  51. </view>
  52. </view>
  53. </template>
  54. <script setup>
  55. import { ref } from 'vue'
  56. import { onLoad } from '@dcloudio/uni-app'
  57. import { dealJobData } from '@/utils/dict'
  58. import { getDict } from '@/hooks/useDictionaries'
  59. import { getText } from '@/utils/getText'
  60. import { dealDictObjData } from '@/utils/position'
  61. import { getPersonCvDetail } from '@/api/enterprise.js'
  62. import baseInfo from './components/baseInfo.vue'
  63. import advantage from './components/advantage.vue'
  64. import jobIntention from './components/jobIntention.vue'
  65. import workExp from './components/workExp.vue'
  66. import eduExp from './components/eduExp.vue'
  67. import trainingExperience from './components/trainingExperience.vue'
  68. import { getJobAdvertisedList } from '@/api/new/position'
  69. import { defaultText, talkToUser } from '@/hooks/useIM'
  70. import { getAccessToken } from '@/utils/request'
  71. import { showAuthModal } from '@/hooks/useModal'
  72. const cvData = ref({})
  73. const skillExp = ref([])
  74. const getSkillExp = async (data) => {
  75. if (!data || !data.length) {
  76. return
  77. }
  78. const { data: _skillList} = await getDict('skillList', {}, 'skillList')
  79. const skillList = _skillList?.data
  80. if (!skillList || !skillList.length) {
  81. return
  82. }
  83. const { data: _skillLevelArr } = await getDict('menduner_skill_level')
  84. const skillLevelArr = _skillLevelArr?.data
  85. if (!skillLevelArr || !skillLevelArr.length) {
  86. return
  87. }
  88. skillExp.value = data.map(e => {
  89. return {
  90. ...e,
  91. title: `${getText(e.skillId, skillList, 'nameCn', 'id')} / ${getText(e.level, skillLevelArr)}`
  92. }
  93. })
  94. }
  95. const getDetail = async (id) => {
  96. uni.showLoading({ title: '加载中' })
  97. try {
  98. const { data } = await getPersonCvDetail(id)
  99. if (!data) return
  100. data.person = dealDictObjData({}, data.person)
  101. cvData.value = data || {}
  102. getSkillExp(data?.skillList || [])
  103. uni.hideLoading()
  104. } catch {
  105. uni.hideLoading()
  106. }
  107. }
  108. // 职位列表
  109. const jobNum = ref(0)
  110. const getJobList = async () => {
  111. const { data } = await getJobAdvertisedList({ pageNo: 1, pageSize: 10, hasExpiredData: false, status: 0 })
  112. jobNum.value = data?.total || 0
  113. }
  114. const btnType = ref('') // 1: 人才详情 2:简历投递
  115. onLoad(async (options) => {
  116. const { id, type } = options
  117. if (!id) {
  118. uni.showToast({
  119. title: '缺少人员id',
  120. icon: 'none'
  121. })
  122. setTimeout(() => {
  123. uni.navigateBack({ delta: 1 })
  124. }, 1000)
  125. return
  126. }
  127. btnType.value = type
  128. await getDetail(id)
  129. getJobList()
  130. })
  131. // 邀请面试
  132. const handleInvite = async () => {
  133. if (!getAccessToken()) {
  134. showAuthModal()
  135. return
  136. }
  137. // 企业必须有招聘中的职位才能发起沟通
  138. if (jobNum.value === 0) {
  139. uni.showToast({
  140. title: '请先发布招聘职位',
  141. icon: 'none',
  142. duration: 2000
  143. })
  144. return
  145. }
  146. }
  147. // 立即沟通
  148. const handleSend = async () => {
  149. if (!getAccessToken()) {
  150. showAuthModal()
  151. return
  152. }
  153. // 企业必须有招聘中的职位才能发起沟通
  154. if (jobNum.value === 0) {
  155. uni.showToast({
  156. title: '请先发布招聘职位',
  157. icon: 'none',
  158. duration: 2000
  159. })
  160. return
  161. }
  162. const userId = cvData.value.person.userId
  163. if (!userId) return
  164. const channel = await talkToUser({ userId, text: defaultText })
  165. const query = {
  166. id: userId,
  167. name: cvData.value?.person?.name || cvData.value?.person?.phone,
  168. channelID: channel.channelID,
  169. channelType: channel.channelType,
  170. avatar: cvData.value.person?.avatar,
  171. sex: cvData.value.person?.sex,
  172. }
  173. const queryStr = Object.keys(query).reduce((r, v) => {
  174. if (!query[v]) {
  175. return r
  176. }
  177. return r += `${v}=${encodeURIComponent(query[v])}&`
  178. }, '?')
  179. uni.navigateTo({
  180. url: `/pagesA/chart/index${queryStr.slice(0, -1)}`
  181. })
  182. }
  183. </script>
  184. <style scoped lang="scss">
  185. .gap-line {
  186. border-bottom: 1px solid #eee;
  187. margin: 30px 0;
  188. }
  189. .title-line {
  190. font-size: 20px;
  191. position: relative;
  192. line-height: 20px;
  193. margin-left: 12px;
  194. margin-bottom: 20px;
  195. padding-left: 10px;
  196. &::before {
  197. content: '';
  198. position: absolute;
  199. width: 6px;
  200. height: 20px;
  201. background: #00B760;
  202. left: -10px;
  203. border-radius: 6px;
  204. }
  205. }
  206. .tags {
  207. display: flex;
  208. flex-wrap: wrap;
  209. .tag {
  210. margin: 0 10rpx 10rpx 0;
  211. border: 2rpx solid #00B760;
  212. color: #00B760;
  213. white-space: nowrap;
  214. padding: 4rpx 10rpx;
  215. border-radius: 10rpx;
  216. font-size: 24rpx;
  217. }
  218. }
  219. .bottom-content {
  220. display: flex;
  221. justify-content: space-evenly;
  222. align-items: center;
  223. width: 100%;
  224. margin: 20rpx 0;
  225. .btnStyle {
  226. flex: 1;
  227. margin-right: 20rpx;
  228. border-radius: 50rpx;
  229. }
  230. .bgButtons {
  231. border: 2rpx solid #00B760;
  232. color: #00B760;
  233. }
  234. .shareButtonCss {
  235. font-size: 16px;
  236. background: unset;
  237. &::after{
  238. border:none !important;
  239. }
  240. }
  241. &-tool {
  242. width: 160rpx;
  243. display: flex;
  244. justify-content: center;
  245. flex-direction: column;
  246. align-items: center;
  247. }
  248. }
  249. </style>