Explorar el Código

老版本龙虾功能同步

lk hace 5 días
padre
commit
84ceef3d9d

+ 62 - 1
src/api/company/tagBinding.js

@@ -10,6 +10,13 @@ export const tagBindingApi = {
       params
     })
   },
+    getListB: (params) => {
+    return request({
+      url: '/workflow/tagBinding/tag-binding/list',
+      method: 'get',
+      params
+    })
+  },
 
   // 根据ID获取
   getById: (id) => {
@@ -27,6 +34,14 @@ export const tagBindingApi = {
       data
     })
   },
+    // 创建绑定
+  createB: (data) => {
+    return request({
+      url: '/workflow/tagBinding/tag-binding',
+      method: 'post',
+      data
+    })
+  },
 
   // 更新绑定
   update: (id, data) => {
@@ -36,6 +51,14 @@ export const tagBindingApi = {
       data
     })
   },
+    // 更新绑定
+  updateB: (id, data) => {
+    return request({
+      url: `/workflow/tagBinding/tag-binding/${id}`,
+      method: 'put',
+      data
+    })
+  },
 
   // 删除绑定
   delete: (id) => {
@@ -44,6 +67,13 @@ export const tagBindingApi = {
       method: 'delete'
     })
   },
+    // 删除绑定
+  deleteB: (id) => {
+    return request({
+      url: `/workflow/tagBinding/tag-binding/${id}`,
+      method: 'delete'
+    })
+  },
 
   // 批量绑定
   batchBind: (templateId, tagCodes) => {
@@ -70,5 +100,36 @@ export const tagBindingApi = {
       method: 'post',
       data: testTags
     })
-  }
+  },
+    // 测试标签匹配
+  testMatchB: (id, testTags) => {
+    return request({
+      url: `/workflow/tagBinding/tag-binding/${id}/test-match`,
+      method: 'post',
+      data: testTags
+    })
+  },
+    //获取龙虾标签
+   getLobsterTags:(userIds) => {
+  return request({
+    url: '/workflow/tagBinding/lobsterTags',
+    method: 'post',
+    data: userIds
+  })
+},
+    getListByStatus: (params) => {
+    return request({
+      url: '/workflow/tagBinding/tag-binding/listByStatus',
+      method: 'get',
+      params
+    })
+  },
+    // 批量添加龙虾标签给客户
+  batchBindLobsterTag: (data) => {
+    return request({
+      url: '/workflow/tagBinding/tag-binding/batch-bind-lobster-tag',
+      method: 'post',
+      data
+    })
+  },
 }

+ 14 - 0
src/api/company/workflowLobster.js

@@ -21,6 +21,13 @@ export function listWorkflowTemplate(query) {
     params: query
   })
 }
+export function listWorkflowTemplateByStatus(query) {
+  return request({
+    url: '/workflow/template/listTemplate',
+    method: 'get',
+    params: query
+  })
+}
 
 export function getWorkflowTemplateDetail(id) {
   return request({
@@ -88,3 +95,10 @@ export function simulateWorkflow(data) {
     data
   })
 }
+
+export function updateWorkflowTemplateStatus(id, status) {
+  return request({
+    url: '/workflow/template/' + id + '/' + status,
+    method: 'put'
+  })
+}

+ 29 - 10
src/views/company/tag/binding.vue

@@ -122,6 +122,7 @@
 
 <script>
 import { tagBindingApi } from '@/api/company/tagBinding'
+import { listWorkflowTemplateByStatus } from '@/api/company/workflowLobster'
 
 export default {
   name: 'TagBinding',
@@ -142,7 +143,8 @@ export default {
         tagName: '',
         templateId: null,
         priority: 0,
-        matchCondition: ''
+        matchCondition: '',
+        templateName: ''
       },
       rules: {
         tagCode: [{ required: true, message: '请输入标签编码', trigger: 'blur' }],
@@ -165,7 +167,7 @@ export default {
     async getList() {
       this.loading = true
       try {
-        const res = await tagBindingApi.getList(this.searchForm)
+        const res = await tagBindingApi.getListB(this.searchForm)
         this.tableData = res.data || []
       } catch (error) {
         this.$message.error('获取列表失败')
@@ -174,8 +176,13 @@ export default {
       }
     },
     async loadTemplateOptions() {
-      // TODO: 从工作流模板API加载模板列表
-      this.templateOptions = []
+      try {
+        const res = await listWorkflowTemplateByStatus({ status: 1 })
+        this.templateOptions = res.data || []
+      } catch (error) {
+        this.templateOptions = []
+        this.$message.error('获取模板列表失败')
+      }
     },
     handleSearch() {
       this.getList()
@@ -194,6 +201,7 @@ export default {
         tagCode: '',
         tagName: '',
         templateId: null,
+        templateName: '',
         priority: 0,
         matchCondition: ''
       }
@@ -209,6 +217,7 @@ export default {
         tagCode: row.tagCode,
         tagName: row.tagName,
         templateId: row.templateId,
+        templateName: row.templateName || '',
         priority: row.priority,
         matchCondition: row.matchCondition
       }
@@ -217,11 +226,13 @@ export default {
     async handleSubmit() {
       try {
         await this.$refs.formRef.validate()
+        const selectedTemplate = this.templateOptions.find(item => item.id === this.form.templateId)
+        this.form.templateName = selectedTemplate ? selectedTemplate.templateName : ''
         if (this.form.id) {
-          await tagBindingApi.update(this.form.id, this.form)
+          await tagBindingApi.updateB(this.form.id, this.form)
           this.$message.success('修改成功')
         } else {
-          await tagBindingApi.create(this.form)
+          await tagBindingApi.createB(this.form)
           this.$message.success('新增成功')
         }
         this.dialogVisible = false
@@ -239,7 +250,7 @@ export default {
           cancelButtonText: '取消',
           type: 'warning'
         })
-        await tagBindingApi.delete(row.id)
+        await tagBindingApi.deleteB(row.id)
         this.$message.success('删除成功')
         this.getList()
       } catch (error) {
@@ -249,11 +260,19 @@ export default {
       }
     },
     async handleStatusChange(row) {
+      const statusText = row.status === 1 ? '启用' : '禁用'
       try {
+        await this.$confirm(`确认${statusText}该绑定关系吗?`, '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        })
         await tagBindingApi.update(row.id, { status: row.status })
-        this.$message.success('状态更新成功')
+        this.$message.success(`修改状态为${statusText}成功`)
       } catch (error) {
-        this.$message.error('状态更新失败')
+        if (error !== 'cancel') {
+          this.$message.error('状态更新失败')
+        }
         this.getList()
       }
     },
@@ -268,7 +287,7 @@ export default {
     async handleTestMatchSubmit() {
       try {
         const tags = this.testForm.tags.split(',').map(t => t.trim()).filter(t => t)
-        const res = await tagBindingApi.testMatch(this.testForm.id, { tags })
+        const res = await tagBindingApi.testMatchB(this.testForm.id, { tags })
         this.testResult = res.data
       } catch (error) {
         this.$message.error('测试失败')

+ 33 - 1
src/views/company/workflowLobster/index.vue

@@ -56,7 +56,13 @@
         <el-table-column prop="templateName" label="模板名称" min-width="180" />
         <el-table-column prop="templateCode" label="模板编码" min-width="140" />
         <el-table-column prop="industryType" label="行业类型" width="110" />
-        <el-table-column prop="status" label="状态" width="80" />
+        <el-table-column prop="status" label="状态" width="0" >
+          <template slot-scope="scope">
+            <el-tag size="mini" :type="scope.row.status === 1 ? 'success' : 'info'">
+              {{ scope.row.status === 1 ? '已发布' : '未发布' }}
+            </el-tag>
+          </template>
+        </el-table-column>
         <el-table-column prop="createTime" label="创建时间" width="160" />
         <el-table-column label="操作" width="280" fixed="right">
           <template slot-scope="scope">
@@ -64,6 +70,8 @@
             <el-button type="text" @click="handleEditTemplate(scope.row)">编辑</el-button>
             <el-button type="text" @click="handleVisual(scope.row)">流程图</el-button>
             <el-button type="text" @click="handleSimulate(scope.row)">模拟</el-button>
+            <el-button v-if="scope.row.status !== 1" type="text" :loading="statusChangingId === scope.row.id" @click="handlePublish(scope.row)">发布</el-button>
+            <el-button v-else type="text" :loading="statusChangingId === scope.row.id" @click="handleUnpublish(scope.row)">取消发布</el-button>
             <el-button type="text" style="color:#f56c6c" @click="handleDeleteTemplate(scope.row)">删除</el-button>
           </template>
         </el-table-column>
@@ -150,6 +158,7 @@ import {
   listWorkflowTemplate,
   getWorkflowTemplateDetail,
   updateWorkflowTemplate,
+  updateWorkflowTemplateStatus,
   deleteWorkflowTemplate,
   aiGenerateWorkflow,
   getGenerateResultDetail,
@@ -174,6 +183,7 @@ export default {
       templateDialogVisible: false,
       templateDialogMode: 'preview',
       templateSaving: false,
+      statusChangingId: null,
       page: {
         current: 1,
         size: 10,
@@ -413,6 +423,28 @@ export default {
         }
       }
     },
+    async handlePublish(row) {
+      await this.changeTemplateStatus(row, 1)
+    },
+    async handleUnpublish(row) {
+      await this.changeTemplateStatus(row, 0)
+    },
+    async changeTemplateStatus(row, status) {
+      const actionText = status === 1 ? '发布' : '取消发布'
+      try {
+        await this.$confirm(`确认${actionText}该模板吗?`, '提示', { type: 'warning' })
+        this.statusChangingId = row.id
+        await updateWorkflowTemplateStatus(row.id, status)
+        this.$message.success(`${actionText}成功`)
+        await this.loadTemplateList()
+      } catch (e) {
+        if (e !== 'cancel') {
+          this.$message.error(e.message || `${actionText}失败`)
+        }
+      } finally {
+        this.statusChangingId = null
+      }
+    },
     handleSimulate(row) {
       this.simulateDialogVisible = true
       this.simulateCurrentTemplateId = row.id

+ 126 - 1
src/views/qw/externalContact/index.vue

@@ -337,6 +337,15 @@
         v-hasPermi="['qw:externalContactInfo:updateTalk']"
 	    >批量更改交流状态</el-button>
 	  </el-col>
+    <el-col :span="1.5">
+      <el-button
+        type="warning"
+        plain
+        size="mini"
+        @click="addLobsterTag"
+        v-hasPermi="['qw:externalContact:addTag']"
+      >批量增加龙虾标签</el-button>
+    </el-col>
 <!--       <el-col :span="1.5">-->
 <!--        <el-button-->
 <!--          type="primary"-->
@@ -401,6 +410,22 @@
           </div>
         </template>
       </el-table-column>
+      <el-table-column label="龙虾标签" align="center" width="200px">
+        <template slot-scope="scope">
+          <div v-if="scope.row.lobsterTagNames && scope.row.lobsterTagNames.length">
+            <el-tag
+              v-for="name in scope.row.lobsterTagNames"
+              :key="name"
+              type="warning"
+              size="small"
+              style="margin: 2px;"
+            >
+              {{ name }}
+            </el-tag>
+          </div>
+          <span v-else style="color: #c0c4cc;">-</span>
+        </template>
+      </el-table-column>
       <el-table-column label="流失风险" align="center" prop="attritionLevel">
         <template slot-scope="scope">
           <el-tag v-if="scope.row.attritionLevel == null" type="info">未分析</el-tag>
@@ -744,6 +769,40 @@
         <el-button @click="addTagCancel">取 消</el-button>
       </div>
     </el-dialog>
+    <el-dialog title="批量增加龙虾标签" :visible.sync="lobsterTagOpen" width="700px" append-to-body>
+      <div v-loading="lobsterTagLoading">
+        <el-alert
+          title="龙虾标签来源于已启用的标签-模板绑定关系"
+          type="info"
+          :closable="false"
+          show-icon
+          style="margin-bottom: 16px"
+        />
+        <el-table
+          :data="lobsterTagList"
+          ref="lobsterTagTable"
+          @selection-change="handleLobsterTagSelect"
+          max-height="400"
+          border
+        >
+          <el-table-column type="selection" width="55" align="center" />
+          <el-table-column prop="tagCode" label="标签编码" width="150" />
+          <el-table-column prop="tagName" label="标签名称" min-width="150" />
+          <el-table-column prop="templateName" label="绑定模板" min-width="180" show-overflow-tooltip />
+          <el-table-column prop="priority" label="优先级" width="100" align="center">
+            <template slot-scope="{ row }">
+              <el-tag :type="row.priority >= 50 ? 'danger' : row.priority >= 30 ? 'warning' : 'info'" size="mini">
+                {{ row.priority }}
+              </el-tag>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" :disabled="lobsterSelectedTags.length === 0" @click="submitLobsterTag">确 定 (已选{{ lobsterSelectedTags.length }})</el-button>
+        <el-button @click="lobsterTagOpen = false">取 消</el-button>
+      </div>
+    </el-dialog>
     <el-dialog title="批量添加客户备注" :visible.sync="notesOpen.open" width="800px" append-to-body>
       <el-card>
         <el-row>
@@ -1030,6 +1089,7 @@ import  selectUser  from "@/views/qw/externalContact/selectUser.vue";
 import  collection  from "@/views/qw/externalContact/collection.vue";
 import info from "@/views/qw/externalContact/info.vue";
 import { editTalk } from "@/api/qw/externalContactInfo";
+import { tagBindingApi } from '@/api/company/tagBinding'
 import PaginationMore from "../../../components/PaginationMore/index.vue";
 import userDetails from '@/views/store/components/userDetails.vue';
 import {courseList, videoList} from "@/api/course/courseRedPacketLog";
@@ -1122,6 +1182,11 @@ export default {
       exportLoading: false,
       tagOpen:false,
       tagDelOpen:false,
+      // 龙虾标签弹窗
+      lobsterTagOpen: false,
+      lobsterTagLoading: false,
+      lobsterTagList: [],
+      lobsterSelectedTags: [],
       // 选中数组
       ids: [],
       isBindActiveName:"all",
@@ -1541,6 +1606,18 @@ export default {
         this.externalContactList = response.rows;
         this.total = response.total;
         this.loading = false;
+                // 加载龙虾标签
+        if (this.externalContactList && this.externalContactList.length) {
+          const userIds = this.externalContactList.map(u => u.id);
+          tagBindingApi.getLobsterTags(userIds).then(res => {
+            const tagMap = res.data || {};
+            this.externalContactList.forEach(row => {
+              const tagData = tagMap[row.id];
+              const tagList = tagData ? (Array.isArray(tagData) ? tagData : [tagData]) : [];
+              this.$set(row, 'lobsterTagNames', tagList.map(tag => tag.tag_name));
+            });
+          }).catch(() => {});
+        }
       });
     },
     bindMiniCustomerId(row){
@@ -2390,6 +2467,54 @@ export default {
 		    this.msgSuccess("成功");
 		  }).catch(() => {});
 	},
+    addLobsterTag() {
+      if (this.ids == null || this.ids.length === 0) {
+        return this.$message('请勾选需要添加龙虾标签的客户');
+      }
+      this.lobsterTagLoading = true;
+      this.lobsterTagOpen = true;
+      this.lobsterSelectedTags = [];
+      tagBindingApi.getListByStatus({ status: 1 }).then(res => {
+        this.lobsterTagList = res.data || [];
+      }).catch(() => {
+        this.$message.error('获取龙虾标签列表失败');
+      }).finally(() => {
+        this.lobsterTagLoading = false;
+        this.$nextTick(() => {
+          if (this.$refs.lobsterTagTable) {
+            this.$refs.lobsterTagTable.clearSelection();
+          }
+        });
+      });
+    },
+    handleLobsterTagSelect(selection) {
+      this.lobsterSelectedTags = selection;
+    },
+    submitLobsterTag() {
+      if (this.lobsterSelectedTags.length === 0) {
+        return this.$message('请选择龙虾标签');
+      }
+      const tagCodes = this.lobsterSelectedTags.map(t => t.tagCode);
+      const tagNames = this.lobsterSelectedTags.map(t => t.tagName);
+      this.$confirm(
+        '将为 ' + this.ids.length + ' 个客户添加以下标签:' + tagNames.join('、') + ',是否确认?',
+        '提示',
+        { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
+      ).then(() => {
+        tagBindingApi.batchBindLobsterTag({
+          userIds: this.ids,
+          tagCodes: tagCodes,
+          qwCorpId: this.queryParams.corpId
+        }).then(res => {
+          this.$message.success('龙虾标签添加任务已提交');
+          this.lobsterTagOpen = false;
+          this.lobsterSelectedTags = [];
+          this.getList();
+        }).catch(() => {
+          this.$message.error('龙虾标签添加失败');
+        });
+      }).catch(() => {});
+    },
     /** 导出按钮操作 */
     handleExport() {
       const { qwUserName, ...queryParams } = this.queryParams;
@@ -2602,4 +2727,4 @@ export default {
   border-radius: 1px;
   margin-bottom: 20px;
 }
-</style>
+</style>