瀏覽代碼

Merge remote-tracking branch 'origin/master'

xdd 1 周之前
父節點
當前提交
cc3394dcf5

+ 9 - 3
src/api/company/company.js

@@ -1,7 +1,13 @@
 import request from '@/utils/request'
 
- 
- 
+export function listCompany(query) {
+  return request({
+    url: '/company/company/list',
+    method: 'get',
+    params: query
+  })
+}
+
 export function getCompanyInfo() {
   return request({
     url: '/company/company/getCompanyInfo',
@@ -27,5 +33,5 @@ export function getCompanyList() {
     method: 'get'
   })
 }
- 
+
 

+ 27 - 0
src/api/company/companyApply.js

@@ -0,0 +1,27 @@
+import request from '@/utils/request'
+
+// 查询更换会员归属申请列表
+export function listApply(query) {
+  return request({
+    url: '/company/apply/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询更换会员归属申请详细
+export function getApply(id) {
+  return request({
+    url: '/company/apply/' + id,
+    method: 'get'
+  })
+}
+
+// 审核更换会员归属申请
+export function auditApply(data) {
+  return request({
+    url: '/company/apply/audit',
+    method: 'post',
+    data: data
+  })
+}

+ 19 - 0
src/api/company/companyUser.js

@@ -209,3 +209,22 @@ export function generateSubDomain() {
     method: 'get'
   })
 }
+
+export function getCitysAreaList(query) {
+  return request({
+    url: '/company/user/getCitysAreaList',
+    method: 'get',
+    params: query
+  })
+}
+
+/**
+ * 批量修改 销售的所属区域(临时的)
+ */
+export function updateCompanyUserAreaList(data) {
+  return request({
+    url: '/company/user/updateCompanyUserAreaList',
+    method: 'post',
+    data: data
+  })
+}

+ 391 - 0
src/views/company/companyApply/index.vue

@@ -0,0 +1,391 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="审核状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择审核状态" clearable size="small">
+          <el-option label="待审核" value="0" />
+          <el-option label="通过" value="1" />
+          <el-option label="拒绝" value="2" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="applyList">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="ID" align="center" prop="id" />
+      <el-table-column label="原归属销售" align="center" prop="fromName" />
+      <el-table-column label="申请归属销售" align="center" prop="toName" />
+      <el-table-column label="审核状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.status === 0" type="warning">待审核</el-tag>
+          <el-tag v-if="scope.row.status === 1" type="success">通过</el-tag>
+          <el-tag v-if="scope.row.status === 2" type="danger">拒绝</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="申请人" align="center" prop="applyBy" />
+      <el-table-column label="申请时间" align="center" prop="applyTime"/>
+      <el-table-column label="审核人" align="center" prop="auditBy" />
+      <el-table-column label="审核时间" align="center" prop="auditTime" />
+      <el-table-column label="拒绝原因" align="center" prop="reason" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            v-if="scope.row.status === 0"
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleAudit(scope.row)"
+            v-hasPermi="['company:apply:audit']"
+          >审核</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDetails(scope.row)"
+            v-hasPermi="['company:apply:query']"
+          >详情</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 详情对话框 -->
+    <el-dialog 
+      title="申请详情" 
+      :visible.sync="detailsVisible" 
+      width="50%"
+      :close-on-click-modal="false"
+      custom-class="apply-details-dialog"
+    >
+      <!-- 申请基本信息 -->
+      <div class="details-header">
+        <div class="info-item">
+          <span class="label">原归属销售:</span>
+          <span class="value">{{ selectedApply.fromName }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">申请归属销售:</span>
+          <span class="value">{{ selectedApply.toName }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">申请时间:</span>
+          <span class="value">{{ selectedApply.applyTime }}</span>
+        </div>
+        <div class="info-item">
+          <span class="label">审核状态:</span>
+          <el-tag :type="selectedApply.status === 0 ? 'warning' : selectedApply.status === 1 ? 'success' : 'danger'">
+            {{ selectedApply.status === 0 ? '待审核' : selectedApply.status === 1 ? '通过' : '拒绝' }}
+          </el-tag>
+        </div>
+      </div>
+      
+      <!-- 用户列表 -->
+      <div class="details-content">
+        <div class="content-title">变更用户列表</div>
+        <el-table 
+          :data="selectedUsers" 
+          border 
+          style="width: 100%"
+          :header-cell-style="{background:'#f5f7fa',color:'#606266'}"
+        >
+          <el-table-column prop="userId" label="用户ID" align="center"></el-table-column>
+          <el-table-column prop="userName" label="姓名" align="center"></el-table-column>
+        </el-table>
+      </div>
+      
+      <!-- 底部操作区 -->
+      <div class="details-footer">
+        <el-button @click="detailsVisible = false">关闭</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 审核对话框 -->
+    <el-dialog 
+      title="审核申请" 
+      :visible.sync="auditVisible" 
+      width="40%"
+      :close-on-click-modal="false"
+      custom-class="audit-dialog"
+    >
+      <!-- 审核表单 -->
+      <el-form ref="auditForm" :model="auditForm" :rules="auditRules" label-width="100px">
+        <el-form-item label="原归属销售" prop="fromName">
+          <span>{{ auditForm.fromName }}</span>
+        </el-form-item>
+        <el-form-item label="申请归属销售" prop="toName">
+          <span>{{ auditForm.toName }}</span>
+        </el-form-item>
+        <el-form-item label="审核结果" prop="status">
+          <el-radio-group v-model="auditForm.status">
+            <el-radio :label="1">通过</el-radio>
+            <el-radio :label="2">拒绝</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item 
+          label="拒绝原因" 
+          prop="reason"
+          v-if="auditForm.status === 2"
+        >
+          <el-input
+            type="textarea"
+            v-model="auditForm.reason"
+            placeholder="请输入拒绝原因"
+            :rows="3"
+          ></el-input>
+        </el-form-item>
+      </el-form>
+      
+      <!-- 底部操作区 -->
+      <div class="audit-footer">
+        <el-button @click="auditVisible = false">取 消</el-button>
+        <el-button type="primary" @click="submitAudit" :loading="submitLoading">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listApply, getApply, auditApply } from "@/api/company/companyApply";
+
+export default {
+  name: "Apply",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 更换会员归属申请表格数据
+      applyList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        status: null
+      },
+      // 详情对话框
+      detailsVisible: false,
+      // 选中的用户
+      selectedUsers: [],
+      // 选中的申请
+      selectedApply: {},
+      // 审核对话框
+      auditVisible: false,
+      // 提交加载状态
+      submitLoading: false,
+      // 审核表单
+      auditForm: {
+        id: undefined,
+        status: 1,
+        reason: ''
+      },
+      // 审核表单校验规则
+      auditRules: {
+        status: [
+          { required: true, message: "请选择审核结果", trigger: "change" }
+        ],
+        reason: [
+          { required: true, message: "请输入拒绝原因", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询更换会员归属申请列表 */
+    getList() {
+      this.loading = true;
+      listApply(this.queryParams).then(response => {
+        this.applyList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 详情按钮操作 */
+    handleDetails(row) {
+      this.detailsVisible = true;
+      this.selectedApply = row;
+      getApply(row.id).then(response => {
+        this.selectedUsers = response.data.users;
+      });
+    },
+    /** 审核按钮操作 */
+    handleAudit(row) {
+      this.auditVisible = true;
+      this.auditForm = {
+        id: row.id,
+        fromName: row.fromName,
+        toName: row.toName,
+        status: 1,
+        reason: ''
+      };
+    },
+    /** 提交审核 */
+    submitAudit() {
+      this.$refs["auditForm"].validate(valid => {
+        if (valid) {
+          // 如果选择通过,不需要填写原因
+          if (this.auditForm.status === 1) {
+            this.auditForm.reason = '';
+          }
+          
+          this.submitLoading = true;
+          auditApply(this.auditForm).then(response => {
+            this.msgSuccess("审核成功");
+            this.auditVisible = false;
+            this.getList();
+          }).catch(error => {
+            this.msgError("审核失败:" + error);
+          }).finally(() => {
+            this.submitLoading = false;
+          });
+        }
+      });
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+/* 详情弹窗样式 */
+.apply-details-dialog {
+  ::v-deep .el-dialog__body {
+    padding: 20px;
+  }
+  
+  /* 头部信息样式 */
+  .details-header {
+    display: flex;
+    flex-wrap: wrap;
+    padding: 15px;
+    background-color: #f8f9fa;
+    border-radius: 4px;
+    margin-bottom: 20px;
+    
+    .info-item {
+      width: 50%;
+      margin-bottom: 10px;
+      display: flex;
+      align-items: center;
+      
+      .label {
+        color: #606266;
+        font-size: 14px;
+        width: 100px;
+        text-align: right;
+      }
+      
+      .value {
+        color: #303133;
+        font-size: 14px;
+        margin-left: 10px;
+      }
+    }
+  }
+  
+  /* 内容区域样式 */
+  .details-content {
+    .content-title {
+      font-size: 16px;
+      color: #303133;
+      font-weight: bold;
+      margin-bottom: 15px;
+      padding-left: 10px;
+      border-left: 3px solid #409EFF;
+    }
+  }
+  
+  /* 底部样式 */
+  .details-footer {
+    margin-top: 20px;
+    text-align: right;
+    padding-top: 15px;
+    border-top: 1px solid #ebeef5;
+  }
+}
+
+/* 表格样式优化 */
+::v-deep .el-table {
+  .el-table__header-wrapper {
+    th {
+      background-color: #f5f7fa;
+      color: #606266;
+      font-weight: bold;
+    }
+  }
+  
+  .el-table__body-wrapper {
+    tr:hover > td {
+      background-color: #f5f7fa;
+    }
+  }
+}
+
+/* 审核弹窗样式 */
+.audit-dialog {
+  ::v-deep .el-dialog__body {
+    padding: 20px;
+  }
+  
+  /* 表单样式 */
+  .el-form {
+    padding: 20px 0;
+    
+    .el-form-item {
+      margin-bottom: 20px;
+      
+      span {
+        color: #606266;
+        font-size: 14px;
+      }
+    }
+  }
+  
+  /* 底部样式 */
+  .audit-footer {
+    margin-top: 20px;
+    text-align: right;
+    padding-top: 15px;
+    border-top: 1px solid #ebeef5;
+  }
+}
+</style>

+ 66 - 0
src/views/company/companyConfig/index.vue

@@ -82,6 +82,53 @@
               <el-button type="primary" @click="onSubmit2">提交</el-button>
             </div>
         </el-tab-pane>
+        <el-tab-pane label="红包商户配置" name="redPacketConfig" >
+          <el-form ref="redPacketConfig" :model="redPacketConfig"  label-width="150px">
+            <el-form-item   label="红包接口类型" prop="isNew">
+              <el-radio-group v-model="redPacketConfig.isNew">
+                <el-radio label="0">商家转账到零钱(旧)</el-radio>
+                <el-radio label="1">商家转账(新)</el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item   label="公众号appid" prop="appId">
+              <el-input   v-model="redPacketConfig.appId"  label="请输入appId"></el-input>
+            </el-form-item>
+            <el-form-item   label="小程序appid" prop="appId">
+              <el-input   v-model="redPacketConfig.miniappId"  label="请输入appId"></el-input>
+            </el-form-item>
+            <el-form-item   label="商户号" prop="mchId">
+              <el-input   v-model="redPacketConfig.mchId"  label="请输入mchId"></el-input>
+            </el-form-item>
+            <el-form-item   label="商户密钥" prop="mchKey">
+              <el-input   v-model="redPacketConfig.mchKey"  label="mchKey"></el-input>
+            </el-form-item>
+            <el-form-item   label="p12证书路径" prop="keyPath">
+              <el-input   v-model="redPacketConfig.keyPath"  label="请输入keyPath"></el-input>
+            </el-form-item>
+            <el-form-item   label="apiV3密钥" prop="apiV3Key">
+              <el-input   v-model="redPacketConfig.apiV3Key"  label="请输入apiV3Key"></el-input>
+            </el-form-item>
+            <el-form-item   label="公钥ID" prop="publicKeyId">
+              <el-input   v-model="redPacketConfig.publicKeyId"  label="请输入公钥ID"></el-input>
+            </el-form-item>
+            <el-form-item   label="公钥证书" prop="publicKeyPath">
+              <el-input   v-model="redPacketConfig.publicKeyPath"  label="请输入publicKeyPath"></el-input>
+            </el-form-item>
+            <el-form-item   label="key路径" prop="privateKeyPath">
+              <el-input   v-model="redPacketConfig.privateKeyPath"  label="请输入"></el-input>
+            </el-form-item>
+            <el-form-item   label="cert路径" prop="privateCertPath">
+              <el-input   v-model="redPacketConfig.privateCertPath"  label="请输入"></el-input>
+            </el-form-item>
+            <el-form-item   label="回调地址" prop="notifyUrl">
+              <el-input   v-model="redPacketConfig.notifyUrl"  label="请输入"></el-input>
+            </el-form-item>
+
+            <div style="float:right;margin-right:20px">
+              <el-button type="primary" @click="onSubmit4">提交</el-button>
+            </div>
+          </el-form>
+        </el-tab-pane>
       </el-tabs>
 
       <el-dialog :title="customerExt.title" :visible.sync="customerExt.open" width="500px" append-to-body>
@@ -151,6 +198,10 @@ export default {
       qwConfig:{
 
       },
+      redPacketConfig:{},
+
+      redPacketConfigForm:{},
+
       qwConfigForm:{},
       qwkfConfigForm:{},
     };
@@ -160,6 +211,7 @@ export default {
     this.getConfigKey("sys:config");
     this.getConfigKey("qw:config");
     this.getConfigKey("customer:config");
+    this.getConfigKey("redPacket:config");
     this.getDicts("sys_company_status").then((response) => {
       this.statusOptions = response.data;
     });
@@ -258,6 +310,11 @@ export default {
                 if(response.data.configValue!=null){
                     this.customerConfig=JSON.parse(response.data.configValue);
                 }
+            }else if(key=="redPacket:config"){
+              this.redPacketConfigForm=response.data;
+              if(response.data.configValue!=null){
+                this.redPacketConfig=JSON.parse(response.data.configValue);
+              }
             }
 
         });
@@ -289,6 +346,15 @@ export default {
         }
       });
     },
+    onSubmit4() {
+      this.redPacketConfigForm.configValue=JSON.stringify(this.redPacketConfig);
+      updateConfig(this.redPacketConfigForm).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess("修改成功");
+          this.getConfigKey("redPacket:config");
+        }
+      });
+    },
   }
 };
 </script>

+ 82 - 6
src/views/company/companyUser/index.vue

@@ -63,15 +63,15 @@
              v-hasPermi="['qw:user:sync']"
            >同步企微员工和部门</el-button>
           </el-col>
-          <!-- <el-col :span="1.5">
+          <el-col :span="1.5">
             <el-button
               type="primary"
               plain
               size="mini"
-              @click="qwSyncDept"
-              v-hasPermi="['qw:qwDept:list']"
-            >同步企微部门</el-button>
-          </el-col> -->
+              :disabled="multiple"
+              @click="handerCompanyUserAreaList"
+            >批量设置销售所属区域(原有的暂用)</el-button>
+          </el-col>
           <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
         </el-row>
 
@@ -305,6 +305,21 @@
               <el-button type="primary" @click="generateSubDomain" style="margin-left: 10px;">生成</el-button>
             </el-form-item>
           </el-col>
+
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="销售区域" prop="addressId">
+              <el-select v-model="form.addressId"  filterable placeholder="请选择所属销售的区域" style="width: 200px;">
+                <el-option
+                  v-for="item in citysAreaList"
+                  :key="item.cityId"
+                  :label="item.cityName"
+                  :value="item.cityId"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
         </el-row>
       </el-form>
       <div slot="footer" class="dialog-footer">
@@ -316,11 +331,37 @@
       <selectUser ref="selectUser" @bindQwUser="bindQwUser"></selectUser>
     </el-dialog>
     <gw-query-list-dialog :visible="gwSearchOpen"/>
+    <el-dialog :title="companyUserArea.title" :visible.sync="companyUserArea.open" width="300px" append-to-body>
+      <el-select v-model="addressId"  filterable placeholder="请选择所属销售的区域" style="width: 200px;">
+        <el-option
+          v-for="item in citysAreaList"
+          :key="item.cityId"
+          :label="item.cityName"
+          :value="item.cityId"
+        ></el-option>
+      </el-select>
+      <div slot="footer" style="text-align: center;">
+        <el-button type="primary" @click="submitFormArea(addressId)">确 定</el-button>
+        <el-button @click="cancelArea">取 消</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
-import {qwList, delUser,addUser,getUser,updateUser,exportUser, resetUserPwd,changeUserStatus,importTemplate,generateSubDomain} from "@/api/company/companyUser";
+import {
+  qwList,
+  delUser,
+  addUser,
+  getUser,
+  updateUser,
+  exportUser,
+  resetUserPwd,
+  changeUserStatus,
+  importTemplate,
+  generateSubDomain,
+  getCitysAreaList, updateCompanyUserAreaList
+} from '@/api/company/companyUser'
 import { getToken } from "@/utils/auth";
 import { treeselect } from "@/api/company/companyDept";
 import Treeselect from "@riophae/vue-treeselect";
@@ -336,6 +377,13 @@ export default {
   components: {GwQueryListDialog, Treeselect ,selectUser},
   data() {
     return {
+      companyUserArea:{
+        open:false,
+        title:"分配区域",
+      },
+      //选择的区域
+      addressId:null,
+      citysAreaList:[],
       // 遮罩层
       loading: false,
       qwUserList:[],
@@ -440,6 +488,9 @@ export default {
         password: [
           { required: true, message: "员工密码不能为空", trigger: "blur" },
         ],
+        addressId: [
+          { required: true, message: "销售所属区域不能为空", trigger: "blur" },
+        ],
         idCard: [
           { required: true, message: "身份证号不能为空", trigger: "blur" },
         ],
@@ -476,6 +527,9 @@ export default {
     },
   },
   created() {
+    getCitysAreaList().then(res=>{
+      this.citysAreaList=res.data;
+    })
     this.getList();
     this.getTreeselect();
     this.getDicts("sys_normal_disable").then((response) => {
@@ -496,6 +550,28 @@ export default {
     });
   },
   methods: {
+    submitFormArea(address){
+      const uIds = this.ids;
+
+      if (address == null){
+        this.$message.error("请选择地区");
+        return;
+      }
+      updateCompanyUserAreaList({userIds:uIds,addressId:address}).then(res=>{
+        this.companyUserArea.open=false;
+        this.getList();
+        this.msgSuccess("操作成功");
+
+      })
+
+    },
+    cancelArea(){
+      this.companyUserArea.open=false;
+      this.addressId=null;
+    },
+    handerCompanyUserAreaList(){
+      this.companyUserArea.open=true;
+    },
     // 绑定个微
     gwBind(row){
       this.gwOpen = true

+ 149 - 78
src/views/components/course/userCourseVideoDetails.vue

@@ -16,9 +16,9 @@
         </el-descriptions-item>
         <el-descriptions-item label="封面图" >
           <el-popover v-if="item!=null"
-            placement="right"
-            title=""
-            trigger="hover">
+                      placement="right"
+                      title=""
+                      trigger="hover">
             <img slot="reference" :src="item.thumbnail" width="50px">
             <img :src="item.thumbnail" style="max-width: 150px;">
           </el-popover>
@@ -27,31 +27,47 @@
           <span v-if="item!=null">{{item.description}}</span>
         </el-descriptions-item>
         <el-descriptions-item label="时长" >
-            <span v-if="item!=null">{{formatDuration(item.duration)}}</span>
+          <span v-if="item!=null">{{formatDuration(item.duration)}}</span>
         </el-descriptions-item>
         <el-descriptions-item label="课程排序" >
-            <span v-if="item!=null">{{item.courseSort}}</span>
+          <span v-if="item!=null">{{item.courseSort}}</span>
         </el-descriptions-item>
 
         <el-descriptions-item label="创建时间" >
-            <span v-if="item!=null">{{item.createTime}}</span>
+          <span v-if="item!=null">{{item.createTime}}</span>
         </el-descriptions-item>
         <el-descriptions-item label="审核时间" >
-            <span v-if="item!=null">{{item.auditTime}}</span>
+          <span v-if="item!=null">{{item.auditTime}}</span>
         </el-descriptions-item>
+
+        <el-descriptions-item label="默认红包" >
+          <span v-if="item!=null">{{item.redPacketMoney}}</span>
+        </el-descriptions-item>
+
         <el-descriptions-item label="备注" >
-            <span v-if="item!=null">{{item.remark}}</span>
+          <span v-if="item!=null">{{item.remark}}</span>
         </el-descriptions-item>
       </el-descriptions>
     </div>
-    <div class="contentx" v-if="item">
+    <div class="contentx" v-if="item" @contextmenu.prevent>
       <div class="desct">
         视频内容
       </div>
-      <div style="display: flex; justify-content: center; align-items: center;">
-          <div style="display: grid; place-items: center;" v-if="item.videoUrl!=null">
-             <video  :src="item.videoUrl" controls style="max-width: 400px; max-height: 400px;"></video>
+      <div style="display: flex; justify-content: center; align-items: center; position: relative;">
+        <!-- 父容器设置为 relative,水印根据此容器定位 -->
+        <div style="display: grid; place-items: center; position: relative;" v-if="item.videoUrl != null">
+          <video ref="video" :src="item.videoUrl" controls disablepictureinpicture controlsList="nodownload nofullscreen nopictureinpicture"
+                 style="max-width: 800px; max-height: 600px;" @loadedmetadata="onVideoLoaded">
+          </video>
+
+          <!-- 动态生成的20个水印,按照4排每排5个 -->
+          <div class="watermarks" :style="watermarkGridStyle">
+            <div class="watermark" v-for="n in 20" :key="n"
+                 :style="getWatermarkPosition(n)">
+              {{ user.nickName }} : {{ user.userId }}
+            </div>
           </div>
+        </div>
       </div>
     </div>
     <div class="contentx" v-if="item">
@@ -85,82 +101,137 @@
 
 <script>
 import {getUserCourseVideo} from "@/api/course/userCourseVideo";
-  export default {
-    name: "userCourseVideoDetails",
-    data() {
+export default {
+  name: "userCourseVideoDetails",
+  data() {
+    return {
+      videoWidth: 0,  // 视频宽度
+      videoHeight: 0, // 视频高度
+      watermarkCount: 20, // 水印数量,固定为20个
+      watermarkSize: 20, // 单个水印的大小,固定为40px
+      user: this.$store.state.user.user,  // 从 Vuex 获取当前用户
+      typeOptions:[],
+      packageTypeOptions: [],
+      diseaseTypeOptions: [],
+      packageSubTypeOptions:[],
+      item:null,
+      packageItem:null,
+      productJson:[],
+      courseIdOptions: [],
+      // 视频状态 1:草稿,2:待审核,3:发布字典
+      statusOptions: [],
+      // 是否展示字典
+      orOptions: [],
+      // 来源 1 用户 2 后台字典
+      sourceOptions: [],
+      // 删除标志字典
+      isDelOptions: [],
+      isAuditOptions:[],
+      specShowOptions:[]
+    }
+  },
+  created() {
+    this.getDicts("sys_course_temp_type").then(response => {
+      this.typeOptions = response.data;
+    });
+  },
+  computed: {
+    // 动态计算水印容器的样式
+    watermarkGridStyle() {
       return {
-        typeOptions:[],
-        packageTypeOptions: [],
-        diseaseTypeOptions: [],
-        packageSubTypeOptions:[],
-        item:null,
-        packageItem:null,
-        productJson:[],
-        courseIdOptions: [],
-        // 视频状态 1:草稿,2:待审核,3:发布字典
-        statusOptions: [],
-        // 是否展示字典
-        orOptions: [],
-        // 来源 1 用户 2 后台字典
-        sourceOptions: [],
-        // 删除标志字典
-        isDelOptions: [],
-        isAuditOptions:[],
-        specShowOptions:[]
-      }
+        position: 'absolute',
+        top: '0',
+        left: '0',
+        width: '100%',
+        height: '100%',
+        zIndex: 1,
+        pointerEvents: 'none', // 让水印不会干扰视频控制
+        display: 'grid',
+        gridTemplateColumns: `repeat(5, 1fr)`,  // 每排5个水印
+        gridTemplateRows: `repeat(4, auto)`,   // 四排水印
+        gap: `${this.gap}px`,  // 水印之间的间隙
+      };
+    }
+  },
+  methods: {
+    // 视频加载完成后获取其尺寸
+    onVideoLoaded() {
+      const video = this.$refs.video;
+      this.videoWidth = video.videoWidth;
+      this.videoHeight = video.videoHeight;
+
+      // // 根据视频尺寸计算水印的数量和大小
+      // this.watermarkSize = Math.floor(this.videoWidth / 30); // 水印大小为视频宽度的 1/10
     },
-    created() {
-      this.getDicts("sys_course_temp_type").then(response => {
-        this.typeOptions = response.data;
-      });
+    // 计算每个水印的位置,按照四排5列排列
+    getWatermarkPosition(n) {
+      return {
+        position: 'relative',
+        color: 'rgba(255, 255, 255, 0.3)', // 提高透明度,确保不干扰视频内容
+        fontSize: `${this.watermarkSize}px`, // 固定水印的字体大小
+        textAlign: 'center',
+        lineHeight: `${this.watermarkSize}px`, // 水平方向和垂直方向居中
+        transform: 'rotate(-30deg)',
+      };
     },
-    methods: {
-      formatDuration(seconds) {
-        if (seconds === null || seconds === undefined) {
-          return '未上传视频'; // 或者您可以根据具体需求返回其他默认值
-        }
-        const hours = Math.floor(seconds / 3600);
-        const minutes = Math.floor((seconds % 3600) / 60);
-        const remainingSeconds = seconds % 60;
+    formatDuration(seconds) {
+      if (seconds === null || seconds === undefined) {
+        return '未上传视频'; // 或者您可以根据具体需求返回其他默认值
+      }
+      const hours = Math.floor(seconds / 3600);
+      const minutes = Math.floor((seconds % 3600) / 60);
+      const remainingSeconds = seconds % 60;
 
-        const formattedHours = hours > 0 ? hours.toString() + ':' : '';
-        const formattedMinutes = minutes.toString().padStart(2, '0');
-        const formattedSeconds = remainingSeconds.toString().padStart(2, '0');
+      const formattedHours = hours > 0 ? hours.toString() + ':' : '';
+      const formattedMinutes = minutes.toString().padStart(2, '0');
+      const formattedSeconds = remainingSeconds.toString().padStart(2, '0');
 
-        return `${formattedHours}${formattedMinutes}:${formattedSeconds}`;
-      },
-      getDetails(videoId) {
-        console.log(videoId);
-        this.item=null;
-        getUserCourseVideo(videoId).then(response => {
-          this.item = response.data;
-        });
-      },
-    }
+      return `${formattedHours}${formattedMinutes}:${formattedSeconds}`;
+    },
+    getDetails(videoId) {
+      console.log(videoId);
+      this.item=null;
+      getUserCourseVideo(videoId).then(response => {
+        this.item = response.data;
+      });
+    },
   }
+}
 </script>
 
 <style>
-  .contentx{
-      height: 100%;
-      background-color: #fff;
-      padding: 0px 20px 20px;
+/* 水印样式 */
+.watermark, .watermark-bottom-right {
+  position: absolute;
+  color: rgba(255, 255, 255, 0.3);  /* 提高透明度,确保水印不干扰视频内容 */
+  font-size: 10vw;  /* 动态字体大小,确保水印不会过大 */
+  transform: rotate(-30deg);
+  z-index: 1;
+  pointer-events: none;  /* 不干扰视频的交互 */
+}
 
+/* 设置视频和水印层的绝对定位,使水印显示在视频上方 */
+.watermark-full {
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  text-align: center;
+  line-height: 100%;  /* 水平和垂直居中 */
+}
+.contentx{
+  height: 100%;
+  background-color: #fff;
+  padding: 0px 20px 20px;
 
-      margin: 20px;
-  }
-  .el-descriptions-item__label.is-bordered-label{
-    font-weight: normal;
-  }
-  .el-descriptions-item__content {
-    max-width: 150px;
-    min-width: 100px;
-  }
-  .desct{
-      padding-top: 20px;
-      padding-bottom: 20px;
-      color: #524b4a;
-      font-weight: bold;
-    }
+
+  margin: 20px;
+}
+.desct{
+  padding-top: 20px;
+  padding-bottom: 20px;
+  color: #524b4a;
+  font-weight: bold;
+}
 
 </style>

+ 31 - 8
src/views/qw/autoTags/dayPartingIndex.vue

@@ -167,10 +167,28 @@
                     <div>
                       <div style="display: flex; align-items: center; flex-wrap: nowrap;">
                         <span style="margin-right: 10px;">规则 {{ index + 1 }}:</span>
-                        <el-form-item label="为每:" prop="week" style="flex: 8;margin-bottom: 0">
-                          <el-select v-model="item.week" remote multiple placeholder="请选择时间" filterable style="width: 350px;">
-                            <el-option v-for="dict in weekOptions" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
-                          </el-select>
+                        <el-form-item label="每天" prop="everyDay" style="flex: 8;margin-bottom: 0">
+                          <el-switch
+                            v-model="item.everyDay"
+                            active-text="每天"
+                            inactive-text="时间范围"
+                            :active-value="1"
+                            :inactive-value="0"
+                          >
+                          </el-switch>
+                        </el-form-item>
+                      </div>
+                      <div style="margin-left: 8%;display: flex; align-items: center; flex-wrap: nowrap;">
+                        <el-form-item label="时间范围:" prop="date" style="flex: 8;margin-bottom: 0" v-if="item.everyDay == 0">
+                          <el-date-picker
+                            v-model="item.date"
+                            type="daterange"
+                            @change="dateChange(item)"
+                            value-format="yyyy-MM-dd"
+                            range-separator="至"
+                            start-placeholder="开始日期"
+                            end-placeholder="结束日期">
+                          </el-date-picker>
                         </el-form-item>
                         <el-form-item prop="startTime" style="margin:0 5px 0 5px">
                           <el-time-select style="width: 120px;" placeholder="起始时间" v-model="item.startTime" :picker-options="{
@@ -347,6 +365,7 @@ export default {
       },
       //选择成员列表
       userSelectList:[],
+      defaultItem: {everyDay:1, tags:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null,date:null,remarks:null},
 
       //详情表单参数
       detailListFrom:{},
@@ -438,7 +457,7 @@ export default {
         // 遍历 tagListFormIndex 并将 tagId 放入对应位置的 tags
         newList.forEach((tags, index) => {
           if (!this.form.rulesTags[index]) {
-            this.$set(this.form.rulesTags, index, {tags:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null,remarks:null});
+            this.$set(this.form.rulesTags, index, JSON.parse(JSON.stringify(this.defaultItem)));
           }
 
           // 确保清空原来的 tags 数组
@@ -574,7 +593,7 @@ export default {
       if (this.form.rulesTags.length >=7) {
         return this.$message.error('当前规则已达上限,无法添加规则');
       }
-      this.form.rulesTags.push({tags:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null,remarks:null})
+      this.form.rulesTags.push(JSON.parse(JSON.stringify(this.defaultItem)))
     },
 
     //选择标签弹窗
@@ -661,7 +680,7 @@ export default {
         createName: null,
         applyUsers: null,
         ruleName: null,
-        rulesTags: [{tags:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null,remarks:null}],
+        rulesTags: [JSON.parse(JSON.stringify(this.defaultItem))],
         totalNumTags: null,
         numTagsTaday: null,
         isApply: null,
@@ -675,7 +694,7 @@ export default {
       this.tagListFormIndex=[];
 
       //规则
-      this.form.rulesTags=[{tags:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null,remarks:null}];
+      this.form.rulesTags=[JSON.parse(JSON.stringify(this.defaultItem))];
       this.resetForm("form");
     },
     /** 搜索按钮操作 */
@@ -759,6 +778,10 @@ export default {
         });
       }
     },
+    dateChange(item){
+      item.startDate = item.date[0];
+      item.endDate = item.date[1];
+    },
 
     /** 删除按钮操作 */
     handleDelete(row) {

+ 11 - 6
src/views/qw/autoTags/dayPartingIndexDetails.vue

@@ -50,12 +50,17 @@
               <div v-for="(rtList,index) in groupIndexFrom.rulesTags">
                 <div style="margin-left: 10%;margin-top: 1%">
                   <span class="spanSize">|规则 {{index +1}} </span>
-                  <div class="spanSize">客户在 每
-                    <span v-for="id in rtList.week" :key="id" style="display: inline;">
-                      <el-tag :disable-transitions="false"  v-for="list in weekOptions" :key="list.value" style="margin: 3px;" v-if="list.value==id">{{list.label}}</el-tag>
-                    </span>
-                    <span>【{{rtList.startTime}}~{{rtList.endTime}}】</span>
-                    时,打上标签
+                  <div class="spanSize">客户在
+                    <el-tag v-if="rtList.everyDay == 1" type="success">
+                      每天
+                    </el-tag>
+                    <el-tag type="success" v-if="rtList.everyDay == 0">{{rtList.startDate}} 到 {{rtList.endDate}}</el-tag>
+<!--                    <span v-for="id in rtList.week" :key="id" style="display: inline;">-->
+<!--                      <el-tag :disable-transitions="false"  v-for="list in weekOptions" :key="list.value" style="margin: 3px;" v-if="list.value==id">{{list.label}}</el-tag>-->
+<!--                    </span>-->
+                    的
+                    <el-tag type="warning">{{rtList.startTime}}~{{rtList.endTime}}</el-tag>
+                    时间段,打上标签
                     <span v-for="tagId in rtList.tags" :key="tagId" style="display: inline;">
                       <el-tag :disable-transitions="false"  v-for="list in tagList" :key="list.id" style="margin: 3px;" v-if="list.tagId==tagId">{{list.name}}</el-tag>
                     </span>

+ 90 - 36
src/views/qw/friendWelcome/index.vue

@@ -1,16 +1,6 @@
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="120px">
-      <el-form-item label="项目" prop="courseId">
-        <el-select filterable  v-model="queryParams.project" placeholder="请选择项目"  clearable size="small">
-          <el-option
-            v-for="dict in projectLists"
-            :key="dict.dictValue"
-            :label="dict.dictLabel"
-            :value="parseInt(dict.dictValue)"
-          />
-        </el-select>
-      </el-form-item>
       <el-form-item label="企微公司" prop="corpId">
           <el-select v-model="queryParams.corpId" placeholder="企微公司"  size="small" @change="updateCorpId()">
             <el-option
@@ -76,8 +66,6 @@
 
 
     <el-table v-loading="loading" :data="friendWelcomeList" @selection-change="handleSelectionChange" border>
-      <el-table-column label="项目" align="center" prop="projectName"/>
-
       <el-table-column label="消息内容" align="left" prop="welcomeText"  width="400px" >
         <template slot-scope="scope">
           <span style="color:rgb(19, 154, 50);" v-if="scope.row.isDayparting==='1'">[共 {{JSON.parse(scope.row.daypartingItemlist).length+1}} 时段]</span>
@@ -151,16 +139,7 @@
           </el-alert>
         </div>
         <el-form ref="form" :model="form" :rules="rules" label-width="120px">
-          <el-form-item label="项目" prop="project">
-            <el-select filterable  v-model="form.project" placeholder="请选择项目"  clearable size="small">
-              <el-option
-                v-for="dict in projectLists"
-                :key="dict.dictValue"
-                :label="dict.dictLabel"
-                :value="parseInt(dict.dictValue)"
-              />
-            </el-select>
-          </el-form-item>
+
           <el-form-item label="选择使用成员:" prop="qwUserIds" style="margin-top: 2%">
             <div>
               <el-button
@@ -208,6 +187,7 @@
                     <div style="flex: 1;">
                     <span v-if="item.msgtype === 'image'">【图片】: {{ item.image.pic_url }}</span>
                     <span v-if="item.msgtype === 'link'">【链接】: {{ item.link.title }}</span>
+                    <span v-if="item.msgtype === 'miniprogram'">【小程序】: {{ item.miniprogram.title }}</span>
                     </div>
                     <div style="  display: flex;gap: 10px;">
                       <el-button
@@ -231,13 +211,16 @@
             </el-row>
 
             <el-dropdown @command="(command) => handleCommand(command, -1)" trigger="click" placement="top-start">
-              <el-dropdown-menu slot="dropdown" style="width: 100px;">
+              <el-dropdown-menu slot="dropdown" style="width: 120px;">
                 <el-dropdown-item command="image">
                   <i class="el-icon-picture" style="margin-right: 10px;"></i>图片
                 </el-dropdown-item>
                 <el-dropdown-item command="link">
                   <i class="el-icon-link" style="margin-right: 10px;"></i>链接
                 </el-dropdown-item>
+                <el-dropdown-item command="miniprogram">
+                  <i class="el-icon-link" style="margin-right: 10px;"></i>小程序
+                </el-dropdown-item>
               </el-dropdown-menu>
 
               <span class="el-dropdown-link">
@@ -328,6 +311,7 @@
                                   <div style="flex: 1;">
                                     <span v-if="attachment.msgtype === 'image'">【图片】: {{ attachment.image.pic_url }}</span>
                                     <span v-if="attachment.msgtype === 'link'">【链接】: {{ attachment.link.title }}</span>
+                                    <span v-if="attachment.msgtype === 'miniprogram'">【小程序】: {{ attachment.miniprogram.title }}</span>
                                   </div>
                                   <div style="  display: flex;gap: 10px;">
                                     <el-button
@@ -351,13 +335,16 @@
                           </el-row>
 
                           <el-dropdown @command="(command) => handleCommand(command, index)" trigger="click" placement="top-start">
-                            <el-dropdown-menu slot="dropdown" style="width: 100px;">
+                            <el-dropdown-menu slot="dropdown" style="width: 120px;">
                               <el-dropdown-item command="image">
                                 <i class="el-icon-picture" style="margin-right: 10px;"></i>图片
                               </el-dropdown-item>
                               <el-dropdown-item command="link">
                                 <i class="el-icon-link" style="margin-right: 10px;"></i>链接
                               </el-dropdown-item>
+                              <el-dropdown-item command="miniprogram">
+                                <i class="el-icon-link" style="margin-right: 10px;"></i>小程序
+                              </el-dropdown-item>
                             </el-dropdown-menu>
 
                             <span class="el-dropdown-link">
@@ -395,7 +382,7 @@
     </el-dialog>
 
     <el-dialog :title="welcomeItem.title" :visible.sync="welcomeItem.open" style="width: 1300px;height: 100%" append-to-body>
-      <el-form ref="fileFrom" :model="fileFrom" :rules="fuleRules" label-width="100px">
+      <el-form ref="fileFrom" :model="fileFrom" :rules="fuleRules" label-width="110px">
         <div v-if="welcomeItem.type==='image'">
           <el-form-item label="图片:" prop="imagePicUrl">
             <ImageUpload v-model="fileFrom.imagePicUrl"  type="image" :num="10" :width="150" :height="150"  disabled/>
@@ -416,7 +403,15 @@
             <el-input v-model="fileFrom.linkUrl" :rows="2"  placeholder="请输入图文链接" />
           </el-form-item>
         </div>
+        <div v-if="welcomeItem.type==='miniprogram'">
+
+          <el-form-item label="小程序标题:" prop="miniprogramTitle" >
+            <el-input v-model="fileFrom.miniprogramTitle" :rows="2" maxlength="64"  show-word-limit placeholder="请输入小程序消息标题,最长为64字" />
+          </el-form-item>
+        </div>
+
       </el-form>
+
       <div slot="footer" class="dialog-footer" style="text-align: center">
         <el-button type="primary" @click="confirmUpload">确定</el-button>
         <el-button type="primary" @click="cancelUpload">取消</el-button>
@@ -450,7 +445,6 @@ export default {
       // 非多个禁用
       multiple: true,
       myQwCompanyList:[],
-      projectLists: [],
       //选择成员列表
       listUser:{
         title:"",
@@ -466,6 +460,10 @@ export default {
         linkUrl:null,
         videoId:null,
         courseId:null,
+        miniprogramTitle:null,
+        miniprogramPage:null,
+        miniprogramPicUrl:null,
+        miniprogramAppid:null,
       },
       courseList:[],
       videoList:[],
@@ -473,6 +471,7 @@ export default {
         imagePicUrl:[ { required: true, message: "图片不能为空", trigger: "submit" }],
         linkTitle:[ { required: true, message: "图文标题不能为空", trigger: "submit" }],
         linkUrl:[ { required: true, message: "图文链接不能为空", trigger: "submit" }],
+        miniprogramTitle:[ { required: true, message: "小程序标题不能为空", trigger: "submit" }],
       },
 
 
@@ -539,7 +538,6 @@ export default {
       form: {},
       // 表单校验
       rules: {
-        project: [{required: true,message: "项目不能为空!",trigger: "submit"}],
         qwUserIds: [
           { required: true, message: "发送企业群发消息的成员账号不能为空", trigger: "submit" }
         ],
@@ -574,9 +572,6 @@ export default {
     this.getDicts("sys_qw_allow_select").then(response => {
       this.allowSelectOptions = response.data;
     });
-    this.getDicts("sys_course_project").then(response => {
-      this.projectLists = response.data;
-    })
     courseList().then(response => {
       this.courseList = response.list;
     });
@@ -667,19 +662,30 @@ export default {
 
       this.welcomeItem = {
         open: true,
-        title: command === 'image' ? '添加图片' : '添加链接',
+        title: this.getTitleByCommand(command),
         type: command,
         index: itemIndex === -1 ? this.form.attachments.length : this.form.daypartingItemlist[itemIndex].attachments.length,
         itemIndex
       };
     },
 
+    getTitleByCommand(command) {
+        switch (command) {
+          case 'image':
+            return '添加图片';
+          case 'link':
+            return '添加链接';
+          case 'miniprogram':
+            return '添加小程序';
+      }
+    },
+
     //修改附件
     editFileItem(item, index, itemIndex){
 
       this.welcomeItem = {
         open: true,
-        title: item.msgtype === 'image' ? '编辑图片' : '编辑链接',
+        title: this.getEditTitleByMsgType(item.msgtype),
         type: item.msgtype,
         index,
         itemIndex
@@ -691,9 +697,44 @@ export default {
         this.fileFrom.linkPicUrl = item.link.picurl;
         this.fileFrom.linkDesc = item.link.desc;
         this.fileFrom.linkUrl = item.link.url;
+
+        this.fileFrom.videoId = item.link.videoId;
+        this.fileFrom.courseId = item.link.courseId;
+        this.fileFrom.expiresDays = item.link.expiresDays;
+
+        videoList(item.link.courseId).then(response => {
+          this.videoList = response.list;
+        });
+
+      }else if (item.msgtype === 'miniprogram') {
+        this.fileFrom.miniprogramAppid = 'wx73f85f8d62769119';
+        this.fileFrom.miniprogramTitle = item.miniprogram.title;
+        this.fileFrom.miniprogramPicUrl = "待生成";
+        this.fileFrom.miniprogramPage = "待生成";
+        this.fileFrom.videoId = item.miniprogram.videoId;
+        this.fileFrom.courseId = item.miniprogram.courseId;
+        this.fileFrom.expiresDays = item.miniprogram.expiresDays;
+
+        videoList(item.miniprogram.courseId).then(response => {
+          this.videoList = response.list;
+        });
+
+
+      }
+    },
+
+    getEditTitleByMsgType(msgType) {
+      switch (msgType) {
+        case 'image':
+          return '编辑图片';
+        case 'link':
+          return '编辑链接';
+        case 'miniprogram':
+          return '编辑小程序';
       }
     },
 
+
     //删除附件
     removeFileItem(data,index, itemIndex) {
 
@@ -720,7 +761,6 @@ export default {
 
       const { type, index, itemIndex } = this.welcomeItem;
 
-      console.log("this.welcomeItem:",this.welcomeItem)
       let attachment = {};
       if (type === 'image') {
         attachment = {
@@ -739,6 +779,16 @@ export default {
             url: this.fileFrom.linkUrl
           }
         };
+      }else if (type==='miniprogram'){
+        attachment = {
+          msgtype: 'miniprogram',
+          miniprogram: {
+            title: this.fileFrom.miniprogramTitle,
+            pic_media_id: this.fileFrom.miniprogramPicUrl,
+            appid: this.fileFrom.miniprogramAppid,
+            page: this.fileFrom.miniprogramPage,
+          }
+        };
       }
 
       if (itemIndex === -1) {
@@ -783,7 +833,13 @@ export default {
         linkTitle: null,
         linkPicUrl: null,
         linkDesc: null,
-        linkUrl: null
+        linkUrl: null,
+        videoId:null,
+        courseId:null,
+        miniprogramTitle:null,
+        miniprogramPage:null,
+        miniprogramPicUrl:null,
+        miniprogramAppid:null,
       };
 
       this.welcomeItem={
@@ -920,8 +976,6 @@ export default {
 
           }
 
-        } else {
-          this.loading=false;
         }
       });
     },

+ 191 - 16
src/views/qw/friendWelcome/indexNew.vue

@@ -187,6 +187,7 @@
                     <div style="flex: 1;">
                     <span v-if="item.msgtype === 'image'">【图片】: {{ item.image.pic_url }}</span>
                     <span v-if="item.msgtype === 'link'">【链接】: {{ item.link.title }}-{{item.link.desc}}</span>
+                    <span v-if="item.msgtype === 'miniprogram'">【小程序】: {{ item.miniprogram.title }}</span>
                     </div>
                     <div style="  display: flex;gap: 10px;">
                       <el-button
@@ -210,13 +211,16 @@
             </el-row>
 
             <el-dropdown @command="(command) => handleCommand(command, -1)" trigger="click" placement="top-start">
-              <el-dropdown-menu slot="dropdown" style="width: 100px;">
+              <el-dropdown-menu slot="dropdown" style="width: 120px;">
                 <el-dropdown-item command="image">
                   <i class="el-icon-picture" style="margin-right: 10px;"></i>图片
                 </el-dropdown-item>
                 <el-dropdown-item command="link">
                   <i class="el-icon-link" style="margin-right: 10px;"></i>链接
                 </el-dropdown-item>
+                <el-dropdown-item command="miniprogram">
+                  <i class="el-icon-link" style="margin-right: 10px;"></i>小程序
+                </el-dropdown-item>
               </el-dropdown-menu>
 
               <span class="el-dropdown-link">
@@ -260,7 +264,7 @@
                 <el-row>
                   <el-col style="width: 965px">
                     <div style="background-color: #fbfbfb;padding: 10px;  border: 1px solid #e6e6e6; margin-bottom: 20px;">
-                                        <el-form ref="friendWelcomeItemForm"  :rules="itemRules" :model="item">
+                          <el-form ref="friendWelcomeItemForm"  :rules="itemRules" :model="item">
                           <div style="display: flex; gap: 10px;">
                             <el-form-item label="发起时间:" prop="week" style="flex: 8;">
                               <el-select v-model="item.week" remote multiple placeholder="请选择" filterable style="width: 580px">
@@ -307,6 +311,7 @@
                                   <div style="flex: 1;">
                                     <span v-if="attachment.msgtype === 'image'">【图片】: {{ attachment.image.pic_url }}</span>
                                     <span v-if="attachment.msgtype === 'link'">【链接】: {{ attachment.link.title }}-{{attachment.link.desc}}</span>
+                                    <span v-if="attachment.msgtype === 'miniprogram'">【小程序】: {{ attachment.miniprogram.title }}</span>
                                   </div>
                                   <div style="  display: flex;gap: 10px;">
                                     <el-button
@@ -330,13 +335,16 @@
                           </el-row>
 
                           <el-dropdown @command="(command) => handleCommand(command, index)" trigger="click" placement="top-start">
-                            <el-dropdown-menu slot="dropdown" style="width: 100px;">
+                            <el-dropdown-menu slot="dropdown" style="width: 120px;">
                               <el-dropdown-item command="image">
                                 <i class="el-icon-picture" style="margin-right: 10px;"></i>图片
                               </el-dropdown-item>
                               <el-dropdown-item command="link">
                                 <i class="el-icon-link" style="margin-right: 10px;"></i>链接
                               </el-dropdown-item>
+                              <el-dropdown-item command="miniprogram">
+                                <i class="el-icon-link" style="margin-right: 10px;"></i>小程序
+                              </el-dropdown-item>
                             </el-dropdown-menu>
 
                             <span class="el-dropdown-link">
@@ -374,7 +382,7 @@
     </el-dialog>
 
     <el-dialog :title="welcomeItem.title" :visible.sync="welcomeItem.open" style="width: 1300px;height: 100%" append-to-body>
-      <el-form ref="fileFrom" :model="fileFrom" :rules="fuleRules" label-width="100px">
+      <el-form ref="fileFrom" :model="fileFrom" :rules="fuleRules" label-width="110px">
         <div v-if="welcomeItem.type==='image'">
           <el-form-item label="图片:" prop="imagePicUrl">
             <ImageUpload v-model="fileFrom.imagePicUrl"  type="image" :num="10" :width="150" :height="150"  disabled/>
@@ -391,7 +399,7 @@
                 :value="parseInt(dict.dictValue)"
               />
             </el-select>
-            <el-select  v-model="fileFrom.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;" @change="videoIdChange(fileFrom, welcomeItem.index, welcomeItem.itemIndex)" >
+            <el-select  v-model="fileFrom.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;" @change="videoIdChange(fileFrom, welcomeItem.index, welcomeItem.itemIndex,welcomeItem.type)" >
               <el-option
                 v-for="dict in videoList"
                 :key="dict.dictValue"
@@ -415,9 +423,19 @@
               <el-input v-model="fileFrom.linkUrl" placeholder="请输入链接地址" style="width: 90%;"/>
             </el-form-item>
           </div>
+          <el-form-item label="图文链接替换为注册链接" prop="isFixed">
+            <el-radio-group v-model="fileFrom.isFixed">
+              <el-radio
+                :label="1"
+              >是</el-radio>
+              <el-radio
+                :label="0"
+              >否</el-radio>
+            </el-radio-group>
+          </el-form-item>
           <div v-if="fileFrom.videoId!=null">
             <el-form-item label="图文链接:"  label-width="100px" >
-              <el-tag type="warning" v-model="fileFrom.linkUrl='待生成'">选择的课程小节 即为卡片链接地址</el-tag>
+              <el-tag type="warning" v-model="fileFrom.linkUrl='待生成'">选择的课程小节 即为卡片链接地址/注册链接</el-tag>
             </el-form-item>
           </div>
           <div v-if="fileFrom.videoId!=null">
@@ -435,6 +453,54 @@
 <!--            <el-input v-model="fileFrom.linkUrl" :rows="2"  placeholder="选择了课程小节会自动设置地址" />-->
 <!--          </el-form-item>-->
         </div>
+        <div v-if="welcomeItem.type==='miniprogram'">
+
+          <el-form-item label="选择课程">
+            <el-select  v-model="fileFrom.courseId" placeholder="请选择课程" style=" margin-right: 10px;" size="mini"  @change="courseChange(fileFrom,welcomeItem.index,welcomeItem.itemIndex)">
+              <el-option
+                v-for="dict in courseList"
+                :key="dict.dictValue"
+                :label="dict.dictLabel"
+                :value="parseInt(dict.dictValue)"
+              />
+            </el-select>
+            <el-select  v-model="fileFrom.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;" @change="videoIdChange(fileFrom, welcomeItem.index, welcomeItem.itemIndex,welcomeItem.type)" >
+              <el-option
+                v-for="dict in videoList"
+                :key="dict.dictValue"
+                :label="dict.dictLabel"
+                :value="parseInt(dict.dictValue)"
+              />
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="小程序标题:" prop="miniprogramTitle">
+            <el-input v-model="fileFrom.miniprogramTitle" :rows="2" maxlength="64" placeholder="请输入小程序消息标题,最长为64字节" @input="checkByteLength(fileFrom)" />
+          </el-form-item>
+          <div v-if="fileFrom.videoId!=null">
+            <el-form-item label="小程序链接:"  label-width="100px" >
+              <el-tag type="warning" v-model="fileFrom.miniprogramPage='待生成'">选择的课程小节 即为卡片小程序链接地址</el-tag>
+            </el-form-item>
+          </div>
+          <el-form-item label="appid" prop="miniprogramAppid" v-show="false" >
+            <el-input v-model="fileFrom.miniprogramAppid='wx73f85f8d62769119' " disabled />
+          </el-form-item>
+
+          <div v-if="fileFrom.videoId!=null">
+            <el-form-item label="课节过期时间" style="margin-top: 1%" required label-width="110px">
+              <el-row>
+                <el-input-number  v-model="fileFrom.expiresDays"  :min="1" :max="9999" ></el-input-number>
+                (天)
+              </el-row>
+              <el-row>
+                <span class="tip">默认为30天</span>
+              </el-row>
+            </el-form-item>
+          </div>
+          <!--          <el-form-item label="图文链接:" prop="linkUrl">-->
+          <!--            <el-input v-model="fileFrom.linkUrl" :rows="2"  placeholder="选择了课程小节会自动设置地址" />-->
+          <!--          </el-form-item>-->
+        </div>
       </el-form>
       <div slot="footer" class="dialog-footer" style="text-align: center">
         <el-button type="primary" @click="confirmUpload">确定</el-button>
@@ -485,6 +551,11 @@ export default {
         videoId:null,
         courseId:null,
         expiresDays:30,
+        isFixed:0,
+        miniprogramTitle:null,
+        miniprogramPage:null,
+        miniprogramPicUrl:null,
+        miniprogramAppid:null,
       },
       courseList:[],
       videoList:[],
@@ -492,6 +563,7 @@ export default {
         imagePicUrl:[ { required: true, message: "图片不能为空", trigger: "submit" }],
         linkTitle:[ { required: true, message: "图文标题不能为空", trigger: "submit" }],
         linkUrl:[ { required: true, message: "图文链接不能为空", trigger: "submit" }],
+        miniprogramTitle:[ { required: true, message: "图文链接不能为空", trigger: "submit" }],
       },
 
 
@@ -631,7 +703,42 @@ export default {
         this.loading = false;
       });
     },
+    // // 检查字节长度
+    checkByteLength(fileFrom) {
+      const text = fileFrom.miniprogramTitle;
+      const byteLength = this.getByteLength(text); // 获取当前字节数
+
+      // 如果字节数超过64,截断输入内容
+      if (byteLength > 64) {
+        this.$set(fileFrom, 'miniprogramTitle', this.truncateTextByByteLength(text,64));
+      }
+    },
+
+    // 计算字符串的字节数
+    getByteLength(text) {
+      return new Blob([text]).size; // 使用 Blob 计算字节数
+    },
 
+    // 根据字节数截断字符串
+    truncateTextByByteLength(text, maxByteLength) {
+      let byteLength = 0;
+      let result = "";
+
+      for (let i = 0; i < text.length; i++) {
+        const char = text[i];
+        const charByteLength = this.getByteLength(char); // 获取当前字符的字节数
+
+        // 如果加上当前字符的字节数后不超过限制,则添加到结果中
+        if (byteLength + charByteLength <= maxByteLength) {
+          result += char;
+          byteLength += charByteLength;
+        } else {
+          break; // 超过限制时停止
+        }
+      }
+
+      return result;
+    },
 
     //选择群发的企业成员账号
     handlelistUser(){
@@ -681,13 +788,25 @@ export default {
 
       this.welcomeItem = {
         open: true,
-        title: command === 'image' ? '添加图片' : '添加链接',
+        title: this.getTitleByCommand(command),
         type: command,
         index: itemIndex === -1 ? this.form.attachments.length : this.form.daypartingItemlist[itemIndex].attachments.length,
         itemIndex
       };
     },
 
+    getTitleByCommand(command) {
+      switch (command) {
+        case 'image':
+          return '添加图片';
+        case 'link':
+          return '添加链接';
+        case 'miniprogram':
+          return '添加小程序';
+      }
+    },
+
+
     courseChange(fileFrom,index,itemIndex){
 
       // 清空 videoId 选择
@@ -705,12 +824,12 @@ export default {
           this.$set(fileFrom, 'linkPicUrl', selectedCourse.dictImgUrl);
         }
 
+
         // 获取新的 videoList
         videoList(fileFrom.courseId).then(response => {
           this.videoList = response.list;
         });
 
-        console.log("this.videoList",this.videoList )
       }
       //
       // // 更新对应的数据层级
@@ -734,15 +853,20 @@ export default {
       //   });
       // }
     },
-    videoIdChange(fileFrom,index, itemIndex){
+    videoIdChange(fileFrom,index, itemIndex,type){
       //选择了课程小节则 默认绑上
       if (fileFrom.videoId != null) {
         // 根据 videoId 获取相关信息(假设有相关的 API 调用)
         let  selectedVideo = this.videoList.find(course => parseInt(course.dictValue) === fileFrom.videoId);
-        if (selectedVideo) {
+        if (selectedVideo && type==='link') {
           this.$set(fileFrom, 'linkDesc', selectedVideo.dictLabel);
           this.$set(fileFrom, 'expiresDays', 30);
         }
+        if (selectedVideo && type==='miniprogram') {
+          this.$set(fileFrom, 'miniprogramTitle', this.truncateTextByByteLength(selectedVideo.dictLabel,64));
+          this.$set(fileFrom, 'expiresDays', 30);
+        }
+
       }
 
       // // 更新对应的数据层级
@@ -769,7 +893,7 @@ export default {
 
       this.welcomeItem = {
         open: true,
-        title: item.msgtype === 'image' ? '编辑图片' : '编辑链接',
+        title: this.getEditTitleByMsgType(item.msgtype),
         type: item.msgtype,
         index,
         itemIndex
@@ -784,14 +908,44 @@ export default {
         this.fileFrom.videoId = item.link.videoId;
         this.fileFrom.courseId = item.link.courseId;
         this.fileFrom.expiresDays = item.link.expiresDays;
+        this.fileFrom.isFixed = item.link.isFixed;
+
+        videoList(item.link.courseId).then(response => {
+          this.videoList = response.list;
+        });
+
+
+      }else if (item.msgtype === 'miniprogram') {
+        this.fileFrom.miniprogramAppid = 'wx73f85f8d62769119';
+        this.fileFrom.miniprogramTitle = item.miniprogram.title;
+        this.fileFrom.miniprogramPicUrl = "待生成";
+        this.fileFrom.miniprogramPage = "待生成";
+        this.fileFrom.videoId = item.miniprogram.videoId;
+        this.fileFrom.courseId = item.miniprogram.courseId;
+        this.fileFrom.expiresDays = item.miniprogram.expiresDays;
+
+        videoList(item.miniprogram.courseId).then(response => {
+          this.videoList = response.list;
+        });
+
       }
 
-      videoList(item.link.courseId).then(response => {
-        this.videoList = response.list;
-      });
 
     },
 
+
+
+    getEditTitleByMsgType(msgType) {
+      switch (msgType) {
+        case 'image':
+          return '编辑图片';
+        case 'link':
+          return '编辑链接';
+        case 'miniprogram':
+          return '编辑小程序';
+      }
+    },
+
     //删除附件
     removeFileItem(data,index, itemIndex) {
 
@@ -814,9 +968,11 @@ export default {
     },
 
     //提交附件
-    confirmUpload(fileFrom) {
+    confirmUpload() {
 
       const { type, index, itemIndex } = this.welcomeItem;
+
+      console.log("this.fileFrom",this.fileFrom)
       let attachment = {};
       if (type === 'image') {
         attachment = {
@@ -836,6 +992,20 @@ export default {
             courseId:this.fileFrom.courseId,
             videoId:this.fileFrom.videoId,
             expiresDays:this.fileFrom.expiresDays,
+            isFixed:this.fileFrom.isFixed,
+          }
+        };
+      }else if (type==='miniprogram'){
+        attachment = {
+          msgtype: 'miniprogram',
+          miniprogram: {
+            title: this.fileFrom.miniprogramTitle,
+            pic_media_id: "待查询",
+            appid: "wx73f85f8d62769119",
+            page: this.fileFrom.miniprogramPage,
+            courseId:this.fileFrom.courseId,
+            videoId:this.fileFrom.videoId,
+            expiresDays:this.fileFrom.expiresDays,
           }
         };
       }
@@ -849,7 +1019,7 @@ export default {
           // 不存在附件则插入
           this.form.attachments.push(attachment);
         }
-        console.log("hujahsa:",this.form.attachments)
+
       } else {
         // 分时段欢迎语附件处理
         if (index < this.form.daypartingItemlist[itemIndex].attachments.length) {
@@ -884,8 +1054,13 @@ export default {
         linkPicUrl: null,
         linkDesc: null,
         linkUrl: null,
+        isFixed: 0,
         videoId:null,
         courseId:null,
+        miniprogramTitle:null,
+        miniprogramPage:null,
+        miniprogramPicUrl:null,
+        miniprogramAppid:null,
       };
 
       this.welcomeItem={

+ 97 - 37
src/views/qw/sop/addSop.vue

@@ -3,23 +3,23 @@
     <div style="margin: 30px;"> 添加sop任务</div>
     <div style="margin-top: 10px;margin-left: 50px;margin-right: 100px;margin-bottom: 60px;">
 
-      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+      <el-form ref="form" :model="form" :rules="rules" label-width="140px">
         <el-form-item label="规则名称" prop="name">
           <el-input v-model="form.name" placeholder="请输入规则名称" />
         </el-form-item>
 
         <el-form-item label="状态" prop="status">
-            <dict-tag :options="statusOptions" :value="form.status"></dict-tag>
+          <dict-tag :options="statusOptions" :value="form.status"></dict-tag>
         </el-form-item>
-<!--        <el-form-item label="状态">-->
-<!--          <el-radio-group v-model="form.status">-->
-<!--            <el-radio-->
-<!--              v-for="dict in statusOptions"-->
-<!--              :key="dict.dictValue"-->
-<!--              :label="parseInt(dict.dictValue)"-->
-<!--            >{{dict.dictLabel}}</el-radio>-->
-<!--          </el-radio-group>-->
-<!--        </el-form-item>-->
+        <!--        <el-form-item label="状态">-->
+        <!--          <el-radio-group v-model="form.status">-->
+        <!--            <el-radio-->
+        <!--              v-for="dict in statusOptions"-->
+        <!--              :key="dict.dictValue"-->
+        <!--              :label="parseInt(dict.dictValue)"-->
+        <!--            >{{dict.dictLabel}}</el-radio>-->
+        <!--          </el-radio-group>-->
+        <!--        </el-form-item>-->
         <el-form-item label="类别" prop="type">
           <el-radio-group v-model="form.type" @change="clearUser">
             <el-radio
@@ -31,11 +31,11 @@
           </el-radio-group>
         </el-form-item>
         <el-form-item label="小转天数" prop="minConversionDay">
-<!--          <el-input class="el-input" type="" v-model="form.minConversionDay" placeholder="请输入" />-->
+          <!--          <el-input class="el-input" type="" v-model="form.minConversionDay" placeholder="请输入" />-->
           <el-input-number   v-model="form.minConversionDay"  :min="1" :max="100" ></el-input-number>
         </el-form-item>
         <el-form-item label="大转天数" prop="maxConversionDay">
-<!--          <el-input class="el-input" v-model="form.maxConversionDay" placeholder="请输入" />-->
+          <!--          <el-input class="el-input" v-model="form.maxConversionDay" placeholder="请输入" />-->
           <el-input-number   v-model="form.maxConversionDay"  :min="1" :max="100" ></el-input-number>
         </el-form-item>
 
@@ -93,13 +93,13 @@
             </el-radio-group>
           </el-form-item>
           <el-form-item label="选择的标签" prop="tags">
-<!--            <el-select v-model="tags" remote multiple placeholder="请选择" filterable  style="width: 100%;">-->
-<!--              <el-option-->
-<!--                v-for="dict in tagList"-->
-<!--                :label="dict.name"-->
-<!--                :value="dict.tagId">-->
-<!--              </el-option>-->
-<!--            </el-select>-->
+            <!--            <el-select v-model="tags" remote multiple placeholder="请选择" filterable  style="width: 100%;">-->
+            <!--              <el-option-->
+            <!--                v-for="dict in tagList"-->
+            <!--                :label="dict.name"-->
+            <!--                :value="dict.tagId">-->
+            <!--              </el-option>-->
+            <!--            </el-select>-->
             <div @click="hangleChangeTags()" style="cursor: pointer; border: 1px solid #e6e6e6; background-color: white; overflow: hidden; flex-grow: 1;width: 390px">
               <div style="min-height: 35px; max-height: 200px; overflow-y: auto;">
                 <el-tag type="success"
@@ -116,13 +116,13 @@
 
           </el-form-item>
           <el-form-item label="排除的标签" prop="excludeTags">
-<!--            <el-select v-model="excludeTags" remote multiple placeholder="请选择" filterable  style="width: 100%;">-->
-<!--              <el-option-->
-<!--                v-for="dict in tagList"-->
-<!--                :label="dict.name"-->
-<!--                :value="dict.tagId">-->
-<!--              </el-option>-->
-<!--            </el-select>-->
+            <!--            <el-select v-model="excludeTags" remote multiple placeholder="请选择" filterable  style="width: 100%;">-->
+            <!--              <el-option-->
+            <!--                v-for="dict in tagList"-->
+            <!--                :label="dict.name"-->
+            <!--                :value="dict.tagId">-->
+            <!--              </el-option>-->
+            <!--            </el-select>-->
             <div @click="hangleChangeOutTags()" style="cursor: pointer; border: 1px solid #e6e6e6; background-color: white; overflow: hidden; flex-grow: 1;width: 390px">
               <div style="min-height: 35px; max-height: 200px; overflow-y: auto;">
                 <el-tag type="success"
@@ -184,6 +184,16 @@
 
           </el-form-item>
         </div>
+        <el-form-item label="是否固定营期" prop="isFixed">
+          <el-radio-group v-model="form.isFixed">
+            <el-radio
+              :label="1"
+            >是</el-radio>
+            <el-radio
+              :label="0"
+            >否</el-radio>
+          </el-radio-group>
+        </el-form-item>
 
         <el-form-item label="开始时间" prop="startTime">
           <el-date-picker clearable size="small"
@@ -199,7 +209,7 @@
             (小时)
           </el-row>
         </el-form-item>
-        <el-form-item v-if="form.sendType==2 || form.sendType==4" label="自动添加SOP" prop="autoSopTime.autoSopType" >
+        <el-form-item v-if="(form.sendType==2 || form.sendType==4)" label="自动添加SOP" prop="autoSopTime.autoSopType" >
           <el-radio-group v-model="form.autoSopTime.autoSopType">
             <el-radio
               :label="1"
@@ -246,6 +256,17 @@
             </el-form-item>
           </div>
         </div>
+        <el-form-item label="所属项目" prop="project">
+          <el-select v-model="form.project" placeholder="请选择项目" filterable clearable size="small">
+            <el-option
+              v-for="dict in projectOptions"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="dict.dictValue"
+            />
+          </el-select>
+        </el-form-item>
+
         <el-form-item  v-if="form.autoSopTime.autoSopType==1" label="过期消息是否发送" prop="autoSopSend"  label-width="130px">
           <el-radio-group v-model="form.autoSopTime.autoSopSend">
             <el-radio
@@ -256,6 +277,19 @@
             >否</el-radio>
           </el-radio-group>
         </el-form-item>
+
+
+        <el-form-item label="是否只发送注册用户" prop="isFixed">
+          <el-radio-group v-model="form.isRegister">
+            <el-radio
+              :label="1"
+            >是</el-radio>
+            <el-radio
+              :label="0"
+            >否</el-radio>
+          </el-radio-group>
+        </el-form-item>
+
         <el-alert
           v-if="form.autoSopTime.autoSopType==1"
           title="起始时间-结束时间之内的,当天立即创建SOP,时间之外的 次日创建SOP"
@@ -277,17 +311,17 @@
         <el-button type="primary" @click="submitForm">确 定</el-button>
         <el-button @click="cancel">取 消</el-button>
       </div>
-    <el-dialog :title="listUser.title" :visible.sync="listUser.open" style="width: 1300px;height: 100%" append-to-body>
-      <qwUserList ref="QwUserList" @selectUserList="selectUserList"></qwUserList>
-    </el-dialog>
+      <el-dialog :title="listUser.title" :visible.sync="listUser.open" style="width: 1300px;height: 100%" append-to-body>
+        <qwUserList ref="QwUserList" @selectUserList="selectUserList"></qwUserList>
+      </el-dialog>
 
-    <el-dialog :title="companyUser.title" :visible.sync="companyUser.open" style="width: 110%;height: 100%" append-to-body>
-      <company-user-list ref="companyUserList" @selectCompanyUserList="selectCompanyUserList"></company-user-list>
-    </el-dialog>
+      <el-dialog :title="companyUser.title" :visible.sync="companyUser.open" style="width: 110%;height: 100%" append-to-body>
+        <company-user-list ref="companyUserList" @selectCompanyUserList="selectCompanyUserList"></company-user-list>
+      </el-dialog>
 
-    <el-dialog title="选择模板"  :visible.sync="tempOpen" append-to-body  >
-      <sop-temp ref="SopTempComments" @sopTemp="sopTemp"></sop-temp>
-    </el-dialog>
+      <el-dialog title="选择模板"  :visible.sync="tempOpen" append-to-body  >
+        <sop-temp ref="SopTempComments" @sopTemp="sopTemp"></sop-temp>
+      </el-dialog>
 
       <!--  选择/排除标签   -->
       <el-dialog :title="changeTagDialog.title" :visible.sync="changeTagDialog.open" style="width:100%;height: 100%" append-to-body>
@@ -380,6 +414,8 @@ export default {
       tagsIdsChangeSelectList:null,
       //排除的标签
       outTagsIdsChangeSelectList:null,
+      //添加的标签
+      addTagsIdsChangeSelectList:null,
       //标签弹窗选择
       tagChange:{
         open:false,
@@ -417,6 +453,7 @@ export default {
 
       //企微SOP发送类型
       sysQwSopType: [],
+      projectOptions:[],
 
       sopLogsDialog:{
         title:'',
@@ -427,6 +464,8 @@ export default {
       form: {
         status: 1,
         sendType:2,
+        isFixed: 0,
+        isRegister: 0,
         type: 2,
         filterType:2,
         expiryTime:2,
@@ -470,6 +509,9 @@ export default {
       this.sysQwSopType = response.data;
     });
 
+    this.getDicts("sys_course_project").then(response => {
+      this.projectOptions = response.data;
+    });
     listUser().then(res => {
         this.companyUserLists = res.rows;
       }
@@ -627,6 +669,23 @@ export default {
       }
     },
 
+    //选择排除标签
+    hangleChangeAddTags(){
+      this.changeTagDialog.title="选择排除的标签"
+      this.changeTagDialog.open=true;
+      this.changeTagDialog.type=2;
+
+      // 获取 tagListFormIndex 中的所有 tagId,用于快速查找
+      const selectedTagIds = new Set(
+        (this.outTagsIdsChangeSelectList || []).map(tagItem => tagItem?.tagId)
+      );
+      for (let i = 0; i < this.tagGroupList.length; i++) {
+        for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+          this.tagGroupList[i].tag[x].isSelected = selectedTagIds.has(this.tagGroupList[i].tag[x].tagId);
+        }
+      }
+    },
+
     //删除一些选择的标签
     handleCloseTags(list){
       const ls = this.tagsIdsChangeSelectList.findIndex(t => t.tagId === list.tagId);
@@ -734,6 +793,7 @@ export default {
         id: null,
         name: null,
         status: 1,
+        isFixed:0,
         sendType:2,
         type: 2,
         filterType:2,

+ 0 - 12
src/views/qw/sopTemp/index.vue

@@ -292,7 +292,6 @@ import {
   exportSopTemp,
   shareSopTemp
 } from "@/api/qw/sopTemp";
-import {getCompanyList,listCompany} from "@/api/company/company";
 export default {
   name: "SopTemp",
   data() {
@@ -377,7 +376,6 @@ export default {
       this.statusOptions = response.data;
     });
 
-    this.getCompanyList();
 
     this.getDicts("sys_qw_sop_type").then(response => {
       this.sysQwSopType = response.data;
@@ -387,21 +385,11 @@ export default {
 
     handleCompanyQuery(){
       this.queryCompanyParams.pageNum = 1;
-      this.getCompanyList();
     },
     resetCompanyQuery(){
       this.resetForm("queryCompanyForm");
       this.handleCompanyQuery();
     },
-    /** 查询企业列表 */
-    getCompanyList() {
-      this.companysloading = true;
-      listCompany(this.queryCompanyParams).then(response => {
-        this.companyList = response.rows;
-        this.companyTotal = response.total;
-        this.companysloading = false;
-      });
-    },
     /** 查询sop模板列表 */
     getList() {
       this.loading = true;

+ 30 - 5
src/views/qw/sopTemp/updateSopTemp.vue

@@ -94,7 +94,7 @@
                                       </el-switch>
                                   </el-form-item>
 
-                                  <el-form-item label="消息类别" v-if="form.sendType != 4">
+                                    <el-form-item label="消息类别" v-if="form.sendType != 4">
                                     <el-radio-group v-model="content.type" :disabled="formType == 3 || content.isOfficial === '1'"
                                                     @change="updateHtml(() => content.contentType = '1')">
                                       <el-radio :label="1">普通</el-radio>
@@ -398,6 +398,31 @@
 
                                             </el-card>
                                           </div>
+                                          <div v-if="setList.contentType == 10 ">
+                                            <el-card class="box-card">
+                                              <el-form-item label="链接标题:" label-width="100px" required>
+                                                <el-input :disabled="formType == 3" v-model="setList.linkTitle"
+                                                          placeholder="请输入链接标题"
+                                                          style="width: 90%;"/>
+                                              </el-form-item>
+                                              <el-form-item label="链接描述:" label-width="100px" required>
+                                                <el-input :disabled="formType == 3" type="textarea" :rows="3"
+                                                          v-model="setList.linkDescribe"
+                                                          placeholder="请输入链接描述"
+                                                          style="width: 90%;margin-top: 1%;"/>
+                                              </el-form-item>
+                                              <el-form-item label="链接封面:" label-width="100px" required>
+                                                <ImageUpload :disabled="formType == 3" v-model="setList.linkImageUrl"
+                                                             type="image" :num="1"
+                                                             :file-size="2" :width="150" :height="150"
+                                                             style="margin-top: 1%;"/>
+                                              </el-form-item>
+                                              <el-form-item label="链接地址:" label-width="100px">
+                                                <el-tag type="warning"> 链接地址自动生成
+                                                </el-tag>
+                                              </el-form-item>
+                                            </el-card>
+                                          </div>
                                         </el-form-item>
                                         <el-form-item label="添加短链"
                                                       v-if="content.type == 2 && setList.contentType == 1  ">
@@ -420,7 +445,7 @@
                                                 style="margin-left: 10px; color: #b1b4ba">不加URL</span>
                                         </el-form-item>
                                         <el-form-item label="课节过期时间"
-                                                      v-if="content.type == 2 && setList.isBindUrl == '1' && setList.contentType != 2  && setList.contentType != 5  && setList.contentType != 6 && setList.contentType != 8 ">
+                                                      v-if="content.type == 2 && setList.isBindUrl == '1' && setList.contentType != 2  && setList.contentType != 5  && setList.contentType != 6 && setList.contentType != 8 && setList.contentType != 9 && setList.contentType != 10  ">
                                           <el-row>
                                             <el-input type="number" v-model="setList.expiresDays"
                                                       style="width: 200px">
@@ -994,15 +1019,15 @@ export default {
                 this.$message.error("图片不能为空")
                 return false;
               }
-              if ((data.content[j].setting[k].contentType == 3 ||data.content[j].setting[k].contentType == 9) && (data.content[j].setting[k].linkTitle == null || data.content[j].setting[k].linkTitle == "")) {
+              if ((data.content[j].setting[k].contentType == 3 ||data.content[j].setting[k].contentType == 9 || data.content[j].setting[k].contentType == 10) && (data.content[j].setting[k].linkTitle == null || data.content[j].setting[k].linkTitle == "")) {
                 this.$message.error("链接标题不能为空")
                 return false;
               }
-              if ((data.content[j].setting[k].contentType == 3 ||data.content[j].setting[k].contentType == 9 ) && (data.content[j].setting[k].linkDescribe == null || data.content[j].setting[k].linkDescribe == "")) {
+              if ((data.content[j].setting[k].contentType == 3 ||data.content[j].setting[k].contentType == 9 || data.content[j].setting[k].contentType == 10 ) && (data.content[j].setting[k].linkDescribe == null || data.content[j].setting[k].linkDescribe == "")) {
                 this.$message.error("链接描述不能为空")
                 return false;
               }
-              if ((data.content[j].setting[k].contentType == 3 ||data.content[j].setting[k].contentType == 9 ) && (data.content[j].setting[k].linkImageUrl == null || data.content[j].setting[k].linkImageUrl == "")) {
+              if ((data.content[j].setting[k].contentType == 3 ||data.content[j].setting[k].contentType == 9 || data.content[j].setting[k].contentType == 10 ) && (data.content[j].setting[k].linkImageUrl == null || data.content[j].setting[k].linkImageUrl == "")) {
                 this.$message.error("链接图片不能为空")
                 return false;
               }

+ 27 - 3
src/views/qw/sopUserLogsInfo/sopUserLogsInfoDetails.vue

@@ -179,6 +179,12 @@
               />
             </el-select>
           </el-form-item>
+          <el-form-item label="只发送注册用户" prop="isRegister" label-width="120px" >
+                  <el-radio-group v-model="msgForm.isRegister">
+                     <el-radio v-model="msgForm.isRegister" :label="1">是</el-radio>
+                     <el-radio v-model="msgForm.isRegister" :label="2">否</el-radio>
+                  </el-radio-group>
+          </el-form-item>
           <el-form-item label="规则" prop="setting"  >
             <div v-for="(item, index) in setting" :key="index" style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;">
               <el-row>
@@ -256,10 +262,23 @@
                           placeholder="输入要转为语音的内容" style="width: 90%;margin-top: 10px;"
                           @input="handleInputVideoText(item.value,item)"/>
                       </div>
-                      <div v-if="item.contentType == 8">
 
+                      <div v-if="item.contentType == 10 ">
+                        <el-card class="box-card">
+                          <el-form-item label="链接标题:"  label-width="100px">
+                            <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
+                          </el-form-item>
+                          <el-form-item label="链接描述:"   label-width="100px" >
+                            <el-input type="textarea" :rows="3" v-model="item.linkDescribe" placeholder="请输入链接描述" style="width: 90%;margin-top: 1%;"/>
+                          </el-form-item>
+                          <el-form-item label="链接封面:"   label-width="100px">
+                            <ImageUpload v-model="item.linkImageUrl" type="image" :num="1" :file-size="2" :width="150" :height="150" style="margin-top: 1%;" />
+                          </el-form-item>
+                          <el-form-item label="链接地址:"  label-width="100px" >
+                            <el-tag type="warning" >链接地址自动生成</el-tag>
+                          </el-form-item>
+                        </el-card>
                       </div>
-
                     </el-form-item>
 
                     <el-form-item label="添加短链" v-if="item.contentType == 1 "  >
@@ -281,7 +300,9 @@
                                                           && item.contentType != 2
                                                           && item.contentType != 5
                                                           && item.contentType != 6
-                                                          && item.contentType != 8"
+                                                          && item.contentType != 8
+                                                          && item.contentType != 9
+                                                          && item.contentType != 10"
                                   style="margin-top: 1%" label-width="100px">
                       <el-row>
                         <el-input-number  v-model="item.expiresDays"  :min="1" :max="100" ></el-input-number>
@@ -416,6 +437,7 @@ export default {
         ids:null,
         sopId: null,
         startTime: null,
+        isRegister:2
       },
       // 表单校验
       rules: {},
@@ -758,6 +780,8 @@ export default {
               courseId:null,
               courseType:null,
               setting:null,
+              isRegister:2,
+
             }
             this.getList();
           }).finally(()=>{

+ 115 - 90
src/views/qw/tagGroup/index.vue

@@ -2,14 +2,14 @@
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
       <el-form-item label="企微公司" prop="corpId">
-                      <el-select v-model="queryParams.corpId" placeholder="企微公司"  size="small" @change="updateCorpId()">
-                        <el-option
-                          v-for="dict in myQwCompanyList"
-                          :key="dict.dictValue"
-                          :label="dict.dictLabel"
-                          :value="dict.dictValue"
-                        />
-                      </el-select>
+        <el-select v-model="queryParams.corpId" placeholder="企微公司" size="small" @change="updateCorpId()">
+          <el-option
+            v-for="dict in myQwCompanyList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
       </el-form-item>
       <el-form-item label="名称" prop="name">
         <el-input
@@ -35,7 +35,8 @@
           size="mini"
           @click="handleSync"
           v-hasPermi="['qw:tagGroup:sync']"
-        >同步</el-button>
+        >同步
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button
@@ -45,7 +46,8 @@
           size="mini"
           @click="handleAdd"
           v-hasPermi="['qw:tagGroup:add']"
-        >新增</el-button>
+        >新增
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button
@@ -56,7 +58,8 @@
           :disabled="single"
           @click="handleUpdate"
           v-hasPermi="['qw:tagGroup:edit']"
-        >修改</el-button>
+        >修改
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button
@@ -67,7 +70,8 @@
           :disabled="multiple"
           @click="handleDelete"
           v-hasPermi="['qw:tagGroup:remove']"
-        >删除</el-button>
+        >删除
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button
@@ -78,13 +82,14 @@
           :loading="exportLoading"
           @click="handleExport"
           v-hasPermi="['qw:tagGroup:export']"
-        >导出</el-button>
+        >导出
+        </el-button>
       </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
     <el-table v-loading="loading" :data="tagGroupList" @selection-change="handleSelectionChange" border>
-      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column type="selection" width="55" align="center"/>
 
       <el-table-column label="来源" align="center" prop="groupFrom">
         <template slot-scope="scope">
@@ -92,11 +97,12 @@
           <span v-else>其他创建</span>
         </template>
       </el-table-column>
-      <el-table-column label="名称" align="center" prop="name" />
-      <el-table-column label="排序" align="center" prop="order" />
-      <el-table-column label="标签" align="center" prop="tag" >
-        <template slot-scope="scope" >
-            <el-tag type="success" v-for="i in scope.row.tag" :key="i.id" style="margin: 2px 5px 0 0;">{{i.name}}</el-tag>
+      <el-table-column label="名称" align="center" prop="name"/>
+      <el-table-column label="排序" align="center" prop="order"/>
+      <el-table-column label="标签" align="center" prop="tag">
+        <template slot-scope="scope">
+          <el-tag type="success" v-for="i in scope.row.tag" :key="i.id" style="margin: 2px 5px 0 0;">{{ i.name }}
+          </el-tag>
         </template>
       </el-table-column>
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
@@ -107,14 +113,16 @@
             icon="el-icon-edit"
             @click="handleUpdate(scope.row)"
             v-hasPermi="['qw:tagGroup:edit']"
-          >修改</el-button>
+          >修改
+          </el-button>
           <el-button
             size="mini"
             type="text"
             icon="el-icon-delete"
             @click="handleDelete(scope.row)"
             v-hasPermi="['qw:tagGroup:remove']"
-          >删除</el-button>
+          >删除
+          </el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -132,38 +140,42 @@
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
 
         <el-form-item label="名称" prop="name">
-          <el-input v-model="form.name" placeholder="请输入名称"  />
+          <el-input v-model="form.name" placeholder="请输入名称"/>
         </el-form-item>
         <el-form-item label="排序" prop="order">
-          <el-input-number v-model="form.order" placeholder="请输入排序" />
+          <el-input-number v-model="form.order" placeholder="请输入排序"/>
         </el-form-item>
         <el-row>
-           <el-col :span="3"><div style="margin-top: 9px;font-weight: bold;float: right; margin-right: 10px;">标签</div></el-col>
+          <el-col :span="3">
+            <div style="margin-top: 9px;font-weight: bold;float: right; margin-right: 10px;">标签</div>
+          </el-col>
           <el-col :span="21">
-            <el-table :data="form.tag"   >
-            <el-table-column label="来源" prop="tagFrom" align="center" width="90" >
-              <template slot-scope="scope">
+            <el-table :data="form.tag">
+              <el-table-column label="来源" prop="tagFrom" align="center" width="90">
+                <template slot-scope="scope">
                   <span v-if="scope.row.tagFrom==1">本应用创建</span>
                   <span v-if="!scope.row.tagFrom && scope.row.tagId">其他创建</span>
-              </template>
-            </el-table-column>
-            <el-table-column label="名称" prop="name" align="center" >
-              <template slot-scope="scope">
-                <el-input v-model="scope.row.name"   ></el-input>
-              </template>
-            </el-table-column>
-            <el-table-column label="排序" prop="order" align="center" >
-              <template slot-scope="scope">
-                <el-input-number v-model="scope.row.order"  ></el-input-number>
-              </template>
-            </el-table-column>
-            <el-table-column label="操作" width="150px" align="center">
-              <template slot-scope="scope" >
-                  <el-button @click="addRow" size="mini" type="text" >新增</el-button>
-                  <el-button @click="deleteRow(scope.$index,scope.row)"   size="mini" type="text" v-if="form.tag.length>1"   >删除</el-button>
-              </template>
-            </el-table-column>
-          </el-table>
+                </template>
+              </el-table-column>
+              <el-table-column label="名称" prop="name" align="center">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.name"></el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="排序" prop="order" align="center">
+                <template slot-scope="scope">
+                  <el-input-number v-model="scope.row.order"></el-input-number>
+                </template>
+              </el-table-column>
+              <el-table-column label="操作" width="150px" align="center">
+                <template slot-scope="scope">
+                  <el-button @click="addRow" size="mini" type="text">新增</el-button>
+                  <el-button @click="deleteRow(scope.$index,scope.row)" size="mini" type="text"
+                             v-if="form.tag.length>1">删除
+                  </el-button>
+                </template>
+              </el-table-column>
+            </el-table>
           </el-col>
         </el-row>
       </el-form>
@@ -176,8 +188,17 @@
 </template>
 
 <script>
-import { syncTag,listTagGroup, getTagGroup, delTagGroup, addTagGroup, updateTagGroup, exportTagGroup } from "@/api/qw/tagGroup";
-import { getMyQwUserList,getMyQwCompanyList } from "@/api/qw/user";
+import {
+  syncTag,
+  listTagGroup,
+  getTagGroup,
+  delTagGroup,
+  addTagGroup,
+  updateTagGroup,
+  exportTagGroup
+} from "@/api/qw/tagGroup";
+import {getMyQwUserList, getMyQwCompanyList} from "@/api/qw/user";
+
 export default {
   name: "TagGroup",
   data() {
@@ -193,7 +214,7 @@ export default {
       // 非多个禁用
       multiple: true,
       myQwUserList: [],
-      myQwCompanyList:[],
+      myQwCompanyList: [],
       // 显示搜索条件
       showSearch: true,
       // 总条数
@@ -214,32 +235,34 @@ export default {
         corpId: null,
         companyId: null,
       },
-      tagJson:[],
+      tagJson: [],
       // 表单参数
       form: {},
       // 表单校验
       rules: {
 
         name: [
-          { required: true, message: "名称不能为空", trigger: "blur" }
+          {required: true, message: "名称不能为空", trigger: "blur"}
         ],
       }
     };
   },
   created() {
     getMyQwCompanyList().then(response => {
-            this.myQwCompanyList = response.data;
-            if(this.myQwCompanyList!=null){
-              this.queryParams.corpId=this.myQwCompanyList[0].dictValue
-              this.getList();
-            }
+      this.myQwCompanyList = response.data;
+      if (this.myQwCompanyList != null && this.myQwCompanyList.length > 0) {
+        this.queryParams.corpId = this.myQwCompanyList[0].dictValue
+        this.getList();
+      }else{
+        this.loading = false;
+      }
     });
   },
   methods: {
 
-    updateCorpId(){
-           this.getList();
-     },
+    updateCorpId() {
+      this.getList();
+    },
     /** 查询企微客户标签组列表 */
     getList() {
       this.loading = true;
@@ -274,13 +297,13 @@ export default {
       this.getList();
     },
     addRow() {
-      this.form.tag.push({ name: "标签", order:0 ,tagFrom:1},);
+      this.form.tag.push({name: "标签", order: 0, tagFrom: 1},);
     },
-    deleteRow(index,row) {
+    deleteRow(index, row) {
 
-      if (row.tagFrom==1){
+      if (row.tagFrom == 1) {
         this.form.tag.splice(index, 1);
-      }else {
+      } else {
         this.$message.error("不能删除非本应用创建的标签")
       }
     },
@@ -288,21 +311,21 @@ export default {
     /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
-      this.queryParams.corpId= this.myQwCompanyList[0].dictValue;
+      this.queryParams.corpId = this.myQwCompanyList[0].dictValue;
       this.handleQuery();
     },
     // 多选框选中数据
     handleSelectionChange(selection) {
       this.ids = selection.map(item => item.id)
-      this.single = selection.length!==1
+      this.single = selection.length !== 1
       this.multiple = !selection.length
     },
     /** 新增按钮操作 */
     handleAdd() {
       this.reset();
       this.open = true;
-      this.form.tag= [
-        { name: "标签", order:0,tagFrom:1},
+      this.form.tag = [
+        {name: "标签", order: 0, tagFrom: 1},
       ];
       this.title = "添加企微客户标签组";
     },
@@ -316,15 +339,15 @@ export default {
         this.title = "修改企微客户标签组";
       });
     },
-    handleSync(){
-      this.loading=true;
+    handleSync() {
+      this.loading = true;
       syncTag(this.queryParams.corpId).then(response => {
         this.msgSuccess("同步成功");
 
         this.getList();
       }).finally(
         () => {
-          this.loading=false;
+          this.loading = false;
         }
       );
     },
@@ -332,7 +355,7 @@ export default {
     submitForm() {
       this.$refs["form"].validate(valid => {
         if (valid) {
-          this.form.corpId=this.queryParams.corpId
+          this.form.corpId = this.queryParams.corpId
           if (this.form.id != null) {
             updateTagGroup(this.form).then(response => {
               this.msgSuccess("修改成功");
@@ -353,30 +376,32 @@ export default {
     handleDelete(row) {
       const ids = row.id || this.ids;
       this.$confirm('是否确认删除企微客户标签组名为"' + row.name + '"的数据项?【注意:仅能删除在本后台创建得标签组及标签】', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(function() {
-          return delTagGroup(ids);
-        }).then(() => {
-          this.getList();
-          this.msgSuccess("删除成功");
-        }).catch(() => {});
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return delTagGroup(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(() => {
+      });
     },
     /** 导出按钮操作 */
     handleExport() {
       const queryParams = this.queryParams;
       this.$confirm('是否确认导出所有企微客户标签组数据项?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(() => {
-          this.exportLoading = true;
-          return exportTagGroup(queryParams);
-        }).then(response => {
-          this.download(response.msg);
-          this.exportLoading = false;
-        }).catch(() => {});
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportTagGroup(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {
+      });
     }
   }
 };

+ 178 - 66
src/views/qw/user/index.vue

@@ -66,8 +66,8 @@
       <el-table-column label="企微成员ID" align="center" prop="id" />
       <el-table-column label="企微账号" align="center" prop="qwUserId" />
       <el-table-column label="企微昵称" align="center" prop="qwUserName" />
-      <el-table-column label="所属部门" align="center" prop="departmentName" />
-      <el-table-column label="活码编号" align="center" prop="configId" />
+      <el-table-column label="员工称呼" align="center" prop="welcomeText" />
+      <!--      <el-table-column label="所属部门" align="center" prop="departmentName" />-->
       <el-table-column label="联系我二维码" align="center" prop="contactWay" >
         <template slot-scope="scope">
           <el-image
@@ -80,19 +80,19 @@
       </el-table-column>
       <el-table-column label="绑定的AI客服" align="center" prop="fastGptRoleName" />
       <el-table-column label="授权码" align="center" prop="appKey" />
-      <el-table-column label="企微状态" align="center" prop="loginStatus">
-        <template slot-scope="scope">
-          <el-tag v-if="scope.row.loginStatus == 1 && scope.row.toolStatus==1" type="success">在线</el-tag>
-          <el-tag v-else type="danger">离线</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="插件状态" align="center" prop="toolStatus">
-        <template slot-scope="scope">
-          <el-tag v-if="scope.row.toolStatus == 1" type="success">在线</el-tag>
-          <el-tag v-else type="danger">离线</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="插件版本" align="center" prop="version"/>
+      <!--      <el-table-column label="企微状态" align="center" prop="loginStatus">-->
+      <!--        <template slot-scope="scope">-->
+      <!--          <el-tag v-if="scope.row.loginStatus == 1 && scope.row.toolStatus==1" type="success">在线</el-tag>-->
+      <!--          <el-tag v-else type="danger">离线</el-tag>-->
+      <!--        </template>-->
+      <!--      </el-table-column>-->
+      <!--      <el-table-column label="插件状态" align="center" prop="toolStatus">-->
+      <!--        <template slot-scope="scope">-->
+      <!--          <el-tag v-if="scope.row.toolStatus == 1" type="success">在线</el-tag>-->
+      <!--          <el-tag v-else type="danger">离线</el-tag>-->
+      <!--        </template>-->
+      <!--      </el-table-column>-->
+      <!--      <el-table-column label="插件版本" align="center" prop="version"/>-->
       <el-table-column label="服务器地址" align="center" prop="loginCodeUrl">
         <template slot-scope="scope">
           <el-tooltip class="item" effect="dark" :content="scope.row.loginCodeUrl" placement="top">
@@ -102,33 +102,44 @@
           </el-tooltip>
         </template>
       </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="100px" fixed="right">
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120px" fixed="right">
         <template slot-scope="scope">
           <el-button
             size="mini"
             type="text"
-            icon="el-icon-sunny"
+            icon="el-icon-user-solid"
             plain
-            @click="handleLoginQwCode(scope.row)"
-            v-hasPermi="['qw:user:login']"
+            @click="handleAppellation(scope.row)"
           >
-            登录企微
-          </el-button>
-          <el-button
-            v-if="scope.row.appKey!=null && scope.row.toolStatus === 1 && scope.row.loginStatus === 1"
-            size="mini"
-            type="text"
-            icon="el-icon-moon"
-            plain
-            @click="handleLoginOutQwStatus(scope.row)"
-            v-hasPermi="['qw:user:login']"
-          >
-            退出企微
+            修改员工称呼
           </el-button>
 
+          <!--          <el-button-->
+          <!--            v-if="scope.row.toolStatus==1"-->
+          <!--            size="mini"-->
+          <!--            type="text"-->
+          <!--            icon="el-icon-sunny"-->
+          <!--            plain-->
+          <!--            @click="handleLoginQwCode(scope.row)"-->
+          <!--            v-hasPermi="['qw:user:login']"-->
+          <!--          >-->
+          <!--            登录企微-->
+          <!--          </el-button>-->
+          <!--          <el-button-->
+          <!--            v-if="scope.row.appKey!=null && scope.row.toolStatus === 1 && scope.row.loginStatus === 1"-->
+          <!--            size="mini"-->
+          <!--            type="text"-->
+          <!--            icon="el-icon-moon"-->
+          <!--            plain-->
+          <!--            @click="handleLoginOutQwStatus(scope.row)"-->
+          <!--            v-hasPermi="['qw:user:login']"-->
+          <!--          >-->
+          <!--            退出企微-->
+          <!--          </el-button>-->
+
         </template>
       </el-table-column>
-      <el-table-column label="主机" align="center" class-name="small-padding fixed-width" width="170px" fixed="right">
+      <el-table-column label="主机" align="center" class-name="small-padding fixed-width" width="110px" fixed="right">
         <template slot-scope="scope">
 
           <el-button
@@ -138,11 +149,11 @@
             icon="el-icon-s-check"
             plain
             v-hasPermi="['qw:user:authAppKey']"
-            @click="handleAuthorizeKey(scope.row)"
+            @click="uploadAuthorizeKey2(scope.row)"
           >授权key
           </el-button>
           <el-button
-            v-if="scope.row.loginCodeUrl==null"
+            v-if="scope.row.loginCodeUrl==null && scope.row.appKey !=null"
             size="mini"
             type="text"
             icon="el-icon-sunny"
@@ -156,7 +167,7 @@
             v-if="scope.row.loginCodeUrl!=null"
             size="mini"
             type="text"
-            icon="el-icon-moon"
+            icon="el-icon-video-camera-solid"
             plain
             @click="handleCloudAP(scope.row.loginCodeUrl)"
             v-hasPermi="['qw:user:cloudAP']"
@@ -176,7 +187,7 @@
           </el-button>
         </template>
       </el-table-column>
-      <el-table-column label="AI客服" align="center" class-name="small-padding fixed-width" width="180px" fixed="right">
+      <el-table-column label="AI客服" align="center" class-name="small-padding fixed-width" width="100px" fixed="right">
         <template slot-scope="scope">
           <el-button
             size="mini"
@@ -219,16 +230,16 @@
       <fast-gpt-role ref="fastGptRole" @refreshFastGptList="refreshFastGptList" ></fast-gpt-role>
     </el-dialog>
 
-<!--    <el-dialog :visible.sync="updateIp.open" width="600px" append-to-body>-->
-<!--      <el-form ref="updateIpForm" :model="updateIpForm" :rules="updateIpRule" label-width="100px">-->
-<!--        <el-form-item label="新云主机IP" prop="Ip">-->
-<!--          <el-input v-model="updateIpForm.newIp" placeholder="请输入新IP" />-->
-<!--        </el-form-item>-->
-<!--      </el-form>-->
-<!--      <div slot="footer" class="dialog-footer" >-->
-<!--        <el-button type="primary" @click="submitUpdateIpForm">确 定</el-button>-->
-<!--      </div>-->
-<!--    </el-dialog>-->
+    <!--    <el-dialog :visible.sync="updateIp.open" width="600px" append-to-body>-->
+    <!--      <el-form ref="updateIpForm" :model="updateIpForm" :rules="updateIpRule" label-width="100px">-->
+    <!--        <el-form-item label="新云主机IP" prop="Ip">-->
+    <!--          <el-input v-model="updateIpForm.newIp" placeholder="请输入新IP" />-->
+    <!--        </el-form-item>-->
+    <!--      </el-form>-->
+    <!--      <div slot="footer" class="dialog-footer" >-->
+    <!--        <el-button type="primary" @click="submitUpdateIpForm">确 定</el-button>-->
+    <!--      </div>-->
+    <!--    </el-dialog>-->
 
     <el-dialog title="云主机信息" :visible.sync="cloudAPOpen.open" append-to-body>
       <el-card class="box-card">
@@ -241,15 +252,26 @@
       </el-card>
     </el-dialog>
 
+    <el-dialog :title="callOpen.title" :visible.sync="callOpen.open" width="500px" append-to-body>
+      <el-form ref="callOpenFrom" :model="callOpenFrom" :rules="callOpenRule" label-width="110px">
+        <el-form-item label="员工称呼" prop="welcomeText">
+          <el-input v-model="callOpenFrom.welcomeText" placeholder="请输入员工称呼" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" >
+        <el-button type="primary" @click="submitCallOpenFrom">确 定</el-button>
+      </div>
+    </el-dialog>
+
     <el-dialog title="授权key" :visible.sync="authorizeKeyOpen" width="500px" append-to-body>
-        <el-form ref="authorizeKeyFrom" :model="authorizeKeyFrom" :rules="authorizeKeyRule" label-width="110px">
-            <el-form-item label="授权的key值" prop="appKey">
-              <el-input v-model="authorizeKeyFrom.appKey" placeholder="请输入授权key" />
-            </el-form-item>
-        </el-form>
-            <div slot="footer" class="dialog-footer" >
-              <el-button type="primary" @click="submitAuthorizeKeyForm">确 定</el-button>
-            </div>
+      <el-form ref="authorizeKeyFrom" :model="authorizeKeyFrom" :rules="authorizeKeyRule" label-width="110px">
+        <el-form-item label="授权的key值" prop="appKey">
+          <el-input v-model="authorizeKeyFrom.appKey" placeholder="请输入授权key" type="Number"/>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" >
+        <el-button type="primary" @click="submitAuthorizeKeyForm">确 定</el-button>
+      </div>
     </el-dialog>
     <!--二维码   -->
     <el-dialog
@@ -383,6 +405,15 @@ export default {
         admin:null,
         passWord:null,
       },
+      callOpen:{
+        open:false,
+        title: '修改员工称呼',
+
+      },
+      callOpenFrom:{
+        id:null,
+        welcomeText:null,
+      },
       qrCodeInterval:null,
       loginQwInterval:null,
       loginOutQwInterval:null,
@@ -403,6 +434,9 @@ export default {
       authorizeKeyRule:{
         appKey:[{required:true,message:"授权码不能为空",trigger:"blur"}]
       },
+      callOpenRule:{
+        welcomeText:[{required:true,message:"员工称呼不能为空",trigger:"blur"}]
+      },
       // 表单校验
       rules: {
       },
@@ -482,13 +516,17 @@ export default {
 
     },
 
+    handleAppellation(val){
+      this.callOpen.open=true;
+      this.callOpenFrom.welcomeText=val.welcomeText;
+      this.callOpenFrom.id=val.id;
+    },
     //登录
-
     handleLoginQwCode(val){
 
       // v-if="scope.row.appKey!=null && scope.row.toolStatus === 1 && scope.row.loginStatus === 0"
       if (val.appKey==null || val.appKey===''){
-        return this.$message.warning("没有授权码,无法登录企业微信,请联系管理员开通");
+        return this.$message.warning("没有授权码,无法登录企业微信,请授权");
       }
 
       if (val.toolStatus===0 || val.toolStatus==null ){
@@ -674,14 +712,31 @@ export default {
     },
 
     handleUnbindCloudHost(val){
-      qwUnbindCloudHost(val.appKey).then(res => {
-        this.$message.success('解绑成功');
 
-      }).finally(() => {
+      const appKey=val.appKey;
+
+      this.$confirm(
+        '确定要给企微账号:<span style="color: green;">' +val.qwUserId + '' +
+        '</span><br>企微昵称:<span style="color: red;">【' + val.qwUserName + '】</span>' +
+        '</span><br><span style="color: orange;">解绑【Ps:解绑后此云主机可能会分配给他人】</span></span>',
+        "警告",
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+          dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
+        }
+      ).then(() => {
+        return  qwUnbindCloudHost(appKey);
+      }).then(response => {
+        this.$message.success('解绑成功');
+      }).finally(res=>{
         this.getList();
       })
+
     },
 
+
     handleAuthorizeKey(val){
       this.authorizeKeyFrom.id=val.id;
       this.authorizeKeyFrom.qwUserId=val.qwUserId;
@@ -699,6 +754,22 @@ export default {
         }
       });
     },
+    submitCallOpenFrom(){
+
+      this.$refs["callOpenFrom"].validate(valid => {
+        if (valid) {
+
+          if (this.callOpenFrom.id != null && this.callOpenFrom.welcomeText != null) {
+            updateUser(this.callOpenFrom).then(res=>{
+              this.$message.success('修改成功');
+              this.callOpen.open=false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+
 
     uploadAuthorizeKey(){
       this.$confirm(
@@ -723,17 +794,58 @@ export default {
       })
 
     },
+
+    uploadAuthorizeKey2(val){
+      const id=val.id;
+      this.$confirm(
+        '确定要给企微账号:<span style="color: green;">' +val.qwUserId + '' +
+        '</span><br>企微昵称:<span style="color: red;">【' + val.qwUserName + '】</span>' +
+        '</span><br>授权key</span>?',
+        "警告",
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+          dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
+        }
+      ).then(() => {
+        return handleAuthAppKey({id:id});
+      }).then(response => {
+        this.msgSuccess("授权key完成");
+      }).finally(res=>{
+        this.resetAuthorizeKeyFrom();
+        this.getList();
+      })
+    },
+
     handleBindCloudHost(val){
 
       if (val.appKey == null || val.appKey == '') {
         return this.$message.warning('没有授权码,无法绑定主机,请联系管理员');
       }
 
-      qwBindCloudHost(val.appKey).then(res => {
-        this.$message.success('绑定成功');
-      }).finally(() => {
+      const appKey=val.appKey;
+
+      this.$confirm(
+        '确定要给企微账号:<span style="color: green;">' +val.qwUserId + '' +
+        '</span><br>企微昵称:<span style="color: red;">【' + val.qwUserName + '】</span>' +
+        '</span><br><span style="color: dodgerblue;">绑定云主机?</span></span>',
+        "警告",
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+          dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
+        }
+      ).then(() => {
+        return qwBindCloudHost(appKey);
+      }).then(response => {
+        this.$message.success('绑定成功,请登录云主机进行配置~~');
+      }).finally(res=>{
         this.getList();
       })
+
+
     },
 
     openImageViewer(url) {
@@ -766,10 +878,10 @@ export default {
     //重置授权
     resetAuthorizeKeyFrom(){
       this.authorizeKeyFrom={
-          id:null,
-          appKey:null,
-          qwUserId:null,
-          qwUserName:null
+        id:null,
+        appKey:null,
+        qwUserId:null,
+        qwUserName:null
       };
     },
     //重置登录