item.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. <template>
  2. <view>
  3. <uni-card v-for="(val, index) in items" :key="index" :is-shadow="true" :border='false' shadow="0px 0px 3px 1px rgba(0,0,0,0.1)">
  4. <!-- 基本信息 -->
  5. <view class="d-flex align-center">
  6. <view class="user-avatar">
  7. <image class="user-avatar-img" :src="getUserAvatar(val.person?.avatar, val.person?.sex)" mode="scaleToFill"></image>
  8. <image class="user-avatar-sex" :src="val?.person?.sex ? val?.person?.sex === '1' ? '/static/img/man.png' : '/static/img/female.png' : ''" alt="" mode="scaleToFill" />
  9. </view>
  10. <view style="flex: 1; margin-left: 10px;">
  11. <view class="d-flex justify-space-between align-center">
  12. <view class="font-size-18">{{ val.person?.name }}</view>
  13. <view :style="{'color': colorData[val.status]}">
  14. {{ val.status ? statusList.find(i => i.value === val.status)?.label : '' }}
  15. </view>
  16. </view>
  17. </view>
  18. </view>
  19. <view class="ss-m-t-15 color-999">
  20. <view>
  21. 投递职位:
  22. <image v-if="val.jobFairId" src="/static/svg/jobFair.svg" style="width: 15px; height: 15px;"></image>
  23. {{ formatName(val.job?.name) }}
  24. </view>
  25. <view>联系电话:{{ val.person?.phone ?? '未填写' }}</view>
  26. <view>面试时间:{{ timesTampChange(val.time, 'Y-M-D h:m') }}</view>
  27. </view>
  28. <view class="sub-li-bottom ss-m-t-20">
  29. <template v-if="val.job?.status !== '1'" >
  30. <view v-if="editStatus.indexOf(val.status) !== -1" class="sub-li-bottom-item color-primary" @tap.stop="handleActionClick('edit', val)">修改面试</view>
  31. <view v-if="againStatus.indexOf(val.status) !== -1" class="sub-li-bottom-item color-primary" @tap.stop="handleActionClick('edit', val)">重新邀约</view>
  32. </template>
  33. <view v-if="val.status === '1'" class="sub-li-bottom-item color-primary" @click="handleActionClick('completed', val)">完成面试</view>
  34. <view v-if="val.status === '3'" class="sub-li-bottom-item color-primary" @click="handleActionClick('feedback', val)">填写反馈</view>
  35. <view
  36. class="sub-li-bottom-item d-flex align-center justify-center"
  37. @tap.stop="handleLoadMore(val)"
  38. :style="{'color': val?.job?.status === '1' || !actionItems(val)?.length ? '#ccc' : '#00B760'}"
  39. >
  40. <view>更多操作</view>
  41. <uni-icons type="list" class="ss-m-l-10" size="20" :color="!actionItems(val)?.length ? '#ccc' : '#00B760'"></uni-icons>
  42. </view>
  43. </view>
  44. </uni-card>
  45. <!-- 更多操作 -->
  46. <uni-popup ref="popup" type="bottom" :mask-click="true">
  47. <view class="actions" v-if="itemData && Object.keys(itemData).length">
  48. <view
  49. class="action-item"
  50. v-for="(val, index) in actionItems(itemData)"
  51. :key="index"
  52. @tap.stop="handleActionClick(val.value, itemData)"
  53. >{{ val.title }}</view>
  54. </view>
  55. <button class="big-cancel-button" @tap.stop="handleClosePopup">取消</button>
  56. </uni-popup>
  57. <!-- 完成面试 -->
  58. <uni-popup ref="finishPopup" type="dialog">
  59. <uni-popup-dialog
  60. type="warn"
  61. cancelText="取消"
  62. confirmText="确定"
  63. title="系统提示"
  64. content="是否确认已完成面试?"
  65. @confirm="handleFinishConfirm"
  66. @close="handleFinishClose"
  67. ></uni-popup-dialog>
  68. </uni-popup>
  69. </view>
  70. </template>
  71. <script setup>
  72. import { ref } from 'vue'
  73. import { timesTampChange } from '@/utils/date'
  74. import { getUserAvatar } from '@/utils/avatar'
  75. import { formatName } from '@/utils/getText'
  76. import { completedInterviewInvite } from '@/api/interview'
  77. const emit = defineEmits(['refresh'])
  78. const props = defineProps({ items: Array, current: [Number, String], statusList: Array })
  79. const editStatus = ['0'] // 修改面试状态
  80. const againStatus = ['98', '99'] // 重新邀约状态
  81. const colorData = {
  82. '0': 'orange',
  83. '1': 'green',
  84. '2': 'green',
  85. '3': '#00B760',
  86. '4': '#999',
  87. '5': '#FE574A',
  88. '98': '#FE574A',
  89. '99': '#999'
  90. }
  91. const popup = ref()
  92. const finishPopup = ref()
  93. const itemData = ref({})
  94. // 更多操作
  95. const handleLoadMore = (val) => {
  96. if (val?.job?.status === '1') {
  97. itemData.value = {}
  98. uni.showToast({ title: '职位已关闭,暂无更多操作', icon: 'none' })
  99. return
  100. }
  101. if (!actionItems(val).length) {
  102. itemData.value = {}
  103. uni.showToast({ title: '暂无更多操作', icon: 'none' })
  104. return
  105. }
  106. itemData.value = val
  107. popup.value.open()
  108. }
  109. // 关闭操作弹窗
  110. const handleClosePopup = () => {
  111. popup.value.close()
  112. itemData.value = {}
  113. }
  114. // 完成面试
  115. const handleFinishClose = () => {
  116. if (actionItems(itemData.value).length && actionItems(itemData.value).find(e => e.value === 'completed')) handleClosePopup()
  117. else itemData.value = {}
  118. finishPopup.value.close()
  119. }
  120. const handleFinishConfirm = async () => {
  121. if (!itemData.value || !itemData.value.id) return
  122. try {
  123. await completedInterviewInvite(itemData.value.id)
  124. uni.showToast({ title: '操作成功', icon: 'none' })
  125. emit('refresh')
  126. handleFinishClose()
  127. } catch {
  128. handleFinishClose()
  129. }
  130. }
  131. const handleActionClick = (type, val) => {
  132. if (val.job?.status === '1' && (type !== 'completed')) {
  133. uni.showToast({ title: '职位已关闭', icon: 'none' })
  134. return
  135. }
  136. itemData.value = val
  137. // 完成面试
  138. if (type === 'completed') {
  139. finishPopup.value.open()
  140. }
  141. // 修改面试、重新邀约
  142. if (type === 'edit') {
  143. uni.navigateTo({
  144. url: '/pagesB/InviteInterview/index?editData=' + encodeURIComponent(JSON.stringify(val))
  145. })
  146. }
  147. // 取消面试、爽约、面试反馈
  148. if (['cancel', 'attended', 'feedback'].includes(type)) {
  149. uni.navigateTo({
  150. url: `/pagesA/interview/${type}?id=${val.id}`
  151. })
  152. }
  153. popup.value.close()
  154. }
  155. const obj = {
  156. '0': [1],
  157. '1': [4, 1, 3],
  158. '2': [3]
  159. }
  160. const actions = ref([
  161. { title: '完成面试', value: 'completed' },
  162. { title: '取消面试', value: 'cancel' },
  163. { title: '填写反馈', value: 'feedback' },
  164. { title: '爽约', value: 'attended' },
  165. { title: '修改面试', value: 'edit' }
  166. ])
  167. const actionItems = (item) => {
  168. const status = item?.status
  169. const jobClosed = item.job?.status === '1'
  170. const type = jobClosed && obj[status] ? [0] : obj[status] // 职位已关闭只能操作完成面试
  171. if (!type || !type.length) return []
  172. let data = type.map(e => actions.value[e])
  173. return data
  174. }
  175. </script>
  176. <style scoped lang="scss">
  177. .user-avatar {
  178. position: relative;
  179. &-img {
  180. width: 45px;
  181. height: 45px;
  182. border-radius: 50%;
  183. }
  184. &-sex {
  185. position: absolute;
  186. right: 0;
  187. bottom: 2px;
  188. width: 20px;
  189. height: 20px;
  190. background-color: #fff;
  191. border-radius: 50%;
  192. }
  193. }
  194. .action {
  195. font-size: 28rpx;
  196. &-item {
  197. text-align: center;
  198. width: 90vw;
  199. border-bottom: 1px solid #eee;
  200. height:44px;
  201. line-height: 44px;
  202. margin: 0 auto;
  203. color: #00B760;
  204. background-color: #fff !important;
  205. &:first-child {
  206. border-radius: 5px 5px 0 0;
  207. }
  208. &:last-child {
  209. border-radius: 0 0 5px 5px;
  210. border-bottom: none;
  211. }
  212. }
  213. }
  214. .big-cancel-button {
  215. width: 90vw;
  216. height:44px;
  217. line-height: 44px;
  218. margin: 10px auto;
  219. color: #fe574a;
  220. background-color: #fff !important;
  221. font-size: 28rpx;
  222. }
  223. .sub-li-bottom {
  224. display: flex;
  225. justify-content: space-between;
  226. // align-items: flex-end;
  227. margin-top: 10px;
  228. font-size: 13px;
  229. &-item {
  230. width: 50%;
  231. height: 35px;
  232. line-height: 35px;
  233. text-align: center;
  234. margin-right: 15px;
  235. background-color: #f7f8fa;
  236. border-radius: 4px;
  237. &:nth-child(2) {
  238. margin-right: 0;
  239. }
  240. }
  241. }
  242. </style>