FocusPersonnelDetail.vue 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <template>
  2. <a-spin :spinning="loading">
  3. <div class="focus-personnel-detail" v-if="detailData">
  4. <!-- 顶部人员概要信息 -->
  5. <div class="detail-header">
  6. <div class="header-avatar">
  7. <a-avatar :size="64" style="background-color: #1890ff">
  8. {{ detailData.fullName ? detailData.fullName.substring(0, 1) : '?' }}
  9. </a-avatar>
  10. </div>
  11. <div class="header-info">
  12. <div class="header-name">
  13. <span class="name">{{ detailData.fullName || '-' }}</span>
  14. <span class="gender">{{ detailData.gender || '' }}</span>
  15. <span class="age" v-if="detailData.age != null">{{ detailData.age }}岁</span>
  16. </div>
  17. <div class="header-tags">
  18. <a-tag v-if="detailData.majorTag" color="blue">{{ detailData.majorTag }}</a-tag>
  19. <a-tag v-if="detailData.minorTag" color="green">{{ detailData.minorTag }}</a-tag>
  20. <template v-if="detailData.customTag">
  21. <a-tag v-for="tag in customTagList" :key="tag" color="orange">{{ tag }}</a-tag>
  22. </template>
  23. </div>
  24. <div class="header-meta">
  25. <span v-if="detailData.education">{{ detailData.education }}</span>
  26. <span v-if="detailData.contactPhone"> | {{ detailData.contactPhone }}</span>
  27. <span v-if="detailData.jobSearchStatus"> | {{ detailData.jobSearchStatus }}</span>
  28. </div>
  29. </div>
  30. </div>
  31. <a-divider style="margin: 12px 0" />
  32. <!-- Tab页签组织详情内容 -->
  33. <a-tabs v-model:activeKey="activeTab">
  34. <!-- Tab1: 基本信息 -->
  35. <a-tab-pane key="basic" tab="基本信息">
  36. <a-descriptions bordered :column="2" size="small">
  37. <a-descriptions-item label="证件类型">{{ detailData.idType || '-' }}</a-descriptions-item>
  38. <a-descriptions-item label="证件号码">{{ detailData.idNumber || '-' }}</a-descriptions-item>
  39. <a-descriptions-item label="出生日期">{{ formatDate(detailData.birthDate) }}</a-descriptions-item>
  40. <a-descriptions-item label="年龄">{{ detailData.age != null ? detailData.age + '岁' : '-' }}</a-descriptions-item>
  41. <a-descriptions-item label="民族">{{ detailData.nation || '-' }}</a-descriptions-item>
  42. <a-descriptions-item label="国籍">{{ detailData.nationality || '-' }}</a-descriptions-item>
  43. <a-descriptions-item label="婚姻状况">{{ detailData.maritalStatus || '-' }}</a-descriptions-item>
  44. <a-descriptions-item label="政治面貌">{{ detailData.politicalStatus || '-' }}</a-descriptions-item>
  45. </a-descriptions>
  46. </a-tab-pane>
  47. <!-- Tab2: 教育与求职 -->
  48. <a-tab-pane key="education" tab="教育与求职">
  49. <a-descriptions bordered :column="2" size="small">
  50. <a-descriptions-item label="学历">{{ detailData.education || '-' }}</a-descriptions-item>
  51. <a-descriptions-item label="毕业日期">{{ formatDate(detailData.graduationDate) }}</a-descriptions-item>
  52. <a-descriptions-item label="毕业院校">{{ detailData.graduateSchool || '-' }}</a-descriptions-item>
  53. <a-descriptions-item label="专业">{{ detailData.major || '-' }}</a-descriptions-item>
  54. <a-descriptions-item label="工作经验">{{ detailData.workExperience || '-' }}</a-descriptions-item>
  55. <a-descriptions-item label="职业技能等级">{{ detailData.skillLevel || '-' }}</a-descriptions-item>
  56. <a-descriptions-item label="是否留学人才">{{ detailData.isOverseasTalent || '-' }}</a-descriptions-item>
  57. <a-descriptions-item label="求职人员类别">{{ detailData.jobSeekerCategory || '-' }}</a-descriptions-item>
  58. <a-descriptions-item label="求职状态">{{ detailData.jobSearchStatus || '-' }}</a-descriptions-item>
  59. <a-descriptions-item label="是否接受推荐职位">{{ detailData.acceptRecommend || '-' }}</a-descriptions-item>
  60. </a-descriptions>
  61. </a-tab-pane>
  62. <!-- Tab3: 联系与居住 -->
  63. <a-tab-pane key="contact" tab="联系与居住">
  64. <a-descriptions bordered :column="2" size="small">
  65. <a-descriptions-item label="联系电话">{{ detailData.contactPhone || '-' }}</a-descriptions-item>
  66. <a-descriptions-item label="邮箱">{{ detailData.email || '-' }}</a-descriptions-item>
  67. <a-descriptions-item label="QQ号码">{{ detailData.qqNumber || '-' }}</a-descriptions-item>
  68. <a-descriptions-item label="微信号">{{ detailData.wechatId || '-' }}</a-descriptions-item>
  69. <a-descriptions-item label="户口性质">{{ detailData.householdType || '-' }}</a-descriptions-item>
  70. <a-descriptions-item label="户口所在地">{{ detailData.householdLocation || '-' }}</a-descriptions-item>
  71. <a-descriptions-item label="现居住地">{{ detailData.currentResidence || '-' }}</a-descriptions-item>
  72. <a-descriptions-item label="现居住地址">{{ detailData.currentAddress || '-' }}</a-descriptions-item>
  73. </a-descriptions>
  74. </a-tab-pane>
  75. <!-- Tab4: 标签信息 -->
  76. <a-tab-pane key="tags" tab="标签信息">
  77. <a-descriptions bordered :column="1" size="small">
  78. <a-descriptions-item label="人员大类标签">
  79. <a-tag v-if="detailData.majorTag" color="blue">{{ detailData.majorTag }}</a-tag>
  80. <span v-else>-</span>
  81. </a-descriptions-item>
  82. <a-descriptions-item label="人员小类标签">
  83. <a-tag v-if="detailData.minorTag" color="green">{{ detailData.minorTag }}</a-tag>
  84. <span v-else>-</span>
  85. </a-descriptions-item>
  86. <a-descriptions-item label="自定义标签">
  87. <template v-if="customTagList.length > 0">
  88. <a-tag v-for="tag in customTagList" :key="tag" color="orange" style="margin-bottom: 4px">{{ tag }}</a-tag>
  89. </template>
  90. <span v-else>-</span>
  91. </a-descriptions-item>
  92. </a-descriptions>
  93. </a-tab-pane>
  94. <!-- Tab5: 服务跟进 -->
  95. <a-tab-pane key="service" tab="服务跟进">
  96. <div style="margin-bottom: 12px">
  97. <a-button type="primary" size="small" @click="handleAddServiceFollow" v-auth="'focus_personnel:serviceFollow'">
  98. 新增跟进
  99. </a-button>
  100. </div>
  101. <a-table
  102. :columns="serviceFollowColumns"
  103. :dataSource="serviceFollowList"
  104. :pagination="false"
  105. size="small"
  106. :locale="{ emptyText: '暂无跟进记录' }"
  107. rowKey="id"
  108. />
  109. </a-tab-pane>
  110. </a-tabs>
  111. </div>
  112. <div v-else-if="!loading" style="text-align: center; padding: 40px; color: #999">
  113. 暂无数据
  114. </div>
  115. </a-spin>
  116. </template>
  117. <script lang="ts" setup>
  118. import { computed, ref, watch } from 'vue';
  119. import { queryDetailById } from '../FocusPersonnel.api';
  120. import dayjs from 'dayjs';
  121. const props = defineProps({
  122. record: { type: Object, default: () => ({}) },
  123. });
  124. const loading = ref<boolean>(false);
  125. const detailData = ref<any>(null);
  126. const activeTab = ref<string>('basic');
  127. /** 服务跟进列表(暂为空,待后端接口开发) */
  128. const serviceFollowList = ref<any[]>([]);
  129. /** 服务跟进表格列 */
  130. const serviceFollowColumns = [
  131. { title: '姓名', dataIndex: 'fullName', width: 100 },
  132. { title: '服务内容', dataIndex: 'serviceContent' },
  133. { title: '服务时间', dataIndex: 'serviceTime', width: 120 },
  134. { title: '服务人员', dataIndex: 'servicePerson', width: 100 },
  135. ];
  136. /** 自定义标签列表(逗号分隔转数组) */
  137. const customTagList = computed(() => {
  138. if (!detailData.value?.customTag) return [];
  139. return detailData.value.customTag.split(',').filter((t: string) => t.trim());
  140. });
  141. /** 格式化日期 */
  142. function formatDate(date: any) {
  143. if (!date) return '-';
  144. return dayjs(date).format('YYYY-MM-DD');
  145. }
  146. /** 新增服务跟进(占位) */
  147. function handleAddServiceFollow() {
  148. // 待开发:打开服务跟进表单弹窗
  149. }
  150. /**
  151. * 加载详情数据
  152. */
  153. async function loadDetail() {
  154. if (!props.record.id) return;
  155. loading.value = true;
  156. activeTab.value = 'basic';
  157. try {
  158. const res = await queryDetailById({ id: props.record.id });
  159. // defHttp默认isTransformResponse=true,返回的res已经是result数据
  160. if (res) {
  161. detailData.value = res;
  162. }
  163. } catch (e) {
  164. console.error('加载详情失败', e);
  165. } finally {
  166. loading.value = false;
  167. }
  168. }
  169. // 监听record变化自动加载
  170. watch(
  171. () => props.record,
  172. () => {
  173. loadDetail();
  174. },
  175. { immediate: true, deep: true }
  176. );
  177. defineExpose({
  178. loadDetail,
  179. });
  180. </script>
  181. <style lang="less" scoped>
  182. .focus-personnel-detail {
  183. padding: 16px;
  184. .detail-header {
  185. display: flex;
  186. align-items: center;
  187. gap: 16px;
  188. .header-info {
  189. flex: 1;
  190. .header-name {
  191. margin-bottom: 6px;
  192. .name {
  193. font-size: 20px;
  194. font-weight: 600;
  195. margin-right: 8px;
  196. }
  197. .gender, .age {
  198. font-size: 14px;
  199. color: #666;
  200. margin-right: 6px;
  201. }
  202. }
  203. .header-tags {
  204. margin-bottom: 6px;
  205. }
  206. .header-meta {
  207. font-size: 13px;
  208. color: #999;
  209. }
  210. }
  211. }
  212. }
  213. </style>