Parcourir la source

Merge remote-tracking branch 'origin/master'

吴树波 il y a 1 semaine
Parent
commit
9361d05aef

+ 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
+  })
+}

+ 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>

+ 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={

+ 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;
               }

+ 19 - 4
src/views/qw/sopUserLogsInfo/sopUserLogsInfoDetails.vue

@@ -179,7 +179,7 @@
               />
             </el-select>
           </el-form-item>
-          <el-form-item label="是否只发送注册用户" prop="isRegister" >
+          <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>
@@ -262,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 "  >
@@ -287,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>