jianggs 3 nedēļas atpakaļ
vecāks
revīzija
31c9bf189c
26 mainītis faili ar 3415 papildinājumiem un 843 dzēšanām
  1. BIN
      web/src/assets/images/noData.png
  2. 1 1
      web/src/router/modules/aiTagging-routes.js
  3. 28 0
      web/src/store/modules/aiTagPermissions.js
  4. 36 7
      web/src/views/aiTagging/externalPage/components/IntelligentRecommend.vue
  5. 32 14
      web/src/views/aiTagging/externalPage/components/ManualTagging.vue
  6. 788 0
      web/src/views/aiTagging/externalPage/externalEdit.vue
  7. 301 0
      web/src/views/aiTagging/externalPage/externalResult.vue
  8. 131 426
      web/src/views/aiTagging/externalPage/index.vue
  9. 165 51
      web/src/views/aiTagging/taggingDetail/index.vue
  10. 11 1
      web/src/views/aiTagging/taggingLogs/index.vue
  11. 145 13
      web/src/views/aiTagging/taggingResults/components/Detail.vue
  12. 16 10
      web/src/views/aiTagging/taggingResults/components/Overview.vue
  13. 45 88
      web/src/views/aiTagging/taggingResults/index.vue
  14. 156 29
      web/src/views/aiTagging/taggingSystemManage/TagSystemEdit.vue
  15. 14 3
      web/src/views/aiTagging/taggingSystemManage/components/CreateTagSystem.vue
  16. 77 5
      web/src/views/aiTagging/taggingSystemManage/components/TagConfig.vue
  17. 17 3
      web/src/views/aiTagging/taggingSystemManage/components/TagDetail.vue
  18. 81 6
      web/src/views/aiTagging/taggingSystemManage/components/TagDetailEdit.vue
  19. 54 29
      web/src/views/aiTagging/taggingSystemManage/components/TagDetailEditRegexDrawer.vue
  20. 18 5
      web/src/views/aiTagging/taggingSystemManage/components/TagDetailView.vue
  21. 111 80
      web/src/views/aiTagging/taggingSystemManage/components/TagTest.vue
  22. 78 12
      web/src/views/aiTagging/taggingSystemManage/components/TagTree.vue
  23. 100 9
      web/src/views/aiTagging/taggingSystemManage/components/TreeSelect.vue
  24. 108 51
      web/src/views/aiTagging/taggingSystemManage/index.vue
  25. 262 0
      web/src/views/aiTagging/taggingTest/TagTestComponent.vue
  26. 640 0
      web/src/views/aiTagging/taggingTest/index.vue

BIN
web/src/assets/images/noData.png


+ 1 - 1
web/src/router/modules/aiTagging-routes.js

@@ -12,7 +12,7 @@ const aiTaggingRoutes = [
     path: "/tag-system-edit",
     component: () => import("@/views/aiTagging/taggingSystemManage/TagSystemEdit.vue"),
     name: "tag-system-edit",
-    meta: { title: '标签列表', noCache: false },
+    meta: { title: '标签配置', noCache: false },
     hidden: true,
   },
   {

+ 28 - 0
web/src/store/modules/aiTagPermissions.js

@@ -0,0 +1,28 @@
+export default {
+  namespaced: true,
+  state: {
+    currentPermissions: {}
+  },
+  mutations: {
+    SET_PERMISSIONS(state, permissions) {
+      state.currentPermissions = permissions;
+    },
+    CLEAR_PERMISSIONS(state) {
+      state.currentPermissions = {};
+    }
+  },
+  actions: {
+    setPermissions({ commit }, permissions) {
+      commit('SET_PERMISSIONS', permissions);
+    },
+    clearPermissions({ commit }) {
+      commit('CLEAR_PERMISSIONS');
+    }
+  },
+  getters: {
+    getPermissions: (state) => state.currentPermissions,
+    hasPermission: (state) => (permissionKey) => {
+      return state.currentPermissions[permissionKey] === true;
+    }
+  }
+}

+ 36 - 7
web/src/views/aiTagging/externalPage/components/IntelligentRecommend.vue

@@ -20,16 +20,15 @@
         :key="index"
         class="recommendation-item"
         :class="{ 'selected': tag.selected }"
-        @click="handleTagSelect(index)"
       >
         <!-- 标签路径 -->
-        <div class="tag-path">
+        <div class="tag-path" @click.stop="handleTagSelect(index)">
           <span>{{ tag.tag_path }}</span>
           <i v-if="tag.selected" class="el-icon-check selected-icon"></i>
         </div>
         
         <!-- 打标依据 -->
-        <div class="tag-basis">
+        <div class="tag-basis" @click.stop="handleTagSelect(index)">
           <div class="basis-label">打标依据:</div>
           <div class="basis-content">{{ tag.desc }}</div>
         </div>
@@ -42,6 +41,7 @@
             type="textarea"
             placeholder="请输入"
             :rows="2"
+            :disabled="tag.selected"
             class="feedback-input"
           ></el-input>
         </div>
@@ -80,12 +80,41 @@ export default {
         this.recommendations.forEach(tag => {
           tag.selected = false;
         });
+        
+        // 计算选中的标签
+        const selectedTags = this.recommendations.filter(tag => tag.selected);
+        // 将选中的标签传递给父组件
+        this.$emit('tagSelect', selectedTags);
       } else {
-        // 如果未选中,则取消其他标签的选中状态,只选中当前标签
-        this.recommendations.forEach((tag, i) => {
-          tag.selected = i === index;
-        });
+        // 如果未选中,检查是否有反馈内容
+        const currentTag = this.recommendations[index];
+        if (currentTag.feedback && currentTag.feedback.trim()) {
+          // 提醒用户选中当前标签不准确反馈情况
+          this.$confirm('当前标签有不准确反馈内容,是否确认选中该标签?选中后将清空反馈内容。', '提示', {
+            confirmButtonText: '确认',
+            cancelButtonText: '取消',
+            type: 'warning'
+          }).then(() => {
+            // 用户确认,清空反馈内容并选中标签
+            currentTag.feedback = '';
+            this.selectTag(index);
+          }).catch(() => {
+            // 用户取消,不做任何操作
+            console.log('用户取消选中操作');
+          });
+        } else {
+          // 没有反馈内容,直接选中标签
+          this.selectTag(index);
+        }
       }
+    },
+    
+    // 选中标签的内部方法
+    selectTag(index) {
+      // 取消其他标签的选中状态,只选中当前标签
+      this.recommendations.forEach((tag, i) => {
+        tag.selected = i === index;
+      });
       
       // 计算选中的标签
       const selectedTags = this.recommendations.filter(tag => tag.selected);

+ 32 - 14
web/src/views/aiTagging/externalPage/components/ManualTagging.vue

@@ -14,9 +14,8 @@
         v-model="searchKeyword"
         placeholder="请输入"
         class="search-input"
-        @input="handleSearch"
       >
-        <i slot="prefix" class="el-input__icon el-icon-search"></i>
+        <i slot="suffix" class="el-input__icon el-icon-search"></i>
       </el-input>
     </div>
 
@@ -28,6 +27,7 @@
         :props="defaultProps"
         node-key="id"
         :highlight-current="true"
+        :filter-node-method="filterNode"
         @node-click="handleTagClick"
         class="tag-tree-list"
       ></el-tree>
@@ -59,7 +59,8 @@ export default {
         children: 'children',
         label: 'tagNm'
       },
-      tagTreeData: []
+      tagTreeData: [],
+      originalTagTreeData: [] // 保存原始标签树数据
     };
   },
   watch: {
@@ -76,25 +77,41 @@ export default {
         this.reverseSelectTags();
       },
       deep: true
+    },
+    // 监听搜索关键词变化,触发树形组件过滤
+    searchKeyword: {
+      handler(val) {
+        console.log('搜索关键词变化:', val);
+        // 调用 el-tree 内置的过滤方法
+        if (this.$refs.tagTree) {
+          console.log('调用过滤方法');
+          this.$refs.tagTree.filter(val);
+        } else {
+          console.log('tagTree ref 不存在');
+        }
+      },
+      immediate: true
     }
   },
   mounted() {
     // 组件挂载时加载标签树数据
     this.loadTagTreeData();
   },
-  watch: {
-    // 监听currentSystem变化,重新加载标签树数据
-    currentSystem: {
-      handler() {
-        this.loadTagTreeData();
-      },
-      immediate: true
-    }
-  },
   methods: {
-    // 处理搜索
+    // 搜索标签
     handleSearch() {
-      this.$emit('search', this.searchKeyword);
+      // 清空搜索时重新加载数据,确保显示完整标签树
+      if (!this.searchKeyword.trim()) {
+        this.loadTagTreeData();
+      }
+    },
+    
+    // 节点过滤方法
+    filterNode(value, data) {
+      // 如果关键词为空,显示所有节点
+      if (!value) return true;
+      // 匹配节点名称(不区分大小写)
+      return data.tagNm && data.tagNm.toLowerCase().includes(value.toLowerCase());
     },
     
     // 处理标签点击
@@ -120,6 +137,7 @@ export default {
         callback: (code, error, response) => {
           if (response.code == '0') {
             this.tagTreeData = response.data || [];
+            this.originalTagTreeData = JSON.parse(JSON.stringify(this.tagTreeData)); // 保存原始数据
             console.log('标签树数据加载成功:', this.tagTreeData);
             // 反显选中的标签
             this.reverseSelectTags();

+ 788 - 0
web/src/views/aiTagging/externalPage/externalEdit.vue

@@ -0,0 +1,788 @@
+<template>
+  <div class="intelligent-tagging">
+    <!-- 顶部通知栏 -->
+    <div class="top-notification">
+      <i class="el-icon-info"></i>
+      <span>系统已自动生成标签推荐结果,请您勾选确认准确标签,针对不符合标签可提交反馈内容以优化后续推荐服务;若无适用标签,可通过手动选标完成打标操作。</span>
+      <div class="button-group">
+        <el-button v-if="taggingState === 2 || taggingState === 3" class="cancel-btn" @click="handleCancel">取消</el-button>
+        <el-button 
+          type="primary" 
+          class="confirm-btn" 
+          :disabled="selectedTags.length === 0"
+          @click="handleConfirmTagging"
+        >确认打标</el-button>
+      </div>
+    </div>
+    
+    <!-- 已选标签 -->
+    <div class="selected-tags">
+      <div class="tags-label">已选标签:</div>
+      <div class="tags-list">
+        <el-tag
+          v-for="(tag, index) in selectedTags"
+          :key="index"
+          :type="getTagType(index)"
+          class="selected-tag"
+          closable
+          @close="handleTagRemove(index)"
+        >
+          {{ tag.tag_name }}
+        </el-tag>
+      </div>
+      
+    </div>
+    
+    <!-- 主体内容 -->
+    <div class="main-content">
+      <!-- 左侧标签体系导航 -->
+      <div class="tag-system-nav">
+        <h3 class="nav-title">标签体系</h3>
+        <div class="nav-list">
+          <div
+            v-for="(system, index) in tagSystems"
+            :key="index"
+            class="nav-item"
+            :class="{ 'active': currentSystem === system }"
+            @click="handleSystemSelect(system)"
+          >
+            {{ system }}
+          </div>
+        </div>
+      </div>
+      
+      <!-- 右侧内容区域 -->
+      <div class="content-area">
+        <!-- 智能推荐组件 -->
+        <IntelligentRecommend
+          v-if="currentMode === 'intelligent'"
+          :recommendations="tagRecommendations"
+          :current-system="currentSystem"
+          @tagSelect="handleTagSelect"
+          @switchToManual="switchToManual"
+          @retryIntelligentTagging="retryIntelligentTagging"
+        />
+        
+        <!-- 人工打标组件 -->
+        <ManualTagging
+          v-else
+          :current-system="currentSystem"
+          :tag-systems-data="tagSystemsData"
+          :selected-tags="selectedTags"
+          @search="handleSearch"
+          @tagCheck="handleTagCheck"
+          @switchToIntelligent="switchToIntelligent"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import IntelligentRecommend from './components/IntelligentRecommend.vue';
+import ManualTagging from './components/ManualTagging.vue';
+
+export default {
+  name: 'ExternalEdit',
+  components: {
+    IntelligentRecommend,
+    ManualTagging
+  },
+  props: {
+    businessAttr: {
+      type: String,
+      default: ''
+    },
+    queryResult: {
+      type: Object,
+      default: null
+    }
+  },
+  data() {
+    return {
+      testData:{
+        "user_id": "12345", 
+        "user_nm": "张三",
+        "contract_no": "TET-2026-03-081613", 
+        "user_org": "XX公司", 
+        "user_endpoint": "XX网点", 
+      },
+      // 当前模式:intelligent(智能推荐)或 manual(人工打标)
+      currentMode: 'intelligent',
+      
+      // 当前选中的标签体系
+      currentSystem: '',
+      
+      // 标签体系列表
+      tagSystems: [],
+      
+      // 完整的标签体系数据
+      tagSystemsData: [],
+      
+      // 已选标签(包含标签体系id、标签名称、标签编码)
+      selectedTags: [],
+      
+      // 标签推荐结果
+      tagRecommendations: [],
+      
+      // 原始推荐数据(用于过滤)
+      originalRecommendations: [],
+      
+      // 原始接口返回数据(用于对比判断是否有修改)
+      originalResponseData: null,
+      
+      // 打标状态:0-打标执行中;1-打标完成;2-客户经理已经确认;3-结果已推送
+      taggingState: null
+    }
+  },
+  mounted() {
+    // 加载标签体系列表
+    this.loadTagSystems();
+  },
+  watch: {
+    queryResult: {
+      handler(newVal) {
+        if (newVal) {
+          this.setRecommendations(newVal);
+        }
+      },
+      immediate: true
+    }
+  },
+  computed: {
+    // 检查是否有选中的标签
+    hasSelectedTag() {
+      return this.selectedTags.length > 0;
+    }
+  },
+  methods: {
+    // 关闭窗口
+    handleClose() {
+      this.$emit('close');
+    },
+    
+    // 取消
+    handleCancel() {
+      this.$emit('close');
+    },
+    
+    // 确认打标
+    handleConfirmTagging() {
+      // 检查是否有智能推荐数据或已选标签
+      const hasRecommendations = this.tagRecommendations.length > 0 || this.originalRecommendations.length > 0;
+      if (!this.hasSelectedTag && !hasRecommendations) {
+        this.$message.warning('请至少选择一个标签或等待智能推荐结果');
+        return;
+      }
+      
+      // 组装打标数据
+      const taggingData = this.assembleTaggingData();
+      console.log('确认打标数据:', taggingData);
+      
+      // 触发确认事件,传递打标数据给父组件
+      this.$emit('confirm', taggingData);
+      
+      // 调用反馈接口
+      yufp.service.request({
+        url: backend.tagServer + "/api/fastapi/feedback",
+        method: 'post',
+        data: taggingData,
+        callback: (code, error, response) => {
+          if (response.code == '0') {
+            this.$message.success('打标成功');
+            // 调用父组件的refresh方法重置页面状态
+            this.$emit('refresh');
+          } else {
+            this.$message.error(response.message || '打标失败');
+          }
+        }
+      });
+    },
+    
+    // 组装打标数据
+    assembleTaggingData() {
+      // 从testData中获取基础信息
+      const { user_id, user_nm, contract_no, user_org, user_endpoint } = this.testData;
+      
+      // 获取business_attr
+      const business_attr = this.businessAttr;
+      
+      // 计算feedback字段
+      const feedback = this.calculateFeedback();
+      
+      // 组装feedback_result
+      const feedback_result = this.assembleFeedbackResult();
+      
+      // 构建最终数据
+      return {
+        user_id: user_id || '',
+        user_nm: user_nm || '',
+        feedback: feedback,
+        feedback_result: JSON.stringify(feedback_result),
+        contract_no: contract_no || '',
+        user_org: user_org || '',
+        user_endpoint: user_endpoint || '',
+        business_attr: business_attr || ''
+      };
+    },
+    
+    // 计算feedback字段
+    calculateFeedback() {
+      // 检查是否有初始数据
+      if (!this.originalResponseData || !this.originalResponseData.data || !this.originalResponseData.data.result) {
+        return 'reject';
+      }
+      
+      // 获取初始数据中passr=true的标签
+      const initialSelectedTags = this.originalResponseData.data.result
+        .filter(item => item.passr === true)
+        .map(item => item.tag_code);
+      
+      // 获取当前已选标签
+      const currentSelectedTags = this.selectedTags.map(tag => tag.tag_code);
+      
+      // 排序后比较
+      initialSelectedTags.sort();
+      currentSelectedTags.sort();
+      
+      // 检查长度是否一致
+      if (initialSelectedTags.length !== currentSelectedTags.length) {
+        return 'reject';
+      }
+      
+      // 检查标签是否完全一致
+      for (let i = 0; i < initialSelectedTags.length; i++) {
+        if (initialSelectedTags[i] !== currentSelectedTags[i]) {
+          return 'reject';
+        }
+      }
+      
+      return 'agree';
+    },
+    
+    // 组装feedback_result
+    assembleFeedbackResult() {
+      // 获取所有展示的标签数据(智能推荐或人工打标)
+      let allDisplayedTags = [];
+      
+      // 从智能推荐中获取标签
+      if (this.currentMode === 'intelligent' && this.tagRecommendations.length > 0) {
+        allDisplayedTags = this.tagRecommendations;
+      } else if (this.currentMode === 'manual' && this.originalRecommendations.length > 0) {
+        allDisplayedTags = this.originalRecommendations;
+      }
+      
+      // 创建已选标签的集合,用于快速查找
+      const selectedTagCodes = new Set(this.selectedTags.map(tag => tag.tag_code));
+      const selectedTagsMap = new Map(this.selectedTags.map(tag => [tag.tag_code, tag]));
+      
+      // 组装feedback_result
+      const feedbackResult = [];
+      
+      // 处理所有展示的标签
+      allDisplayedTags.forEach(tag => {
+        const isSelected = selectedTagCodes.has(tag.tag_code);
+        feedbackResult.push({
+          id: tag.id || '',
+          desc: isSelected ? '' : (tag.feedback || ''),
+          passr: isSelected,
+          tag_code: tag.tag_code || '',
+          tag_name: tag.tag_name || '',
+          tag_path: tag.tag_path || '',
+          category_id: tag.category_id || ''
+        });
+        // 从已选标签集合中移除已处理的标签
+        selectedTagCodes.delete(tag.tag_code);
+      });
+      
+      // 处理已选标签中存在但展示标签中不存在的标签
+      selectedTagCodes.forEach(tagCode => {
+        const tag = selectedTagsMap.get(tagCode);
+        if (tag) {
+          feedbackResult.push({
+            id: tag.id || '',
+            desc: '',
+            passr: true,
+            tag_code: tag.tag_code || '',
+            tag_name: tag.tag_name || '',
+            tag_path: tag.tag_path || '',
+            category_id: tag.category_id || ''
+          });
+        }
+      });
+      
+      return feedbackResult;
+    },
+    
+    // 选择标签
+    handleTagSelect(selectedTags) {
+      // 从选中的标签中提取完整信息,按照要求的数据结构
+      const selectedTagInfos = selectedTags
+        .map(tag => ({
+          id: tag.id || '',
+          desc: tag.feedback || '',
+          passr: tag.selected || tag.passr || false,
+          tag_code: tag.tag_code || '',
+          tag_name: tag.tag_name || '',
+          tag_path: tag.tag_path || '',
+          category_id: tag.category_id || ''
+        }));
+      
+      // 更新已选标签
+      this.selectedTags = selectedTagInfos;
+    },
+    
+    // 切换到人工打标
+    switchToManual() {
+      this.currentMode = 'manual';
+      // 切换后,手动触发一次标签树数据加载,确保数据正确
+      // 注意:实际加载逻辑在ManualTagging组件的watch中处理
+    },
+    
+    // 切换到智能推荐
+    switchToIntelligent() {
+      this.currentMode = 'intelligent';
+      // 更新智能推荐的选中状态
+      this.updateIntelligentSelectedState();
+    },
+    
+    // 更新智能推荐的选中状态
+    updateIntelligentSelectedState() {
+      if (!this.tagRecommendations.length) {
+        return;
+      }
+      
+      // 遍历推荐标签,检查是否在已选标签列表中
+      this.tagRecommendations.forEach(tag => {
+        // 检查是否在已选标签列表中(通过标签编码匹配)
+        tag.selected = this.selectedTags.some(selectedTag => 
+          selectedTag.tag_code === tag.tag_code
+        );
+      });
+    },
+    
+    // 重新发起智能打标
+    retryIntelligentTagging() {
+      this.$message.info('重新发起智能打标');
+      // 这里可以添加重新发起智能打标的逻辑
+    },
+    
+    // 选择标签体系
+    handleSystemSelect(system) {
+      // 如果点击的是当前已选中的体系,则取消选中
+      if (this.currentSystem === system) {
+        this.currentSystem = '';
+      } else {
+        this.currentSystem = system;
+      }
+      // 过滤智能推荐数据,只展示当前体系下的推荐标签
+      this.filterRecommendationsBySystem();
+    },
+    
+    // 根据当前选中的标签体系过滤智能推荐数据
+    filterRecommendationsBySystem() {
+      // 保存原始推荐数据
+      if (!this.originalRecommendations) {
+        this.originalRecommendations = [...this.tagRecommendations];
+      }
+      
+      // 查找当前选中的标签体系对应的id
+      const selectedSystem = this.tagSystemsData.find(system => system.categoryNm === this.currentSystem);
+      
+      // 如果没有选中标签体系,展示全部数据
+      if (!selectedSystem || !selectedSystem.id) {
+        this.tagRecommendations = [...this.originalRecommendations];
+        return;
+      }
+      
+      // 过滤推荐数据,只保留与当前体系id匹配的推荐标签
+      this.tagRecommendations = this.originalRecommendations.filter(tag => {
+        return tag.category_id == selectedSystem.id;
+      });
+    },
+    
+    // 加载标签体系列表
+    loadTagSystems() {
+      yufp.service.request({
+        url: backend.tagServer + "/api/aitag-tagcategory/enablelistnoauth",
+        method: 'get',
+        data: {},
+        callback: (code, error, response) => {
+          if (response.code == '0') {
+            // 处理返回的标签体系数据
+            const systems = response.data || [];
+            // 保存完整的标签体系数据
+            this.tagSystemsData = systems;
+            // 更新标签体系列表,使用categoryNm字段
+            this.tagSystems = systems.map(system => system.categoryNm || '').filter(name => name);
+            
+            // 默认选中第一个标签体系
+            if (this.tagSystems.length > 0) {
+              this.currentSystem = this.tagSystems[0];
+            }
+          } else {
+            this.$message.error(response.message || '获取标签体系列表失败');
+          }
+        }
+      });
+    },
+    
+    // 处理搜索
+    handleSearch(keyword) {
+      console.log('搜索标签:', keyword);
+      // 这里可以添加搜索标签的逻辑
+    },
+    
+    // 处理标签勾选
+    handleTagCheck(data, checkedInfo) {
+      console.log('勾选标签:', data, checkedInfo);
+      
+      // 查找当前选中的标签体系对应的id
+      const selectedSystem = this.tagSystemsData.find(system => system.categoryNm === this.currentSystem);
+      if (!selectedSystem || !selectedSystem.id) {
+        return;
+      }
+      
+      // 过滤掉当前体系的标签,保留其他体系的标签
+      const otherSystemTags = this.selectedTags.filter(tag => tag.category_id !== selectedSystem.id);
+      
+      // 添加当前选择的标签,按照要求的数据结构
+      const newSelectedTags = [...otherSystemTags, {
+        id: data.id || '',
+        desc: '',
+        passr: true,
+        tag_code: data.tagCode || '',
+        tag_name: data.tagNm || '',
+        tag_path: data.tagPath || '',
+        category_id: selectedSystem.id
+      }];
+      
+      // 更新已选标签
+      this.selectedTags = newSelectedTags;
+      
+      // 更新智能推荐的选中状态
+      this.updateIntelligentSelectedState();
+    },
+    
+    // 删除已选标签
+    handleTagRemove(index) {
+      // 从已选标签列表中移除指定索引的标签
+      this.selectedTags.splice(index, 1);
+      
+      // 更新主体内容的选中状态
+      this.updateIntelligentSelectedState();
+    },
+    
+    // 获取标签类型
+    getTagType(index) {
+      const types = ['', 'success', 'warning', 'danger', 'info'];
+      return types[index % types.length];
+    },
+    
+    // 处理打标执行中状态
+    handleTaggingInProgress() {
+      // 清空推荐数据
+      this.originalRecommendations = [];
+      this.tagRecommendations = [];
+    },
+    
+    // 处理打标完成状态
+    handleTaggingCompleted(responseData) {
+      if (responseData.result) {
+        const result = responseData.result;
+        // 更新原始推荐数据,使用passr字段设置选中状态
+        this.originalRecommendations = result.map(item => ({
+          ...item,
+          feedback: '',
+          selected: item.passr === true
+        }));
+        
+        // 根据passr字段更新已选标签,使用新的数据结构
+        const selectedTags = result
+          .filter(item => item.passr === true)
+          .map(item => ({
+            id: item.id || '',
+            desc: '',
+            passr: true,
+            tag_code: item.tag_code || '',
+            tag_name: item.tag_name || '',
+            tag_path: item.tag_path || '',
+            category_id: item.category_id || ''
+          }));
+        this.selectedTags = selectedTags;
+        
+        // 切换到智能推荐模式
+        this.currentMode = 'intelligent';
+        // 根据当前选中的标签体系过滤推荐数据
+        this.filterRecommendationsBySystem();
+      }
+    },
+    
+    // 处理已确认或已推送状态
+    handleTaggingConfirmed(responseData) {
+      // 获取feedback_result作为已选择标签数据
+      let feedbackResult = [];
+      if (responseData.feedback_result) {
+        if (typeof responseData.feedback_result === 'string') {
+          try {
+            feedbackResult = JSON.parse(responseData.feedback_result);
+          } catch (error) {
+            console.error('解析feedback_result失败:', error);
+          }
+        } else {
+          feedbackResult = responseData.feedback_result;
+        }
+      }
+      
+      // 设置已选标签,只包含feedback_result中passr为true的数据
+      this.selectedTags = feedbackResult
+        .filter(item => item.passr === true)
+        .map(item => ({
+          id: item.id || '',
+          desc: '',
+          passr: true,
+          tag_code: item.tag_code || '',
+          tag_name: item.tag_name || '',
+          tag_path: item.tag_path || '',
+          category_id: item.category_id || ''
+        }));
+      
+      // 处理智能推荐数据,根据feedback_result匹配passr字段
+      if (responseData.result) {
+        const result = responseData.result;
+        
+        // 创建feedback_result的映射,用于快速查找
+        const feedbackMap = new Map(feedbackResult.map(item => [item.tag_code, item]));
+        
+        // 更新原始推荐数据,根据feedback_result设置passr、selected和feedback字段
+        this.originalRecommendations = result.map(item => {
+          const feedbackItem = feedbackMap.get(item.tag_code);
+          const isInFeedback = feedbackItem !== undefined;
+          const isPassrTrue = isInFeedback && feedbackItem.passr === true;
+          
+          return {
+            ...item,
+            passr: isPassrTrue,
+            selected: isPassrTrue,
+            feedback: isPassrTrue ? '' : (feedbackItem ? feedbackItem.desc : '')
+          };
+        });
+        
+        // 切换到智能推荐模式
+        this.currentMode = 'intelligent';
+        // 根据当前选中的标签体系过滤推荐数据
+        this.filterRecommendationsBySystem();
+      }
+    },
+    
+    // 刷新打标结果
+    refreshTaggingResult() {
+      this.$emit('refresh');
+    },
+    
+    // 设置推荐数据
+    setRecommendations(data) {
+      this.originalResponseData = data;
+      
+      if (data && data.data) {
+        const responseData = data.data;
+        
+        // 获取state状态
+        this.taggingState = responseData.state;
+        
+        // 根据state状态处理不同的逻辑
+        switch(this.taggingState) {
+          case 0:
+            // 打标执行中,显示loading效果
+            this.handleTaggingInProgress();
+            break;
+          case 1:
+            // 打标完成,正常处理推荐数据
+            this.handleTaggingCompleted(responseData);
+            break;
+          case 2:
+          case 3:
+            // 客户经理已经确认或结果已推送,处理feedback_result
+            this.handleTaggingConfirmed(responseData);
+            break;
+          default:
+            // 默认处理
+            this.handleTaggingCompleted(responseData);
+        }
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.intelligent-tagging {
+  padding: 0;
+  background-color: transparent !important;
+  height: 100% !important;
+  box-shadow: none !important;
+}
+
+/* 顶部通知栏 */
+.top-notification {
+  display: flex;
+  align-items: center;
+  padding: 12px 20px;
+  background-color: #FFF9E6;
+  border-bottom: 1px solid #FFE7BA;
+}
+
+.top-notification i {
+  color: #E6A23C;
+  margin-right: 12px;
+  font-size: 16px;
+}
+
+.top-notification span {
+  flex: 1;
+  font-size: 14px;
+  color: #606266;
+  line-height: 1.5;
+}
+
+.button-group {
+  display: flex;
+  gap: 10px;
+}
+
+.cancel-btn {
+  margin-right: 10px;
+}
+
+.confirm-btn {
+  margin-left: 0;
+}
+
+/* 已选标签 */
+.selected-tags {
+  display: flex;
+  align-items: center;
+  padding: 16px 20px;
+  background-color: #F9FAFC;
+  border-bottom: 1px solid #EBEEF5;
+}
+
+.tags-label {
+  font-size: 14px;
+  color: #606266;
+  margin-right: 12px;
+  white-space: nowrap;
+}
+
+.tags-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+
+.selected-tag {
+  margin-right: 8px;
+}
+
+/* 主体内容 */
+.main-content {
+  display: flex;
+  min-height: 600px;
+}
+
+/* 左侧标签体系导航 */
+.tag-system-nav {
+  width: 180px;
+  background-color: #FFFFFF;
+  border-right: 1px solid #EBEEF5;
+  padding: 20px;
+}
+
+.nav-title {
+  font-size: 14px;
+  font-weight: 500;
+  color: #303133;
+  margin: 0 0 16px 0;
+}
+
+.nav-list {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+
+.nav-item {
+  padding: 8px 12px;
+  margin-bottom: 8px;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 14px;
+  color: #606266;
+  transition: all 0.3s ease;
+}
+
+.nav-item:hover {
+  background-color: #ECF5FF;
+  color: #409EFF;
+}
+
+.nav-item.active {
+  background-color: #ECF5FF;
+  color: #409EFF;
+  font-weight: 500;
+}
+
+/* 右侧内容区域 */
+.content-area {
+  flex: 1;
+  background-color: #FFFFFF;
+  padding: 0;
+}
+
+/* 响应式布局 */
+@media screen and (max-width: 768px) {
+  .main-content {
+    flex-direction: column;
+  }
+  
+  .tag-system-nav {
+    width: 100%;
+    border-right: none;
+    border-bottom: 1px solid #EBEEF5;
+  }
+  
+  .nav-list {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px;
+  }
+  
+  .nav-item {
+    margin-bottom: 0;
+  }
+  
+  .top-notification {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 12px;
+  }
+  
+  .button-group {
+    align-self: flex-end;
+  }
+  
+  .cancel-btn {
+    margin-right: 10px;
+  }
+  
+  .confirm-btn {
+    margin-left: 0;
+  }
+  
+  .selected-tags {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 8px;
+  }
+}
+</style>

+ 301 - 0
web/src/views/aiTagging/externalPage/externalResult.vue

@@ -0,0 +1,301 @@
+<template>
+  <div class="tag-result">
+    <!-- 顶部通知栏 -->
+    <div class="top-notification">
+      <i class="el-icon-success"></i>
+      <span>标签已确认,您可以点击"编辑标签"按钮进行标签结果修改</span>
+      <el-button type="primary" class="edit-btn" @click="handleEditTags">编辑标签</el-button>
+    </div>
+    
+    <!-- 标签详情列表 -->
+    <div class="tag-detail-list">
+      <div class="list-header">
+        <h3 class="list-title">标签详情</h3>
+      </div>
+      <div 
+        v-for="(tag, index) in tagResults" 
+        :key="index"
+        class="tag-detail-item"
+      >
+        <!-- 标签路径 -->
+        <div class="tag-path">
+          <span>{{ tag.tag_path }}</span>
+        </div>
+        
+        <!-- 打标方式和打标依据 -->
+        <div class="tag-info">
+          <div class="tag-method">
+            <span class="info-label">打标方式:</span>
+            <span class="info-content">{{ tag.tag_method }}</span>
+          </div>
+          <div class="tag-basis">
+            <span class="info-label">打标依据:</span>
+            <span class="info-content">{{ tag.tag_basis || '-' }}</span>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'ExternalResult',
+  props: {
+    rawData: {
+      type: Object,
+      default: null
+    }
+  },
+  data() {
+    return {
+      tagResults: []
+    }
+  },
+  watch: {
+    rawData: {
+      handler(newVal) {
+        console.log('接收到的原始数据:', newVal);
+        
+        // 处理原始数据
+        this.processRawData(newVal);
+      },
+      deep: true,
+      immediate: true
+    }
+  },
+  methods: {
+    // 处理原始数据
+    processRawData(data) {
+      console.log('处理原始数据:', data);
+      
+      // 检查data是否存在
+      if (!data) {
+        console.error('数据为空:', data);
+        this.tagResults = [];
+        return;
+      }
+      
+      // 从data中获取业务数据
+      const businessData = data.data || data;
+      
+      // 从feedback_result或result中获取标签数据
+      let feedbackResult = [];
+      let resultData = [];
+      
+      // 检查businessData是否有feedback_result字段
+      if (businessData.feedback_result) {
+        // 如果feedback_result是字符串,尝试解析为JSON
+        if (typeof businessData.feedback_result === 'string') {
+          try {
+            feedbackResult = JSON.parse(businessData.feedback_result);
+          } catch (error) {
+            console.error('解析feedback_result失败:', error);
+            feedbackResult = [];
+          }
+        } else {
+          // 如果feedback_result已经是数组或对象
+          feedbackResult = businessData.feedback_result;
+        }
+      }
+      
+      // 检查businessData是否有result字段
+      if (businessData.result) {
+        // 如果result是字符串,尝试解析为JSON
+        if (typeof businessData.result === 'string') {
+          try {
+            resultData = JSON.parse(businessData.result);
+          } catch (error) {
+            console.error('解析result失败:', error);
+            resultData = [];
+          }
+        } else {
+          // 如果result已经是数组或对象
+          resultData = businessData.result;
+        }
+      }
+      
+      // 检查feedbackResult和resultData是否为数组
+      if (!Array.isArray(feedbackResult)) {
+        console.error('feedbackResult不是数组:', feedbackResult);
+        this.tagResults = [];
+        return;
+      }
+      
+      if (!Array.isArray(resultData)) {
+        console.error('resultData不是数组:', resultData);
+        this.tagResults = [];
+        return;
+      }
+      
+      // 过滤出passr为true的标签
+      const filteredFeedback = feedbackResult.filter(item => item.passr === true);
+      
+      // 构建标签结果数据
+      this.tagResults = filteredFeedback.map(item => {
+        // 在resultData中查找对应的标签
+        const matchedResult = resultData.find(resultItem => resultItem.tag_path === item.tag_path);
+        
+        // 确定打标方式和打标依据
+        let tagMethod = '';
+        let tagBasis = '';
+        
+        if (matchedResult) {
+          // 如果找到匹配的标签,打标方式为智能打标,打标依据为matchedResult.desc
+          tagMethod = '智能打标';
+          tagBasis = matchedResult.desc || '';
+        } else {
+          // 如果没有找到匹配的标签,打标方式为人工打标,打标依据为“-”
+          tagMethod = '人工打标';
+          tagBasis = '-';
+        }
+        
+        return {
+          tag_path: item.tag_path,
+          tag_method: tagMethod,
+          tag_basis: tagBasis
+        };
+      });
+      
+      console.log('构建后的标签数据:', this.tagResults);
+    },
+    
+    // 编辑标签
+    handleEditTags() {
+      this.$emit('edit');
+    }
+  }
+}
+</script>
+
+<style scoped>
+.tag-result {
+  padding: 0;
+  background-color: transparent !important;
+  height: 100% !important;
+  box-shadow: none !important;
+}
+
+/* 顶部通知栏 */
+.top-notification {
+  display: flex;
+  align-items: center;
+  padding: 12px 20px;
+  background-color: #F0F9EB;
+  border-bottom: 1px solid #C2E7B0;
+}
+
+.top-notification i {
+  color: #67C23A;
+  margin-right: 12px;
+  font-size: 16px;
+}
+
+.top-notification span {
+  flex: 1;
+  font-size: 14px;
+  color: #606266;
+  line-height: 1.5;
+}
+
+.edit-btn {
+  margin-left: 20px;
+}
+
+/* 标签详情列表 */
+.tag-detail-list {
+  margin-top: 20px;
+}
+
+/* 列表头部 */
+.list-header {
+  margin-bottom: 16px;
+}
+
+/* 列表标题 */
+.list-title {
+  font-size: 18px;
+  font-weight: 600;
+  color: #303133;
+  margin: 0;
+  line-height: 1.4;
+}
+
+/* 标签详情项 */
+.tag-detail-item {
+  padding: 20px;
+  border: 1px solid #E4E7ED;
+  border-radius: 8px;
+  margin-bottom: 20px;
+  background-color: #FFFFFF;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+}
+
+/* 标签路径 */
+.tag-path {
+  font-size: 16px;
+  font-weight: 600;
+  color: #303133;
+  margin-bottom: 16px;
+  line-height: 1.4;
+}
+
+/* 标签信息 */
+.tag-info {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+/* 信息标签 */
+.info-label {
+  font-size: 14px;
+  color: #606266;
+  margin-right: 12px;
+  font-weight: 500;
+}
+
+/* 信息内容 */
+.info-content {
+  font-size: 14px;
+  color: #303133;
+  line-height: 1.6;
+  word-break: break-word;
+}
+
+/* 打标方式 */
+.tag-method {
+  display: flex;
+  align-items: flex-start;
+}
+
+/* 打标依据 */
+.tag-basis {
+  display: flex;
+  align-items: flex-start;
+}
+
+/* 打标依据内容 */
+.tag-basis .info-content {
+  flex: 1;
+  min-width: 0;
+}
+
+/* 响应式布局 */
+@media screen and (max-width: 768px) {
+  .top-notification {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 12px;
+  }
+  
+  .edit-btn {
+    margin-left: 0;
+    align-self: flex-end;
+  }
+  
+  .tag-info {
+    flex-direction: column;
+  }
+}
+</style>

+ 131 - 426
web/src/views/aiTagging/externalPage/index.vue

@@ -1,15 +1,9 @@
 <template>
   <div class="intelligent-tagging">
-    <!-- 顶部通知栏 -->
-    <div class="top-notification">
-      <i class="el-icon-info"></i>
-      <span>系统已自动生成标签推荐结果,请您勾选确认准确标签,针对不符合标签可提交反馈内容以优化后续推荐服务;若无适用标签,可通过手动选标完成打标操作。</span>
-      <el-button type="primary" class="confirm-btn" @click="handleConfirmTagging" :disabled="!hasSelectedTag">确认打标</el-button>
-    </div>
     <div class="business-attr-input">
         <el-input
           v-model="businessAttr"
-          placeholder="请输入businessAttr"
+          placeholder="请输入"
           style="width: 200px"
           suffix-icon="el-icon-search"
           @keyup.enter="queryBusinessAttr"
@@ -23,335 +17,157 @@
           查询
         </el-button>
       </div>
-    <!-- 已选标签 -->
-    <div class="selected-tags">
-      <div class="tags-label">已选标签:</div>
-      <div class="tags-list">
-        <el-tag
-          v-for="(tag, index) in selectedTags"
-          :key="index"
-          :type="getTagType(index)"
-          class="selected-tag"
-        >
-          {{ tag }}
-        </el-tag>
-      </div>
       
-    </div>
-    
-    <!-- 主体内容 -->
-    <div class="main-content">
-      <!-- 左侧标签体系导航 -->
-      <div class="tag-system-nav">
-        <h3 class="nav-title">标签体系</h3>
-        <div class="nav-list">
-          <div
-            v-for="(system, index) in tagSystems"
-            :key="index"
-            class="nav-item"
-            :class="{ 'active': currentSystem === system }"
-            @click="handleSystemSelect(system)"
-          >
-            {{ system }}
-          </div>
-        </div>
+      <!-- Loading状态 -->
+      <div v-if="isLoading" class="loading-container">
+        <i class="el-icon-loading loading-icon"></i>
+        <div class="loading-text">{{ loadingText }}</div>
+        <el-button 
+          v-if="taggingState === 0" 
+          type="primary" 
+          size="small" 
+          @click="refreshTaggingResult"
+          class="refresh-btn"
+        >
+          <i class="el-icon-refresh"></i>
+          刷新结果
+        </el-button>
       </div>
       
-      <!-- 右侧内容区域 -->
-      <div class="content-area">
-        <!-- 智能推荐组件 -->
-        <IntelligentRecommend
-          v-if="currentMode === 'intelligent'"
-          :recommendations="tagRecommendations"
-          :current-system="currentSystem"
-          @tagSelect="handleTagSelect"
-          @switchToManual="switchToManual"
-          @retryIntelligentTagging="retryIntelligentTagging"
-        />
-        
-        <!-- 人工打标组件 -->
-        <ManualTagging
-          v-else
-          :current-system="currentSystem"
-          :tag-systems-data="tagSystemsData"
-          :selected-tags="selectedTags"
-          @search="handleSearch"
-          @tagCheck="handleTagCheck"
-          @switchToIntelligent="switchToIntelligent"
-        />
-      </div>
-    </div>
+      <!-- 编辑状态 -->
+      <ExternalEdit 
+        v-else-if="currentState === 'edit'"
+        :business-attr="businessAttr"
+        :query-result="queryData"
+        @refresh="queryBusinessAttr"
+        @confirm="handleTaggingConfirm"
+        @close="handleCancelEdit"
+        ref="externalEditRef"
+      />
+      
+      <!-- 结果状态 -->
+      <ExternalResult 
+        v-else
+        :raw-data="queryData"
+        @edit="switchToEdit"
+      />
   </div>
 </template>
 
 <script>
-import IntelligentRecommend from './components/IntelligentRecommend.vue';
-import ManualTagging from './components/ManualTagging.vue';
+import ExternalEdit from './externalEdit.vue';
+import ExternalResult from './externalResult.vue';
 
 export default {
   name: 'IntelligentTagging',
   components: {
-    IntelligentRecommend,
-    ManualTagging
-  },
-  props: {
-    visible: {
-      type: Boolean,
-      default: false
-    },
-    isEditMode: {
-      type: Boolean,
-      default: false
-    },
-    editData: {
-      type: Object,
-      default: () => ({})
-    }
+    ExternalEdit,
+    ExternalResult
   },
   data() {
     return {
-      // 当前模式:intelligent(智能推荐)或 manual(人工打标)
-      currentMode: 'intelligent',
-      
-      // 当前选中的标签体系
-      currentSystem: '',
-      
-      // 标签体系列表
-      tagSystems: [],
-      
-      // 完整的标签体系数据
-      tagSystemsData: [],
-      
-      // 已选标签(包含标签体系id、标签名称、标签编码)
-      selectedTags: [],
-      
-      // 标签推荐结果
-      tagRecommendations: [],
-      
-      // 原始推荐数据(用于过滤)
-      originalRecommendations: [],
-      
       // businessAttr
       businessAttr: '',
-      
-
-    }
-  },
-  mounted() {
-    // 加载标签体系列表
-    this.loadTagSystems();
-  },
-  computed: {
-    // 检查是否有选中的标签
-    hasSelectedTag() {
-      return this.selectedTags.length > 0;
+      // 是否显示loading状态
+      isLoading: true,
+      // loading提示文本
+      loadingText: '打标执行中...',
+      // 打标状态:0-打标执行中;1-打标完成;2-客户经理已经确认;3-结果已推送
+      taggingState: null,
+      // 当前状态:edit(编辑)或 result(结果)
+      currentState: 'edit',
+      // 查询结果数据(统一变量)
+      queryData: null
     }
   },
   methods: {
-    // 关闭窗口
-    handleClose() {
-      this.$emit('close');
-    },
-    
-    // 取消
-    handleCancel() {
-      this.$emit('close');
-    },
-    
-    // 确认打标
-    handleConfirmTagging() {
-      if (!this.hasSelectedTag) {
-        this.$message.warning('请至少选择一个标签');
-        return;
-      }
-      
-      // 获取选中的标签
-      console.log('确认打标:', this.selectedTags);
-      
-      // 这里可以添加打标逻辑
-      this.$message.success('打标成功');
-      this.$emit('close');
-    },
-    
-    // 选择标签
-    handleTagSelect(selectedTags) {
-      // 从选中的标签中提取完整信息(标签体系id、标签名称、标签编码)
-      const selectedTagInfos = selectedTags
-        .map(tag => ({
-          categoryId: tag.category_id,
-          tagName: tag.tag_name,
-          tagCode: tag.tag_code
-        }));
-      
-      // 更新已选标签
-      this.selectedTags = selectedTagInfos;
-    },
-    
-    // 切换到人工打标
-    switchToManual() {
-      this.currentMode = 'manual';
-      // 切换后,手动触发一次标签树数据加载,确保数据正确
-      // 注意:实际加载逻辑在ManualTagging组件的watch中处理
-    },
-    
-    // 切换到智能推荐
-    switchToIntelligent() {
-      this.currentMode = 'intelligent';
-      // 更新智能推荐的选中状态
-      this.updateIntelligentSelectedState();
-    },
-    
-    // 更新智能推荐的选中状态
-    updateIntelligentSelectedState() {
-      if (!this.tagRecommendations.length) {
+    // 查询businessAttr
+    queryBusinessAttr() {
+      if (!this.businessAttr) {
+        this.$message.warning('请输入businessAttr');
         return;
       }
       
-      // 遍历推荐标签,检查是否在已选标签列表中
-      this.tagRecommendations.forEach(tag => {
-        // 检查是否在已选标签列表中(通过标签编码匹配)
-        tag.selected = this.selectedTags.some(selectedTag => 
-          selectedTag.tagCode === tag.tag_code
-        );
-      });
-    },
-    
-    // 重新发起智能打标
-    retryIntelligentTagging() {
-      this.$message.info('重新发起智能打标');
-      // 这里可以添加重新发起智能打标的逻辑
-    },
-    
-    // 选择标签体系
-    handleSystemSelect(system) {
-      this.currentSystem = system;
-      // 过滤智能推荐数据,只展示当前体系下的推荐标签
-      this.filterRecommendationsBySystem();
-    },
-    
-    // 根据当前选中的标签体系过滤智能推荐数据
-    filterRecommendationsBySystem() {
-      // 保存原始推荐数据
-      if (!this.originalRecommendations) {
-        this.originalRecommendations = [...this.tagRecommendations];
-      }
+      // 显示loading状态
+      this.isLoading = true;
+      this.loadingText = '打标执行中...';
       
-      // 查找当前选中的标签体系对应的id
-      const selectedSystem = this.tagSystemsData.find(system => system.categoryNm === this.currentSystem);
-      
-      // 如果没有选中标签体系,展示全部数据
-      if (!selectedSystem || !selectedSystem.id) {
-        this.tagRecommendations = [...this.originalRecommendations];
-        return;
-      }
-      
-      // 过滤推荐数据,只保留与当前体系id匹配的推荐标签
-      this.tagRecommendations = this.originalRecommendations.filter(tag => {
-        return tag.category_id == selectedSystem.id;
-      });
-    },
-    
-    // 加载标签体系列表
-    loadTagSystems() {
       yufp.service.request({
-        url: backend.tagServer + "/api/aitag-tagcategory/enablelistnoauth",
+        url: backend.tagServer + "/api/fastapi/query",
         method: 'get',
-        data: {},
+        data: {
+          businessAttr: this.businessAttr
+        },
         callback: (code, error, response) => {
+          // 关闭loading状态
+          this.isLoading = false;
+          
           if (response.code == '0') {
-            // 处理返回的标签体系数据
-            const systems = response.data || [];
-            // 保存完整的标签体系数据
-            this.tagSystemsData = systems;
-            // 更新标签体系列表,使用categoryNm字段
-            this.tagSystems = systems.map(system => system.categoryNm || '').filter(name => name);
+            console.log('查询结果:', response.data);
             
-            // 不默认选中任何标签体系
-            // if (this.tagSystems.length > 0) {
-            //   this.currentSystem = this.tagSystems[0];
-            // }
+            // 处理返回数据
+            this.handleQueryResult(response.data);
           } else {
-            this.$message.error(response.message || '获取标签体系列表失败');
+            this.$message.error(response.message || '查询失败');
           }
         }
       });
     },
     
-    // 处理搜索
-    handleSearch(keyword) {
-      console.log('搜索标签:', keyword);
-      // 这里可以添加搜索标签的逻辑
+    // 处理查询结果
+    handleQueryResult(data) {
+      // 保存原始数据(统一变量)
+      this.queryData = data;
+      
+      // 获取state状态
+      this.taggingState = data && data.data ? data.data.state : null;
+      
+      // 根据state状态处理不同的逻辑
+      switch(this.taggingState) {
+        case 0:
+          // 打标执行中,显示loading效果
+          this.isLoading = true;
+          this.loadingText = '打标执行中,请稍候...';
+          break;
+        case 1:
+          // 打标完成,正常处理推荐数据
+          this.currentState = 'edit';
+          break;
+        case 2:
+        case 3:
+          // 客户经理已经确认或结果已推送,显示结果状态
+          this.currentState = 'result';
+          break;
+        default:
+          break;
+      }
     },
     
-    // 处理标签勾选
-    handleTagCheck(data, checkedInfo) {
-      console.log('勾选标签:', data, checkedInfo);
+    // 处理打标确认
+    handleTaggingConfirm(data) {
+      console.log('打标确认数据:', data);
       
-      // 查找当前选中的标签体系对应的id
-      const selectedSystem = this.tagSystemsData.find(system => system.categoryNm === this.currentSystem);
-      if (!selectedSystem || !selectedSystem.id) {
-        return;
+      // 刷新数据状态
+      this.queryBusinessAttr();
+    },
+    
+    // 处理取消编辑
+    handleCancelEdit() {
+      // 如果taggingState是2或3,返回到结果状态
+      if (this.taggingState === 2 || this.taggingState === 3) {
+        this.currentState = 'result';
       }
-      
-      // 过滤掉当前体系的标签,保留其他体系的标签
-      const otherSystemTags = this.selectedTags.filter(tag => tag.categoryId !== selectedSystem.id);
-      
-      // 添加当前选择的标签(包含标签体系id、标签名称、标签编码)
-      const newSelectedTags = [...otherSystemTags, {
-        categoryId: selectedSystem.id,
-        tagName: data.tagNm,
-        tagCode: data.tagCode
-      }];
-      
-      // 更新已选标签
-      this.selectedTags = newSelectedTags;
-      
-      // 更新智能推荐的选中状态
-      this.updateIntelligentSelectedState();
+      // 否则保持在编辑状态
     },
     
-    // 获取标签类型
-    getTagType(index) {
-      const types = ['', 'success', 'warning', 'danger', 'info'];
-      return types[index % types.length];
+    // 切换到编辑状态
+    switchToEdit() {
+      this.currentState = 'edit';
     },
     
-    // 查询businessAttr
-    queryBusinessAttr() {
-      if (!this.businessAttr) {
-        this.$message.warning('请输入businessAttr');
-        return;
-      }
-      
-      yufp.service.request({
-        url: backend.tagServer + "/api/fastapi/query",
-        method: 'get',
-        data: {
-          businessAttr: this.businessAttr
-        },
-        callback: (code, error, response) => {
-          if (response.code == '0') {
-            console.log('查询结果:', response.data);
-            // 处理返回的数据,直接使用接口返回的字段
-            if (response.data && response.data.data && response.data.data.result) {
-              const result = response.data.data.result;
-              // 更新原始推荐数据
-              this.originalRecommendations = result.map(item => ({
-                ...item,
-                feedback: '',
-                selected: false
-              }));
-              // 切换到智能推荐模式
-              this.currentMode = 'intelligent';
-              // 根据当前选中的标签体系过滤推荐数据
-              this.filterRecommendationsBySystem();
-            }
-            this.$message.success('查询成功');
-          } else {
-            this.$message.error(response.message || '查询失败');
-          }
-        }
-      });
+    // 刷新打标结果
+    refreshTaggingResult() {
+      this.queryBusinessAttr();
     }
   }
 }
@@ -365,157 +181,46 @@ export default {
   box-shadow: none !important;
 }
 
-/* 顶部通知栏 */
-.top-notification {
-  display: flex;
-  align-items: center;
-  padding: 12px 20px;
-  background-color: #FFF9E6;
-  border-bottom: 1px solid #FFE7BA;
-}
-
-.top-notification i {
-  color: #E6A23C;
-  margin-right: 12px;
-  font-size: 16px;
-}
-
-.top-notification span {
-  flex: 1;
-  font-size: 14px;
-  color: #606266;
-  line-height: 1.5;
-}
-
-.confirm-btn {
-  margin-left: 20px;
-}
-
-/* 已选标签 */
-.selected-tags {
+.business-attr-input {
+  margin-left: auto;
   display: flex;
   align-items: center;
+  justify-content: flex-end;
   padding: 16px 20px;
   background-color: #F9FAFC;
   border-bottom: 1px solid #EBEEF5;
 }
 
-.business-attr-input {
-  margin-left: auto;
+/* Loading状态容器 */
+.loading-container {
   display: flex;
+  flex-direction: column;
   align-items: center;
-  justify-content: flex-end;
-}
-
-.tags-label {
-  font-size: 14px;
-  color: #606266;
-  margin-right: 12px;
-  white-space: nowrap;
-}
-
-.tags-list {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 8px;
-}
-
-.selected-tag {
-  margin-right: 8px;
-}
-
-/* 主体内容 */
-.main-content {
-  display: flex;
-  min-height: 600px;
-}
-
-/* 左侧标签体系导航 */
-.tag-system-nav {
-  width: 180px;
-  background-color: #FFFFFF;
-  border-right: 1px solid #EBEEF5;
-  padding: 20px;
+  justify-content: center;
+  min-height: 400px;
+  padding: 40px;
 }
 
-.nav-title {
-  font-size: 14px;
-  font-weight: 500;
-  color: #303133;
-  margin: 0 0 16px 0;
-}
-
-.nav-list {
-  list-style: none;
-  padding: 0;
-  margin: 0;
-}
-
-.nav-item {
-  padding: 8px 12px;
-  margin-bottom: 8px;
-  border-radius: 4px;
-  cursor: pointer;
-  font-size: 14px;
-  color: #606266;
-  transition: all 0.3s ease;
-}
-
-.nav-item:hover {
-  background-color: #ECF5FF;
+.loading-icon {
+  font-size: 48px;
   color: #409EFF;
+  margin-bottom: 20px;
 }
 
-.nav-item.active {
-  background-color: #ECF5FF;
-  color: #409EFF;
-  font-weight: 500;
+.loading-text {
+  font-size: 16px;
+  color: #606266;
+  margin-bottom: 20px;
 }
 
-/* 右侧内容区域 */
-.content-area {
-  flex: 1;
-  background-color: #FFFFFF;
-  padding: 0;
+.refresh-btn {
+  margin-top: 10px;
 }
 
 /* 响应式布局 */
 @media screen and (max-width: 768px) {
-  .main-content {
-    flex-direction: column;
-  }
-  
-  .tag-system-nav {
-    width: 100%;
-    border-right: none;
-    border-bottom: 1px solid #EBEEF5;
-  }
-  
-  .nav-list {
-    display: flex;
-    flex-wrap: wrap;
-    gap: 8px;
-  }
-  
-  .nav-item {
-    margin-bottom: 0;
-  }
-  
-  .top-notification {
-    flex-direction: column;
-    align-items: flex-start;
-    gap: 12px;
-  }
-  
-  .confirm-btn {
-    margin-left: 0;
-    align-self: flex-end;
-  }
-  
-  .selected-tags {
-    flex-direction: column;
-    align-items: flex-start;
-    gap: 8px;
+  .business-attr-input {
+    justify-content: flex-start;
   }
 }
-</style>
+</style>

+ 165 - 51
web/src/views/aiTagging/taggingDetail/index.vue

@@ -33,27 +33,48 @@
       </div>
     </div>
 
+    <!-- 人工打标结果 -->
+    <div v-if="manualResults.length > 0" class="section">
+      <h2 class="section-title">人工打标结果</h2>
+      
+      <!-- 人工打标列表 -->
+      <div class="tagging-results">
+        <div v-for="(result, index) in manualResults" :key="'manual-' + index" class="tagging-item">
+          <div class="tagging-header">
+            <div class="tag-path">{{ result.tagPath }}</div>
+          </div>
+          
+          <div class="feedback-info-simple">
+            <span class="meta-item">客户经理:{{ result.labeler || '-' }}</span>
+            <span class="meta-item">反馈时间:{{ result.feedbackTime || '-' }}</span>
+          </div>
+        </div>
+      </div>
+    </div>
+
     <!-- 智能打标结果 -->
-    <div class="section">
+    <div v-if="intelligentResults.length > 0" class="section">
       <div class="section-header">
         <h2 class="section-title">智能打标结果</h2>
         <div class="tagging-time">打标时间:{{ taggingInfo.taggingTime }}</div>
       </div>
       
       <!-- 反馈信息 -->
-      <div class="feedback-info">
-        <span class="meta-item">法人机构:-</span>
-        <span class="meta-item">网点:-</span>
-        <span class="meta-item">客户经理:-</span>
-        <span class="meta-item">反馈时间:-</span>
-        <el-tag size="small">未反馈</el-tag>
+      <div class="feedback-info" style="justify-content: flex-end;">
+        <span class="meta-item">客户经理:{{ taggingInfo.feedbackUserNm || '-' }}</span>
+        <span class="meta-item">所属行社:{{ taggingInfo.feedbackUserEndpoint || '-' }}</span>
+        <span class="meta-item">所属机构:{{ taggingInfo.feedbackUserOrg || '-' }}</span>
+        <span class="meta-item">反馈时间:{{ taggingInfo.feedbackTime || '-' }}</span>
       </div>
       
       <!-- 打标结果列表 -->
       <div class="tagging-results">
-        <div v-for="(result, index) in taggingInfo.results" :key="index" class="tagging-item">
+        <div v-for="(result, index) in intelligentResults" :key="'intelligent-' + index" class="tagging-item">
           <div class="tagging-header">
             <div class="tag-path">{{ result.tagPath }}</div>
+            <el-tag size="small" :type="result.isCorrect === null ? 'info' : (result.isCorrect ? 'success' : 'danger')" effect="plain">
+              {{ result.isCorrect === null ? '未反馈' : (result.isCorrect ? '准确' : '不准确') }}
+            </el-tag>
           </div>
           
           <div class="tagging-details">
@@ -63,7 +84,7 @@
             </div>
             <div class="detail-item">
               <span class="detail-label">打标结果反馈:</span>
-              <div class="detail-content">暂未反馈</div>
+              <div class="detail-content">{{ result.feedback || '-' }}</div>
             </div>
           </div>
         </div>
@@ -86,8 +107,17 @@ export default {
       },
       taggingInfo: {
         taggingTime: '',
-        results: []
-      }
+        results: [],
+        feedbackUserNm: '',
+        feedbackUserOrg: '',
+        feedbackUserEndpoint: '',
+        feedbackTime: '',
+        feedback: ''
+      },
+      // 智能打标结果
+      intelligentResults: [],
+      // 人工打标结果
+      manualResults: []
     }
   },
   created() {
@@ -96,8 +126,30 @@ export default {
     if (id) {
       this.loadDetailData(id)
     }
+    
+    // 动态设置页面标题和标签页标题:打标详情-businessAttr
+    const businessAttr = this.$route.query.businessAttr || '';
+    this.setPageTitle(businessAttr);
   },
   methods: {
+    // 设置页面标题
+    setPageTitle(id) {
+      let pageTitle = '打标详情';
+      
+      if (id) {
+        pageTitle = `打标详情-${id}`;
+      } 
+      
+      // 更新当前路由的meta.title
+      this.$route.meta.title = pageTitle;
+      
+      // 更新标签页标题 - 只传递必要信息,避免循环引用
+      this.$store.dispatch('tagsView/updateVisitedView', {
+        fullPath: this.$route.fullPath,
+        meta: { ...this.$route.meta }
+      });
+    },
+    
     // 加载详情数据
     loadDetailData(id) {
       // 调用后端接口
@@ -111,10 +163,21 @@ export default {
             // 直接使用接口返回的所有字段
             this.businessInfo = data;
             
+            // 解析反馈结果和result
+            const feedbackResult = this.parseFeedbackResult(data.feedbackResult || '');
+            const result = this.parseResult(data.result || '');
+            
+            // 分类处理:以feedbackResult为主,与result匹配
+            this.processTaggingResults(feedbackResult, result, data);
+            
             // 更新打标信息
             this.taggingInfo = {
               taggingTime: data.insertTime || '',
-              results: this.parseTagResults(data.result || '', data)
+              feedbackUserNm: data.feedbackUserNm || '',
+              feedbackUserOrg: data.feedbackUserOrg || '',
+              feedbackUserEndpoint: data.feedbackUserEndpoint || '',
+              feedbackTime: data.feedbackTime || '',
+              feedback: data.feedback || ''
             };
           } else {
             this.$message.error(response.message || '获取打标详情失败');
@@ -123,23 +186,75 @@ export default {
       });
     },
     
-    // 解析标签结果
-    parseTagResults(resultStr, data) {
+    // 处理打标结果分类
+    processTaggingResults(feedbackResult, result, data) {
+      const intelligentResults = [];
+      const manualResults = [];
+      
+      if (feedbackResult.length > 0) {
+        // 以feedbackResult为主进行遍历
+        feedbackResult.forEach(feedbackItem => {
+          // 在result中查找匹配的标签
+          const matchedResult = result.find(r => r.id === feedbackItem.id);
+          
+          if (matchedResult) {
+            // 匹配上了,放到智能打标结果
+            intelligentResults.push({
+              id: feedbackItem.id || '',
+              tagPath: feedbackItem.tag_path || '',
+              labeler: data.feedbackUserNm || '',
+              tagTime: data.insertTime || '',
+              isCorrect: feedbackItem.passr || false,
+              tagBasis: matchedResult.desc || '', // 从result获取desc作为打标依据
+              feedback: feedbackItem.desc || '' // 从feedbackResult获取desc作为打标结果反馈
+            });
+          } else {
+            // 没匹配上,放到人工打标结果
+            manualResults.push({
+              id: feedbackItem.id || '',
+              tagPath: feedbackItem.tag_path || '',
+              labeler: data.feedbackUserNm || '',
+              feedbackTime: data.feedbackTime || ''
+            });
+          }
+        });
+      } else if (result.length > 0) {
+        // feedbackResult为空,将result放到智能打标结果
+        result.forEach(resultItem => {
+          intelligentResults.push({
+            id: resultItem.id || '',
+            tagPath: resultItem.tag_path || '',
+            labeler: data.feedbackUserNm || '',
+            tagTime: data.insertTime || '',
+            isCorrect: null, // 未反馈状态
+            tagBasis: resultItem.desc || '',
+            feedback: ''
+          });
+        });
+      }
+      
+      this.intelligentResults = intelligentResults;
+      this.manualResults = manualResults;
+    },
+    
+    // 解析result字段
+    parseResult(resultStr) {
       if (!resultStr) return [];
       try {
-        const resultArray = JSON.parse(resultStr);
-        return resultArray.map(item => ({
-          tagPath: item.tag_path || '',
-          labeler: data.feedbackUserNm || '',
-          tagTime: data.insertTime || '',
-          isCorrect: item.passr || false,
-          tagMethod: '', // 接口可能没有此字段
-          agent: '', // 接口可能没有此字段
-          tagBasis: item.desc || '',
-          validationResult: item.passr ? '打标正确' : '打标不准确'
-        }));
+        return JSON.parse(resultStr);
+      } catch (error) {
+        console.error('解析result失败:', error);
+        return [];
+      }
+    },
+    
+    // 解析反馈结果
+    parseFeedbackResult(feedbackResultStr) {
+      if (!feedbackResultStr) return [];
+      try {
+        return JSON.parse(feedbackResultStr);
       } catch (error) {
-        console.error('解析标签结果失败:', error);
+        console.error('解析反馈结果失败:', error);
         return [];
       }
     },
@@ -252,6 +367,18 @@ export default {
   color: #606266;
 }
 
+/* 简化版反馈信息(用于人工打标结果) */
+.feedback-info-simple {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+  padding: 12px 0;
+  font-size: 14px;
+  color: #606266;
+  border-top: 1px solid #e4e7ed;
+  margin-top: 12px;
+}
+
 /* 信息网格 */
 .info-grid {
   display: grid;
@@ -294,14 +421,14 @@ export default {
 .tagging-results {
   display: flex;
   flex-direction: column;
-  gap: 20px;
+  gap: 16px;
 }
 
 .tagging-item {
   padding: 20px;
-  border: 1px solid #f0f0f0;
+  border: 1px solid #e4e7ed;
   border-radius: 4px;
-  background-color: #f9f9f9;
+  background-color: #fff;
 }
 
 .tagging-header {
@@ -309,26 +436,13 @@ export default {
   justify-content: space-between;
   align-items: center;
   margin-bottom: 16px;
-  padding-bottom: 12px;
-  border-bottom: 1px solid #e4e7ed;
 }
 
 .tag-path {
   font-weight: bold;
   color: #303133;
   flex: 1;
-}
-
-.tag-meta {
-  display: flex;
-  align-items: center;
-  gap: 16px;
-  font-size: 14px;
-  color: #606266;
-}
-
-.meta-item {
-  white-space: nowrap;
+  font-size: 16px;
 }
 
 /* 打标详情 */
@@ -340,26 +454,26 @@ export default {
 
 .detail-item {
   display: flex;
-  align-items: flex-start;
+  flex-direction: column;
+  gap: 8px;
 }
 
 .detail-label {
   font-weight: bold;
-  margin-right: 16px;
-  min-width: 100px;
   color: #606266;
-  flex-shrink: 0;
-  padding-top: 4px;
+  font-size: 14px;
+  margin: 0;
 }
 
 .detail-content {
   flex: 1;
-  padding: 10px;
+  padding: 12px;
   border: 1px solid #dcdfe6;
   border-radius: 4px;
-  background-color: #fff;
+  background-color: #f9f9f9;
   line-height: 1.5;
-  min-height: 80px;
+  font-size: 14px;
+  color: #303133;
 }
 
 /* 响应式设计 */

+ 11 - 1
web/src/views/aiTagging/taggingLogs/index.vue

@@ -3,7 +3,7 @@
     <!-- 顶部搜索栏 -->
     <div class="search-bar">
       <el-form :inline="true" :model="searchForm" label-width="110px">
-        <el-form-item label="调用时间:">
+        <el-form-item label="操作时间:">
           <el-date-picker
             v-model="searchForm.callTime"
             type="daterange"
@@ -26,6 +26,9 @@
     <div class="table-and-pagination-container">
       <!-- 数据表格 -->
       <div class="table-container">
+        <div class="table-header">
+          <el-button type="primary" size="small" icon="el-icon-refresh" @click="handleRefresh">刷新</el-button>
+        </div>
         <el-table :data="logs" style="width: 100%">
           <el-table-column prop="userName" label="用户名称" min-width="120"></el-table-column>
           <el-table-column prop="userId" label="用户id" min-width="180"></el-table-column>
@@ -151,6 +154,8 @@ export default {
     // 刷新日志
     handleRefresh() {
       console.log('刷新日志')
+      this.currentPage = 1;
+      this.pageSize = 10;
       this.loadLogs();
     },
 
@@ -268,6 +273,11 @@ export default {
   padding: 20px 20px 10px;
 }
 
+/* 表格头部 */
+.table-header {
+  margin-bottom: 16px;
+}
+
 /* 分页 */
 .pagination {
   display: flex;

+ 145 - 13
web/src/views/aiTagging/taggingResults/components/Detail.vue

@@ -16,7 +16,7 @@
           <el-button @click="handleDetailReset">重置</el-button>
         </el-form-item>
         <el-form-item>
-          <el-button type="success" icon="el-icon-download" @click="handleExport">导出数据</el-button>
+          <el-button v-if="checkCtrl('downloadFile')" type="success" icon="el-icon-download" @click="handleExport">导出数据</el-button>
         </el-form-item>
       </el-form>
     </div>
@@ -33,10 +33,47 @@
         </el-table-column>
         <el-table-column label="智能标签">
           <template slot-scope="scope">
-            <el-tag v-for="(tag, index) in parseTags(scope.row.result)" :key="index" size="small" class="tag-item">{{ tag.label }}</el-tag>
+            <div class="tags-container">
+              <!-- 状态为0:正在打标 -->
+              <span v-if="scope.row.state === 0" class="tag-pending">正在打标...</span>
+              <!-- 状态为1、2、3:展示标签数据 -->
+              <template v-else-if="scope.row.state === 1 || scope.row.state === 2 || scope.row.state === 3">
+                <template v-if="getTagDataByState(scope.row).displayTags.length > 0">
+                  <el-tag 
+                    v-for="(tag, index) in getTagDataByState(scope.row).displayTags" 
+                    :key="index" 
+                    size="small" 
+                    class="tag-item"
+                    :type="scope.row.state === 2 || scope.row.state === 3 ? 'primary' : 'info'"
+                    :title="tag.label"
+                  >
+                    {{ truncateLabel(tag.label) }}
+                  </el-tag>
+                  <el-tooltip 
+                    v-if="getTagDataByState(scope.row).remainingTags.length > 0" 
+                    effect="dark" 
+                    placement="top"
+                  >
+                    <div slot="content">
+                      <div v-for="(tag, idx) in getTagDataByState(scope.row).remainingTags" :key="idx" class="tooltip-tag">
+                        {{ tag.label }}
+                      </div>
+                    </div>
+                    <span class="more-tags">+{{ getTagDataByState(scope.row).remainingTags.length }}</span>
+                  </el-tooltip>
+                </template>
+                <span v-else class="tag-empty">-</span>
+              </template>
+              <!-- 其他状态或无数据 -->
+              <span v-else class="tag-empty">-</span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="标注人员" width="120">
+          <template slot-scope="scope">
+            {{ scope.row.feedbackUserNm || '智能打标平台' }}
           </template>
         </el-table-column>
-        <el-table-column prop="feedbackUserNm" label="标注人员" width="120"></el-table-column>
         <el-table-column label="操作" width="100">
           <template slot-scope="scope">
             <el-button type="text" @click="handleViewDetail(scope.row)">查看详情</el-button>
@@ -104,6 +141,13 @@ export default {
   },
 
   methods: {
+    // 刷新所有数据(由父组件调用)
+    refreshAllData() {
+      console.log('刷新所有明细数据');
+      this.currentDetailPage = 1;
+      this.loadDetailData();
+    },
+    
     // 加载明细数据
     loadDetailData() {
       // 确保分页参数有默认值
@@ -150,7 +194,7 @@ export default {
       };
       console.log('明细搜索:', searchParams)
       // 重置页码为第一页
-      this.$emit('update:currentDetailPage', 1);
+      this.currentDetailPage = 1;
       // 调用接口获取明细数据
       this.loadDetailData()
     },
@@ -170,7 +214,10 @@ export default {
       // 跳转到详情页面
       this.$router.push({
         path: '/taggingDetail',
-        query: { id: row.id || row.businessAttr }
+        query: { 
+          id: row.id || '', 
+          businessAttr: row.businessAttr || ''
+        }
       })
     },
 
@@ -191,7 +238,7 @@ export default {
       yufp.service.request({
         url: backend.tagServer + "/api/aitagtaglog/exportData",
         method: 'get',
-        params: exportParams,
+        data: exportParams,
         responseType: 'blob',
         callback: (code, error, response) => {
           if (response && response.data) {
@@ -213,8 +260,7 @@ export default {
             document.body.removeChild(a);
             URL.revokeObjectURL(url);
             this.$message.success('数据导出成功');
-            // 触发父组件事件
-            this.$emit('export-data');
+
           } else {
             this.$message.error('数据导出失败');
           }
@@ -239,23 +285,72 @@ export default {
     },
     
     // 解析标签数据
-    parseTags(result) {
+    parseTags(result, state) {
       if (!result) return [];
       try {
         const tags = JSON.parse(result);
         if (Array.isArray(tags)) {
-          return tags.map(tag => ({
+          let processedTags = tags.map(tag => ({
             label: tag.tag_name || tag.label || '',
             code: tag.tag_code || tag.label_code || '',
             desc: tag.desc || '',
             passr: tag.passr || false
           }));
+          
+          // 状态为2或3时,只保留passr为true的标签
+          if (state === 2 || state === 3) {
+            processedTags = processedTags.filter(tag => tag.passr === true);
+          }
+          
+          return processedTags;
         }
         return [];
       } catch (error) {
         console.error('解析标签数据失败:', error);
         return [];
       }
+    },
+    
+    // 获取要展示的标签(最多2个)
+    getDisplayTags(result, state) {
+      const tags = this.parseTags(result, state);
+      return tags.slice(0, 2);
+    },
+    
+    // 获取剩余标签(第3个及以后)
+    getRemainingTags(result, state) {
+      const tags = this.parseTags(result, state);
+      return tags.slice(2);
+    },
+    
+    // 截断标签文字(最多5个字符)
+    truncateLabel(label) {
+      if (!label) return '';
+      return label.length > 5 ? label.substring(0, 5) + '...' : label;
+    },
+    
+    // 根据状态获取标签数据
+    getTagDataByState(row) {
+      if (!row) return { displayTags: [], remainingTags: [] };
+      
+      const { state, result, feedbackResult } = row;
+      
+      // 状态为1:使用result数据
+      if (state === 1) {
+        return {
+          displayTags: this.getDisplayTags(result, state),
+          remainingTags: this.getRemainingTags(result, state)
+        };
+      }
+      // 状态为2或3:使用feedbackResult数据
+      else if (state === 2 || state === 3) {
+        return {
+          displayTags: this.getDisplayTags(feedbackResult, state),
+          remainingTags: this.getRemainingTags(feedbackResult, state)
+        };
+      }
+      // 其他状态:空数据
+      return { displayTags: [], remainingTags: [] };
     }
   }
 }
@@ -268,8 +363,7 @@ export default {
 
 /* 明细搜索栏 */
 .detail-search-bar {
-  padding: 20px;
-  border-bottom: 1px solid #f0f0f0;
+  padding: 0 20px;
 }
 
 .detail-search-form {
@@ -280,13 +374,51 @@ export default {
 
 /* 表格容器 */
 .table-container {
-  padding: 20px;
+  padding: 0 20px;
+}
+
+/* 标签容器 */
+.tags-container {
+  display: flex;
+  align-items: center;
+  flex-wrap: nowrap;
 }
 
 /* 标签样式 */
 .tag-item {
   margin-right: 8px;
   margin-bottom: 8px;
+  max-width: 100px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* 更多标签样式 */
+.more-tags {
+  color: #409EFF;
+  cursor: pointer;
+  font-size: 12px;
+  margin-left: 4px;
+}
+
+/* 正在打标样式 */
+.tag-pending {
+  color: #909399;
+  font-size: 12px;
+  font-style: italic;
+}
+
+/* 空标签样式 */
+.tag-empty {
+  color: #C0C4CC;
+  font-size: 12px;
+}
+
+/* tooltip标签样式 */
+.tooltip-tag {
+  padding: 4px 0;
+  white-space: nowrap;
 }
 
 /* 分页 */

+ 16 - 10
web/src/views/aiTagging/taggingResults/components/Overview.vue

@@ -61,6 +61,13 @@ export default {
     tagSort: {
       type: String,
       default: 'desc'
+    },
+    searchParams: {
+      type: Object,
+      default: () => ({
+        dateRange: [],
+        tagSystem: ''
+      })
     }
   },
   data() {
@@ -80,15 +87,6 @@ export default {
       }
     }
   },
-  props: {
-    searchParams: {
-      type: Object,
-      default: () => ({
-        dateRange: [],
-        tagSystem: ''
-      })
-    }
-  },
   mounted() {
     // 使用nextTick确保DOM元素已渲染完成
     this.$nextTick(() => {
@@ -127,6 +125,14 @@ export default {
   },
 
   methods: {
+    // 刷新所有数据(由父组件调用)
+    refreshAllData() {
+      console.log('刷新所有概览数据');
+      this.loadOverviewData();
+      this.loadTrendData();
+      this.loadDistributionData();
+    },
+    
     // 加载概览数据
     loadOverviewData() {
       // 构建请求参数
@@ -218,7 +224,7 @@ export default {
         return
       }
       
-      const labels = data.map(item => item.stat || '')
+      const labels = data.map(item => item.statShow || '')
       const values = data.map(item => parseInt(item.val) || 0)
       
       const option = {

+ 45 - 88
web/src/views/aiTagging/taggingResults/index.vue

@@ -17,7 +17,7 @@
         </el-form-item>
         <el-form-item label="标签体系:">
           <el-select v-model="searchForm.tagSystem" placeholder="全部" style="width: 200px;">
-            <el-option label="全部" value=""></el-option>
+            <!-- <el-option label="全部" value=""></el-option> -->
             <el-option v-for="(system, index) in tagSystems" :key="index" :label="system.categoryNm" :value="system.categoryId"></el-option>
           </el-select>
         </el-form-item>
@@ -48,7 +48,6 @@
           ref="detailRef"
           :search-params="searchForm"
           @view-detail="handleViewDetail"
-          @export-data="handleExport"
         />
       </el-tab-pane>
     </el-tabs>
@@ -72,7 +71,7 @@ export default {
       
       // 搜索表单
       searchForm: {
-        dateRange: [],
+        dateRange: this.getDefaultDateRange(),
         tagSystem: ''
       },
       
@@ -92,78 +91,7 @@ export default {
       },
       
       // 明细数据
-      detailData: [
-        {
-          loanApplyNo: 'AP20240302001',
-          contractNo: '',
-          taggingTime: '2023-10-28 14:23:12',
-          tags: ['标签A', '标签B', '标签C'],
-          operator: '智能打标平台'
-        },
-        {
-          loanApplyNo: 'AP20240302002',
-          contractNo: 'CT20250203002',
-          taggingTime: '2023-10-27 14:23:12',
-          tags: ['标签B'],
-          operator: '王武'
-        },
-        {
-          loanApplyNo: 'AP20240302003',
-          contractNo: 'CT20250203003',
-          taggingTime: '2023-10-26 14:23:12',
-          tags: ['标签C'],
-          operator: '王武'
-        },
-        {
-          loanApplyNo: 'AP20240302003',
-          contractNo: 'CT20250203001',
-          taggingTime: '2023-10-26 14:23:12',
-          tags: ['标签E', '标签F', '标签G'],
-          operator: '智能打标平台'
-        },
-        {
-          loanApplyNo: 'AP20240302001',
-          contractNo: 'CT20250203002',
-          taggingTime: '2023-10-26 14:23:12',
-          tags: ['标签B'],
-          operator: '王武'
-        },
-        {
-          loanApplyNo: 'AP20240302002',
-          contractNo: 'CT20250203003',
-          taggingTime: '2023-10-26 14:23:12',
-          tags: ['标签C'],
-          operator: '王武'
-        },
-        {
-          loanApplyNo: 'AP20240302003',
-          contractNo: 'CT20250203001',
-          taggingTime: '2023-10-26 14:23:12',
-          tags: ['标签A'],
-          operator: '王武'
-        },
-        {
-          loanApplyNo: 'AP20240302003',
-          contractNo: 'CT20250203002',
-          taggingTime: '2023-10-26 14:23:12',
-          tags: ['标签B'],
-          operator: '王武'
-        },
-        {
-          loanApplyNo: 'AP20240302003',
-          contractNo: 'CT20250203003',
-          taggingTime: '2023-10-26 14:23:12',
-          tags: ['标签C'],
-          operator: '王武'
-        },
-        {
-          loanApplyNo: 'AP20240302003',
-          contractNo: 'CT20250203001',
-          taggingTime: '2023-10-26 14:23:12',
-          tags: ['标签C'],
-          operator: '王武'
-        }
-      ],
+      detailData: [],
       
       // 分页数据
       totalDetailCount: 120,
@@ -195,25 +123,28 @@ export default {
       console.log('搜索:', this.searchForm)
       // 手动触发子组件数据刷新
       if (this.$refs.overviewRef) {
-        this.$refs.overviewRef.loadOverviewData();
+        this.$refs.overviewRef.refreshAllData();
       }
       if (this.$refs.detailRef) {
-        this.$refs.detailRef.handleDetailSearch();
+        this.$refs.detailRef.refreshAllData();
       }
-      this.$message.success('搜索成功')
     },
 
     // 重置搜索
     handleReset() {
       this.searchForm.dateRange = []
-      this.searchForm.tagSystem = ''
-      console.log('重置搜索')
+      // 重置为默认选中第一条数据
+      if (this.tagSystems.length > 0) {
+        this.searchForm.tagSystem = this.tagSystems[0].categoryId;
+      } else {
+        this.searchForm.tagSystem = '';
+      }
       // 手动触发子组件数据刷新
       if (this.$refs.overviewRef) {
-        this.$refs.overviewRef.loadOverviewData();
+        this.$refs.overviewRef.refreshAllData();
       }
       if (this.$refs.detailRef) {
-        this.$refs.detailRef.handleDetailSearch();
+        this.$refs.detailRef.refreshAllData();
       }
     },
     
@@ -230,6 +161,10 @@ export default {
               categoryId: item.categoryId || item.id || ''
             }));
             console.log('标签体系数据:', this.tagSystems);
+            // 默认选中第一条数据
+            if (this.tagSystems.length > 0) {
+              this.searchForm.tagSystem = this.tagSystems[0].categoryId;
+            }
           } else {
             this.$message.error(response.message || '获取标签体系列表失败');
           }
@@ -293,12 +228,7 @@ export default {
       // 这里可以打开详情对话框
     },
 
-    // 导出数据
-    handleExport() {
-      console.log('导出数据')
-      this.$message.success('导出成功')
-      // 这里可以调用后端接口导出数据
-    },
+
 
     // 明细分页大小变化
     handleDetailSizeChange(size) {
@@ -318,6 +248,33 @@ export default {
       if (this.$refs.detailRef) {
         this.$refs.detailRef.loadDetailData();
       }
+    },
+    
+    // 获取默认日期范围(本年1月1日至今)
+    getDefaultDateRange() {
+      const now = new Date();
+      const currentYear = now.getFullYear();
+      
+      // 本年1月1日 00:00:00
+      const startDate = new Date(currentYear, 0, 1, 0, 0, 0);
+      const startDateStr = this.formatDateTime(startDate);
+      
+      // 当前时间
+      const endDateStr = this.formatDateTime(now);
+      
+      return [startDateStr, endDateStr];
+    },
+    
+    // 格式化日期时间为 yyyy-MM-dd HH:mm:ss 格式
+    formatDateTime(date) {
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, '0');
+      const day = String(date.getDate()).padStart(2, '0');
+      const hours = String(date.getHours()).padStart(2, '0');
+      const minutes = String(date.getMinutes()).padStart(2, '0');
+      const seconds = String(date.getSeconds()).padStart(2, '0');
+      
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
     }
   }
 }

+ 156 - 29
web/src/views/aiTagging/taggingSystemManage/TagSystemEdit.vue

@@ -3,11 +3,24 @@
     <!-- 页面头部 -->
     <div class="edit-header">
       <div class="header-left">
-        <h1 class="system-title">{{ currentSystem.name }}</h1>
-        <p class="system-description">{{ currentSystem.description }}</p>
+        <h1 class="system-title">{{ currentSystem.categoryNm }}</h1>
+        <p class="system-description">{{ currentSystem.categoryDesc }}</p>
       </div>
       <div class="header-right">
-        <el-switch v-model="currentSystem.status" active-text="已启用" inactive-text="已停用" @change="handleStatusChange"></el-switch>
+        <span class="status-label">{{ currentSystem.state === 0 ? '已启用' : '未启用' }}</span>
+        <el-tooltip 
+          :content="canOperateStatus ? '' : '暂无权限操作'" 
+          :disabled="canOperateStatus"
+          placement="top"
+        >
+          <el-switch 
+            :value="currentSystem.state === 0" 
+            :disabled="!canOperateStatus"
+            active-text="已启用" 
+            inactive-text="未启用" 
+            @input="handleStatusChange"
+          ></el-switch>
+        </el-tooltip>
       </div>
     </div>
 
@@ -19,9 +32,11 @@
           <!-- 标签树区域 -->
           <div class="tag-tree-section">
             <TagConfig 
+              ref="tagConfig"
               :tag-system="currentSystem"
               :tree-props="treeProps"
-              :system-status="currentSystem.status"
+              :system-status="currentSystem.state === 0"
+              :can-edit-tag="canEditTag"
               @node-click="handleNodeClick"
               @dropdown-command="handleDropdownCommand"
               @edit-tag="handleEditTag"
@@ -69,6 +84,16 @@ export default {
         description: ''
       },
       
+      // 用户操作权限
+      userPermissions: {
+        canCreateTagSystem: false,
+        canUpdateTagSystem: false,
+        canEnableTagSystem: false,
+        canDisableTagSystem: false,
+        canDeleteTagSystem: false,
+        canEditTag: false
+      },
+      
       // 搜索关键词
       searchKeyword: '',
       
@@ -82,9 +107,58 @@ export default {
       activeTab: 'config'
     }
   },
+  computed: {
+    // 判断是否有操作状态开关的权限
+    canOperateStatus() {
+      const hasPermission = this.userPermissions?.canEnableTagSystem || this.userPermissions?.canDisableTagSystem;
+      console.log('canOperateStatus:', hasPermission, 'permissions:', this.userPermissions);
+      return hasPermission;
+    },
+    // 判断是否有编辑标签的权限(控制标签配置区域的编辑功能)
+    canEditTag() {
+      const hasPermission = this.userPermissions?.canEditTag === true;
+      console.log('canEditTag:', hasPermission, 'userPermissions.canEditTag:', this.userPermissions?.canEditTag);
+      return hasPermission;
+    },
+    // 综合判断是否允许编辑标签(需要同时满足:已停用状态 且 有编辑权限)
+    allowEditTag() {
+      const allow = this.currentSystem.state !== 0 && this.canEditTag;
+      console.log('allowEditTag:', allow, 'status:', this.currentSystem.state, 'canEditTag:', this.canEditTag);
+      return allow;
+    }
+  },
   mounted() {
+    // 动态设置页面标题和标签页标题:标签配置-体系名称
+    const systemId = this.$route.query.id;
+    const systemName = this.$route.query.name;
+    let pageTitle = '标签配置';
+    
+    if (systemId && systemName) {
+      pageTitle = `标签配置-${systemName}`;
+    } 
+    
+    // 更新当前路由的meta.title
+    this.$route.meta.title = pageTitle;
+    
+    // 更新标签页标题 - 只传递必要信息,避免循环引用
+    this.$store.dispatch('tagsView/updateVisitedView', {
+      fullPath: this.$route.fullPath,
+      meta: { ...this.$route.meta }
+    });
+    
+    console.log('TagSystemEdit.vue checkCtrl(aitagTagcategoryCreate)', this.checkCtrl('aitagTagcategoryCreate'))
+    
+    // 从 Vuex 读取用户权限数据
+    this.userPermissions = this.$store.getters['aiTagPermissions/getPermissions'];
+    console.log('从 Vuex 获取的用户权限:', this.userPermissions);
+    
     this.loadTagSystemData();
   },
+  beforeDestroy() {
+    // 清理 Vuex 中的权限数据
+    this.$store.dispatch('aiTagPermissions/clearPermissions');
+    console.log('清理 Vuex 权限数据');
+  },
   methods: {
     // 加载标签体系数据
     loadTagSystemData() {
@@ -105,12 +179,7 @@ export default {
         callback: (code, error, response) => {
           if (response.code == '0') {
             const data = response.data || {};
-            this.currentSystem = {
-              id: data.id,
-              name: data.categoryNm || '未命名',
-              status: data.state === 0,
-              description: data.categoryDesc || ''
-            };
+            this.currentSystem = data;
           } else {
             this.$message.error(response.message || '获取标签体系详情失败');
           }
@@ -194,29 +263,86 @@ export default {
     // 返回列表页面
     handleBackToList() {
       console.log('返回标签体系列表页面');
-      this.$router.push('/home/test');
+      // 强制刷新列表页面
+      this.$router.push({
+        path: '/home/test',
+        query: { refresh: Date.now().toString() }
+      });
     },
     // 处理状态变化
-    handleStatusChange() {
-      console.log('状态变化:', this.currentSystem.status);
+    handleStatusChange(newStatus) {
+      console.log('状态变化:', newStatus);
+
+      // 检查是否有操作权限
+      if (!this.canOperateStatus) {
+        this.$message.warning('暂无权限操作标签体系状态');
+        return;
+      }
+
+      // 检查具体权限:启用需要 canEnableTagSystem,停用需要 canDisableTagSystem
+      if (newStatus && !this.userPermissions?.canEnableTagSystem) {
+        this.$message.warning('暂无权限启用标签体系');
+        return;
+      }
+      if (!newStatus && !this.userPermissions?.canDisableTagSystem) {
+        this.$message.warning('暂无权限停用标签体系');
+        return;
+      }
+
+      // 检查是否有标签处于编辑状态(仅在启用时检查)
+      if (newStatus) {
+        const isEditing = this.$refs.tagConfig?.getEditStatus();
+        if (isEditing) {
+          this.$message.warning('当前有标签处于编辑状态,请先完成编辑保存后再启用标签体系');
+          // 恢复开关状态
+          this.$nextTick(() => {
+            this.currentSystem.state = 1;
+          });
+          return;
+        }
+      }
+
+      // 二次确认
+      const actionText = newStatus ? '启用' : '停用';
+      const systemName = this.currentSystem?.categoryNm || '当前标签体系';
+      const confirmMessage = newStatus ? 
+        `确定要启用标签体系「${systemName}」吗?启用后标签体系将可用。` : 
+        `确定要停用标签体系「${systemName}」吗?停用后标签体系将不可用。`;
       
-      // 调用后端接口更新状态
-      yufp.service.request({
-        url: backend.tagServer + "/api/aitag-tagcategory/modify",
-        method: 'post',
-        data: {
-          id: this.currentSystem.id,
-          state: this.currentSystem.status ? 0 : 1
-        },
-        callback: (code, error, response) => {
-          if (response.code == '0') {
-            this.$message.success('标签体系状态更新成功');
-          } else {
-            // 恢复原状态
-            this.currentSystem.status = !this.currentSystem.status;
-            this.$message.error(response.message || '标签体系状态更新失败');
+      this.$confirm(confirmMessage, `${actionText}确认`, {
+        confirmButtonText: actionText,
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        // 保存原始状态
+        const originalStatus = this.currentSystem.state;
+
+        // 根据状态调用不同的接口
+        const url = backend.tagServer + (newStatus ? "/api/aitag-tagcategory/enable?id=" + this.currentSystem.id : "/api/aitag-tagcategory/disable?id=" + this.currentSystem.id);
+
+        // 调用后端接口更新状态
+        yufp.service.request({
+          url: url,
+          method: 'post',
+          data: {},
+          callback: (code, error, response) => {
+            if (response && response.code == '0') {
+              // 接口调用成功,更新状态
+              this.currentSystem.state = newStatus ? 0 : 1;
+              this.$message.success('标签体系状态更新成功');
+            } else {
+              // 接口调用失败,恢复原状态
+              this.currentSystem.state = originalStatus;
+              this.$message.error(response?.message || '标签体系状态更新失败');
+            }
           }
-        }
+        });
+      }).catch(() => {
+        console.log(`用户取消${actionText}标签体系`);
+        // 恢复开关状态
+        this.$nextTick(() => {
+          this.currentSystem.state = newStatus ? 1 : 0;
+        });
       });
     }
   }
@@ -262,6 +388,7 @@ export default {
 .header-right {
   display: flex;
   align-items: center;
+  gap: 5px;
 }
 
 .warning-box {

+ 14 - 3
web/src/views/aiTagging/taggingSystemManage/components/CreateTagSystem.vue

@@ -8,7 +8,10 @@
   >
     <el-form :model="form" :rules="rules" ref="form" label-width="120px">
       <!-- 标签体系名称 -->
-      <el-form-item label="标签体系名称*" prop="name">
+      <el-form-item label="标签体系名称" prop="name" required>
+        <template #label>
+          <span class="required-label">标签体系名称</span>
+        </template>
         <el-input v-model="form.name" placeholder="请输入标签体系名称,如'海洋经济'" />
       </el-form-item>
       
@@ -139,8 +142,8 @@ export default {
                 // 重置表单
                 this.resetForm();
                 
-                // 触发父组件的成功事件
-                this.$emit('success');
+                // 触发父组件的成功事件,传递创建的数据
+                this.$emit('success', response.data);
               } else {
                 this.$message.error(response.message || (this.isEditMode ? '编辑标签体系失败' : '创建标签体系失败'));
               }
@@ -183,4 +186,12 @@ export default {
   justify-content: flex-end;
   gap: 12px;
 }
+
+/* 自定义必填标识 */
+.required-label::before {
+  content: '●';
+  color: #f56c6c;
+  margin-right: 4px;
+  font-size: 12px;
+}
 </style>

+ 77 - 5
web/src/views/aiTagging/taggingSystemManage/components/TagConfig.vue

@@ -11,6 +11,7 @@
           :tag-tree-data="tagTreeData"
           :current-node-key="currentNodeKey"
           :system-status="systemStatus"
+          :can-edit-tag="canEditTag"
           @node-click="handleNodeClick"
           @dropdown-command="handleDropdownCommand"
           @refresh="loadTagTreeData"
@@ -27,7 +28,9 @@
           :parent-options="tagTreeData"
           :tree-props="treeProps"
           :category-id="tagSystem.id"
+          :category-name="tagSystem.categoryNm"
           :system-status="systemStatus"
+          :can-edit-tag="canEditTag"
           @edit="handleEditTag"
           @save-success="loadTagTreeData"
           @version-rollback="handleVersionRollback"
@@ -35,13 +38,28 @@
           @cancel="handleCancelEdit"
         />
         <div v-else class="empty-data">
-          <div v-if="isTagTreeEmpty" class="empty-data-import">
-            <!-- <i class="el-icon-upload2 empty-icon"></i> -->
+          <div v-if="isTagTreeEmpty && !systemStatus && canEditTag" class="empty-data-import">
+            <!-- 提示条 -->
+            <div class="import-tip">
+              <i class="el-icon-warning-outline tip-icon"></i>
+              <span>小提示:您可以通过批量导入方式,快速完成标签配置,无需手动逐个添加~</span>
+              <a href="javascript:void(0)" class="import-link" @click="handleImportClick">点击此处</a>
+              <span>开始导入</span>
+            </div>
+            <img src="@/assets/images/noData.png" alt="暂无数据">
+          </div>
+          <div v-else-if="isTagTreeEmpty && systemStatus" class="empty-data-import">
+            <img src="@/assets/images/noData.png" alt="暂无数据">
+            <h4>暂无标签数据</h4>
+            <p>标签体系已启用,无法导入标签</p>
+          </div>
+          <div v-else-if="isTagTreeEmpty && !canEditTag" class="empty-data-import">
+            <img src="@/assets/images/noData.png" alt="暂无数据">
             <h4>暂无标签数据</h4>
-            <p>您可以通过批量导入的方式快速创建标签</p>
-            <el-button type="primary" @click="handleImportClick">点击此处导入</el-button>
+            <p>当前暂无标签数据,且您没有编辑权限</p>
           </div>
           <div v-else class="empty-data-select">
+            <img src="@/assets/images/noData.png" alt="暂无数据">
             <p>请选择一个标签节点查看详情</p>
           </div>
         </div>
@@ -75,6 +93,10 @@ export default {
     systemStatus: {
       type: Boolean,
       default: true
+    },
+    canEditTag: {
+      type: Boolean,
+      default: false
     }
   },
   data() {
@@ -123,6 +145,21 @@ export default {
           if (response.code == '0') {
             this.tagTreeData = response.data || [];
             console.log('标签树数据:', this.tagTreeData);
+            
+            // 刷新后重新选中之前的节点
+            if (this.currentNodeKey) {
+              setTimeout(() => {
+                // 模拟点击操作,执行完整的点击逻辑
+                // 1. 加载标签详情
+                this.loadTagDetail(this.currentNodeKey);
+                // 2. 退出编辑模式(与handleNodeClick保持一致)
+                this.isEditMode = false;
+                // 3. 触发TagTree组件的节点选中效果
+                if (this.$refs.tagTree && this.$refs.tagTree.$refs.tree) {
+                  this.$refs.tagTree.$refs.tree.setCurrentKey(this.currentNodeKey);
+                }
+              }, 100);
+            }
           } else {
             this.$message.error(response.message || '获取标签树数据失败');
           }
@@ -311,6 +348,11 @@ export default {
       if (this.$refs.tagTree) {
         this.$refs.tagTree.showImportDialog = true;
       }
+    },
+    
+    // 获取当前编辑状态
+    getEditStatus() {
+      return this.isEditMode;
     }
   }
 }
@@ -403,7 +445,7 @@ export default {
   flex-direction: column;
   align-items: center;
   gap: 16px;
-  max-width: 400px;
+  width: 100%;
 }
 
 .empty-icon {
@@ -438,4 +480,34 @@ export default {
   color: #909399;
   margin: 0;
 }
+
+/* 导入提示条样式 */
+.import-tip {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  padding: 12px 16px;
+  background-color: #f7f7f7;
+  border: 1px solid #e6e6e6;
+  border-radius: 4px;
+  margin-bottom: 20px;
+  font-size: 14px;
+  color: #606266;
+}
+
+.tip-icon {
+  color: #e6a23c;
+  font-size: 16px;
+}
+
+.import-link {
+  color: #409eff;
+  text-decoration: none;
+  cursor: pointer;
+}
+
+.import-link:hover {
+  text-decoration: underline;
+  color: #66b1ff;
+}
 </style>

+ 17 - 3
web/src/views/aiTagging/taggingSystemManage/components/TagDetail.vue

@@ -2,12 +2,17 @@
   <div class="tag-detail">
     <!-- 详情展示模式 -->
     <div v-if="!isEditMode">
-      <TagDetailView :tag-data="tagData" @version-rollback="handleVersionRollback" />
+      <TagDetailView 
+        :tag-data="tagData" 
+        :system-status="systemStatus" 
+        :can-edit-tag="canEditTag"
+        @version-rollback="handleVersionRollback" 
+      />
       
       <!-- 操作按钮 -->
       <div class="action-buttons">
-        <el-button v-if="!systemStatus" type="danger" @click="handleDelete">删除</el-button>
-        <el-button v-if="!systemStatus" type="primary" @click="handleEdit">编辑</el-button>
+        <el-button v-if="!systemStatus && canEditTag" type="danger" @click="handleDelete">删除</el-button>
+        <el-button v-if="!systemStatus && canEditTag" type="primary" @click="handleEdit">编辑</el-button>
       </div>
     </div>
     
@@ -19,6 +24,7 @@
         :parent-options="parentOptions"
         :tree-props="treeProps"
         :category-id="categoryId"
+        :category-name="categoryName"
         @save="handleSave"
         @save-success="$emit('save-success')"
         @cancel="handleCancel"
@@ -50,6 +56,10 @@ export default {
       type: String,
       default: ''
     },
+    categoryName: {
+      type: String,
+      default: ''
+    },
     parentOptions: {
       type: Array,
       default: () => []
@@ -64,6 +74,10 @@ export default {
     systemStatus: {
       type: Boolean,
       default: true
+    },
+    canEditTag: {
+      type: Boolean,
+      default: false
     }
   },
   data() {

+ 81 - 6
web/src/views/aiTagging/taggingSystemManage/components/TagDetailEdit.vue

@@ -9,6 +9,15 @@
       ></el-input>
     </div>
     
+    <!-- 标签编号 -->
+    <div class="edit-section">
+      <h4 class="section-title">标签编号<span class="required">*</span></h4>
+      <el-input
+        v-model="editForm.tagCode"
+        placeholder="请输入标签编号"
+      ></el-input>
+    </div>
+    
     <!-- 上级标签 -->
     <div class="edit-section">
       <div class="section-title-container">
@@ -27,9 +36,11 @@
       <div class="tree-select-container">
         <TreeSelect
           v-model="editForm.parent"
-          :data="parentOptions"
+          :data="processedParentOptions"
           :tree-props="treeProps"
+          :category-name="categoryName"
           placeholder="请选择"
+          @change="handleParentChange"
         ></TreeSelect>
       </div>
     </div>
@@ -144,6 +155,10 @@ export default {
     categoryId: {
       type: String,
       default: ''
+    },
+    categoryName: {
+      type: String,
+      default: ''
     }
   },
   data() {
@@ -155,6 +170,8 @@ export default {
         keywords: '',
         judgment: ''
       },
+      // 初始parent值
+      initialParent: '',
       showRegexDrawer: false,
       keywordTools: [
         { char: '^', title: '脱字符' },
@@ -173,6 +190,29 @@ export default {
       ]
     }
   },
+  computed: {
+    // 处理后的父选项数据,将标签体系作为最外层选项
+    processedParentOptions() {
+      // 如果没有标签体系信息,直接返回原数据
+      if (!this.categoryId || !this.categoryName) {
+        return this.parentOptions;
+      }
+      
+      // 检查是否已经包含标签体系作为最外层
+      if (this.parentOptions.length > 0 && this.parentOptions[0].id === this.categoryId) {
+        return this.parentOptions;
+      }
+      
+      // 创建标签体系作为最外层选项
+      const systemOption = {
+        id: this.categoryId,
+        tagNm: this.categoryName,
+        children: this.parentOptions
+      };
+      
+      return [systemOption];
+    }
+  },
   watch: {
     formData: {
       handler(newData) {
@@ -191,15 +231,15 @@ export default {
   methods: {
     updateEditForm(data) {
       console.log('TagDetailEdit updateEditForm - 传入数据:', data);
-      console.log('TagDetailEdit updateEditForm - parentOptions:', this.parentOptions);
+      console.log('TagDetailEdit updateEditForm - processedParentOptions:', this.processedParentOptions);
       
       let parent = data.parent || data.parentId || '';
       console.log('TagDetailEdit updateEditForm - 原始parent值:', parent);
       
-      // 如果 parent 是ID,尝试在 parentOptions 中找到对应的对象
-      if (parent && typeof parent === 'string' && this.parentOptions && this.parentOptions.length > 0) {
+      // 如果 parent 是ID,尝试在 processedParentOptions 中找到对应的对象
+      if (parent && typeof parent === 'string' && this.processedParentOptions && this.processedParentOptions.length > 0) {
         console.log('TagDetailEdit updateEditForm - 开始查找parent对象,ID:', parent);
-        parent = this.findNodeById(this.parentOptions, parent);
+        parent = this.findNodeById(this.processedParentOptions, parent);
         console.log('TagDetailEdit updateEditForm - 查找结果:', parent);
       }
       
@@ -208,8 +248,18 @@ export default {
         console.log('TagDetailEdit updateEditForm - parent仍然是字符串,无法反显:', parent);
       }
       
+      // 如果找不到parent节点(被删除),使用第一个根节点作为默认值
+      if (!parent && this.processedParentOptions && this.processedParentOptions.length > 0) {
+        console.log('TagDetailEdit updateEditForm - 找不到parent节点,使用第一个根节点作为默认值');
+        parent = this.processedParentOptions[0];
+      }
+      
+      // 保存初始parent值
+      this.initialParent = parent || '';
+      
       this.editForm = {
         name: data.name || '',
+        tagCode: data.tagCode || data.tag_code || '',
         parent: parent || '',
         description: data.description || '',
         keywords: data.keywords || '',
@@ -217,6 +267,7 @@ export default {
       };
       
       console.log('TagDetailEdit updateEditForm - 最终editForm:', this.editForm);
+      console.log('TagDetailEdit updateEditForm - 保存初始parent值:', this.initialParent);
     },
     
     // 根据ID在树结构中查找节点
@@ -234,6 +285,25 @@ export default {
       }
       return null;
     },
+    
+    // 处理上级标签变化
+    handleParentChange(value) {
+      console.log('TagDetailEdit handleParentChange - 新值:', value);
+      // 如果值为空(空字符串、null、undefined),恢复初始值
+      if (!value || value === '') {
+        console.log('TagDetailEdit handleParentChange - 清空选择,恢复初始值:', this.initialParent);
+        // 使用$nextTick确保DOM更新后再设置值
+        this.$nextTick(() => {
+          // 如果初始值不存在,使用处理后数据的第一个根节点
+          let defaultParent = this.initialParent;
+          if (!defaultParent && this.processedParentOptions && this.processedParentOptions.length > 0) {
+            defaultParent = this.processedParentOptions[0];
+          }
+          this.editForm.parent = defaultParent;
+          console.log('TagDetailEdit handleParentChange - 已恢复初始值:', this.editForm.parent);
+        });
+      }
+    },
     insertKeywordChar(char) {
       this.editForm.keywords += char;
     },
@@ -249,6 +319,11 @@ export default {
         return;
       }
       
+      if (!this.editForm.tagCode) {
+        this.$message.error('请输入标签编号');
+        return;
+      }
+      
       if (!this.categoryId) {
         this.$message.error('缺少标签体系ID');
         return;
@@ -274,7 +349,7 @@ export default {
       const requestData = {
         categoryId: this.categoryId,
         tagNm: this.editForm.name,
-        tagCode: this.generateTagCode(),
+        tagCode: this.editForm.tagCode,
         tagRemark: this.editForm.description,
         parentId: this.getParentId(),
         reg: this.editForm.keywords,

+ 54 - 29
web/src/views/aiTagging/taggingSystemManage/components/TagDetailEditRegexDrawer.vue

@@ -25,12 +25,12 @@
               <tr>
                 <td class="symbol-cell">^</td>
                 <td>匹配字符串的开始位置</td>
-                <td class="example-cell">^(?!.*A) 表示从字符串开头就要满足后面的条件</td>
+                <td class="example-cell">^(?!.*) 表示从字符串开头就要满足后面的条件</td>
               </tr>
               <tr>
                 <td class="symbol-cell">$</td>
                 <td>匹配字符串的结束位置</td>
-                <td class="example-cell">部分规则末尾有 $,表示匹配项必须到结尾,确保整个字符串被检查</td>
+                <td class="example-cell">部分规则末尾有 $,表示匹配项必须延伸到结尾,确保整个字符串被检查</td>
               </tr>
               <tr>
                 <td class="symbol-cell">.</td>
@@ -40,31 +40,31 @@
               <tr>
                 <td class="symbol-cell">*</td>
                 <td>前面的元素重复 0 次或多次(尽可能多)</td>
-                <td class="example-cell">.* 表示任意数量的任意字符,常用于包含或排除检查</td>
+                <td class="example-cell">.* 表示任意数量的任意字符,常用于"包含""排除"检查</td>
               </tr>
               <tr>
                 <td class="symbol-cell">?</td>
-                <td>1. 量词:前面的元素重复 0 次或 1 次<br>2. 跟在量词后表示非贪婪(尽可能少)<br>3. 放在 (?!...) 中表示否定前瞻</td>
-                <td class="example-cell">(?!.*A) 中的 ! 与 ? 组合,表示后面不能出现 A</td>
+                <td>1. 量词:前面的元素重复 0 次或 1 次(可选)<br>2. 跟在(后表示非捕获分组(?!...),跟在 ! 后表示否定前瞻</td>
+                <td class="example-cell">(?!.*) 中的 ? 与 ! 组合,表示"后面不能出现..."</td>
               </tr>
               <tr>
                 <td class="symbol-cell">+</td>
                 <td>前面的元素重复 1 次或多次</td>
-                <td class="example-cell">本规则中未使用,但常见于其他则</td>
+                <td class="example-cell">本规则中未使用,但常见于其他则</td>
               </tr>
               <tr>
                 <td class="symbol-cell">|</td>
-                <td>逻辑或,分隔多个可选项</td>
-                <td class="example-cell">(淡水|池塘|河|湖|河中) 表示匹配 "淡水" 或 "池塘" 或 "河" 中的任何一个词</td>
+                <td>逻辑"",分隔多个可选项</td>
+                <td class="example-cell">(淡水|池塘|河|湖|河中) 表示匹配"淡水"或"池塘"或"河"中的任意一个</td>
               </tr>
               <tr>
                 <td class="symbol-cell">()</td>
-                <td>1. 分组:将多个元素组合成一个整体<br>2. 捕获分组:会捕获匹配的内容(本规则中未使用捕获功能)</td>
-                <td class="example-cell">(养殖|捕捞) 作为一个整体(养殖捕捞) 表示一组候选词</td>
+                <td>1. 分组:将多个元素组合成一个整体<br>2. 捕获分组:会保存匹配的内容(本规则中未使用捕获功能)</td>
+                <td class="example-cell">(养殖|捕捞) 作为一个整体(养殖捕捞) 表示一组候选词</td>
               </tr>
               <tr>
                 <td class="symbol-cell">[ ]</td>
-                <td>字符集,匹配其中任意一个字符,例如 [abc] 匹配 a、b、c 中的任意一个</td>
+                <td>字符集,匹配其中任意一个字符。例如 [abc] 匹配 a、b 或 c</td>
                 <td class="example-cell">本规则中未使用,但常见于匹配特定字符范围</td>
               </tr>
               <tr>
@@ -78,9 +78,14 @@
                 <td class="example-cell">^(?!.*A) 表示字符串中不能包含 A</td>
               </tr>
               <tr>
-                <td class="symbol-cell">[^...]</td>
-                <td>在方括号中使用脱字符(^),表示匹配其中任意一个字符的否定</td>
-                <td class="example-cell">在排除条件中 (?!.*[^A]) 表示字符串不能包含非 A 的字符</td>
+                <td class="symbol-cell">.*</td>
+                <td>任意长度的任意字符(除换行外)</td>
+                <td class="example-cell">在排除条件中 ^(?!.*A) 表示字符串不能包含 A</td>
+              </tr>
+              <tr>
+                <td class="symbol-cell">| (在括号内)</td>
+                <td>在 ( ) 中 | 分隔多个选项,表示匹配其中任意一个</td>
+                <td class="example-cell">(淡水|池塘|河|湖) 匹配任意一个词</td>
               </tr>
             </tbody>
           </table>
@@ -95,37 +100,39 @@
         </div>
         <div class="card">
           <p class="regex-paragraph">每条规则通常由两部分组成:排除条件 + 匹配模式(多个匹配分支用 | 连接)。</p>
+          <p class="regex-paragraph">例如:</p>
           <div class="example-box">
-            <code class="regex-code">^(?!.*(淡水|池塘|河|湖|江湖|海洋|鱼缸|鱼塘|龙鱼|花鸟|海).{0,6}(养殖|捕捞|渔网|渔船|贝|虾|蟹|鱼|海参|对虾|扇贝|贻贝|牡蛎|海蜇|海胆|鲍鱼|鱿鱼|墨鱼|章鱼|贝类|鱼粉|鱼油|鱼干|鱼罐头|水产加工))</code>
+            <code class="regex-code">^(?!.*(淡水|池塘|内河|江河|湖泊|水库|观赏鱼|锦鲤|龙鱼|花鸟)).*?(?:海.{0,100}(养殖|鲍|参|虾|蟹|贝|蚝|蛎|蛤|扇贝|鲍鱼|海参|对虾|虾|贻贝|牡蛎|蛏|螺|紫菜|海带)|(养殖|鲍|参|虾|蟹|贝|蚝|蛎|蛤|扇贝|鲍鱼|海参|对虾|蟹|虾|贻贝|牡蛎|蛏|螺|紫菜|海带).{0,100}海)</code>
           </div>
           
           <div class="structure-item">
-            <h4 class="regex-subtitle">1. 排除条件 (?!...) 解析:</h4>
+            <h4 class="regex-subtitle">1. 排除条件 ^(?!.*(词1|词2|...))</h4>
             <ul class="regex-list">
-              <li><span class="highlight">^(?!...):</span>确保从字符串开头开始检查。</li>
+              <li><span class="highlight">^:</span>确保从字符串开头开始检查。</li>
+              <li><span class="highlight">(?!...):</span>否定前瞻:确保后面不能出现括号内的模式。</li>
               <li><span class="highlight">.*:</span>允许任意字符出现任意次,只要字符串中包含后面的任何一个词,就触发排除。</li>
               <li><span class="highlight">(词1|词2|...):</span>列出所有要排除的关键词(用 | 分隔)。</li>
-              <li><span class="highlight">含义:</span>整个字符串不能包含这些词中的任何一个。如果包含,则该规则不匹配。</li>
+              <li><span class="highlight">含义:</span>整个字符串不能包含这些词中的任何一个。如果包含,则该规则不匹配。本例排除词为:淡水、池塘、内河、江河、湖泊、水库、观赏鱼、锦鲤、龙鱼、花鸟。</li>
             </ul>
           </div>
           
           <div class="structure-item">
-            <h4 class="regex-subtitle">2. 匹配模式 (A.{0,6}B|B.{0,6}A) 解析:</h4>
+            <h4 class="regex-subtitle">2. 匹配模式 .*?(?:A.{0,100}(B)|(B).{0,100}A)</h4>
             <ul class="regex-list">
-              <li>这是一种常见模式,用于匹配两个词(A 和 B)同时出现,且它们之间的字符数不超过 6 个(顺序可互换)。</li>
-              <li><span class="highlight">(A):</span>表示关键词 A(例如 "海")。</li>
-              <li><span class="highlight">.{0,6}:</span>表示 0 到 6 个任意字符(中间可以有任何内容)。</li>
-              <li><span class="highlight">(B):</span>表示关键词 B(例如 "养殖")。</li>
-              <li><span class="highlight">两个分支:</span>(A.{0,6}B) 和 (B.{0,6}A) 分别覆盖两种顺序。</li>
-              <li><span class="highlight">含义:</span>字符串中必须同时出现 A 和 B,且它们之间的距离不超过 6 个字符(无论顺序)。</li>
+              <li><span class="highlight">.*? :</span>非贪婪匹配任意字符(0次或多次),使引擎从字符串开头开始尝试匹配后续模式,但实际因为排除条件已断言,此处的 .*? 通常匹配0个字符,可视为占位。</li>
+              <li><span class="highlight">(?:...) :</span>非捕获组,用于将两个分支组合在一起,但不保存匹配内容,提升性能。</li>
+              <li>两个分支分别对应关键词 A(此处为“海”)与关键词组 B(多个水产相关词)同时出现,且它们之间的字符数不超过100个(顺序可互换)</li>
+              <li><span class="highlight">分支1:</span>海.{0,100}(B) 表示“海”在前,B在后,中间隔0到100个任意字符。</li>
+              <li><span class="highlight">分支2:</span>(B).{0,100}海 表示B在前,“海”在后,中间隔0到100个任意字符。</li>
+              <li><span class="highlight">含义:</span>字符串中必须同时出现“海”和B中的至少一个词,且两者距离不超过100个字符(无论顺序)。</li>
+              <li><span class="highlight">B关键词组为:</span>养殖|鲍|参|虾|蟹|贝|蚝|蛎|蛤|扇贝|鲍鱼|海参|对虾|蟹|虾|贻贝|牡蛎|蛏|螺|紫菜|海带(注意两个分支中的B列表略有差异,但实际使用时通常保持一致,此处以规则原文为准)。</li>
             </ul>
           </div>
           
           <div class="structure-item">
-            <h4 class="regex-subtitle">3. 直接匹配关键词/短语 (词1|词2|...) 解析:</h4>
+            <h4 class="regex-subtitle">整体逻辑</h4>
             <ul class="regex-list">
-              <li>有些规则末尾直接给出一个关键词组,例如 <code class="inline-code">(鱼丸|鱼糕|鱼饼|虾饼|虾米|咸鱼干|咸鱼|鱼干|水产加工)</code>。</li>
-              <li>此时只要字符串中包含这些词中的任何一个,就匹配成功。</li>
+              <li>规则先过滤掉包含淡水/观赏鱼等无关词的字符串,然后匹配“海”与水产相关词在100字符内共现的字符串,从而识别出与海洋养殖/海产相关的内容。</li>
             </ul>
           </div>
         </div>
@@ -159,7 +166,25 @@
               <i class="el-icon-info"></i>
             </div>
             <div class="tip-content">
-              <strong>{0,6} 的精确含义</strong>:表示前面的元素(通常是一个点 .)重复 0 到 6 次。重复 0 次意味着两个词可以紧邻(中间无字符)。
+              <strong>{0,100} 的精确含义</strong>:表示前面的元素(通常是一个点 .)重复 0 到 100 次。重复 0 次意味着两个词可以紧邻(中间无字符)。此处距离上限从常见的6字符扩大到100字符,允许更宽松的上下文匹配。
+
+            </div>
+          </div>
+          <div class="tip-item">
+            <div class="tip-icon">
+              <i class="el-icon-info"></i>
+            </div>
+            <div class="tip-content">
+              <strong>非捕获组 (?:...)</strong>:与普通括号 (...) 功能类似,但不会将匹配内容存入捕获组,可减少内存占用,提高正则执行效率。
+            </div>
+          </div>
+          <div class="tip-item">
+            <div class="tip-icon">
+              <i class="el-icon-info"></i>
+            </div>
+            <div class="tip-content">
+              <strong>非贪婪量词 .*?</strong>:在排除条件后使用 .*? 表示尽可能少地匹配任意字符,然后尝试后续模式。由于排除条件已确保字符串不含排除词,此处的 .*? 实际不影响匹配结果,但可使正则引擎更快进入后续匹配。
+
             </div>
           </div>
           <div class="tip-item">

+ 18 - 5
web/src/views/aiTagging/taggingSystemManage/components/TagDetailView.vue

@@ -4,10 +4,15 @@
     <div class="tag-path">
       {{ tagData.path || '无路径信息' }}
     </div>
-    
     <!-- 版本信息 -->
     <div class="version-info">
-      版本{{ tagData.version || '1.0' }}. 修订时间: {{ tagData.revisionTime || '未知' }} 修订人: {{ tagData.revisionUser || '未知' }}
+      {{ tagData.tagVersionShow || '版本' + (tagData.version || '1.0') }} 修订时间: {{ tagData.revisionTime || '未知' }} 修订人: {{ tagData.revisionUser || '未知' }}
+    </div>
+
+    <!-- 标签编号 -->
+    <div class="detail-section">
+      <h4 class="section-title">标签编号</h4>
+      <p class="section-content">{{ tagData.tagCode || tagData.tag_code || '无编号信息' }}</p>
     </div>
     
     <!-- 标签定义 -->
@@ -41,7 +46,7 @@
           class="revision-item"
         >
           <div class="revision-info">
-            {{ revision.tagVersionShow }}. 修订时间: {{ revision.revisionTime }} 修订人: {{ revision.reviser }}
+            {{ revision.tagVersionShow }} 修订时间: {{ revision.revisionTime }} 修订人: {{ revision.reviser }}
           </div>
           <el-button 
             type="text" 
@@ -67,7 +72,7 @@
           {{ selectedVersion.tagNm || '无名称' }}
         </div>
         <div class="detail-meta">
-          {{ selectedVersion.tagVersionShow }}. 修订时间: {{ selectedVersion.revisionTime }} 修订人: {{ selectedVersion.reviser }}
+          {{ selectedVersion.tagVersionShow }} 修订时间: {{ selectedVersion.revisionTime }} 修订人: {{ selectedVersion.reviser }}
         </div>
         
         <div class="detail-content">
@@ -91,7 +96,7 @@
         </div>
       </div>
       <span slot="footer" class="dialog-footer">
-        <el-button @click="handleVersionRollback">版本回溯</el-button>
+        <el-button v-if="!systemStatus && canEditTag" @click="handleVersionRollback">版本回溯</el-button>
       </span>
     </el-dialog>
     
@@ -112,6 +117,14 @@ export default {
     tagData: {
       type: Object,
       default: () => ({})
+    },
+    systemStatus: {
+      type: Boolean,
+      default: true
+    },
+    canEditTag: {
+      type: Boolean,
+      default: false
     }
   },
   data() {

+ 111 - 80
web/src/views/aiTagging/taggingSystemManage/components/TagTest.vue

@@ -10,7 +10,7 @@
           <el-input
             type="textarea"
             v-model="testText"
-            placeholder="请输入要测试的文本内容"
+            placeholder="请输入职业、投向及贷款用途"
             :rows="8"
             style="width: 100%; margin-bottom: 16px;"
           ></el-input>
@@ -46,54 +46,39 @@
     </div>
     
     <!-- 智能打标结果区 -->
-    <div class="smart-tagging-result" v-if="taggingResults.length > 0">
+    <div class="smart-tagging-result">
       <div class="result-header">
         <h4 class="section-title">智能打标结果</h4>
-        <div class="result-info">
+        <div class="result-info" v-if="taggingResults.length > 0">
           <span>打标结果数:{{ taggingResults.length }}</span>
           <span>打标时间:{{ taggingTime }}</span>
         </div>
       </div>
       
-      <div class="result-content">
+      <!-- 有结果时显示结果卡片 -->
+      <div class="result-content" v-if="taggingResults.length > 0">
         <el-card 
           v-for="(result, index) in taggingResults" 
           :key="index"
           class="tag-result-card"
         >
           <div class="tag-result-header">
-            <span class="tag-code">{{ result.code }}</span>
-            <span class="tag-name">{{ result.name }}</span>
+            <span class="tag-path">{{ result.path }}</span>
           </div>
           <div class="tag-result-body">
             <div class="result-item">
-              <span class="result-label">打标手段:</span>
-              <el-input 
-                v-model="result.method" 
-                readonly 
-                size="small"
-                class="result-input"
-              ></el-input>
-            </div>
-            <div class="result-item">
               <span class="result-label">打标依据:</span>
-              <el-input 
-                v-model="result.reason" 
-                readonly 
-                size="small"
-                type="textarea"
-                :rows="3"
-                class="result-input"
-              ></el-input>
+              <div class="result-text">{{ result.reason || '暂无依据' }}</div>
             </div>
           </div>
         </el-card>
       </div>
-    </div>
-    
-    <!-- 空结果提示 -->
-    <div class="empty-result" v-else>
-      <p>暂无打标结果,请输入文本或上传文件并点击智能打标按钮</p>
+      
+      <!-- 空结果提示 -->
+      <div class="empty-result" v-else>
+        <img src="@/assets/images/noData.png" alt="暂无数据">
+        <p>{{ noMatchFound ? '未找到匹配的标签' : '暂无数据' }}</p>
+      </div>
     </div>
   </div>
 </template>
@@ -117,6 +102,8 @@ export default {
       uploadedFile: null,
       // 智能打标结果
       taggingResults: [],
+      // 是否未找到匹配的标签
+      noMatchFound: false,
       // 打标时间
       taggingTime: '',
       // 轮询相关
@@ -146,7 +133,7 @@ export default {
       // 显示加载提示
       this.$message({
         message: '正在进行智能打标,请稍候...',
-        type: 'loading',
+        type: 'info',
         duration: 3000
       });
       
@@ -166,7 +153,7 @@ export default {
       // 构建请求参数
       const requestData = {
         businessAttr: businessAttr,
-        tag_category_id: this.tagSystem.id || this.tagSystem.categoryCode || '',
+        tagCategoryId: this.tagSystem.id || this.tagSystem.categoryCode || '',
         phrase: this.testText.trim()
       };
       
@@ -206,7 +193,7 @@ export default {
       const queryParams = {
         businessAttr: businessAttr
       };
-      
+
       // 调用查询接口
       yufp.service.request({
         url: backend.tagServer + "/api/fastapi/query",
@@ -215,44 +202,66 @@ export default {
         callback: (code, error, response) => {
           if (response.code == '0') {
             // 处理查询结果
-            const resultData = response.data;
-            if (resultData && (Array.isArray(resultData) || resultData.result)) {
-              // 有结果,停止轮询
+            // 实际数据在 response.data.data 中
+            const responseData = response.data && response.data.data ? response.data.data : null;
+
+            if (!responseData) {
+              // 无数据,继续轮询
+              this.pollingTimer = setTimeout(() => {
+                this.pollTaggingResult(businessAttr, loadingInstance);
+              }, 3000);
+              return;
+            }
+
+            // 获取state状态:0-打标执行中;1-打标完成;2-已确认;3-已推送
+            const state = responseData.state;
+
+            if (state === 0) {
+              // 打标执行中,继续轮询
+              this.pollingTimer = setTimeout(() => {
+                this.pollTaggingResult(businessAttr, loadingInstance);
+              }, 3000);
+            } else if (state === 1 || state === 2 || state === 3) {
+              // 打标完成、已确认或已推送,停止轮询
               loadingInstance.close();
               this.clearPolling();
-              
-              if (Array.isArray(resultData)) {
-                this.taggingResults = resultData.map(item => ({
-                  code: item.label_code || item.tag_code || '',
-                  name: item.label || item.tag_name || '',
-                  method: '',
-                  reason: item.desc || ''
-                }));
-              } else if (resultData.result) {
-                try {
-                  const parsedResult = JSON.parse(resultData.result);
-                  if (Array.isArray(parsedResult)) {
-                    this.taggingResults = parsedResult.map(item => ({
-                      code: item.label_code || item.tag_code || '',
-                      name: item.label || item.tag_name || '',
-                      method: '',
-                      reason: item.desc || ''
-                    }));
-                  } else {
-                    this.taggingResults = [];
-                    this.$message.warning('打标结果格式异常');
+
+              // 处理result字段
+              let resultList = [];
+              if (responseData.result) {
+                if (typeof responseData.result === 'string') {
+                  try {
+                    const parsedResult = JSON.parse(responseData.result);
+                    if (Array.isArray(parsedResult)) {
+                      resultList = parsedResult;
+                    }
+                  } catch (error) {
+                    console.error('解析result失败:', error);
                   }
-                } catch (error) {
-                  this.taggingResults = [];
-                  this.$message.warning('打标结果解析失败');
+                } else if (Array.isArray(responseData.result)) {
+                  resultList = responseData.result;
                 }
               }
-              
-              // 设置打标时间
-              this.taggingTime = new Date().toLocaleString();
-              
-              this.$message.success('智能打标完成');
-              
+
+              // 处理打标结果(测试环境不过滤passr状态)
+              this.taggingResults = resultList.map(item => ({
+                code: item.tag_code || '',
+                name: item.tag_name || '',
+                path: item.tag_path || '',
+                method: '智能打标',
+                reason: item.desc || ''
+              }));
+
+              // 设置打标时间(使用接口返回的insert_time)
+              this.taggingTime = responseData.insert_time || new Date().toLocaleString();
+
+              if (this.taggingResults.length > 0) {
+                this.$message.success('智能打标完成');
+                this.noMatchFound = false;
+              } else {
+                this.noMatchFound = true;
+              }
+
               // 触发父组件事件
               this.$emit('smart-tagging', {
                 inputType: this.activeInputTab,
@@ -261,7 +270,7 @@ export default {
                 time: this.taggingTime
               });
             } else {
-              // 无结果,继续轮询
+              // 未知状态,继续轮询
               this.pollingTimer = setTimeout(() => {
                 this.pollTaggingResult(businessAttr, loadingInstance);
               }, 3000);
@@ -376,7 +385,6 @@ export default {
   display: flex;
   justify-content: flex-end;
   gap: 12px;
-  margin-top: 16px;
 }
 
 /* 智能打标结果区 */
@@ -391,9 +399,8 @@ export default {
   display: flex;
   justify-content: space-between;
   align-items: center;
-  margin-bottom: 16px;
-  padding-bottom: 12px;
-  border-bottom: 1px solid #e8e8e8;
+  margin-bottom: 0;
+  padding-bottom: 0;
 }
 
 .result-info {
@@ -426,12 +433,7 @@ export default {
   gap: 12px;
 }
 
-.tag-code {
-  font-weight: 600;
-  color: #1890ff;
-}
-
-.tag-name {
+.tag-path {
   font-weight: 600;
   color: #303133;
 }
@@ -453,20 +455,49 @@ export default {
   font-size: 12px;
   color: #606266;
   margin-bottom: 4px;
+  text-align: left;
 }
 
 .result-input {
   font-size: 14px;
 }
 
+.result-text {
+  font-size: 14px;
+  color: #303133;
+  line-height: 1.6;
+  padding: 8px 12px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+  min-height: 60px;
+  text-align: left;
+}
+
 /* 空结果提示 */
 .empty-result {
-  text-align: center;
-  padding: 40px 20px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 60px 20px;
   color: #909399;
-  background-color: #f5f7fa;
-  border-radius: 4px;
-  margin-top: 24px;
+  background-color: #fafafa;
+  margin-top: 16px;
+  min-height: 200px;
+}
+
+.empty-result img {
+  width: 120px;
+  height: 120px;
+  margin-bottom: 16px;
+  opacity: 0.6;
+}
+
+.empty-result p {
+  font-size: 14px;
+  color: #909399;
+  margin: 0;
+  line-height: 1.5;
 }
 
 /* 响应式设计 */

+ 78 - 12
web/src/views/aiTagging/taggingSystemManage/components/TagTree.vue

@@ -4,9 +4,9 @@
     <div class="tree-header">
       <h3 class="tree-title">{{ tagSystem.name || '标签树' }}</h3>
       <div class="tree-header-buttons">
-        <el-button v-if="!systemStatus" type="text" size="small" icon="el-icon-plus" @click="handleAddRootNode"></el-button>
+        <el-button v-if="!systemStatus && canEditTag" type="text" size="small" icon="el-icon-plus" @click="handleAddRootNode"></el-button>
         <el-button 
-          v-if="!systemStatus && isTagTreeEmpty" 
+          v-if="!systemStatus && canEditTag && isTagTreeEmpty" 
           type="text" 
           size="small" 
           icon="el-icon-upload2" 
@@ -26,6 +26,7 @@
     
     <!-- 标签树 -->
     <el-tree
+      ref="tree"
       :data="filteredTreeData"
       :props="treeProps"
       node-key="id"
@@ -45,9 +46,10 @@
       <TagDetailEdit
         :form-data="addForm"
         :is-add-mode="true"
-        :parent-options="tagTreeData"
+        :parent-options="parentOptionsWithRoot"
         :tree-props="treeProps"
         :category-id="tagSystem.id"
+        :category-name="tagSystem.name || tagSystem.categoryNm || ''"
         @save="handleAddSave"
         @cancel="handleAddCancel"
       />
@@ -63,7 +65,7 @@
         <!-- 提示信息 -->
         <div class="import-hint">
           <i class="el-icon-info"></i>
-          <span>请先下载模板,按照模板要求填写后上传</span>
+          <span>请先<span class="download-link" @click="handleDownloadTemplate">下载模板</span>,按照模板要求填写后上传</span>
         </div>
         
         <!-- 文件上传区域 -->
@@ -78,10 +80,10 @@
           <div class="upload-content">
             <i class="el-icon-upload upload-icon"></i>
             <p>点击或拖拽文件到此处上传</p>
-            <p class="upload-format">仅支持单个Excel(xlsx)或CSV格式文件,文件不超过5MB</p>
-            <p v-if="file" class="file-name">{{ file.name }}</p>
+            <p class="upload-format">仅支持单个Excel(.xlsx)或CSV格式文件,文件内数据量不超过1000条</p>
           </div>
         </div>
+        <p v-if="file" class="file-name">{{ file.name }}</p>
       </div>
       <span slot="footer" class="dialog-footer">
         <el-button @click="showImportDialog = false">取消</el-button>
@@ -126,6 +128,10 @@ export default {
     systemStatus: {
       type: Boolean,
       default: true
+    },
+    canEditTag: {
+      type: Boolean,
+      default: false
     }
   },
   data() {
@@ -174,15 +180,38 @@ export default {
     // 判断标签树数据是否为空
     isTagTreeEmpty() {
       return !this.tagTreeData || this.tagTreeData.length === 0;
+    },
+    // 构建上级标签选项(包含标签体系作为根节点)
+    parentOptionsWithRoot() {
+      const categoryName = this.tagSystem.name || this.tagSystem.categoryNm || '标签体系';
+      const rootNode = {
+        id: this.tagSystem.id || 'root',
+        tagId: this.tagSystem.id || 'root',
+        tagNm: categoryName,
+        tagName: categoryName,
+        // 标记为根节点
+        isRoot: true,
+        children: this.tagTreeData || []
+      };
+      return [rootNode];
     }
   },
   methods: {
     // 新增根节点(一级标签)
     handleAddRootNode() {
       console.log('新增根节点(一级标签)');
+      // 构建标签体系根节点对象
+      const categoryName = this.tagSystem.name || this.tagSystem.categoryNm || '标签体系';
+      const rootNode = {
+        id: this.tagSystem.id || 'root',
+        tagId: this.tagSystem.id || 'root',
+        tagNm: categoryName,
+        tagName: categoryName,
+        isRoot: true
+      };
       this.addForm = {
         name: '',
-        parent: '',
+        parent: rootNode, // 默认选中标签体系根节点
         description: '',
         keywords: '',
         judgment: ''
@@ -197,6 +226,10 @@ export default {
     
     // 渲染树节点内容
     renderContent(h, { node, data, store }) {
+      // 判断是否允许编辑:已停用状态 且 有编辑权限
+      const allowEdit = !this.systemStatus && this.canEditTag;
+      // 判断是否允许新增:最多4个层级,第4层级不显示新增操作
+      const allowAdd = allowEdit && node.level < 4;
       return h('span', { class: 'custom-tree-node' }, [
         h('span', {
           class: 'node-label',
@@ -204,7 +237,7 @@ export default {
         }, data.tagNm),
         h('span', {
           class: 'node-dropdown',
-          style: { display: !this.systemStatus ? 'inline-block' : 'none' }
+          style: { display: allowEdit ? 'inline-block' : 'none' }
         }, [
           h('el-dropdown', {
             props: { trigger: 'click' },
@@ -220,11 +253,11 @@ export default {
             }, [
               h('el-dropdown-item', {
                 props: { command: 'add' },
-                style: { display: !this.systemStatus ? 'block' : 'none' }
+                style: { display: allowAdd ? 'block' : 'none' }
               }, '新增'),
               h('el-dropdown-item', {
                 props: { command: 'delete' },
-                style: { display: !this.systemStatus ? 'block' : 'none' }
+                style: { display: allowEdit ? 'block' : 'none' }
               }, '删除')
             ])
           ])
@@ -305,9 +338,9 @@ export default {
       formData.append('file', this.file);
       formData.append('reviser', yufp.session.userId || ''); // 当前登录人id
       formData.append('categoryId', this.tagSystem.id); // 当前体系id
-      
+
       console.log('导入标签请求数据:', formData);
-      
+
       // 调用接口
       yufp.service.request({
         url: backend.tagServer + "/api/aitagtaginfo/batchImport",
@@ -328,6 +361,29 @@ export default {
           }
         }
       });
+    },
+
+    // 下载模板
+    handleDownloadTemplate() {
+      console.log('开始下载模板...');
+      // 直接使用浏览器下载,避免AJAX请求处理文件流的问题
+      const downloadUrl = backend.tagServer + "/api/aitagtaginfo/downloadFile";
+      
+      // 方法1:使用window.open(简单直接)
+      // window.open(downloadUrl, '_blank');
+      
+      // 方法2:使用隐藏的iframe(不会打开新窗口,体验更好)
+      const iframe = document.createElement('iframe');
+      iframe.style.display = 'none';
+      iframe.src = downloadUrl;
+      document.body.appendChild(iframe);
+      
+      // 延迟移除iframe
+      setTimeout(() => {
+        document.body.removeChild(iframe);
+      }, 5000);
+      
+      this.$message.success('模板下载中,请查看下载文件');
     }
   }
 }
@@ -447,7 +503,17 @@ export default {
   padding: 12px;
   background-color: #ecf5ff;
   border-radius: 4px;
+  color: #606266;
+}
+
+.import-hint .download-link {
   color: #409eff;
+  cursor: pointer;
+  text-decoration: underline;
+}
+
+.import-hint .download-link:hover {
+  color: #66b1ff;
 }
 
 .upload-area {

+ 100 - 9
web/src/views/aiTagging/taggingSystemManage/components/TreeSelect.vue

@@ -29,6 +29,7 @@
         :highlight-current="true"
         :current-node-key="selectedNode?.id"
         @node-click="handleNodeClick"
+        :render-content="renderContent"
       ></el-tree>
     </div>
   </div>
@@ -56,6 +57,10 @@ export default {
     placeholder: {
       type: String,
       default: '请选择'
+    },
+    categoryName: {
+      type: String,
+      default: ''
     }
   },
   data() {
@@ -86,14 +91,25 @@ export default {
     },
     displayText() {
       console.log('TreeSelect displayText - selectedNode:', this.selectedNode);
-      console.log('TreeSelect displayText - resolvedTreeProps:', this.resolvedTreeProps);
+      console.log('TreeSelect displayText - inputText:', this.inputText);
+      console.log('TreeSelect displayText - categoryName:', this.categoryName);
+      
       if (this.selectedNode) {
         const labelField = this.resolvedTreeProps.label;
         const labelValue = this.selectedNode[labelField];
         console.log('TreeSelect displayText - labelField:', labelField, 'labelValue:', labelValue);
-        return this.inputText || (labelValue || '');
+        
+        // 始终显示节点本身的名称(标签体系名称或标签名称)
+        console.log('TreeSelect displayText - 显示节点名称:', labelValue);
+        return labelValue || '';
       }
-      return this.inputText || '';
+      
+      // 如果没有选中节点,但有输入文本,显示输入文本
+      if (this.inputText) {
+        return this.inputText;
+      }
+      
+      return '';
     },
     filteredData() {
       console.log('TreeSelect filteredData - data:', this.data);
@@ -142,26 +158,61 @@ export default {
         console.log('TreeSelect value change - newVal:', newVal);
         if (newVal) {
           try {
-            // 如果传入的值是对象,直接使用该对象
+            // 如果传入的值是对象,尝试在data中查找对应节点
             if (typeof newVal === 'object' && newVal !== null) {
-              this.selectedNode = newVal;
-              console.log('TreeSelect value change - 直接使用对象:', this.selectedNode);
+              // 尝试根据ID在data中查找节点
+              const nodeId = newVal.id;
+              if (nodeId) {
+                const foundNode = this.findNodeById(this.data, nodeId);
+                if (foundNode) {
+                  this.selectedNode = foundNode;
+                  console.log('TreeSelect value change - 根据对象ID查找节点:', this.selectedNode);
+                } else {
+                  // 如果找不到,使用第一个根节点作为默认值
+                  if (this.data && this.data.length > 0) {
+                    this.selectedNode = this.data[0];
+                    console.log('TreeSelect value change - 找不到对象,使用第一个根节点:', this.selectedNode);
+                  } else {
+                    this.selectedNode = null;
+                  }
+                }
+              } else {
+                this.selectedNode = newVal;
+                console.log('TreeSelect value change - 直接使用对象(无ID):', this.selectedNode);
+              }
             } else {
               // 如果传入的值是ID,查找对应的节点
               const nodeId = newVal;
               if (nodeId) {
                 this.selectedNode = this.findNodeById(this.data, nodeId);
                 console.log('TreeSelect value change - 根据ID查找节点:', this.selectedNode);
+                // 如果找不到,使用第一个根节点作为默认值
+                if (!this.selectedNode && this.data && this.data.length > 0) {
+                  this.selectedNode = this.data[0];
+                  console.log('TreeSelect value change - 找不到ID对应节点,使用第一个根节点:', this.selectedNode);
+                }
               } else {
                 this.selectedNode = null;
               }
             }
           } catch (error) {
             console.error('处理选中值失败:', error);
-            this.selectedNode = null;
+            // 出错时,使用第一个根节点作为默认值
+            if (this.data && this.data.length > 0) {
+              this.selectedNode = this.data[0];
+              console.log('TreeSelect value change - 处理出错,使用第一个根节点:', this.selectedNode);
+            } else {
+              this.selectedNode = null;
+            }
           }
         } else {
-          this.selectedNode = null;
+          // 值为空时,使用第一个根节点作为默认值
+          if (this.data && this.data.length > 0) {
+            this.selectedNode = this.data[0];
+            console.log('TreeSelect value change - 值为空,使用第一个根节点:', this.selectedNode);
+          } else {
+            this.selectedNode = null;
+          }
         }
       },
       immediate: true
@@ -194,7 +245,12 @@ export default {
       console.log('点击后showTree:', this.showTree);
     },
     handleNodeClick(data, node) {
-      console.log('点击节点:', data, 'node:', node);
+      console.log('点击节点:', data, 'node:', node, 'level:', node.level);
+      // 最多选中到第四级,第五级及之后不允许选中
+      if (node.level >= 5) {
+        console.log('第五级及之后不允许选中');
+        return;
+      }
       const labelField = this.resolvedTreeProps.label;
       const labelValue = data[labelField];
       console.log('点击节点 - labelField:', labelField, 'labelValue:', labelValue);
@@ -247,6 +303,27 @@ export default {
       if (treeSelectEl && !treeSelectEl.contains(event.target)) {
         this.showTree = false;
       }
+    },
+    // 自定义树节点渲染
+    renderContent(h, { node, data, store }) {
+      // 第五级及之后节点
+      const isDisabled = node.level >= 5;
+      const labelField = this.resolvedTreeProps.label;
+      const labelValue = data[labelField] || '';
+      
+      return h('span', {
+        class: {
+          'tree-node-content': true,
+          'disabled-node': isDisabled
+        },
+        style: {
+          cursor: isDisabled ? 'not-allowed' : 'pointer'
+        }
+      }, [
+        h('span', {
+          class: 'node-label'
+        }, labelValue)
+      ]);
     }
   }
 }
@@ -283,4 +360,18 @@ export default {
 .tree-dropdown ::v-deep .el-tree-node.is-current > .el-tree-node__content {
   background-color: #ecf5ff;
 }
+.disabled-node {
+  color: #c0c4cc;
+  pointer-events: auto; /* 允许鼠标事件,以便显示cursor: not-allowed */
+}
+
+.tree-node-content {
+  display: flex;
+  align-items: center;
+  width: 100%;
+}
+
+.node-label {
+  flex: 1;
+}
 </style>

+ 108 - 51
web/src/views/aiTagging/taggingSystemManage/index.vue

@@ -18,7 +18,7 @@
     <!-- 标签体系卡片网格 -->
     <div class="tag-system-grid">
       <!-- 新建标签体系卡片 -->
-      <div class="tag-system-card create-card" @click="handleCreateTagSystem">
+      <div class="tag-system-card create-card" v-if="checkCtrl('aitagTagcategoryCreate')" @click="handleCreateTagSystem">
         <div class="create-icon">
           <i class="el-icon-plus"></i>
         </div>
@@ -29,28 +29,28 @@
       <!-- 标签体系卡片 -->
       <div v-for="system in tagSystems" :key="system.id" class="tag-system-card">
         <div class="card-header">
-          <div class="system-name">{{ system.categoryNm }}</div>
+          <div class="system-name" :title="system.categoryNm">{{ system.categoryNm }}</div>
           <div class="card-actions">
-            <el-button v-if="system.state !== 0" type="text" icon="el-icon-edit" @click.stop="handleEditTagSystem(system)"></el-button>
-            <el-dropdown trigger="click" @command="(command) => handleDropdownCommand(command, system)">
+            <el-button v-if="system.state !== 0 && checkCtrl('update')" type="text" icon="el-icon-edit" @click.stop="handleEditTagSystem(system)"></el-button>
+            <el-dropdown v-if="checkCtrl('enable') || checkCtrl('disable') || checkCtrl('delete')" trigger="click" @command="(command) => handleDropdownCommand(command, system)">
               <el-button type="text" icon="el-icon-more"></el-button>
               <el-dropdown-menu slot="dropdown">
-                <el-dropdown-item v-if="system.state === 0" command="disable">停用</el-dropdown-item>
-                <el-dropdown-item v-else command="enable">启用</el-dropdown-item>
-                <el-dropdown-item v-if="system.state !== 0" command="delete" divided>删除</el-dropdown-item>
+                <el-dropdown-item v-if="system.state === 0 && checkCtrl('disable')" command="disable">停用</el-dropdown-item>
+                <el-dropdown-item v-else-if="system.state !== 0 && checkCtrl('enable')" command="enable">启用</el-dropdown-item>
+                <el-dropdown-item v-if="system.state !== 0 && checkCtrl('delete')" command="delete" divided>删除</el-dropdown-item>
               </el-dropdown-menu>
             </el-dropdown>
           </div>
         </div>
         <div class="system-status">
           <el-tag :type="system.state === 0 ? 'success' : 'info'">
-            {{ system.state === 0 ? '已启用' : '已停用' }}
+            {{ system.state === 0 ? '已启用' : '未启用' }}
           </el-tag>
         </div>
         <div class="system-desc">{{ system.categoryDesc }}</div>
         <div class="system-meta">
           <span class="tag-count">标签数量:{{ system.tagNum || 0 }}</span>
-          <span class="create-date">{{ system.createDate || '' }}</span>
+          <span class="update-time"><i class="el-icon-date"></i> {{ system.updateTime || '' }}</span>
         </div>
         <div class="card-footer">
           <el-button type="text" @click.stop="handleViewTags(system)">
@@ -65,7 +65,7 @@
       <el-pagination
         :current-page="currentPage"
         :page-size="pageSize"
-        :page-sizes="[10, 20, 50]"
+        :page-sizes="[5, 11, 17]"
         layout="total, sizes, prev, pager, next, jumper"
         :total="totalCount"
         @size-change="handleSizeChange"
@@ -113,7 +113,7 @@ export default {
       // 分页数据
       totalCount: 0,
       currentPage: 1,
-      pageSize: 10,
+      pageSize: 5,
       
       // 创建标签体系对话框
       createDialogVisible: false,
@@ -127,6 +127,15 @@ export default {
     // 默认加载标签体系列表
     this.loadTagSystems();
   },
+  watch: {
+    // 监听路由参数变化,实现返回时自动刷新
+    '$route.query.refresh': {
+      handler() {
+        this.loadTagSystems();
+      },
+      immediate: true
+    }
+  },
   methods: {
     // 加载标签体系列表
     loadTagSystems() {
@@ -135,7 +144,7 @@ export default {
         url: backend.tagServer + "/api/aitag-tagcategory/list",
         method: 'get',
         data: {
-          categoryNm: this.searchForm.name,
+          categoryNm: this.searchForm.categoryNm,
           page: this.currentPage,
           pageSize: this.pageSize
         },
@@ -171,9 +180,15 @@ export default {
     },
     
     // 处理创建成功
-    handleCreateSuccess() {
-      console.log('标签体系创建成功')
+    handleCreateSuccess(createdSystem) {
+      console.log('标签体系创建成功:', createdSystem)
       this.createDialogVisible = false
+      
+      // 如果创建成功且有返回数据,自动跳转到标签列表
+      if (createdSystem) {
+        console.log('自动跳转到标签列表:', createdSystem)
+        this.handleViewTags(createdSystem);
+      } 
       this.loadTagSystems();
     },
     
@@ -230,45 +245,61 @@ export default {
 
     // 启用标签体系
     handleEnableTagSystem(system) {
-      console.log('启用标签体系:', system.categoryNm)
-      
-      // 调用后端接口启用标签体系
-      yufp.service.request({
-        url: backend.tagServer + "/api/aitag-tagcategory/enable?id=" + system.id,
-        method: 'post',
-        data: {},
-        callback: (code, error, response) => {
-          if (response.code == '0') {
-            this.$message.success(`启用标签体系: ${system.categoryNm}`)
-            
-            // 重新加载标签体系列表
-            this.loadTagSystems();
-          } else {
-            this.$message.error(response.message || '启用标签体系失败');
+      this.$confirm(`您确定要启用标签体系「${system.categoryNm}」吗?\n启用后该标签体系将可用于业务打标。`, '确认启用', {
+        confirmButtonText: '启用',
+        cancelButtonText: '取消',
+        type: 'success'
+      }).then(() => {
+        console.log('启用标签体系:', system.categoryNm)
+        
+        // 调用后端接口启用标签体系
+        yufp.service.request({
+          url: backend.tagServer + "/api/aitag-tagcategory/enable?id=" + system.id,
+          method: 'post',
+          data: {},
+          callback: (code, error, response) => {
+            if (response.code == '0') {
+              this.$message.success(`启用标签体系: ${system.categoryNm}`)
+              
+              // 重新加载标签体系列表
+              this.loadTagSystems();
+            } else {
+              this.$message.error(response.message || '启用标签体系失败');
+            }
           }
-        }
+        });
+      }).catch(() => {
+        console.log('用户取消启用标签体系');
       });
     },
 
     // 停用标签体系
     handleDisableTagSystem(system) {
-      console.log('停用标签体系:', system.categoryNm)
-      
-      // 调用后端接口停用标签体系
-      yufp.service.request({
-        url: backend.tagServer + "/api/aitag-tagcategory/disable?id=" + system.id,
-        method: 'post',
-        data: {},
-        callback: (code, error, response) => {
-          if (response.code == '0') {
-            this.$message.success(`停用标签体系: ${system.categoryNm}`)
-            
-            // 重新加载标签体系列表
-            this.loadTagSystems();
-          } else {
-            this.$message.error(response.message || '停用标签体系失败');
+      this.$confirm(`您确定要停用标签体系「${system.categoryNm}」吗?\n停用后该标签体系将不可用于业务打标。\n已打标的历史数据不受影响,仍可正常查看。`, '确认停用', {
+        confirmButtonText: '停用',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        console.log('停用标签体系:', system.categoryNm)
+        
+        // 调用后端接口停用标签体系
+        yufp.service.request({
+          url: backend.tagServer + "/api/aitag-tagcategory/disable?id=" + system.id,
+          method: 'post',
+          data: {},
+          callback: (code, error, response) => {
+            if (response.code == '0') {
+              this.$message.success(`停用标签体系: ${system.categoryNm}`)
+              
+              // 重新加载标签体系列表
+              this.loadTagSystems();
+            } else {
+              this.$message.error(response.message || '停用标签体系失败');
+            }
           }
-        }
+        });
+      }).catch(() => {
+        console.log('用户取消停用标签体系');
       });
     },
 
@@ -309,12 +340,25 @@ export default {
     // 查看标签列表
     handleViewTags(system) {
       console.log('查看标签列表:', system.name)
-      // this.$message.success(`查看标签列表: ${system.name}`)
-      // 这里可以跳转到标签列表页面
-      // 使用Vue Router跳转到编辑页面,并携带参数
+      // 构建权限对象
+      const permissions = {
+        canEnableTagSystem: this.checkCtrl('enable'),
+        canDisableTagSystem: this.checkCtrl('disable'),
+        canEditTag: this.checkCtrl('labelEdit')
+      };
+      
+      console.log('存储权限数据到 Vuex:', permissions);
+      
+      // 将权限数据存储到 Vuex
+      this.$store.dispatch('aiTagPermissions/setPermissions', permissions);
+      
+      // 跳转到标签体系编辑页面,传递体系名称用于动态设置页签标题
       this.$router.push({
         path: '/tag-system-edit',
-        query: { id: system.id }
+        query: { 
+          id: system.id,
+          name: system.categoryNm
+        }
       })
     },
 
@@ -438,6 +482,11 @@ export default {
   font-size: 16px;
   font-weight: 500;
   color: #303133;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  flex: 1;
+  margin-right: 12px;
 }
 
 .card-actions {
@@ -457,6 +506,14 @@ export default {
   line-height: 1.4;
   margin-bottom: 16px;
   min-height: 40px;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  word-wrap: break-word;
+  overflow-wrap: break-word;
+  word-break: break-all;
 }
 
 .system-meta {
@@ -474,7 +531,7 @@ export default {
   gap: 4px;
 }
 
-.create-date {
+.update-time {
   display: flex;
   align-items: center;
   gap: 4px;

+ 262 - 0
web/src/views/aiTagging/taggingTest/TagTestComponent.vue

@@ -0,0 +1,262 @@
+<template>
+  <div class="tag-test-component">
+    <div class="component-header">
+      <h3>标签树形结构</h3>
+    </div>
+    
+    <div class="component-content">
+      <el-tree
+        :data="treeData"
+        node-key="id"
+        default-expand-all
+        show-checkbox
+        @node-click="handleNodeClick"
+        class="tag-tree"
+      >
+        <template slot-scope="{ node, data }">
+          <span class="custom-tree-node">
+            <span class="node-label">{{ node.label }}</span>
+            <span class="node-actions" v-if="data.children && data.children.length > 0">
+              <span class="node-count">{{ data.children.length }}</span>
+              <el-dropdown trigger="click" @command="(command) => handleDropdownCommand(command, data)">
+                <span class="el-dropdown-link">
+                  <el-button size="mini" type="primary" plain>
+                    操作
+                    <i class="el-icon-arrow-down el-icon--right"></i>
+                  </el-button>
+                </span>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item command="add">新增</el-dropdown-item>
+                  <el-dropdown-item command="delete">删除</el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </span>
+          </span>
+        </template>
+      </el-tree>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'TagTestComponent',
+  data() {
+    return {
+      treeData: [
+        {
+          id: 1,
+          label: '海洋经济',
+          children: [
+            {
+              id: 2,
+              label: '海洋产业(A)',
+              children: []
+            },
+            {
+              id: 3,
+              label: '海洋科教(B)',
+              children: []
+            },
+            {
+              id: 4,
+              label: '海洋公共管理服务(C)',
+              children: [
+                {
+                  id: 5,
+                  label: '海洋管理(C18)',
+                  children: []
+                },
+                {
+                  id: 6,
+                  label: '海洋社会组织(20)与国际组织(21)',
+                  children: []
+                },
+                {
+                  id: 7,
+                  label: '海洋技术服务(20)',
+                  children: []
+                },
+                {
+                  id: 8,
+                  label: '海洋信息服务(21)',
+                  children: []
+                },
+                {
+                  id: 9,
+                  label: '海洋生态环境保护修复(22)',
+                  children: []
+                },
+                {
+                  id: 10,
+                  label: '海洋地质勘查(23)',
+                  children: [
+                    {
+                      id: 11,
+                      label: '海洋矿产地质勘查(231)',
+                      children: [
+                        {
+                          id: 12,
+                          label: '海洋能源矿产地质勘查(2311)',
+                          children: []
+                        },
+                        {
+                          id: 13,
+                          label: '海洋固体矿产地质勘查(2312)',
+                          children: []
+                        },
+                        {
+                          id: 14,
+                          label: '其他海洋矿产地质勘查(2319)',
+                          children: []
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ]
+            },
+            {
+              id: 15,
+              label: '海洋上游相关产业(D)',
+              children: []
+            },
+            {
+              id: 16,
+              label: '海洋下游相关产业(E)',
+              children: []
+            }
+          ]
+        }
+      ]
+    }
+  },
+  methods: {
+    // 处理节点点击
+    handleNodeClick(data, node) {
+      console.log('点击节点:', data, node);
+    },
+    
+    // 处理下拉菜单命令
+    handleDropdownCommand(command, data) {
+      console.log('命令:', command, '数据:', data);
+      if (command === 'add') {
+        this.$message.info('新增功能开发中');
+      } else if (command === 'delete') {
+        this.$confirm('确定要删除这个节点吗?', '删除确认', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          this.$message.success('删除成功');
+        }).catch(() => {
+          console.log('取消删除');
+        });
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.tag-test-component {
+  padding: 16px;
+  background-color: #fff;
+  border-radius: 4px;
+  border: 1px solid #e8e8e8;
+  height: 100%;
+}
+
+.component-header {
+  margin-bottom: 16px;
+  border-bottom: 1px solid #e8e8e8;
+  padding-bottom: 8px;
+}
+
+.component-header h3 {
+  margin: 0;
+  font-size: 14px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.component-content {
+  height: calc(100% - 44px);
+  overflow: auto;
+}
+
+.custom-tree-node {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  width: 100%;
+  padding: 4px 0;
+}
+
+.node-label {
+  flex: 1;
+  word-break: break-all;
+  font-size: 14px;
+  color: #303133;
+}
+
+.node-actions {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.node-count {
+  background-color: #f0f9eb;
+  color: #67c23a;
+  padding: 2px 8px;
+  border-radius: 10px;
+  font-size: 12px;
+  min-width: 24px;
+  text-align: center;
+}
+
+/* 调整树形结构样式 */
+.tag-tree {
+  height: 100%;
+}
+
+.el-tree-node {
+  white-space: normal;
+}
+
+.el-tree-node__content {
+  height: auto;
+  min-height: 36px;
+  align-items: center;
+  padding: 0 8px;
+}
+
+.el-tree-node__content:hover {
+  background-color: #f5f7fa;
+}
+
+/* 调整下拉菜单样式 */
+.el-dropdown-link .el-button {
+  padding: 0 8px;
+  font-size: 12px;
+  border-radius: 4px;
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+  .tag-test-component {
+    padding: 12px;
+  }
+  
+  .custom-tree-node {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 8px;
+  }
+  
+  .node-actions {
+    align-self: flex-start;
+  }
+}
+</style>

+ 640 - 0
web/src/views/aiTagging/taggingTest/index.vue

@@ -0,0 +1,640 @@
+<template>
+  <div class="tag-test">
+    <div class="treeTest">
+      <TagTestComponent />
+    </div>
+    <!-- 测试输入区 -->
+    <div class="test-input-section">
+      <h4 class="section-title">测试输入</h4>
+      
+      <!-- 输入信息区域 -->
+      <div class="input-section">
+        <!-- 第一行:标签体系 + 贷款申请编号 -->
+        <div class="form-row">
+          <div class="form-item">
+            <label class="form-label">标签体系:</label>
+            <el-select v-model="tagCategoryId" placeholder="请选择标签体系" style="width: 250px;">
+              <el-option v-for="(system, index) in tagSystems" :key="index" :label="system.categoryNm" :value="system.categoryId"></el-option>
+            </el-select>
+          </div>
+          <div class="form-item">
+            <label class="form-label">贷款申请编号:</label>
+            <div class="business-attr-wrapper">
+              <el-input v-model="businessAttr" readonly style="width: 200px;"></el-input>
+              <el-button type="text" @click="generateBusinessAttr">更新</el-button>
+            </div>
+          </div>
+        </div>
+        
+        <!-- 第二行:用户ID + 用户姓名 -->
+        <div class="form-row">
+          <div class="form-item">
+            <label class="form-label">用户ID:</label>
+            <el-input v-model="userId" placeholder="请输入用户ID" style="width: 250px;"></el-input>
+          </div>
+          <div class="form-item">
+            <label class="form-label">用户姓名:</label>
+            <el-input v-model="userNm" placeholder="请输入用户姓名" style="width: 250px;"></el-input>
+          </div>
+        </div>
+        
+        <!-- 第三行:所属机构 + 所属行社 -->
+        <div class="form-row">
+          <div class="form-item">
+            <label class="form-label">所属机构:</label>
+            <el-input v-model="userOrg" placeholder="请输入所属机构" style="width: 250px;"></el-input>
+          </div>
+          <div class="form-item">
+            <label class="form-label">所属行社:</label>
+            <el-input v-model="userEndpoint" placeholder="请输入所属行社" style="width: 250px;"></el-input>
+          </div>
+        </div>
+      </div>
+      
+      <!-- 打标内容 -->
+      <div class="form-item">
+        <label class="form-label">打标内容:</label>
+        <el-input
+          type="textarea"
+          v-model="phrase"
+          placeholder="请输入要测试的文本内容,例如:职业:水产养殖人员 投向:海水养殖 用途:个人经营"
+          :rows="6"
+          style="width: 100%;"
+        ></el-input>
+      </div>
+      
+      <!-- 操作按钮 -->
+      <div class="input-actions">
+        <el-button @click="handleClear">清空</el-button>
+        <el-button type="primary" @click="handleSmartTagging">智能打标发起</el-button>
+      </div>
+    </div>
+    
+    <!-- 智能打标结果区 -->
+    <div class="smart-tagging-result">
+      <div class="result-header">
+        <h4 class="section-title">智能打标结果</h4>
+        <div class="result-info" v-if="taggingResults.length > 0">
+          <span>打标结果数:{{ taggingResults.length }}</span>
+          <span>打标时间:{{ taggingTime }}</span>
+        </div>
+      </div>
+      
+      <!-- 有结果时显示结果卡片 -->
+      <div class="result-content" v-if="taggingResults.length > 0">
+        <el-card 
+          v-for="(result, index) in taggingResults" 
+          :key="index"
+          class="tag-result-card"
+        >
+          <div class="tag-result-header">
+            <span class="tag-path">{{ result.path }}</span>
+          </div>
+          <div class="tag-result-body">
+            <div class="result-item">
+              <span class="result-label">打标依据:</span>
+              <div class="result-text">{{ result.reason || '暂无依据' }}</div>
+            </div>
+          </div>
+        </el-card>
+      </div>
+      
+      <!-- 空结果提示 -->
+        <div class="empty-result" v-else>
+          <img src="@/assets/images/noData.png" alt="暂无数据">
+          <p>{{ noMatchFound ? '未找到匹配的标签' : '暂无打标结果,请输入内容并点击智能打标按钮' }}</p>
+        </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import TagTestComponent from './TagTestComponent.vue'
+
+export default {
+  name: 'TagTest',
+  components: {
+    TagTestComponent
+  },
+  data() {
+    return {
+      // 标签体系选择
+      tagCategoryId: '',
+      // 标签体系列表
+      tagSystems: [],
+      // 贷款申请编号(20位随机字符串)
+      businessAttr: '',
+      // 打标内容
+      phrase: '',
+      // 用户信息
+      userId: yufp.session.userId || '',
+      userNm: yufp.session.userName || '',
+      userOrg: yufp.session.org ? yufp.session.org.name : '',
+      userEndpoint: '',
+      // 智能打标结果
+      taggingResults: [],
+      // 是否未找到匹配的标签
+      noMatchFound: false,
+      // 打标时间
+      taggingTime: '',
+      // 轮询相关
+      pollingTimer: null
+    }
+  },
+  mounted() {
+    // 生成初始的businessAttr
+    this.generateBusinessAttr();
+    // 加载标签体系列表
+    this.loadTagSystems();
+  },
+  beforeDestroy() {
+    // 组件销毁前清除轮询定时器
+    this.clearPolling();
+  },
+  methods: {
+    // 生成20位随机字符串(数字和小写英文)
+    generateBusinessAttr() {
+      const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
+      let result = '';
+      for (let i = 0; i < 20; i++) {
+        result += chars.charAt(Math.floor(Math.random() * chars.length));
+      }
+      this.businessAttr = result;
+      console.log('生成businessAttr:', this.businessAttr);
+    },
+    
+    // 加载标签体系列表
+    loadTagSystems() {
+      yufp.service.request({
+        url: backend.tagServer + "/api/aitag-tagcategory/enablelist",
+        method: 'get',
+        data: {},
+        callback: (code, error, response) => {
+          if (response.code == '0') {
+            this.tagSystems = (response.data || []).map(item => ({
+              ...item,
+              categoryId: item.categoryId || item.id || ''
+            }));
+            console.log('标签体系数据:', this.tagSystems);
+            // 默认选中第一条数据
+            if (this.tagSystems.length > 0) {
+              this.tagCategoryId = this.tagSystems[0].categoryId;
+            }
+          } else {
+            this.$message.error(response.message || '获取标签体系列表失败');
+          }
+        }
+      });
+    },
+    
+    // 智能打标
+    handleSmartTagging() {
+      // 检查必填项
+      if (!this.tagCategoryId) {
+        this.$message.warning('请选择标签体系');
+        return;
+      }
+      if (!this.businessAttr) {
+        this.$message.warning('贷款申请编号不能为空');
+        return;
+      }
+      if (!this.phrase.trim()) {
+        this.$message.warning('请输入打标内容');
+        return;
+      }
+      
+      console.log('智能打标:', {
+        businessAttr: this.businessAttr,
+        phrase: this.phrase,
+        tagCategoryId: this.tagCategoryId,
+        userId: this.userId,
+        userNm: this.userNm,
+        userOrg: this.userOrg,
+        userEndpoint: this.userEndpoint
+      });
+      
+      // 显示加载提示
+      this.$message({
+        message: '正在进行智能打标,请稍候...',
+        type: 'loading',
+        duration: 3000
+      });
+      
+      // 构建请求参数
+      const requestData = {
+        businessAttr: this.businessAttr,
+        phrase: this.phrase.trim(),
+        tagCategoryId: this.tagCategoryId,
+        userId: this.userId,
+        userNm: this.userNm,
+        userOrg: this.userOrg,
+        userEndpoint: this.userEndpoint
+      };
+      
+      console.log('智能打标请求参数:', requestData);
+      
+      // 调用智能打标接口
+      yufp.service.request({
+        url: backend.tagServer + "/api/fastapi/tagging",
+        method: 'post',
+        data: requestData,
+        callback: (code, error, response) => {
+          if (response.code == '0') {
+            // 清除之前的轮询
+            this.clearPolling();
+            
+            // 显示处理中提示
+            const loadingInstance = this.$loading({
+              lock: true,
+              text: '智能打标中,请稍候...',
+              spinner: 'el-icon-loading',
+              background: 'rgba(0, 0, 0, 0.7)',
+              target: this.$el
+            });
+            
+            // 开始轮询查询打标结果
+            this.pollTaggingResult(this.businessAttr, loadingInstance);
+          } else {
+            this.$message.error(response.message || '智能打标失败');
+          }
+        }
+      });
+    },
+    
+    // 轮询查询打标结果
+    pollTaggingResult(businessAttr, loadingInstance) {
+      // 构建查询参数
+      const queryParams = {
+        businessAttr: businessAttr
+      };
+
+      // 调用查询接口
+      yufp.service.request({
+        url: backend.tagServer + "/api/fastapi/query",
+        method: 'get',
+        data: queryParams,
+        callback: (code, error, response) => {
+          if (response.code == '0') {
+            // 处理查询结果
+            // 实际数据在 response.data.data 中
+            const responseData = response.data && response.data.data ? response.data.data : null;
+
+            if (!responseData) {
+              // 无数据,继续轮询
+              this.pollingTimer = setTimeout(() => {
+                this.pollTaggingResult(businessAttr, loadingInstance);
+              }, 3000);
+              return;
+            }
+
+            // 获取state状态:0-打标执行中;1-打标完成;2-已确认;3-已推送
+            const state = responseData.state;
+
+            if (state === 0) {
+              // 打标执行中,继续轮询
+              this.pollingTimer = setTimeout(() => {
+                this.pollTaggingResult(businessAttr, loadingInstance);
+              }, 3000);
+            } else if (state === 1 || state === 2 || state === 3) {
+              // 打标完成、已确认或已推送,停止轮询
+              loadingInstance.close();
+              this.clearPolling();
+
+              // 处理result字段
+              let resultList = [];
+              if (responseData.result) {
+                if (typeof responseData.result === 'string') {
+                  try {
+                    const parsedResult = JSON.parse(responseData.result);
+                    if (Array.isArray(parsedResult)) {
+                      resultList = parsedResult;
+                    }
+                  } catch (error) {
+                    console.error('解析result失败:', error);
+                  }
+                } else if (Array.isArray(responseData.result)) {
+                  resultList = responseData.result;
+                }
+              }
+
+              // 处理打标结果(测试环境不过滤passr状态)
+              this.taggingResults = resultList.map(item => ({
+                code: item.tag_code || '',
+                name: item.tag_name || '',
+                path: item.tag_path || '',
+                method: '智能打标',
+                reason: item.desc || ''
+              }));
+
+              // 设置打标时间(使用接口返回的insert_time)
+              this.taggingTime = responseData.insert_time || new Date().toLocaleString();
+
+              if (this.taggingResults.length > 0) {
+                this.$message.success('智能打标完成');
+                this.noMatchFound = false;
+              } else {
+                this.noMatchFound = true;
+              }
+
+              // 触发父组件事件
+              this.$emit('smart-tagging', {
+                businessAttr: this.businessAttr,
+                phrase: this.phrase,
+                tagCategoryId: this.tagCategoryId,
+                userId: this.userId,
+                userNm: this.userNm,
+                userOrg: this.userOrg,
+                userEndpoint: this.userEndpoint,
+                results: this.taggingResults,
+                time: this.taggingTime
+              });
+            } else {
+              // 未知状态,继续轮询
+              this.pollingTimer = setTimeout(() => {
+                this.pollTaggingResult(businessAttr, loadingInstance);
+              }, 3000);
+            }
+          } else {
+            // 接口调用失败,停止轮询
+            loadingInstance.close();
+            this.clearPolling();
+            this.$message.error(response.message || '查询打标结果失败');
+          }
+        }
+      });
+    },
+    
+    // 清除轮询
+    clearPolling() {
+      if (this.pollingTimer) {
+        clearTimeout(this.pollingTimer);
+        this.pollingTimer = null;
+      }
+    },
+    
+    // 清空
+    handleClear() {
+      // 重新生成businessAttr
+      this.generateBusinessAttr();
+      // 清空打标内容
+      this.phrase = '';
+      // 清空用户信息
+      this.userId = '';
+      this.userNm = '';
+      this.userOrg = '';
+      this.userEndpoint = '';
+      // 清空打标结果
+      this.taggingResults = [];
+      this.taggingTime = '';
+      
+      this.$message.info('已清空测试内容');
+      
+      // 触发父组件事件
+      this.$emit('test-clear');
+    }
+  }
+}
+</script>
+
+<style scoped>
+  .treeTest{
+    width: 240px;
+    height: 400px;
+  }
+.tag-test {
+  height: 100%;
+}
+
+/* 测试输入区 */
+.test-input-section {
+  background-color: #fafafa;
+  border-radius: 8px;
+  padding: 20px;
+  margin-bottom: 24px;
+}
+
+.section-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: #303133;
+  margin: 0 0 16px 0;
+  text-align: left;
+}
+
+/* 输入信息区域 */
+.input-section {
+  margin-bottom: 16px;
+}
+
+/* 表单行 */
+.form-row {
+  display: flex;
+  gap: 24px;
+  margin-bottom: 16px;
+  flex-wrap: wrap;
+}
+
+/* 表单项 */
+.form-item {
+  display: flex;
+  align-items: center;
+  flex: 1;
+  min-width: 350px;
+}
+
+/* 标签统一宽度 */
+.form-label {
+  width: 100px;
+  font-size: 14px;
+  color: #606266;
+  text-align: right;
+  margin-right: 12px;
+  flex-shrink: 0;
+}
+
+/* businessAttr包装器 */
+.business-attr-wrapper {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  flex: 1;
+}
+
+.business-attr-wrapper .el-input {
+  flex: 1;
+}
+
+/* 输入操作按钮 */
+.input-actions {
+  display: flex;
+  justify-content: flex-end;
+  gap: 12px;
+}
+
+/* 智能打标结果区 */
+.smart-tagging-result {
+  background-color: #fafafa;
+  border-radius: 8px;
+  padding: 20px;
+}
+
+/* 结果头部 */
+.result-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 16px;
+  padding-bottom: 12px;
+  border-bottom: 1px solid #e8e8e8;
+}
+
+.result-info {
+  display: flex;
+  gap: 20px;
+  font-size: 12px;
+  color: #909399;
+}
+
+/* 结果内容 */
+.result-content {
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+}
+
+/* 标签结果卡片 */
+.tag-result-card {
+  border-radius: 8px;
+  overflow: hidden;
+}
+
+/* 标签结果头部 */
+.tag-result-header {
+  background-color: #e6f7ff;
+  padding: 12px 16px;
+  margin: -16px -16px 16px;
+  display: flex;
+  align-items: center;
+  gap: 12px;
+}
+
+.tag-path {
+  font-weight: 600;
+  color: #303133;
+}
+
+/* 标签结果主体 */
+.tag-result-body {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.result-item {
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+}
+
+.result-label {
+  font-size: 12px;
+  color: #606266;
+  margin-bottom: 4px;
+  text-align: left;
+}
+
+.result-input {
+  font-size: 14px;
+}
+
+.result-text {
+  font-size: 14px;
+  color: #303133;
+  line-height: 1.6;
+  padding: 8px 12px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+  min-height: 60px;
+  text-align: left;
+}
+
+/* 空结果提示 */
+.empty-result {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 60px 20px;
+  color: #909399;
+  background-color: #fafafa;
+  margin-top: 16px;
+  min-height: 200px;
+}
+
+.empty-result img {
+  width: 120px;
+  height: 120px;
+  margin-bottom: 16px;
+  opacity: 0.6;
+}
+
+.empty-result p {
+  font-size: 14px;
+  color: #909399;
+  margin: 0;
+  line-height: 1.5;
+}
+
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+  .result-header {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 8px;
+  }
+  
+  .result-info {
+    align-self: stretch;
+    justify-content: space-between;
+  }
+  
+  .input-actions {
+    flex-direction: column;
+  }
+  
+  .input-actions .el-button {
+    width: 100%;
+  }
+  
+  .form-row {
+    flex-direction: column;
+    gap: 16px;
+  }
+  
+  .form-item {
+    flex-direction: column;
+    align-items: flex-start;
+    min-width: auto;
+  }
+  
+  .form-label {
+    text-align: left;
+    margin-bottom: 8px;
+    width: 100%;
+  }
+  
+  .business-attr-wrapper {
+    width: 100%;
+  }
+  
+  .business-attr-wrapper .el-input {
+    width: 100% !important;
+  }
+  
+  .form-item .el-select,
+  .form-item .el-input {
+    width: 100% !important;
+  }
+}
+</style>