浏览代码

修改部分样式

yangg 1 周之前
父节点
当前提交
415b7a0305

二进制
src/assets/首页图标1.png


+ 10 - 0
src/views/JobApplication/list/api.ts

@@ -86,3 +86,13 @@ export function getApplicationStatusSummary() {
 		method: 'get',
 	});
 }
+
+
+/* 候选人改到人才库 */
+export function candidateToTalentPool(data:any) {
+	return request({
+		url: '/api/system/talent_pool/import-from-application/',
+		method: 'post',
+		data
+	});
+}

+ 23 - 2
src/views/JobApplication/list/components/BatchStatusDialog.vue

@@ -35,7 +35,7 @@
 <script lang="ts" setup>
 import { ref, reactive } from 'vue';
 import { ElMessage, FormInstance } from 'element-plus';
-import { updateBatchStatus } from '../api';
+import { updateBatchStatus, candidateToTalentPool } from '../api';
 
 const dialogVisible = ref(false);
 const loading = ref(false);
@@ -86,7 +86,28 @@ const handleSubmit = async () => {
       const res = await updateBatchStatus(data);
       
       if (res.code === 2000) {
-        ElMessage.success('批量修改状态成功');
+        // 如果是拒绝并加入人才库,则调用加入人才库接口
+        if (form.new_status === 5 || form.new_status === 3) {
+          const talentPoolPromises = form.application_ids.map(id => 
+            candidateToTalentPool({
+              job_application_id: id,
+              to_external_pool: form.new_status === 5 ? true : false,
+              recommendation_reason: "该候选人技术栈与未来项目匹配,虽然本次面试未通过,但有很大潜力,建议加入人才库持续跟进。",
+              source: 1 // 1:面试拒绝后加入
+            })
+          );
+          
+          try {
+            await Promise.all(talentPoolPromises);
+            ElMessage.success('已成功将候选人加入人才库');
+          } catch (error) {
+            console.error('加入人才库失败:', error);
+            ElMessage.warning('状态更新成功,但加入人才库失败');
+          }
+        } else {
+          ElMessage.success('批量修改状态成功');
+        }
+        
         dialogVisible.value = false;
         emit('success');
         props.crudExpose.doRefresh();

+ 1 - 1
src/views/JobApplication/list/crud.tsx

@@ -310,7 +310,7 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 						text: '重新分析',
 						iconRight: 'Refresh',
 						type: 'text',
-						show:false /* compute(({ row }) => {
+						show:true /* compute(({ row }) => {
 							return row.status === 2; // 只在状态为"已面试"时显示
 						}) */,
 						order: 2,

+ 27 - 2
src/views/JobApplication/report/report.vue

@@ -395,7 +395,7 @@
                           <div class="flex flex-col space-x-2">
                             <div class="px-2 py-1 rounded text-sm" id="candidate-answer" style="font-size: 14px;">候选人回答:</div>
                             <div class="flex-1">
-                              <p class="text-gray-700 mb-0" style="font-size: 14px; line-height: 20px;  color: #808080;">{{ record.answer }}</p>
+                              <p class="text-gray-700 mb-0" style="font-size: 14px; line-height: 20px;  color: #808080;">{{ getAnswerText(record.answer) }}</p>
                             </div>
                           </div>
                         </div>
@@ -425,7 +425,7 @@
                   <!-- 追问题列表 -->
                   <div v-if="record.follow_up_questions && record.follow_up_questions.length > 0" 
                        class="mt-2  border-gray-200">
-                    <div v-for="(followUp, fIndex) in record.follow_up_questions" 
+                    <div v-for="(followUp, fIndex) in record.follow_up_questions.slice(0, 1)" 
                          :key="fIndex" 
                          class="">
                       <div class="flex items-start space-x-2">
@@ -2102,6 +2102,31 @@ export default {
       if (!text) return '';
       return text.replace(/【(.*?)】/g, '<span  style="color:#fb752f; font-weight: 500;">【$1】</span>');
     },
+    // 添加处理答案文本的方法
+    getAnswerText(answer) {
+      if (!answer) return '暂无回答';
+      
+      // 如果是字符串,直接返回
+      if (typeof answer === 'string') {
+        return answer;
+      }
+      console.log(answer);
+      // 如果是数组,尝试获取第一个元素的text
+      if (Array.isArray(answer) && answer.length > 0) {
+        const firstItem = answer[0];
+        if (firstItem && firstItem.text) {
+          return firstItem.text;
+        }
+      }
+      
+      // 如果是对象,尝试获取text属性
+      if (typeof answer === 'object' && answer.text) {
+        return answer.text;
+      }
+      
+      // 其他情况返回默认文本
+      return '暂无回答';
+    },
   }
 }
 </script>

+ 43 - 8
src/views/position/create/index.vue

@@ -216,7 +216,7 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, onMounted, onBeforeUnmount, watch } from 'vue';
+import { ref, reactive, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
 import { useRouter } from 'vue-router';
 import { ElMessage } from 'element-plus';
 import * as api from '../list/api';
@@ -254,7 +254,7 @@ interface PositionFormData {
   job_type_display: string;
   city: string;
   location: string[]; // 存储选中的代码
-  location_names: string[]; // 存储选中的名称
+  location_names?: string[]; // 存储选中的名称,设为可选
   address_detail: string;
   benefits: string[];
   requirements: string;
@@ -303,6 +303,23 @@ const formData = reactive<PositionFormData>({
   competency_tags: [],
 });
 
+// 富文本内容验证函数
+const validateRichText = (rule: any, value: string, callback: any) => {
+  if (!value) {
+    callback(new Error(rule.message));
+    return;
+  }
+  
+  // 去除HTML标签并检查是否有实际内容
+  const textContent = value.replace(/<[^>]*>/g, '').trim();
+  if (!textContent) {
+    callback(new Error(rule.message));
+    return;
+  }
+  
+  callback();
+};
+
 // 表单验证规则
 const rules = {
   title: [{ required: true, message: '职位名称必填', trigger: 'blur' }],
@@ -312,8 +329,18 @@ const rules = {
   department: [{ required: true, message: '所属部门必填', trigger: 'blur' }],
   job_type: [{ required: true, message: '职位状态必填', trigger: 'change' }],
   end_date: [{ required: true, message: '截止日期必填', trigger: 'change' }],
-  requirements: [{ required: true, message: '职位要求必填', trigger: 'blur' }],
-  description: [{ required: true, message: '职位描述必填', trigger: 'blur' }],
+  requirements: [{ 
+    required: true, 
+    message: '职位要求必填', 
+    validator: validateRichText,
+    trigger: 'blur' 
+  }],
+  description: [{ 
+    required: true, 
+    message: '职位描述必填', 
+    validator: validateRichText,
+    trigger: 'blur' 
+  }],
   salary_range: [{ required: true, message: '请完整填写薪资类型、起始薪资和最高薪资', trigger: 'blur' }],
   /* competency_tags: [
     { required: true, message: '请选择胜任力标签', trigger: 'change' }
@@ -501,6 +528,10 @@ onMounted(() => {
     // 监听内容变化
     editor.on('text-change', () => {
       formData.requirements = editor.root.innerHTML;
+      // 触发表单验证
+      nextTick(() => {
+        formRef.value?.validateField('requirements');
+      });
     });
   }
   
@@ -536,6 +567,10 @@ onMounted(() => {
     // 监听内容变化
     descEditor.on('text-change', () => {
       formData.description = descEditor.root.innerHTML;
+      // 触发表单验证
+      nextTick(() => {
+        formRef.value?.validateField('description');
+      });
     });
   }
   
@@ -575,7 +610,7 @@ const submitForm = async () => {
         const formToSubmit = { ...formData };
         
         // 处理地址信息 - 只提交名称
-        if (formToSubmit.location_names.length > 0) {
+        if (formToSubmit.location_names && formToSubmit.location_names.length > 0) {
           formToSubmit.province = formToSubmit.location_names[0];
           formToSubmit.city = formToSubmit.location_names[1] || '';
           formToSubmit.district = formToSubmit.location_names[2] || '';
@@ -583,10 +618,10 @@ const submitForm = async () => {
           formToSubmit.location_str = formToSubmit.location_names.join('/');
         }
         
-        // 删除多余的字段
-        delete formToSubmit.location_names;
+        // 创建新对象,排除 location_names 字段
+        const {location_names, ...submitData} = formToSubmit;
         
-        const response = await api.AddObj(formToSubmit);
+        const response = await api.AddObj(submitData);
         ElMessage.success('职位添加成功');
         router.push(`/position/detail?id=${response.data.id}`);
       } catch (error) {

+ 59 - 14
src/views/position/detail/index.vue

@@ -317,7 +317,7 @@
               inactive-text=""
               @change="handleStatusChange"
             />
-            <span class="status-text">{{ positionStatus ? '开启职位' : '关闭职位' }}</span>
+            <span class="status-text">{{ positionStatus ? '已启用' : '待启用' }}</span>
           </div>
         </el-card>
         
@@ -325,11 +325,25 @@
           <div class="section-title">
             <div class="section-line"></div>
             <span>职位性质</span>
-            <el-button type="text" class="edit-btn" @click="handleEditUsage">编辑</el-button>
+            <el-button type="text" class="edit-btn" @click="startEditJobType" v-if="!isEditingJobType">编辑</el-button>
           </div>
           <div class="usage-content">
-            <div class="usage-label">职位用途</div>
-            <div class="usage-value">正式招聘</div>
+           <!--  <div class="usage-label">职位用途</div> -->
+            <div class="usage-value" v-if="!isEditingJobType">{{ getJobTypeText(positionData.job_type) }}</div>
+            
+            <!-- 编辑模式 -->
+            <div v-if="isEditingJobType" class="title-edit-container">
+              <el-select v-model="editingJobType" placeholder="请选择职位性质" style="width: 100%;">
+                <el-option label="全职" :value="0"></el-option>
+                <el-option label="兼职" :value="1"></el-option>
+                <el-option label="实习" :value="2"></el-option>
+                <el-option label="其他" :value="3"></el-option>
+              </el-select>
+              <div class="title-edit-actions">
+                <el-button size="small" @click="cancelEditJobType">取消</el-button>
+                <el-button size="small" type="primary" @click="saveEditJobType">保存</el-button>
+              </div>
+            </div>
           </div>
         </el-card>
         
@@ -2122,9 +2136,9 @@ const positionData = reactive({
 // 招聘流程数据 - 修改为普通数组而非 ref 对象
 const recruitmentProcess = reactive([
   { id: 5, name: '资料收集', description: '资料收集', active: true },
-  { id: 7, name: '常识问题', description: '常识问题', active: true },
-  { id: 6, name: '心理问题', description: '心理问题', active: true },
-  { id: 2, name: 'AI视频', description: 'AI视频', active: true },
+ /*  { id: 7, name: '常识问题', description: '常识问题', active: true },
+  { id: 6, name: '心理问题', description: '心理问题', active: true }, */
+  { id: 2, name: 'AI考察', description: 'AI考察', active: true },
   { id: 1, name: '待核验', description: '待核验', active: true },
   { id: 3, name: '已通过', description: '已通过', active: true },
   { id: 4, name: '已淘汰', description: '已淘汰', active: false },
@@ -2136,7 +2150,7 @@ const showProcessDialog = ref(false);
 
 // 定义可选的流程步骤类型
 const processStepOptions = [
-  { label: 'AI视频', value: 'ai_video' },
+  { label: 'AI考察', value: 'ai_video' },
   { label: '视频宣讲', value: 'video_presentation' },
   { label: 'AI实时对话', value: 'ai_chat' },
   { label: '资料收集', value: 'data_collection' },
@@ -2682,10 +2696,7 @@ const handleStatusChange = async (value: boolean) => {
   }
 };
 
-// 编辑职位用途
-const handleEditUsage = () => {
-  ElMessage.info('编辑职位用途功能开发中');
-};
+
 
 // 编辑招聘流程
 const handleEditProcess = () => {
@@ -2763,7 +2774,7 @@ const getJobTypeText = (type: number) => {
 
 // 显示选项菜单
 const showStepOptions = (index: number, event: MouseEvent) => {
-  /* currentAddIndex.value = index;
+  currentAddIndex.value = index;
   showOptionsMenu.value = true;
   
   // 计算菜单位置 - 获取按钮元素
@@ -2786,7 +2797,7 @@ const showStepOptions = (index: number, event: MouseEvent) => {
   }
   
   // 阻止事件冒泡
-  event.stopPropagation(); */
+  event.stopPropagation();
   
 };
 // 修改添加选定类型的步骤方法
@@ -3850,6 +3861,40 @@ const saveDescription = async () => {
   }
 };
 
+// 职位性质编辑
+const isEditingJobType = ref(false);
+const editingJobType = ref(0);
+
+// 开始编辑职位性质
+const startEditJobType = () => {
+  editingJobType.value = positionData.job_type || 0;
+  isEditingJobType.value = true;
+};
+
+// 取消编辑职位性质
+const cancelEditJobType = () => {
+  isEditingJobType.value = false;
+};
+
+// 保存职位性质
+const saveEditJobType = async () => {
+  try {
+    const id = route.query.id;
+    await api.UpdateObj({
+      id: id,
+      job_type: editingJobType.value
+    });
+    
+    // 更新本地数据
+    positionData.job_type = editingJobType.value;
+    isEditingJobType.value = false;
+    ElMessage.success('职位性质已更新');
+  } catch (error) {
+    console.error('更新职位性质失败:', error);
+    ElMessage.error('更新职位性质失败');
+  }
+};
+
 // 添加能力标签相关的响应式变量
 const competencyTags = ref<CompetencyTag[]>([]);
 const isEditingCompetency = ref(false);

+ 11 - 4
src/views/position/list/crud.tsx

@@ -142,11 +142,12 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp
 				width: 320,
 				buttons: {
 					view: {
-						show: true,
+						show: false,
 						iconRight:"View",
 						type:"text"
 					},
 					edit: {
+						text: '查看',
 						show: auth('role:Update'),
 						iconRight:"Edit",
 						type:"text",
@@ -160,9 +161,9 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp
 						type:"text"
 					},
 					qrcode: {
-						text: '小程序码',
+						text: '分享',
 						type: "text",
-						icon: '',
+						iconRight: 'Share',
 						click: (ctx) => {
 							context.generateQRCode(ctx.row);
 						},
@@ -171,7 +172,10 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp
 					publish: {
 						text: '发布',
 						type: "text",
-						icon: '',
+						iconRight: '',
+						show:compute(({ row }) => {
+							return row.status === 0; // 只在状态为"已面试"时显示
+						}),
 						click: (ctx) => {
 							context.publishPosition(ctx.row);
 						},
@@ -198,6 +202,9 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp
 				},
 			},
 			table: {
+				remove: {
+					confirmMessage: '确定要删除这个职位吗?删除后无法恢复!',
+				},
 				onSelectionChange: (selection: any[]) => {
 					context.selectedRows = selection;
 					console.log('选择变化:', selection);

+ 1 - 1
src/views/talent/list/api.ts

@@ -1,7 +1,7 @@
 import { request } from '/@/utils/service';
 import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
 
-export const apiPrefix = '/api/system/talent_pool/list';
+export const apiPrefix = '/api/system/talent_pool/';
 export function GetList(query: UserPageQuery) {
 	return request({
 		url: apiPrefix,

+ 19 - 6
src/views/talent/list/components/BatchStatusDialog.vue

@@ -9,10 +9,11 @@
     <el-form :model="form" label-width="100px">
       <el-form-item label="选择状态">
         <el-select v-model="form.status" placeholder="请选择状态" style="width: 100%">
-          <el-option label="待面试" value="待面试" />
-          <el-option label="已面试" value="已面试" />
-          <el-option label="已录用" value="已录用" />
-          <el-option label="已拒绝" value="已拒绝" />
+          <el-option label="试用期" value="1" />
+          <el-option label="考察期" value="2" />
+          <el-option label="在职" value="3" />
+          <el-option label="已离职" value="4" />
+          <el-option label="转入在外人才库" value="7" />
         </el-select>
       </el-form-item>
     </el-form>
@@ -31,6 +32,13 @@
 import { ref, reactive } from 'vue';
 import { successMessage, warningMessage } from '/@/utils/message';
 import axios from 'axios';
+import { Session } from '/@/utils/storage';
+const props = defineProps({
+  crudExpose: {
+    type: Object,
+    required: true
+  }
+});
 
 const dialogVisible = ref(false);
 const loading = ref(false);
@@ -59,16 +67,21 @@ const handleConfirm = async () => {
   loading.value = true;
   try {
     // 这里需要替换为实际的API调用
-    const response = await axios.post(`${import.meta.env.VITE_API_URL}/api/talent/batch-update-status`, {
-      ids: selectedRows.value.map(row => row.id),
+    const response = await axios.post(`${import.meta.env.VITE_API_URL}/api/system/talent_pool/batch-update-status/`, {
+      ids: selectedRows.value.map((row: any) => row.id),
       status: form.status,
       tenant_id: 1
+    },{
+      headers:{
+        'authorization':'JWT '+ Session.get('token'),
+      },
     });
 
     if (response.data.code === 2000) {
       successMessage('状态修改成功');
       dialogVisible.value = false;
       emit('success');
+      props.crudExpose.doRefresh();
     } else {
       warningMessage(response.data.message || '操作失败');
     }

+ 20 - 3
src/views/talent/list/components/BatchTagsDialog.vue

@@ -41,7 +41,14 @@
 import { ref, reactive, onMounted } from 'vue';
 import { successMessage, warningMessage } from '/@/utils/message';
 import axios from 'axios';
+import { Session } from '/@/utils/storage';
 
+const props = defineProps({
+  crudExpose: {
+    type: Object,
+    required: true
+  }
+});
 const dialogVisible = ref(false);
 const loading = ref(false);
 const selectedRows = ref([]);
@@ -57,10 +64,15 @@ const emit = defineEmits(['success']);
 const getTagList = async () => {
   try {
     // 这里需要替换为实际的API调用
-    const response = await axios.get(`${import.meta.env.VITE_API_URL}/api/talent/tags`, {
+    const response = await axios.get(`${import.meta.env.VITE_API_URL}/api/system/talent_tag/`, {
       params: {
+        page: 1,
+        limit: 1000,
         tenant_id: 1
-      }
+      },
+      headers:{
+        'authorization':'JWT '+ Session.get('token'),
+      },
     });
     if (response.data.code === 2000) {
       tagOptions.value = response.data.data.map((tag: any) => ({
@@ -90,16 +102,21 @@ const handleConfirm = async () => {
   loading.value = true;
   try {
     // 这里需要替换为实际的API调用
-    const response = await axios.post(`${import.meta.env.VITE_API_URL}/api/talent/batch-add-tags`, {
+    const response = await axios.post(`${import.meta.env.VITE_API_URL}/api/system/talent_pool/batch-bind-tags/`, {
       ids: selectedRows.value.map(row => row.id),
       tag_ids: form.tags,
       tenant_id: 1
+    },{
+      headers:{
+        'authorization':'JWT '+ Session.get('token'),
+      },
     });
 
     if (response.data.code === 2000) {
       successMessage('标签添加成功');
       dialogVisible.value = false;
       emit('success');
+      props.crudExpose.doRefresh();
     } else {
       warningMessage(response.data.message || '操作失败');
     }

+ 109 - 36
src/views/talent/list/crud.tsx

@@ -263,29 +263,27 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 				width: 280, // 增加宽度以容纳新按钮
 				buttons: {
 					view: {
+						text: '查看个人信息',
+						iconRight: 'User',
+						show: true,
+						type: 'text',
+						
+					},
+					profile: { // 添加查看个人信息按钮
 						text: '查看报告',
 						iconRight: 'view',
-						show: true,
 						type: 'text',
+						show: true,
+						order: 1,
 						click: ({ row }) => {
 							// 在新窗口中打开报告详情页面
 							console.log(row)
 							const baseUrl = window.location.origin;
-							const url = `${baseUrl}/#/JobApplication/report/report?id=${row.id}&tenant_id=${1}&application_id=${row.applicant}`;
+							const url = `${baseUrl}/#/JobApplication/report/report?id=${row.original_application}&tenant_id=${1}&application_id=${row.original_application}`;
 							
 							window.open(url, '_blank');
 						}
 					},
-					profile: { // 添加查看个人信息按钮
-						text: '查看个人信息',
-						iconRight: 'User',
-						type: 'text',
-						show: true,
-						order: 1,
-						click: ({ row }) => {
-							showUserProfileDialog(row.id);
-						}
-					},
 					edit: {
 						text: '编辑',
 						iconRight: 'Edit',
@@ -365,7 +363,7 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 						show: false,
 					},
 				},
-				name: {
+				'user.name': {
 					title: '姓名',
 					search: {
 						show: true,
@@ -379,11 +377,71 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 					column: {
 						minWidth: 100,
 						formatter: ({ row }) => {
-							return row.name || row.nikename || '未填写';
+							return row.user.name || row.user.nikename || '未填写';
 						}
 					},
 				},
-				phone: {
+				tags: {
+					title: '标签',
+					type: 'dict-select',
+					column: {
+						minWidth: 180,
+						component: {
+							name: 'fs-component',
+							render: ({ row }: { row: any }) => {
+								if (!row.tags || row.tags.length === 0) return <span>-</span>;
+								
+								const displayTags = row.tags.slice(0, 2); // 只显示前两个标签
+								const remainingCount = row.tags.length - 2; // 计算剩余标签数量
+								
+								return (
+									<div style="display: flex; gap: 4px; align-items: center;">
+										{displayTags.map((tag: any) => (
+											<el-tag
+												key={tag.id}
+												type="warning"
+												effect="plain"
+												size="mini"
+												style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"
+												title={tag.name}
+											>
+												{tag.name.length > 4 ? tag.name.slice(0, 4) + '...' : tag.name}
+											</el-tag>
+										))}
+										{remainingCount > 0 && (
+											<el-tooltip
+												placement="top"
+												effect="light"
+												popper-class="tag-tooltip"
+											>
+												{{
+													default: () => (
+														<el-tag
+															type="info"
+															effect="plain"
+															size="mini"
+														>
+															+{remainingCount}
+														</el-tag>
+													),
+													content: () => (
+														<div>
+															<div style="font-weight: bold; margin-bottom: 5px">剩余{remainingCount}个标签:</div>
+															{row.tags.slice(2).map((tag: any) => (
+																<div key={tag.id} style="margin: 3px 0">{tag.name}</div>
+															))}
+														</div>
+													)
+												}}
+											</el-tooltip>
+										)}
+									</div>
+								);
+							}
+						}
+					},
+				},
+				'user.phone': {
 					title: '电话',
 					search: {
 						show: true,
@@ -397,11 +455,11 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 					column: {
 						minWidth: 120,
 						formatter: ({ row }) => {
-							return row.phone || '未填写';
+							return row.user.phone || '未填写';
 						}
 					},
 				},
-				gender_name: {
+				gender_display: {
 					title: '性别',
 					search: {
 						show: true,
@@ -426,9 +484,9 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 					type: 'number',
 					column: {
 						minWidth: 80,
-						formatter: ({ row }) => {
+						/* formatter: ({ row }) => {
 							return row.age || '未填写';
-						}
+						} */
 					},
 				},
 				/* 'profile_summary.political_status': {
@@ -461,7 +519,7 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 						}
 					},
 				},*/
-				'profile_summary.expected_salary': {
+				expected_salary_max: {
 					title: '期望薪资',
 					type: 'number',
 					column: {
@@ -471,7 +529,7 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 						}
 					},
 				},
-				'application_summary.latest_position': {
+				'suitable_positions.title': {
 					title: '岗位',
 					search: {
 						show: true,
@@ -485,52 +543,67 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 					column: {
 						minWidth: 120,
 						formatter: ({ row }) => {
-							return row.application_summary?.latest_position || '未填写';
+							return row.suitable_positions[0]?.title || '未填写';
 						}
 					},
 				},
-				'application_summary.latest_status': {
+				status: {
 					title: '状态',
+					type: 'dict-select',
+					dict: dict({
+						data: [
+							{ value: 1, label: '试用期' },
+							{ value: 2, label: '考察期' },
+							{ value: 3, label: '在职' },
+							{ value: 4, label: '已离职' },
+							{ value: 5, label: '在职已盘点' },
+							{ value: 7, label: '转入在外人才库' },
+						
+						]
+					}),
 					search: {
 						show: true,
 						component: {
 							name: 'el-select',
 							options: [
-								{ value: '待面试', label: '待面试' },
-								{ value: '已面试', label: '已面试' },
-								{ value: '已录用', label: '已录用' },
-								{ value: '已拒绝', label: '已拒绝' },
+								{ value: 1, label: '试用期' },
+								{ value: 2, label: '考察期' },
+								{ value: 3, label: '在职' },
+								{ value: 4, label: '已离职' },
+								{ value: 5, label: '在职已盘点' },
+								
 							]
 						},
 						size: 'small',
 						col:{ span:3},
 					},
-					type: 'input',
+					
 					column: {
 						minWidth: 100,
-						formatter: ({ row }) => {
+						
+						/* formatter: ({ row }) => {
 							return row.application_summary?.latest_status || '未填写';
-						}
+						} */
 					},
 				},
-				'application_summary.highest_score': {
+				interview_score: {
 					title: '面试得分',
 					type: 'input',
 					column: {
 						minWidth: 100,
-						formatter: ({ row }) => {
+						/* formatter: ({ row }) => {
 							return row.application_summary?.highest_score || '';
-						}
+						} */
 					},
 				},
-				'remark': {
+				remarks: {
 					title: '备注',
 					type: 'textarea',
 					column: {
 						minWidth: 100,
-						formatter: ({ row }) => {
+						/* formatter: ({ row }) => {
 							return row.application_summary?.latest_status_name || '未填写';
-						}
+						} */
 					},
 				},
 				/*'application_summary.highest_score': {

+ 16 - 14
src/views/talent/list/index.vue

@@ -47,7 +47,7 @@
 					</div>
 					
 					<!-- 试用期 -->
-					<div class="tree-item" :class="{ active: activeNode === 'status-1' }" @click="handleNodeClick({ type: 'status', value: 0, id: 'status-1' })">
+					<div class="tree-item" :class="{ active: activeNode === 'status-1' }" @click="handleNodeClick({ type: 'status', value: 1, id: 'status-1' })">
 						<div class="item-content">
 							<el-icon><Clock /></el-icon>
 							<span>试用期</span>
@@ -103,18 +103,12 @@
 					
 					<!-- 正式员工子菜单 -->
 					<div v-if="showFormalSubMenu" class="submenu">
-						<div class="tree-item" :class="{ active: activeNode === 'status-3-1' }" @click="handleNodeClick({ type: 'status', value: '3-1', id: 'status-3-1' })">
+						<div class="tree-item" :class="{ active: activeNode === 'status-3-1' }" @click="handleNodeClick({ type: 'status', value: '5', id: 'status-3-1' })">
 							<div class="item-content">
 								<span>已盘点</span>
 							</div>
 							<div class="item-count" v-if="statusCounts['3-1']">{{ statusCounts['3-1'] }}</div>
 						</div>
-						<div class="tree-item" :class="{ active: activeNode === 'status-3-2' }" @click="handleNodeClick({ type: 'status', value: '3-2', id: 'status-3-2' })">
-							<div class="item-content">
-								<span>未盘点</span>
-							</div>
-							<div class="item-count" v-if="statusCounts['3-2']">{{ statusCounts['3-2'] }}</div>
-						</div>
 					</div>
 					
 					<!-- 已离职 -->
@@ -281,9 +275,11 @@ const handleNodeClick = (data: any) => {
 			console.warn('重置表单失败:', error);
 		}
 		
-		// 无论如何都执行搜索,确保数据刷新
+		// 点击"全部"时也只显示状态为1,2,3,4,5的数据
 		crudExpose.doSearch({
-			form: {}
+			form: {
+				status: '1, 2, 3, 4, 5' // 试用期、考察期、在职、已离职、在职已盘点
+			}
 		});
 	} else if (data.type === 'status') {
 		// 按状态筛选
@@ -351,9 +347,11 @@ const handleShowResignedChange = (value: boolean) => {
 			}
 		});
 	} else {
-		// 重置搜索,显示所有员工
+		// 重置搜索,只显示状态为1,2,3,4,5的员工
 		crudExpose.doSearch({
-			form: {}
+			form: {
+				status: '1, 2, 3, 4, 5' // 试用期、考察期、在职、已离职、在职已盘点
+			}
 		});
 	}
 };
@@ -382,8 +380,12 @@ onMounted(async () => {
 	// 重置crudBinding
 	resetCrudOptions(newOptions);
 	
-	// 刷新
-	crudExpose.doRefresh();
+	// 初始化时只显示状态为1,2,3,4,5的数据(排除状态7:转入在外人才库)
+	crudExpose.doSearch({
+		form: {
+			status: '1, 2, 3, 4, 5' // 试用期、考察期、在职、已离职、在职已盘点
+		}
+	});
 });
 </script>
 

+ 1 - 1
src/views/talent/overseas/api.ts

@@ -1,7 +1,7 @@
 import { request } from '/@/utils/service';
 import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
 
-export const apiPrefix = '/api/system/talent_pool/list';
+export const apiPrefix = '/api/system/talent_pool/';
 export function GetList(query: UserPageQuery) {
 	return request({
 		url: apiPrefix,

+ 10 - 8
src/views/talent/overseas/components/BatchStatusDialog.vue

@@ -9,10 +9,12 @@
     <el-form :model="form" label-width="100px">
       <el-form-item label="选择状态">
         <el-select v-model="form.status" placeholder="请选择状态" style="width: 100%">
-          <el-option label="待面试" value="待面试" />
-          <el-option label="已面试" value="已面试" />
-          <el-option label="已录用" value="已录用" />
-          <el-option label="已拒绝" value="已拒绝" />
+          <el-option label="状态6" :value="6" />
+          <el-option label="状态7" :value="7" />
+          <el-option label="待面试" :value="0" />
+          <el-option label="已面试" :value="1" />
+          <el-option label="已录用" :value="2" />
+          <el-option label="已拒绝" :value="3" />
         </el-select>
       </el-form-item>
     </el-form>
@@ -34,19 +36,19 @@ import axios from 'axios';
 
 const dialogVisible = ref(false);
 const loading = ref(false);
-const selectedRows = ref([]);
+const selectedRows = ref<Array<{ id: number | string }>>([]);
 
 const form = reactive({
-  status: '',
+  status: 0,
 });
 
 const emit = defineEmits(['success']);
 
 // 打开对话框的方法
-const open = (rows: any[]) => {
+const open = (rows: Array<{ id: number | string }>) => {
   selectedRows.value = rows;
   dialogVisible.value = true;
-  form.status = '';
+  form.status = 0;
 };
 
 // 确认修改状态

+ 1 - 1
src/views/talent/overseas/components/BatchTagsDialog.vue

@@ -57,7 +57,7 @@ const emit = defineEmits(['success']);
 const getTagList = async () => {
   try {
     // 这里需要替换为实际的API调用
-    const response = await axios.get(`${import.meta.env.VITE_API_URL}/api/talent/tags`, {
+    const response = await axios.get(`${import.meta.env.VITE_API_URL}/api/system/talent_tag/`, {
       params: {
         tenant_id: 1
       }

+ 110 - 36
src/views/talent/overseas/crud.tsx

@@ -263,29 +263,27 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 				width: 280, // 增加宽度以容纳新按钮
 				buttons: {
 					view: {
+						text: '查看个人信息',
+						iconRight: 'User',
+						show: true,
+						type: 'text',
+						
+					},
+					profile: { // 添加查看个人信息按钮
 						text: '查看报告',
 						iconRight: 'view',
-						show: true,
 						type: 'text',
+						show: true,
+						order: 1,
 						click: ({ row }) => {
 							// 在新窗口中打开报告详情页面
 							console.log(row)
 							const baseUrl = window.location.origin;
-							const url = `${baseUrl}/#/JobApplication/report/report?id=${row.id}&tenant_id=${1}&application_id=${row.applicant}`;
+							const url = `${baseUrl}/#/JobApplication/report/report?id=${row.original_application}&tenant_id=${1}&application_id=${row.original_application}`;
 							
 							window.open(url, '_blank');
 						}
 					},
-					profile: { // 添加查看个人信息按钮
-						text: '查看个人信息',
-						iconRight: 'User',
-						type: 'text',
-						show: true,
-						order: 1,
-						click: ({ row }) => {
-							showUserProfileDialog(row.id);
-						}
-					},
 					edit: {
 						text: '编辑',
 						iconRight: 'Edit',
@@ -365,7 +363,7 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 						show: false,
 					},
 				},
-				name: {
+				'user.name': {
 					title: '姓名',
 					search: {
 						show: true,
@@ -379,11 +377,71 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 					column: {
 						minWidth: 100,
 						formatter: ({ row }) => {
-							return row.name || row.nikename || '未填写';
+							return row.user.name || row.user.nikename || '未填写';
 						}
 					},
 				},
-				phone: {
+				tags: {
+					title: '标签',
+					type: 'dict-select',
+					column: {
+						minWidth: 180,
+						component: {
+							name: 'fs-component',
+							render: ({ row }: { row: any }) => {
+								if (!row.tags || row.tags.length === 0) return <span>-</span>;
+								
+								const displayTags = row.tags.slice(0, 2); // 只显示前两个标签
+								const remainingCount = row.tags.length - 2; // 计算剩余标签数量
+								
+								return (
+									<div style="display: flex; gap: 4px; align-items: center;">
+										{displayTags.map((tag: any) => (
+											<el-tag
+												key={tag.id}
+												type="warning"
+												effect="plain"
+												size="mini"
+												style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"
+												title={tag.name}
+											>
+												{tag.name.length > 4 ? tag.name.slice(0, 4) + '...' : tag.name}
+											</el-tag>
+										))}
+										{remainingCount > 0 && (
+											<el-tooltip
+												placement="top"
+												effect="light"
+												popper-class="tag-tooltip"
+											>
+												{{
+													default: () => (
+														<el-tag
+															type="info"
+															effect="plain"
+															size="mini"
+														>
+															+{remainingCount}
+														</el-tag>
+													),
+													content: () => (
+														<div>
+															<div style="font-weight: bold; margin-bottom: 5px">剩余{remainingCount}个标签:</div>
+															{row.tags.slice(2).map((tag: any) => (
+																<div key={tag.id} style="margin: 3px 0">{tag.name}</div>
+															))}
+														</div>
+													)
+												}}
+											</el-tooltip>
+										)}
+									</div>
+								);
+							}
+						}
+					},
+				},
+				'user.phone': {
 					title: '电话',
 					search: {
 						show: true,
@@ -397,11 +455,11 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 					column: {
 						minWidth: 120,
 						formatter: ({ row }) => {
-							return row.phone || '未填写';
+							return row.user.phone || '未填写';
 						}
 					},
 				},
-				gender_name: {
+				gender_display: {
 					title: '性别',
 					search: {
 						show: true,
@@ -426,9 +484,9 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 					type: 'number',
 					column: {
 						minWidth: 80,
-						formatter: ({ row }) => {
+						/* formatter: ({ row }) => {
 							return row.age || '未填写';
-						}
+						} */
 					},
 				},
 				/* 'profile_summary.political_status': {
@@ -461,7 +519,7 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 						}
 					},
 				},*/
-				'profile_summary.expected_salary': {
+				expected_salary_max: {
 					title: '期望薪资',
 					type: 'number',
 					column: {
@@ -471,7 +529,7 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 						}
 					},
 				},
-				'application_summary.latest_position': {
+				'suitable_positions.title': {
 					title: '岗位',
 					search: {
 						show: true,
@@ -485,52 +543,68 @@ export const createCrudOptions = function ({ crudExpose, context}: CreateCrudOpt
 					column: {
 						minWidth: 120,
 						formatter: ({ row }) => {
-							return row.application_summary?.latest_position || '未填写';
+							return row.suitable_positions[0]?.title || '未填写';
 						}
 					},
 				},
-				'application_summary.latest_status': {
+				status: {
 					title: '状态',
+					type: 'dict-select',
+					dict: dict({
+						data: [
+							/* { value: 1, label: '试用期' },
+							{ value: 2, label: '考察期' },
+							{ value: 3, label: '在职' },
+							{ value: 4, label: '已离职' },
+							{ value: 5, label: '在职已盘点' }, */
+							{ value: 6, label: '面试未通过' },
+							{ value: 7, label: '曾就职' },
+						
+						]
+					}),
 					search: {
 						show: true,
 						component: {
 							name: 'el-select',
 							options: [
-								{ value: '待面试', label: '待面试' },
-								{ value: '已面试', label: '已面试' },
-								{ value: '已录用', label: '已录用' },
-								{ value: '已拒绝', label: '已拒绝' },
+								{ value: 1, label: '试用期' },
+								{ value: 2, label: '考察期' },
+								{ value: 3, label: '在职' },
+								{ value: 4, label: '已离职' },
+								{ value: 5, label: '在职已盘点' },
+								
 							]
 						},
 						size: 'small',
 						col:{ span:3},
 					},
-					type: 'input',
+					
 					column: {
 						minWidth: 100,
-						formatter: ({ row }) => {
+						
+						/* formatter: ({ row }) => {
 							return row.application_summary?.latest_status || '未填写';
-						}
+						} */
 					},
 				},
-				'application_summary.highest_score': {
+				interview_score: {
 					title: '面试得分',
 					type: 'input',
 					column: {
 						minWidth: 100,
-						formatter: ({ row }) => {
+						/* formatter: ({ row }) => {
 							return row.application_summary?.highest_score || '';
-						}
+						} */
 					},
 				},
-				'remark': {
+				remarks: {
 					title: '备注',
 					type: 'textarea',
 					column: {
 						minWidth: 100,
-						formatter: ({ row }) => {
+						/* formatter: ({ row }) => {
 							return row.application_summary?.latest_status_name || '未填写';
-						}
+						} */
 					},
 				},
 				/*'application_summary.highest_score': {

+ 34 - 6
src/views/talent/overseas/index.vue

@@ -46,23 +46,41 @@
 						<div class="item-count">{{ totalCount }}人</div>
 					</div>
 					
+					<!-- 状态6 -->
+					<div class="tree-item" :class="{ active: activeNode === 'status-6' }" @click="handleNodeClick({ type: 'status', value: 7, id: 'status-6' })">
+						<div class="item-content">
+							<el-icon><RefreshRight /></el-icon>
+							<span>曾就职</span>
+						</div>
+						<div class="item-count" v-if="statusCounts['7']">{{ statusCounts['7'] }}</div>
+					</div>
+					
+					<!-- 状态7 -->
+					<div class="tree-item" :class="{ active: activeNode === 'status-7' }" @click="handleNodeClick({ type: 'status', value: 6, id: 'status-7' })">
+						<div class="item-content">
+							<el-icon><RefreshRight /></el-icon>
+							<span>面试未通过</span>
+						</div>
+						<div class="item-count" v-if="statusCounts['6']">{{ statusCounts['6'] }}</div>
+					</div>
+					
 					<!-- 曾就职 -->
-					<div class="tree-item" :class="{ active: activeNode === 'status-1' }" @click="handleNodeClick({ type: 'status', value: 1, id: 'status-1' })">
+					<!-- <div class="tree-item" :class="{ active: activeNode === 'status-1' }" @click="handleNodeClick({ type: 'status', value: 1, id: 'status-1' })">
 						<div class="item-content">
 							<el-icon><RefreshRight /></el-icon>
 							<span>曾就职</span>
 						</div>
 						<div class="item-count" v-if="statusCounts['1']">{{ statusCounts['1'] }}</div>
 					</div>
-					
+					 -->
 					<!-- 面试未通过 -->
-					<div class="tree-item" :class="{ active: activeNode === 'status-2' }" @click="handleNodeClick({ type: 'status', value: 2, id: 'status-2' })">
+					<!-- <div class="tree-item" :class="{ active: activeNode === 'status-2' }" @click="handleNodeClick({ type: 'status', value: 2, id: 'status-2' })">
 						<div class="item-content">
 							<el-icon><CircleClose /></el-icon>
 							<span>面试未通过</span>
 						</div>
 						<div class="item-count" v-if="statusCounts['2']">{{ statusCounts['2'] }}</div>
-					</div>
+					</div> -->
 					
 				</div>
 			</div>
@@ -116,6 +134,8 @@ const positions = ref<Array<{id: number|string, title: string, count?: number}>>
 // 状态计数
 const totalCount = ref(0);
 const statusCounts = reactive<Record<string, number>>({
+	'6': 0,      // 状态6
+	'7': 0,      // 状态7
 	'1': 0,      // 曾就职
 	'2': 0,      // 面试未通过
 });
@@ -152,7 +172,11 @@ const fetchStatusSummary = async () => {
 			// 更新状态计数
 			if (data.data.status_data && Array.isArray(data.data.status_data)) {
 				data.data.status_data.forEach((item: any) => {
-					if (item.status === 1) { // 曾就职
+					if (item.status === 6) { // 状态6
+						statusCounts['6'] = item.count;
+					} else if (item.status === 7) { // 状态7
+						statusCounts['7'] = item.count;
+					} else if (item.status === 1) { // 曾就职
 						statusCounts['1'] = item.count;
 					} else if (item.status === 2) { // 面试未通过
 						statusCounts['2'] = item.count;
@@ -256,7 +280,11 @@ onMounted(async () => {
 	resetCrudOptions(newOptions);
 	
 	// 刷新
-	crudExpose.doRefresh();
+	crudExpose.doSearch({
+		form: {
+			status: '6,7' // 试用期、考察期、在职、已离职、在职已盘点
+		}
+	});
 });
 </script>
 

+ 41 - 0
src/views/talent/tagsList/api.ts

@@ -0,0 +1,41 @@
+import { request } from '/@/utils/service';
+import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
+
+export const apiPrefix = '/api/system/talent_tag/';
+export function GetList(query: UserPageQuery) {
+	return request({
+		url: apiPrefix,
+		method: 'get',
+		params: {...query,tenant_id:1},
+	});
+}
+export function GetObj(id: InfoReq) {
+	return request({
+		url: apiPrefix + id,
+		method: 'get',
+	});
+}
+
+export function AddObj(obj: AddReq) {
+	return request({
+		url: apiPrefix,
+		method: 'post',
+		data: {...obj,tenant_id:1},
+	});
+}
+
+export function UpdateObj(obj: EditReq) {
+	return request({
+		url: apiPrefix + obj.id + '/',
+		method: 'put',
+		data: {...obj,tenant_id:1},
+	});
+}
+
+export function DelObj(id: DelReq) {
+	return request({
+		url: apiPrefix + id + '/',
+		method: 'delete',
+		data: { id,tenant_id:1},
+	});
+}

+ 257 - 0
src/views/talent/tagsList/crud.tsx

@@ -0,0 +1,257 @@
+import * as api from './api';
+import {
+    dict,
+    UserPageQuery,
+    AddReq,
+    DelReq,
+    EditReq,
+    compute,
+    CreateCrudOptionsProps,
+    CreateCrudOptionsRet
+} from '@fast-crud/fast-crud';
+import {request} from '/@/utils/service';
+import {dictionary} from '/@/utils/dictionary';
+import {successMessage} from '/@/utils/message';
+import {auth} from '/@/utils/authFunction'
+
+export const createCrudOptions = function ({crudExpose}: CreateCrudOptionsProps): CreateCrudOptionsRet {
+    const pageRequest = async (query: UserPageQuery) => {
+        return await api.GetList(query);
+    };
+    const editRequest = async ({form, row}: EditReq) => {
+        form.id = row.id;
+        return await api.UpdateObj(form);
+    };
+    const delRequest = async ({row}: DelReq) => {
+        return await api.DelObj(row.id);
+    };
+    const addRequest = async ({form}: AddReq) => {
+        return await api.AddObj(form);
+    };
+
+    return {
+        crudOptions: {
+            request: {
+                pageRequest,
+                addRequest,
+                editRequest,
+                delRequest,
+            },
+            actionbar: {
+                buttons: {
+                    add: {
+                        text: "新增标签",
+                        icon: "Plus",
+                        type: "primary",
+                        show: true
+                    }
+                }
+            },
+            rowHandle: {
+                fixed: 'right',
+                width: 260,
+                buttons: {
+                    view: {
+                        iconRight: 'View',
+                        type: 'text',
+                        show: true,
+                    },
+                    edit: {
+                        iconRight: 'Edit',
+                        type: 'text',
+                        show: true,
+                    },
+                    remove: {
+                        iconRight: 'Delete',
+                        type: 'text',
+                        show: true,
+                    },
+                },
+            },
+            form: {
+                col: {span: 24},
+                labelWidth: '110px',
+                wrapper: {
+                    is: 'el-dialog',
+                    width: '600px',
+                    title: "标签信息"
+                },
+                defaultValue: {
+                    color: "#4A90E2",
+                    is_system: false,
+                    status: true
+                },
+                display: "flex",
+                labelPosition: "right"
+            },
+            columns: {
+                _index: {
+                    title: '序号',
+                    form: {show: false},
+                    column: {
+                        align: 'center',
+                        width: '70px',
+                        columnSetDisabled: true,
+                        formatter: (context) => {
+                            let index = context.index ?? 1;
+                            let pagination: any = crudExpose!.crudBinding.value.pagination;
+                            return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
+                        },
+                    },
+                },
+                name: {
+                    title: '标签名称',
+                    search: {
+                        show: true,
+                        component: {
+                            props: {
+                                clearable: true,
+                            },
+                            placeholder: '请输入标签名称',
+                        },
+                    },
+                    form: {
+                        rules: [
+                            { required: true, message: '请输入标签名称' },
+                            { max: 50, message: '标签名称不能超过50个字符' }
+                        ],
+                        component: {
+                            span: 24,
+                            props: {
+                                clearable: true,
+                                placeholder: '请输入标签名称'
+                            },
+                        },
+                    },
+                    column: {
+                        minWidth: 120,
+                    },
+                },
+                description: {
+                    title: '标签描述',
+                    search: {
+                        show: false,
+                    },
+                    form: {
+                        rules: [
+                            { max: 200, message: '标签描述不能超过200个字符' }
+                        ],
+                        component: {
+                            span: 24,
+                            name: 'el-input',
+                            type: 'textarea',
+                            props: {
+                                rows: 3,
+                                placeholder: '请输入标签描述'
+                            },
+                        },
+                    },
+                    column: {
+                        minWidth: 200,
+                        showOverflowTooltip: true,
+                    },
+                },
+                color: {
+                    title: '标签颜色',
+                    search: {
+                        show: false,
+                    },
+                    form: {
+                        show: false,
+                        rules: [{ required: true, message: '请选择标签颜色' }],
+                        component: {
+                            span: 24,
+                            name: 'el-color-picker',
+                            props: {
+                                showAlpha: false
+                            }
+                        },
+                    },
+                    column: {
+                        show: false,
+                        width: 80,
+                        component: {
+                            name: 'el-tag',
+                            props: (context: any) => {
+                                return {
+                                    color: context.row.color,
+                                    effect: 'plain',
+                                };
+                            },
+                        },
+                    },
+                },
+                is_system: {
+                    title: '系统标签',
+                    search: {
+                        show: true,
+                    },
+                    type: 'dict-select',
+                    dict: dict({
+                        data: [
+                            { label: '是', value: true },
+                            { label: '否', value: false },
+                        ],
+                    }),
+                    form: {
+                        value: false,
+                        component: {
+                            span: 24,
+                        }
+                    },
+                    column: {
+                        width: 160,
+                        formatter: (context: any) => {
+                            return context.row.is_system ? '是' : '否';
+                        },
+                        component: {
+                            name: 'el-tag',
+                            props: (context: any) => {
+                                return {
+                                    type: context.row.is_system ? '' : 'info',
+                                    effect: 'plain',
+                                    round: true
+                                };
+                            },
+                        },
+                    },
+                },
+                status: {
+                    title: '状态',
+                    search: {
+                        show: true,
+                    },
+                    type: 'dict-radio',
+                    dict: dict({
+                        data: [
+                            { label: '启用', value: true },
+                            { label: '禁用', value: false },
+                        ],
+                    }),
+                    form: {
+                        value: true,
+                        component: {
+                            span: 24,
+                        }
+                    },
+                    column: {
+                        width: 160,  formatter: (context: any) => {
+                                return context.row.status ? '启用' : '禁用';
+                            },
+                            component: {
+                                name: 'el-tag',
+                                props: (context: any) => {
+                                    return {
+                                        type: context.row.status ? '' : 'info',
+                                        effect: 'plain',
+                                        round: true
+                                    };
+                                },
+                            },
+                        
+                    },
+                },
+            },
+        },
+    };
+};

+ 18 - 0
src/views/talent/tagsList/index.vue

@@ -0,0 +1,18 @@
+<template>
+    <fs-page>
+        <fs-crud ref="crudRef" v-bind="crudBinding"></fs-crud>
+    </fs-page>
+</template>
+
+<script lang="ts" setup name="whiteList">
+import {ref, onMounted} from 'vue';
+import {useFs} from '@fast-crud/fast-crud';
+import {createCrudOptions} from './crud';
+
+const {crudBinding, crudRef, crudExpose} = useFs({createCrudOptions});
+
+// 页面打开后获取列表数据
+onMounted(() => {
+    crudExpose.doRefresh();
+});
+</script>