lk 1 неделя назад
Родитель
Сommit
fdf51b1cc6

+ 16 - 0
src/api/company/tagBinding.js

@@ -10,6 +10,13 @@ export const tagBindingApi = {
       params
     })
   },
+    getListByStatus: (params) => {
+    return request({
+      url: '/workflow/tag-binding/listByStatus',
+      method: 'get',
+      params
+    })
+  },
 
   // 根据ID获取
   getById: (id) => {
@@ -70,5 +77,14 @@ export const tagBindingApi = {
       method: 'post',
       data: testTags
     })
+  },
+
+  // 批量添加龙虾标签给客户
+  batchBindLobsterTag: (data) => {
+    return request({
+      url: '/workflow/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({
@@ -37,6 +44,13 @@ export function updateWorkflowTemplate(id, data) {
   })
 }
 
+export function updateWorkflowTemplateStatus(id, status) {
+  return request({
+    url: '/workflow/template/' + id + '/' + status,
+    method: 'put'
+  })
+}
+
 export function saveWorkflowCanvas(id, data) {
   return request({
     url: '/workflow/canvas/' + id,

+ 15 - 3
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,9 @@ export default {
         tagName: '',
         templateId: null,
         priority: 0,
-        matchCondition: ''
+        matchCondition: '',
+        templateName: ''
+
       },
       rules: {
         tagCode: [{ required: true, message: '请输入标签编码', trigger: 'blur' }],
@@ -174,8 +177,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 +202,7 @@ export default {
         tagCode: '',
         tagName: '',
         templateId: null,
+        templateName: '',
         priority: 0,
         matchCondition: ''
       }
@@ -209,6 +218,7 @@ export default {
         tagCode: row.tagCode,
         tagName: row.tagName,
         templateId: row.templateId,
+        templateName: row.templateName || '',
         priority: row.priority,
         matchCondition: row.matchCondition
       }
@@ -217,6 +227,8 @@ 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)
           this.$message.success('修改成功')

+ 4 - 0
src/views/company/workflowLobster/WorkflowSolutionEditor.vue

@@ -96,6 +96,9 @@
             <el-form-item label="消息模板" v-if="node.nodeType === 2">
               <el-input v-model="node.messageTemplate" :size="fieldSize" type="textarea" :rows="compact ? 2 : 3" :disabled="readonly" />
             </el-form-item>
+            <el-form-item label="发送时间" v-if="node.nodeType === 2">
+              <el-input v-model="node.sendTime" :size="fieldSize" :disabled="readonly" placeholder="例如:09:00" />
+            </el-form-item>
             <el-form-item label="条件表达式" v-if="node.nodeType === 3">
               <el-input v-model="node.conditionExpr" :size="fieldSize" type="textarea" :rows="compact ? 2 : 3" :disabled="readonly" />
             </el-form-item>
@@ -179,6 +182,7 @@ export default {
         nodeType: 2,
         nodeConfig: '{}',
         messageTemplate: '',
+        sendTime: '',
         conditionExpr: '',
         nextNodeCode: ''
       })

+ 77 - 6
src/views/company/workflowLobster/index.vue

@@ -56,14 +56,41 @@
         <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="90">
+          <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="220" fixed="right">
           <template slot-scope="scope">
-            <el-button type="text" @click="handlePreview(scope.row)">预览</el-button>
-            <el-button type="text" @click="handleEditTemplate(scope.row)">编辑</el-button>
-            <el-button type="text" @click="handleVisual(scope.row)">流程图</el-button>
-            <el-button type="text" style="color:#f56c6c" @click="handleDeleteTemplate(scope.row)">删除</el-button>
+            <div class="table-action-group">
+              <el-button class="action-btn" type="text" @click="handlePreview(scope.row)">预览</el-button>
+              <el-button v-if="scope.row.status !== 1" class="action-btn" type="text" @click="handleEditTemplate(scope.row)">编辑</el-button>
+              <el-button class="action-btn" type="text" @click="handleVisual(scope.row)">流程图</el-button>
+              <el-button
+                v-if="scope.row.status !== 1"
+                class="action-btn danger-btn"
+                type="text"
+                @click="handleDeleteTemplate(scope.row)"
+              >删除</el-button>
+              <el-button
+                v-if="scope.row.status !== 1"
+                class="action-btn"
+                type="text"
+                :loading="statusChangingId === scope.row.id"
+                @click="handlePublish(scope.row)"
+              >发布</el-button>
+              <el-button
+                v-else
+                class="action-btn"
+                type="text"
+                :loading="statusChangingId === scope.row.id"
+                @click="handleUnpublish(scope.row)"
+              >取消发布</el-button>
+            </div>
           </template>
         </el-table-column>
       </el-table>
@@ -102,6 +129,7 @@ import {
   listWorkflowTemplate,
   getWorkflowTemplateDetail,
   updateWorkflowTemplate,
+  updateWorkflowTemplateStatus,
   deleteWorkflowTemplate,
   aiGenerateWorkflow,
   getGenerateResultDetail,
@@ -125,6 +153,7 @@ export default {
       templateDialogVisible: false,
       templateDialogMode: 'preview',
       templateSaving: false,
+      statusChangingId: null,
       page: {
         current: 1,
         size: 10,
@@ -293,7 +322,9 @@ export default {
       await this.openTemplateDialog(row, 'edit')
     },
     handleVisual(row) {
-      this.$router.push('/workflow/visual/' + row.id)
+      this.$router.push({
+        path: '/workflow/visual/' + row.id,
+      })
     },
     async openTemplateDialog(row, mode) {
       this.templateDialogMode = mode
@@ -352,6 +383,28 @@ export default {
           this.$message.error(e.message || '删除失败')
         }
       }
+    },
+    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
+      }
     }
   }
 }
@@ -397,5 +450,23 @@ export default {
     display: flex;
     justify-content: flex-end;
   }
+
+  .table-action-group {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    gap: 2px 10px;
+  }
+
+  ::v-deep .table-action-group .action-btn {
+    margin: 0;
+    min-width: 44px;
+    text-align: center;
+    padding: 2px 0;
+  }
+
+  ::v-deep .table-action-group .danger-btn {
+    color: #f56c6c;
+  }
 }
 </style>

Разница между файлами не показана из-за своего большого размера
+ 478 - 146
src/views/company/workflowLobster/visual.vue


+ 96 - 0
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"-->
@@ -744,6 +753,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 +1073,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 +1166,11 @@ export default {
       exportLoading: false,
       tagOpen:false,
       tagDelOpen:false,
+      // 龙虾标签弹窗
+      lobsterTagOpen: false,
+      lobsterTagLoading: false,
+      lobsterTagList: [],
+      lobsterSelectedTags: [],
       // 选中数组
       ids: [],
       isBindActiveName:"all",
@@ -2390,6 +2439,53 @@ 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 = [];
+        }).catch(() => {
+          this.$message.error('龙虾标签添加失败');
+        });
+      }).catch(() => {});
+    },
     /** 导出按钮操作 */
     handleExport() {
       const { qwUserName, ...queryParams } = this.queryParams;

Некоторые файлы не были показаны из-за большого количества измененных файлов