Kaynağa Gözat

页面优化

三七 14 saat önce
ebeveyn
işleme
9c053b44c9

+ 6 - 16
src/views/course/courseWatchLog/indexApp.vue

@@ -32,24 +32,14 @@
           </el-option>
         </el-select>
       </el-form-item>
-      <el-form-item label="科普" prop="courseId">
-        <el-select filterable  v-model="queryParams.courseId" placeholder="请选择科普"  clearable size="small" @change="courseChange(queryParams.courseId)">
+      <el-form-item label="课程" prop="courseId">
+        <el-select filterable  v-model="queryParams.courseId" placeholder="请选择课程"  clearable size="small" @change="courseChange(queryParams.courseId)">
           <el-option
             v-for="dict in courseLists"
             :key="dict.dictValue"
-            :label="dict.remark"
+            :label="(dict.remark || dict.dictLabel) + '(' + dict.dictValue + ')'"
             :value="dict.dictValue"
           >
-          <!-- <el-tooltip
-        v-if="dict.remark"
-        :content="dict.remark"
-        placement="right"
-        effect="dark"
-        :disabled="!dict.remark"
-      >
-        <span>{{ dict.dictLabel }}</span>
-      </el-tooltip>
-      <span v-else>{{ dict.dictLabel }}</span> -->
         </el-option>
         </el-select>
       </el-form-item>
@@ -203,7 +193,7 @@
           </div>
         </template>
       </el-table-column>
-      <el-table-column label="科普名称" align="center" prop="courseName" />
+      <el-table-column label="课程名称" align="center" prop="courseName" />
       <el-table-column label="小节名称" align="center" prop="videoName" />
       <el-table-column label="记录类型" align="center" prop="logType">
         <template slot-scope="scope">
@@ -741,7 +731,7 @@ export default {
     handleAdd() {
       this.reset();
       this.open = true;
-      this.title = "添加短链科普学习记录";
+      this.title = "添加短链课程学习记录";
     },
    
     /** 提交按钮 */
@@ -769,7 +759,7 @@ export default {
     handleExport() {
       const that = this
       const queryParams = this.queryParams;
-      this.$confirm('是否确认导出所有短链科普学习记录数据项?', "警告", {
+      this.$confirm('是否确认导出所有短链课程学习记录数据项?', "警告", {
           confirmButtonText: "确定",
           cancelButtonText: "取消",
           type: "warning"

+ 437 - 110
src/views/qw/sopTemp/deptIndex.vue

@@ -220,120 +220,218 @@
       @pagination="getList"
     />
 
-    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
-      <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-form-item>
-        <el-form-item label="状态">
-          <el-radio-group v-model="form.status">
-            <el-radio
-              v-for="dict in statusOptions"
-              :key="dict.dictValue"
-              :label="dict.dictValue"
-            >{{ dict.dictLabel }}
-            </el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="所属项目" prop="project" v-if="form.sendType != 5">
-          <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-dialog
+      :title="title"
+      :visible.sync="open"
+      width="1200px"
+      append-to-body
+      custom-class="sop-temp-dialog"
+      :close-on-click-modal="false"
+    >
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px" class="sop-temp-form">
+        <div class="form-section">
+          <div class="form-section__title">基本信息</div>
+          <el-row :gutter="28">
+            <el-col :span="12">
+              <el-form-item label="名称" prop="name">
+                <el-input v-model="form.name" placeholder="请输入模板标题" maxlength="50" show-word-limit />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="状态">
+                <el-radio-group v-model="form.status">
+                  <el-radio
+                    v-for="dict in statusOptions"
+                    :key="dict.dictValue"
+                    :label="dict.dictValue"
+                  >{{ dict.dictLabel }}</el-radio>
+                </el-radio-group>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12" v-if="form.sendType != 5">
+              <el-form-item label="所属项目" prop="project">
+                <el-select v-model="form.project" placeholder="请选择项目" filterable clearable class="w-full">
+                  <el-option
+                    v-for="dict in projectOptions"
+                    :key="dict.dictValue"
+                    :label="dict.dictLabel"
+                    :value="dict.dictValue"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="归属部门" prop="createByDept">
+                <treeselect
+                  class="dept-treeselect"
+                  :clearable="false"
+                  v-model="form.createByDept"
+                  :options="deptOptions"
+                  :show-count="true"
+                  placeholder="请选择归属部门"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12" v-if="form.sendType == 11 && !form.id">
+              <el-form-item label="课程" prop="courseId">
+                <el-select v-model="form.courseId" placeholder="请选择课程" filterable class="w-full">
+                  <el-option
+                    v-for="dict in courseList"
+                    :key="dict.dictValue"
+                    :label="dict.dictLabel"
+                    :value="parseInt(dict.dictValue)"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="间隔天数" prop="gap">
+                <el-input-number v-model="form.gap" :min="1" controls-position="right" class="num-input" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="排序" prop="sort">
+                <el-input-number v-model="form.sort" :min="0" controls-position="right" class="num-input" />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
 
-        <el-form-item label="归属部门" prop="createByDept">
-          <treeselect
-            style="width: 220px"
-            :clearable="false"
-            v-model="form.createByDept"
-            :options="deptOptions"
-            clearable
-            :show-count="true"
-            placeholder="请选择归属部门"
-          />
-        </el-form-item>
-        <el-form-item label="课程" prop="courseId" v-if="form.sendType == 11 && !form.id">
-          <el-select v-model="form.courseId"placeholder="请选择课程" style=" margin-right: 10px;" size="mini" filterable>
-            <el-option
-              v-for="dict in courseList"
-              :key="dict.dictValue"
-              :label="dict.dictLabel"
-              :value="parseInt(dict.dictValue)"
-            />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="间隔天数" prop="gap">
-          <el-input-number v-model="form.gap" :min="1" label="间隔天数"></el-input-number>
-        </el-form-item>
-        <el-form-item label="排序" prop="sort">
-          <el-input-number v-model="form.sort" :min="0" label="排序"></el-input-number>
-        </el-form-item>
-        <el-form-item label="是否开启官方群发" v-if="form.sendType == 11 && (form.id === null || form.id === undefined)">
-          <el-radio-group v-model="form.openOfficial">
-            <el-radio
-              v-for="dict in openOfficialOptions"
-              :key="dict.dictValue"
-              :label="dict.dictValue"
-            >{{ dict.dictLabel }}
-            </el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="是否开启app看课" v-if="showAppCourseToggle && form.sendType == 11 && (form.id === null || form.id === undefined)">
-          <el-radio-group v-model="form.openAppCourse">
-            <el-radio
-              v-for="dict in openOfficialOptions"
-              :key="dict.dictValue"
-              :label="dict.dictValue"
-            >{{ dict.dictLabel }}
-            </el-radio>
-          </el-radio-group>
-        </el-form-item>
-
-        <el-form-item label="内容" prop="modeContent">
-          <el-input v-model="form.modeContent" placeholder="请输入文字内容"  type="textarea" :rows="3"/>
-        </el-form-item>
-        <el-form-item label="发课时间" prop="time" v-if="form.sendType == 11 && !form.id">
-          <el-time-picker
-            class="custom-input"
-            v-model="form.time"
-            value-format="HH:mm"
-            format="HH:mm"
-            placeholder="时间"
-            style="width: 200px;height: 20px;margin-left: 10px;margin-top: 10px">
-          </el-time-picker>
-        </el-form-item>
-        <el-form-item label="每天催课次数" prop="num" v-if="form.sendType == 11 && !form.id">
-          <el-input-number v-model="form.num" :min="0" label="每天催课次数" @change="sendNumChange"></el-input-number>
-        </el-form-item>
-        <el-form-item label="催课时间" v-if="form.sendType == 11 && !form.id && form.num > 0">
-          <div v-for="(item, index) in form.timeList" :key="index" style="margin-bottom: 10px;">
-            <el-time-picker
-              class="custom-input"
-              v-model="item.value"
-              value-format="HH:mm"
-              format="HH:mm"
-              :picker-options="{ selectableRange: startTimeRange }"
-              placeholder="时间"
-              style="width: 150px; height: 20px; margin-left: 10px; margin-top: 10px">
-            </el-time-picker>
+        <div
+          class="form-section"
+          v-if="form.sendType == 11 && (form.id === null || form.id === undefined)"
+        >
+          <div class="form-section__title">
+            发送能力
+            <span class="form-section__hint">默认小程序看课;开启后会叠加对应能力</span>
+          </div>
+          <div class="capability-grid">
+            <div class="capability-item">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">官方群发</span>
+                <span class="capability-item__desc">首条走企微官方群发</span>
+              </div>
+              <el-switch v-model="form.openOfficial" active-value="1" inactive-value="0" />
+            </div>
+            <div class="capability-item" v-if="showAppCourseToggle">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">APP 看课</span>
+                <span class="capability-item__desc">与官方群发二选一</span>
+              </div>
+              <el-switch v-model="form.openAppCourse" active-value="1" inactive-value="0" />
+            </div>
+            <div class="capability-item">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">飞书看课</span>
+                <span class="capability-item__desc">叠加飞书链接内容</span>
+              </div>
+              <el-switch v-model="form.openFeishu" active-value="1" inactive-value="0" />
+            </div>
+            <div class="capability-item" v-if="projectFrom === 'sxjz'">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">SIP 外呼</span>
+                <span class="capability-item__desc">叠加外呼催课任务</span>
+              </div>
+              <el-switch v-model="form.openSipCall" active-value="1" inactive-value="0" />
+            </div>
+            <div class="capability-item">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">@所有人</span>
+                <span class="capability-item__desc">群消息是否 @所有人</span>
+              </div>
+              <el-switch v-model="form.openIsAtAll" active-value="1" inactive-value="0" />
+            </div>
+          </div>
+          <el-row :gutter="28" v-if="projectFrom === 'sxjz' && form.openSipCall == '1'" class="sip-task-row">
+            <el-col :span="12">
+              <el-form-item label="外呼任务" required>
+                <el-select
+                  v-model="form.aiCustomerRoleId"
+                  placeholder="请选择 SIP 外呼任务"
+                  filterable
+                  clearable
+                  class="w-full"
+                  @visible-change="onSipSelectVisible"
+                >
+                  <el-option
+                    v-for="task in aiSipCallTaskList"
+                    :key="task.batchId"
+                    :label="task.batchName"
+                    :value="task.batchId"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
 
+        <div class="form-section form-section--content">
+          <div class="form-section__title">内容与时间</div>
+          <el-form-item label="文案内容" prop="modeContent">
             <el-input
-              v-model="item.desc"
-              placeholder="催课内容"
-              type="textarea" :rows="2"
-              style="width: 500px; margin-left: 10px; margin-top: 10px;">
-            </el-input>
+              v-model="form.modeContent"
+              type="textarea"
+              :rows="4"
+              placeholder="发课时附带的文字内容"
+              maxlength="500"
+              show-word-limit
+            />
+          </el-form-item>
+          <el-row :gutter="28" v-if="form.sendType == 11 && !form.id">
+            <el-col :span="12">
+              <el-form-item label="发课时间" prop="time">
+                <el-time-picker
+                  v-model="form.time"
+                  value-format="HH:mm"
+                  format="HH:mm"
+                  placeholder="选择时间"
+                  class="w-full"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="催课次数" prop="num">
+                <el-input-number
+                  v-model="form.num"
+                  :min="0"
+                  class="num-input"
+                  @change="sendNumChange"
+                />
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <div v-if="form.sendType == 11 && !form.id && form.num > 0" class="remind-list">
+            <div class="remind-list__label">催课安排</div>
+            <div class="remind-grid">
+              <div
+                v-for="(item, index) in form.timeList"
+                :key="index"
+                class="remind-row"
+              >
+                <span class="remind-row__index">第 {{ index + 1 }} 次</span>
+                <el-time-picker
+                  v-model="item.value"
+                  value-format="HH:mm"
+                  format="HH:mm"
+                  :picker-options="{ selectableRange: startTimeRange }"
+                  placeholder="催课时间"
+                  class="remind-row__time"
+                />
+                <el-input
+                  v-model="item.desc"
+                  type="textarea"
+                  :rows="2"
+                  placeholder="催课文案"
+                  class="remind-row__desc"
+                />
+              </div>
+            </div>
           </div>
-        </el-form-item>
+        </div>
       </el-form>
-      <div slot="footer" class="dialog-footer" style="display: flex;justify-content: flex-end;">
-        <el-button type="primary" @click="submitForm">确 定</el-button>
-        <el-button @click="cancel">取 消</el-button>
+      <div slot="footer" class="dialog-footer">
+        <el-button size="medium" @click="cancel">取 消</el-button>
+        <el-button type="primary" size="medium" @click="submitForm">确 定</el-button>
       </div>
     </el-dialog>
 
@@ -427,7 +525,7 @@ import {
   redList,
   shareSopTemp,
   updateRedPackage,
-  updateTemp, listSopTempDeptList
+  updateTemp, listSopTempDeptList, getConfigByKey
 } from "../../../api/qw/sopTemp";
 import { getCompanyList, listCompany } from '@/api/company/company'
 import {courseList, getRoles} from "@/api/qw/sop";
@@ -435,12 +533,14 @@ import {treeselect} from "../../../api/company/companyDept";
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
 import {getCompanyUserListLikeNameByDept} from "../../../api/company/companyUser";
+import { listAiSipCallTask } from "@/api/aiSipCall/aiSipCallTask";
 export default {
   name: "SopTemp",
   components: {Treeselect},
   data() {
     return {
       projectFrom:process.env.VUE_APP_PROJECT_FROM,
+      aiSipCallTaskList: [],
       // 遮罩层
       loading: true,
       companysloading: false,
@@ -608,9 +708,32 @@ export default {
       if (this.showAppCourseToggle && newVal === '1') {
         this.$set(this.form, 'openOfficial', '0');
       }
+    },
+    'form.openSipCall'(newVal) {
+      if (newVal !== '1') {
+        this.$set(this.form, 'aiCustomerRoleId', null);
+      } else {
+        this.loadSipTemplates();
+      }
     }
   },
   methods: {
+    onSipSelectVisible(visible) {
+      if (visible) {
+        this.loadSipTemplates();
+      }
+    },
+    loadSipTemplates() {
+      if (this.aiSipCallTaskList.length > 0) {
+        return;
+      }
+      listAiSipCallTask({ taskType: 1, status: 0, pageNum: 1, pageSize: 100 }).then(response => {
+        this.aiSipCallTaskList = response.rows || response.data || [];
+      }).catch(() => {
+        this.$message.error('加载SIP外呼任务失败');
+      });
+    },
+
 
     handleCompanyUserChange(value) {
       // 当清空选择时,将空字符串转为 null
@@ -729,6 +852,10 @@ export default {
         sort: 0,
         openOfficial: "1",
         openAppCourse: "0", //默认 app 看课为关
+        openSipCall: "0",
+        openFeishu: "0",
+        aiCustomerRoleId: null,
+        openIsAtAll: "0",
         time: "",
         num: 1,
         timeList: [{value: "",desc:""}],
@@ -891,6 +1018,16 @@ export default {
               return;
             }
           }
+          if (this.form.sendType === 11 && !this.form.id) {
+            if (this.projectFrom === 'sxjz' && this.form.openSipCall === '1' && !this.form.aiCustomerRoleId) {
+              this.$message.error("请选择SIP外呼任务!");
+              return;
+            }
+            if (this.projectFrom !== 'sxjz') {
+              this.form.openSipCall = '0';
+              this.form.aiCustomerRoleId = null;
+            }
+          }
           if (this.command != 2 && this.form.id == null && this.form.sendType == 11){
 
             const hasEmptyFields = this.form.timeList.some(item => {
@@ -1021,3 +1158,193 @@ export default {
   }
 };
 </script>
+<style scoped>
+.sop-temp-form {
+  max-height: 72vh;
+  overflow-y: auto;
+  padding-right: 6px;
+}
+
+.form-section {
+  margin-bottom: 24px;
+  padding: 28px 32px 16px;
+  background: #fafbfc;
+  border: 1px solid #ebeef5;
+  border-radius: 12px;
+}
+
+.form-section:last-child {
+  margin-bottom: 4px;
+}
+
+.form-section__title {
+  display: flex;
+  align-items: baseline;
+  gap: 14px;
+  margin: 0 0 24px;
+  padding-left: 12px;
+  border-left: 4px solid #409eff;
+  font-size: 16px;
+  font-weight: 600;
+  color: #1f2d3d;
+  line-height: 1.3;
+  letter-spacing: 0.02em;
+}
+
+.form-section__hint {
+  font-size: 13px;
+  font-weight: 400;
+  color: #909399;
+}
+
+.w-full {
+  width: 100%;
+}
+
+.num-input {
+  width: 100%;
+}
+
+.num-input >>> .el-input__inner {
+  text-align: left;
+}
+
+.dept-treeselect {
+  width: 100%;
+  line-height: 40px;
+}
+
+.capability-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+  gap: 16px;
+  margin-bottom: 4px;
+}
+
+.capability-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px;
+  min-height: 88px;
+  padding: 20px 18px;
+  background: #fff;
+  border: 1px solid #e4e7ed;
+  border-radius: 10px;
+}
+
+.capability-item__meta {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  min-width: 0;
+}
+
+.capability-item__name {
+  font-size: 15px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.capability-item__desc {
+  font-size: 12px;
+  color: #909399;
+  line-height: 1.45;
+}
+
+.sip-task-row {
+  margin-top: 18px;
+}
+
+.remind-list {
+  margin: 4px 0 8px 100px;
+}
+
+.remind-list__label {
+  margin-bottom: 12px;
+  font-size: 14px;
+  font-weight: 500;
+  color: #606266;
+}
+
+.remind-grid {
+  display: grid;
+  grid-template-columns: 1fr;
+  gap: 14px;
+}
+
+.remind-row {
+  display: flex;
+  align-items: flex-start;
+  gap: 12px;
+  padding: 14px 16px;
+  background: #fff;
+  border: 1px solid #e4e7ed;
+  border-radius: 10px;
+}
+
+.remind-row__index {
+  flex: 0 0 58px;
+  margin-top: 8px;
+  font-size: 13px;
+  font-weight: 500;
+  color: #606266;
+}
+
+.remind-row__time {
+  flex: 0 0 130px;
+}
+
+.remind-row__desc {
+  flex: 1;
+  min-width: 0;
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 12px;
+}
+</style>
+
+<style>
+.sop-temp-dialog {
+  border-radius: 12px;
+  overflow: hidden;
+}
+
+.sop-temp-dialog .el-dialog__header {
+  padding: 24px 32px 18px;
+  border-bottom: 1px solid #ebeef5;
+}
+
+.sop-temp-dialog .el-dialog__title {
+  font-size: 18px;
+  font-weight: 600;
+  color: #1f2d3d;
+  letter-spacing: 0.02em;
+}
+
+.sop-temp-dialog .el-dialog__body {
+  padding: 28px 32px 18px;
+}
+
+.sop-temp-dialog .el-dialog__footer {
+  padding: 18px 32px 24px;
+  border-top: 1px solid #ebeef5;
+}
+
+.sop-temp-dialog .el-form-item {
+  margin-bottom: 22px;
+}
+
+.sop-temp-dialog .el-input__inner,
+.sop-temp-dialog .el-textarea__inner {
+  border-radius: 6px;
+}
+
+.sop-temp-dialog .el-button--medium {
+  min-width: 104px;
+  padding: 11px 24px;
+}
+</style>

+ 436 - 121
src/views/qw/sopTemp/index.vue

@@ -229,131 +229,218 @@
       @pagination="getList"
     />
 
-    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
-      <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-form-item>
-        <el-form-item label="状态">
-          <el-radio-group v-model="form.status">
-            <el-radio
-              v-for="dict in statusOptions"
-              :key="dict.dictValue"
-              :label="dict.dictValue"
-            >{{ dict.dictLabel }}
-            </el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="所属项目" prop="project" v-if="form.sendType != 5">
-          <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-dialog
+      :title="title"
+      :visible.sync="open"
+      width="1200px"
+      append-to-body
+      custom-class="sop-temp-dialog"
+      :close-on-click-modal="false"
+    >
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px" class="sop-temp-form">
+        <div class="form-section">
+          <div class="form-section__title">基本信息</div>
+          <el-row :gutter="28">
+            <el-col :span="12">
+              <el-form-item label="名称" prop="name">
+                <el-input v-model="form.name" placeholder="请输入模板标题" maxlength="50" show-word-limit />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="状态">
+                <el-radio-group v-model="form.status">
+                  <el-radio
+                    v-for="dict in statusOptions"
+                    :key="dict.dictValue"
+                    :label="dict.dictValue"
+                  >{{ dict.dictLabel }}</el-radio>
+                </el-radio-group>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12" v-if="form.sendType != 5">
+              <el-form-item label="所属项目" prop="project">
+                <el-select v-model="form.project" placeholder="请选择项目" filterable clearable class="w-full">
+                  <el-option
+                    v-for="dict in projectOptions"
+                    :key="dict.dictValue"
+                    :label="dict.dictLabel"
+                    :value="dict.dictValue"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="归属部门" prop="createByDept">
+                <treeselect
+                  class="dept-treeselect"
+                  :clearable="false"
+                  v-model="form.createByDept"
+                  :options="deptOptions"
+                  :show-count="true"
+                  placeholder="请选择归属部门"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12" v-if="form.sendType == 11 && !form.id">
+              <el-form-item label="课程" prop="courseId">
+                <el-select v-model="form.courseId" placeholder="请选择课程" filterable class="w-full">
+                  <el-option
+                    v-for="dict in courseList"
+                    :key="dict.dictValue"
+                    :label="dict.dictLabel"
+                    :value="parseInt(dict.dictValue)"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="间隔天数" prop="gap">
+                <el-input-number v-model="form.gap" :min="1" controls-position="right" class="num-input" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="排序" prop="sort">
+                <el-input-number v-model="form.sort" :min="0" controls-position="right" class="num-input" />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
 
-        <el-form-item label="归属部门" prop="createByDept">
-          <treeselect
-            style="width: 220px"
-            :clearable="false"
-            v-model="form.createByDept"
-            :options="deptOptions"
-            clearable
-            :show-count="true"
-            placeholder="请选择归属部门"
-          />
-        </el-form-item>
-        <el-form-item label="课程" prop="courseId" v-if="form.sendType == 11 && !form.id">
-          <el-select v-model="form.courseId"placeholder="请选择课程" style=" margin-right: 10px;" size="mini" filterable>
-            <el-option
-              v-for="dict in courseList"
-              :key="dict.dictValue"
-              :label="dict.dictLabel"
-              :value="parseInt(dict.dictValue)"
-            />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="间隔天数" prop="gap">
-          <el-input-number v-model="form.gap" :min="1" label="间隔天数"></el-input-number>
-        </el-form-item>
-        <el-form-item label="排序" prop="sort">
-          <el-input-number v-model="form.sort" :min="0" label="排序"></el-input-number>
-        </el-form-item>
-        <el-form-item label="是否开启官方群发" v-if="form.sendType == 11 && (form.id === null || form.id === undefined)">
-          <el-radio-group v-model="form.openOfficial">
-            <el-radio
-              v-for="dict in openOfficialOptions"
-              :key="dict.dictValue"
-              :label="dict.dictValue"
-            >{{ dict.dictLabel }}
-            </el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="是否开启app看课" v-if="showAppCourseToggle && form.sendType == 11 && (form.id === null || form.id === undefined)">
-          <el-radio-group v-model="form.openAppCourse">
-            <el-radio
-              v-for="dict in openOfficialOptions"
-              :key="dict.dictValue"
-              :label="dict.dictValue"
-            >{{ dict.dictLabel }}
-            </el-radio>
-          </el-radio-group>
-        </el-form-item>
-
-        <el-form-item label="是否开启@所有人" v-if="form.sendType == 11 && (form.id === null || form.id === undefined)">
-          <el-radio-group v-model="form.openIsAtAll">
-            <el-radio
-              v-for="dict in openIsAtAllOptions"
-              :key="dict.dictValue"
-              :label="dict.dictValue"
-            >{{ dict.dictLabel }}
-            </el-radio>
-          </el-radio-group>
-        </el-form-item>
-
-        <el-form-item label="内容" prop="modeContent">
-          <el-input v-model="form.modeContent" placeholder="请输入文字内容"  type="textarea" :rows="3"/>
-        </el-form-item>
-        <el-form-item label="发课时间" prop="time" v-if="form.sendType == 11 && !form.id">
-          <el-time-picker
-            class="custom-input"
-            v-model="form.time"
-            value-format="HH:mm"
-            format="HH:mm"
-            placeholder="时间"
-            style="width: 200px;height: 20px;margin-left: 10px;margin-top: 10px">
-          </el-time-picker>
-        </el-form-item>
-        <el-form-item label="每天催课次数" prop="num" v-if="form.sendType == 11 && !form.id">
-          <el-input-number v-model="form.num" :min="0" label="每天催课次数" @change="sendNumChange"></el-input-number>
-        </el-form-item>
-        <el-form-item label="催课时间" v-if="form.sendType == 11 && !form.id && form.num > 0">
-          <div v-for="(item, index) in form.timeList" :key="index" style="margin-bottom: 10px;">
-            <el-time-picker
-              class="custom-input"
-              v-model="item.value"
-              value-format="HH:mm"
-              format="HH:mm"
-              :picker-options="{ selectableRange: startTimeRange }"
-              placeholder="时间"
-              style="width: 150px; height: 20px; margin-left: 10px; margin-top: 10px">
-            </el-time-picker>
+        <div
+          class="form-section"
+          v-if="form.sendType == 11 && (form.id === null || form.id === undefined)"
+        >
+          <div class="form-section__title">
+            发送能力
+            <span class="form-section__hint">默认小程序看课;开启后会叠加对应能力</span>
+          </div>
+          <div class="capability-grid">
+            <div class="capability-item">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">官方群发</span>
+                <span class="capability-item__desc">首条走企微官方群发</span>
+              </div>
+              <el-switch v-model="form.openOfficial" active-value="1" inactive-value="0" />
+            </div>
+            <div class="capability-item" v-if="showAppCourseToggle">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">APP 看课</span>
+                <span class="capability-item__desc">与官方群发二选一</span>
+              </div>
+              <el-switch v-model="form.openAppCourse" active-value="1" inactive-value="0" />
+            </div>
+            <div class="capability-item">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">飞书看课</span>
+                <span class="capability-item__desc">叠加飞书链接内容</span>
+              </div>
+              <el-switch v-model="form.openFeishu" active-value="1" inactive-value="0" />
+            </div>
+            <div class="capability-item" v-if="projectFrom === 'sxjz'">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">SIP 外呼</span>
+                <span class="capability-item__desc">叠加外呼催课任务</span>
+              </div>
+              <el-switch v-model="form.openSipCall" active-value="1" inactive-value="0" />
+            </div>
+            <div class="capability-item">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">@所有人</span>
+                <span class="capability-item__desc">群消息是否 @所有人</span>
+              </div>
+              <el-switch v-model="form.openIsAtAll" active-value="1" inactive-value="0" />
+            </div>
+          </div>
+          <el-row :gutter="28" v-if="projectFrom === 'sxjz' && form.openSipCall == '1'" class="sip-task-row">
+            <el-col :span="12">
+              <el-form-item label="外呼任务" required>
+                <el-select
+                  v-model="form.aiCustomerRoleId"
+                  placeholder="请选择 SIP 外呼任务"
+                  filterable
+                  clearable
+                  class="w-full"
+                  @visible-change="onSipSelectVisible"
+                >
+                  <el-option
+                    v-for="task in aiSipCallTaskList"
+                    :key="task.batchId"
+                    :label="task.batchName"
+                    :value="task.batchId"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
 
+        <div class="form-section form-section--content">
+          <div class="form-section__title">内容与时间</div>
+          <el-form-item label="文案内容" prop="modeContent">
             <el-input
-              v-model="item.desc"
-              placeholder="催课内容"
-              type="textarea" :rows="2"
-              style="width: 500px; margin-left: 10px; margin-top: 10px;">
-            </el-input>
+              v-model="form.modeContent"
+              type="textarea"
+              :rows="4"
+              placeholder="发课时附带的文字内容"
+              maxlength="500"
+              show-word-limit
+            />
+          </el-form-item>
+          <el-row :gutter="28" v-if="form.sendType == 11 && !form.id">
+            <el-col :span="12">
+              <el-form-item label="发课时间" prop="time">
+                <el-time-picker
+                  v-model="form.time"
+                  value-format="HH:mm"
+                  format="HH:mm"
+                  placeholder="选择时间"
+                  class="w-full"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="催课次数" prop="num">
+                <el-input-number
+                  v-model="form.num"
+                  :min="0"
+                  class="num-input"
+                  @change="sendNumChange"
+                />
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <div v-if="form.sendType == 11 && !form.id && form.num > 0" class="remind-list">
+            <div class="remind-list__label">催课安排</div>
+            <div class="remind-grid">
+              <div
+                v-for="(item, index) in form.timeList"
+                :key="index"
+                class="remind-row"
+              >
+                <span class="remind-row__index">第 {{ index + 1 }} 次</span>
+                <el-time-picker
+                  v-model="item.value"
+                  value-format="HH:mm"
+                  format="HH:mm"
+                  :picker-options="{ selectableRange: startTimeRange }"
+                  placeholder="催课时间"
+                  class="remind-row__time"
+                />
+                <el-input
+                  v-model="item.desc"
+                  type="textarea"
+                  :rows="2"
+                  placeholder="催课文案"
+                  class="remind-row__desc"
+                />
+              </div>
+            </div>
           </div>
-        </el-form-item>
+        </div>
       </el-form>
-      <div slot="footer" class="dialog-footer" style="display: flex;justify-content: flex-end;">
-        <el-button type="primary" @click="submitForm">确 定</el-button>
-        <el-button @click="cancel">取 消</el-button>
+      <div slot="footer" class="dialog-footer">
+        <el-button size="medium" @click="cancel">取 消</el-button>
+        <el-button type="primary" size="medium" @click="submitForm">确 定</el-button>
       </div>
     </el-dialog>
 
@@ -470,12 +557,14 @@ import {treeselect} from "../../../api/company/companyDept";
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
 import {getCompanyUserListLikeName} from "../../../api/company/companyUser";
+import { listAiSipCallTask } from "@/api/aiSipCall/aiSipCallTask";
 export default {
   name: "SopTemp",
   components: {Treeselect},
   data() {
     return {
        projectFrom:process.env.VUE_APP_PROJECT_FROM,
+       aiSipCallTaskList: [],
 
       officialForm:{
         tempId:null,
@@ -654,9 +743,31 @@ export default {
       if (this.showAppCourseToggle && newVal === '1') {
         this.$set(this.form, 'openOfficial', '0');
       }
+    },
+    'form.openSipCall'(newVal) {
+      if (newVal !== '1') {
+        this.$set(this.form, 'aiCustomerRoleId', null);
+      } else {
+        this.loadSipTemplates();
+      }
     }
   },
   methods: {
+    onSipSelectVisible(visible) {
+      if (visible) {
+        this.loadSipTemplates();
+      }
+    },
+    loadSipTemplates() {
+      if (this.aiSipCallTaskList.length > 0) {
+        return;
+      }
+      listAiSipCallTask({ taskType: 1, status: 0, pageNum: 1, pageSize: 100 }).then(response => {
+        this.aiSipCallTaskList = response.rows || response.data || [];
+      }).catch(() => {
+        this.$message.error('加载SIP外呼任务失败');
+      });
+    },
 
     handleCompanyUserChange(value) {
       // 当清空选择时,将空字符串转为 null
@@ -774,7 +885,10 @@ export default {
         sort: 0,
         openOfficial: "1",
         openAppCourse: "0", //默认 app 看课为关
-        openIsAtAll: "1",
+        openSipCall: "0",
+        openFeishu: "0",
+        aiCustomerRoleId: null,
+        openIsAtAll: "0",
         time: "",
         num: 1,
         timeList: [{value: "",desc:""}],
@@ -953,6 +1067,16 @@ export default {
               return;
             }
           }
+          if (this.form.sendType === 11 && !this.form.id) {
+            if (this.projectFrom === 'sxjz' && this.form.openSipCall === '1' && !this.form.aiCustomerRoleId) {
+              this.$message.error("请选择SIP外呼任务!");
+              return;
+            }
+            if (this.projectFrom !== 'sxjz') {
+              this.form.openSipCall = '0';
+              this.form.aiCustomerRoleId = null;
+            }
+          }
           if (this.command != 2 && this.form.id == null && this.form.sendType == 11){
 
             const hasEmptyFields = this.form.timeList.some(item => {
@@ -1105,3 +1229,194 @@ export default {
   }
 };
 </script>
+
+<style scoped>
+.sop-temp-form {
+  max-height: 72vh;
+  overflow-y: auto;
+  padding-right: 6px;
+}
+
+.form-section {
+  margin-bottom: 24px;
+  padding: 28px 32px 16px;
+  background: #fafbfc;
+  border: 1px solid #ebeef5;
+  border-radius: 12px;
+}
+
+.form-section:last-child {
+  margin-bottom: 4px;
+}
+
+.form-section__title {
+  display: flex;
+  align-items: baseline;
+  gap: 14px;
+  margin: 0 0 24px;
+  padding-left: 12px;
+  border-left: 4px solid #409eff;
+  font-size: 16px;
+  font-weight: 600;
+  color: #1f2d3d;
+  line-height: 1.3;
+  letter-spacing: 0.02em;
+}
+
+.form-section__hint {
+  font-size: 13px;
+  font-weight: 400;
+  color: #909399;
+}
+
+.w-full {
+  width: 100%;
+}
+
+.num-input {
+  width: 100%;
+}
+
+.num-input >>> .el-input__inner {
+  text-align: left;
+}
+
+.dept-treeselect {
+  width: 100%;
+  line-height: 40px;
+}
+
+.capability-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+  gap: 16px;
+  margin-bottom: 4px;
+}
+
+.capability-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px;
+  min-height: 88px;
+  padding: 20px 18px;
+  background: #fff;
+  border: 1px solid #e4e7ed;
+  border-radius: 10px;
+}
+
+.capability-item__meta {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  min-width: 0;
+}
+
+.capability-item__name {
+  font-size: 15px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.capability-item__desc {
+  font-size: 12px;
+  color: #909399;
+  line-height: 1.45;
+}
+
+.sip-task-row {
+  margin-top: 18px;
+}
+
+.remind-list {
+  margin: 4px 0 8px 100px;
+}
+
+.remind-list__label {
+  margin-bottom: 12px;
+  font-size: 14px;
+  font-weight: 500;
+  color: #606266;
+}
+
+.remind-grid {
+  display: grid;
+  grid-template-columns: 1fr;
+  gap: 14px;
+}
+
+.remind-row {
+  display: flex;
+  align-items: flex-start;
+  gap: 12px;
+  padding: 14px 16px;
+  background: #fff;
+  border: 1px solid #e4e7ed;
+  border-radius: 10px;
+}
+
+.remind-row__index {
+  flex: 0 0 58px;
+  margin-top: 8px;
+  font-size: 13px;
+  font-weight: 500;
+  color: #606266;
+}
+
+.remind-row__time {
+  flex: 0 0 130px;
+}
+
+.remind-row__desc {
+  flex: 1;
+  min-width: 0;
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 12px;
+}
+</style>
+
+<style>
+.sop-temp-dialog {
+  border-radius: 12px;
+  overflow: hidden;
+}
+
+.sop-temp-dialog .el-dialog__header {
+  padding: 24px 32px 18px;
+  border-bottom: 1px solid #ebeef5;
+}
+
+.sop-temp-dialog .el-dialog__title {
+  font-size: 18px;
+  font-weight: 600;
+  color: #1f2d3d;
+  letter-spacing: 0.02em;
+}
+
+.sop-temp-dialog .el-dialog__body {
+  padding: 28px 32px 18px;
+}
+
+.sop-temp-dialog .el-dialog__footer {
+  padding: 18px 32px 24px;
+  border-top: 1px solid #ebeef5;
+}
+
+.sop-temp-dialog .el-form-item {
+  margin-bottom: 22px;
+}
+
+.sop-temp-dialog .el-input__inner,
+.sop-temp-dialog .el-textarea__inner {
+  border-radius: 6px;
+}
+
+.sop-temp-dialog .el-button--medium {
+  min-width: 104px;
+  padding: 11px 24px;
+}
+</style>

+ 436 - 110
src/views/qw/sopTemp/myIndex.vue

@@ -203,120 +203,218 @@
       @pagination="getMyList"
     />
 
-    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
-      <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-form-item>
-        <el-form-item label="状态">
-          <el-radio-group v-model="form.status">
-            <el-radio
-              v-for="dict in statusOptions"
-              :key="dict.dictValue"
-              :label="dict.dictValue"
-            >{{ dict.dictLabel }}
-            </el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="所属项目" prop="project" v-if="form.sendType != 5">
-          <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-dialog
+      :title="title"
+      :visible.sync="open"
+      width="1200px"
+      append-to-body
+      custom-class="sop-temp-dialog"
+      :close-on-click-modal="false"
+    >
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px" class="sop-temp-form">
+        <div class="form-section">
+          <div class="form-section__title">基本信息</div>
+          <el-row :gutter="28">
+            <el-col :span="12">
+              <el-form-item label="名称" prop="name">
+                <el-input v-model="form.name" placeholder="请输入模板标题" maxlength="50" show-word-limit />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="状态">
+                <el-radio-group v-model="form.status">
+                  <el-radio
+                    v-for="dict in statusOptions"
+                    :key="dict.dictValue"
+                    :label="dict.dictValue"
+                  >{{ dict.dictLabel }}</el-radio>
+                </el-radio-group>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12" v-if="form.sendType != 5">
+              <el-form-item label="所属项目" prop="project">
+                <el-select v-model="form.project" placeholder="请选择项目" filterable clearable class="w-full">
+                  <el-option
+                    v-for="dict in projectOptions"
+                    :key="dict.dictValue"
+                    :label="dict.dictLabel"
+                    :value="dict.dictValue"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="归属部门" prop="createByDept">
+                <treeselect
+                  class="dept-treeselect"
+                  :clearable="false"
+                  v-model="form.createByDept"
+                  :options="deptOptions"
+                  :show-count="true"
+                  placeholder="请选择归属部门"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12" v-if="form.sendType == 11 && !form.id">
+              <el-form-item label="课程" prop="courseId">
+                <el-select v-model="form.courseId" placeholder="请选择课程" filterable class="w-full">
+                  <el-option
+                    v-for="dict in courseList"
+                    :key="dict.dictValue"
+                    :label="dict.dictLabel"
+                    :value="parseInt(dict.dictValue)"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="间隔天数" prop="gap">
+                <el-input-number v-model="form.gap" :min="1" controls-position="right" class="num-input" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="排序" prop="sort">
+                <el-input-number v-model="form.sort" :min="0" controls-position="right" class="num-input" />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
 
-        <el-form-item label="归属部门" prop="createByDept">
-          <treeselect
-            style="width: 220px"
-            :clearable="false"
-            v-model="form.createByDept"
-            :options="deptOptions"
-            clearable
-            :show-count="true"
-            placeholder="请选择归属部门"
-          />
-        </el-form-item>
-        <el-form-item label="课程" prop="courseId" v-if="form.sendType == 11 && !form.id">
-          <el-select v-model="form.courseId"placeholder="请选择课程" style=" margin-right: 10px;" size="mini" filterable>
-            <el-option
-              v-for="dict in courseList"
-              :key="dict.dictValue"
-              :label="dict.dictLabel"
-              :value="parseInt(dict.dictValue)"
-            />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="间隔天数" prop="gap">
-          <el-input-number v-model="form.gap" :min="1" label="间隔天数"></el-input-number>
-        </el-form-item>
-        <el-form-item label="排序" prop="sort">
-          <el-input-number v-model="form.sort" :min="0" label="排序"></el-input-number>
-        </el-form-item>
-        <el-form-item label="是否开启官方群发" v-if="form.sendType == 11 && (form.id === null || form.id === undefined)">
-          <el-radio-group v-model="form.openOfficial">
-            <el-radio
-              v-for="dict in openOfficialOptions"
-              :key="dict.dictValue"
-              :label="dict.dictValue"
-            >{{ dict.dictLabel }}
-            </el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="是否开启app看课" v-if="showAppCourseToggle && form.sendType == 11 && (form.id === null || form.id === undefined)">
-          <el-radio-group v-model="form.openAppCourse">
-            <el-radio
-              v-for="dict in openOfficialOptions"
-              :key="dict.dictValue"
-              :label="dict.dictValue"
-            >{{ dict.dictLabel }}
-            </el-radio>
-          </el-radio-group>
-        </el-form-item>
-
-        <el-form-item label="内容" prop="modeContent">
-          <el-input v-model="form.modeContent" placeholder="请输入文字内容"  type="textarea" :rows="3"/>
-        </el-form-item>
-        <el-form-item label="发课时间" prop="time" v-if="form.sendType == 11 && !form.id">
-          <el-time-picker
-            class="custom-input"
-            v-model="form.time"
-            value-format="HH:mm"
-            format="HH:mm"
-            placeholder="时间"
-            style="width: 200px;height: 20px;margin-left: 10px;margin-top: 10px">
-          </el-time-picker>
-        </el-form-item>
-        <el-form-item label="每天催课次数" prop="num" v-if="form.sendType == 11 && !form.id">
-          <el-input-number v-model="form.num" :min="0" label="每天催课次数" @change="sendNumChange"></el-input-number>
-        </el-form-item>
-        <el-form-item label="催课时间" v-if="form.sendType == 11 && !form.id && form.num > 0">
-          <div v-for="(item, index) in form.timeList" :key="index" style="margin-bottom: 10px;">
-            <el-time-picker
-              class="custom-input"
-              v-model="item.value"
-              value-format="HH:mm"
-              format="HH:mm"
-              :picker-options="{ selectableRange: startTimeRange }"
-              placeholder="时间"
-              style="width: 150px; height: 20px; margin-left: 10px; margin-top: 10px">
-            </el-time-picker>
+        <div
+          class="form-section"
+          v-if="form.sendType == 11 && (form.id === null || form.id === undefined)"
+        >
+          <div class="form-section__title">
+            发送能力
+            <span class="form-section__hint">默认小程序看课;开启后会叠加对应能力</span>
+          </div>
+          <div class="capability-grid">
+            <div class="capability-item">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">官方群发</span>
+                <span class="capability-item__desc">首条走企微官方群发</span>
+              </div>
+              <el-switch v-model="form.openOfficial" active-value="1" inactive-value="0" />
+            </div>
+            <div class="capability-item" v-if="showAppCourseToggle">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">APP 看课</span>
+                <span class="capability-item__desc">与官方群发二选一</span>
+              </div>
+              <el-switch v-model="form.openAppCourse" active-value="1" inactive-value="0" />
+            </div>
+            <div class="capability-item">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">飞书看课</span>
+                <span class="capability-item__desc">叠加飞书链接内容</span>
+              </div>
+              <el-switch v-model="form.openFeishu" active-value="1" inactive-value="0" />
+            </div>
+            <div class="capability-item" v-if="projectFrom === 'sxjz'">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">SIP 外呼</span>
+                <span class="capability-item__desc">叠加外呼催课任务</span>
+              </div>
+              <el-switch v-model="form.openSipCall" active-value="1" inactive-value="0" />
+            </div>
+            <div class="capability-item">
+              <div class="capability-item__meta">
+                <span class="capability-item__name">@所有人</span>
+                <span class="capability-item__desc">群消息是否 @所有人</span>
+              </div>
+              <el-switch v-model="form.openIsAtAll" active-value="1" inactive-value="0" />
+            </div>
+          </div>
+          <el-row :gutter="28" v-if="projectFrom === 'sxjz' && form.openSipCall == '1'" class="sip-task-row">
+            <el-col :span="12">
+              <el-form-item label="外呼任务" required>
+                <el-select
+                  v-model="form.aiCustomerRoleId"
+                  placeholder="请选择 SIP 外呼任务"
+                  filterable
+                  clearable
+                  class="w-full"
+                  @visible-change="onSipSelectVisible"
+                >
+                  <el-option
+                    v-for="task in aiSipCallTaskList"
+                    :key="task.batchId"
+                    :label="task.batchName"
+                    :value="task.batchId"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
 
+        <div class="form-section form-section--content">
+          <div class="form-section__title">内容与时间</div>
+          <el-form-item label="文案内容" prop="modeContent">
             <el-input
-              v-model="item.desc"
-              placeholder="催课内容"
-              type="textarea" :rows="2"
-              style="width: 500px; margin-left: 10px; margin-top: 10px;">
-            </el-input>
+              v-model="form.modeContent"
+              type="textarea"
+              :rows="4"
+              placeholder="发课时附带的文字内容"
+              maxlength="500"
+              show-word-limit
+            />
+          </el-form-item>
+          <el-row :gutter="28" v-if="form.sendType == 11 && !form.id">
+            <el-col :span="12">
+              <el-form-item label="发课时间" prop="time">
+                <el-time-picker
+                  v-model="form.time"
+                  value-format="HH:mm"
+                  format="HH:mm"
+                  placeholder="选择时间"
+                  class="w-full"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="催课次数" prop="num">
+                <el-input-number
+                  v-model="form.num"
+                  :min="0"
+                  class="num-input"
+                  @change="sendNumChange"
+                />
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <div v-if="form.sendType == 11 && !form.id && form.num > 0" class="remind-list">
+            <div class="remind-list__label">催课安排</div>
+            <div class="remind-grid">
+              <div
+                v-for="(item, index) in form.timeList"
+                :key="index"
+                class="remind-row"
+              >
+                <span class="remind-row__index">第 {{ index + 1 }} 次</span>
+                <el-time-picker
+                  v-model="item.value"
+                  value-format="HH:mm"
+                  format="HH:mm"
+                  :picker-options="{ selectableRange: startTimeRange }"
+                  placeholder="催课时间"
+                  class="remind-row__time"
+                />
+                <el-input
+                  v-model="item.desc"
+                  type="textarea"
+                  :rows="2"
+                  placeholder="催课文案"
+                  class="remind-row__desc"
+                />
+              </div>
+            </div>
           </div>
-        </el-form-item>
+        </div>
       </el-form>
-      <div slot="footer" class="dialog-footer" style="display: flex;justify-content: flex-end;">
-        <el-button type="primary" @click="submitForm">确 定</el-button>
-        <el-button @click="cancel">取 消</el-button>
+      <div slot="footer" class="dialog-footer">
+        <el-button size="medium" @click="cancel">取 消</el-button>
+        <el-button type="primary" size="medium" @click="submitForm">确 定</el-button>
       </div>
     </el-dialog>
 
@@ -417,6 +515,7 @@ import {courseList, getRoles} from "@/api/qw/sop";
 import {treeselect} from "../../../api/company/companyDept";
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+import { listAiSipCallTask } from "@/api/aiSipCall/aiSipCallTask";
 export default {
   name: "SopTemp",
   components: {Treeselect},
@@ -424,6 +523,7 @@ export default {
     return {
 
       projectFrom:process.env.VUE_APP_PROJECT_FROM,
+      aiSipCallTaskList: [],
       // 遮罩层
       loading: true,
       companysloading: false,
@@ -578,9 +678,31 @@ export default {
       if (this.showAppCourseToggle && newVal === '1') {
         this.$set(this.form, 'openOfficial', '0');
       }
+    },
+    'form.openSipCall'(newVal) {
+      if (newVal !== '1') {
+        this.$set(this.form, 'aiCustomerRoleId', null);
+      } else {
+        this.loadSipTemplates();
+      }
     }
   },
   methods: {
+    onSipSelectVisible(visible) {
+      if (visible) {
+        this.loadSipTemplates();
+      }
+    },
+    loadSipTemplates() {
+      if (this.aiSipCallTaskList.length > 0) {
+        return;
+      }
+      listAiSipCallTask({ taskType: 1, status: 0, pageNum: 1, pageSize: 100 }).then(response => {
+        this.aiSipCallTaskList = response.rows || response.data || [];
+      }).catch(() => {
+        this.$message.error('加载SIP外呼任务失败');
+      });
+    },
 
     handleCompanyQuery() {
       this.queryCompanyParams.pageNum = 1;
@@ -627,6 +749,10 @@ export default {
         sort: 0,
         openOfficial: "1",
         openAppCourse: "0", //默认 app 看课为关
+        openSipCall: "0",
+        openFeishu: "0",
+        aiCustomerRoleId: null,
+        openIsAtAll: "0",
         time: "",
         num: 1,
         timeList: [{value: "",desc:""}],
@@ -792,6 +918,16 @@ export default {
               return;
             }
           }
+          if (this.form.sendType === 11 && !this.form.id) {
+            if (this.projectFrom === 'sxjz' && this.form.openSipCall === '1' && !this.form.aiCustomerRoleId) {
+              this.$message.error("请选择SIP外呼任务!");
+              return;
+            }
+            if (this.projectFrom !== 'sxjz') {
+              this.form.openSipCall = '0';
+              this.form.aiCustomerRoleId = null;
+            }
+          }
 
           if (this.command != 2 && this.form.id == null && this.form.sendType == 11){
 
@@ -872,7 +1008,7 @@ export default {
       }).then(function () {
         return delSopTemp(ids);
       }).then(() => {
-        this.getMyListgetMyList();
+        this.getMyList();
         this.msgSuccess("删除成功");
       }).catch(() => {
       });
@@ -924,3 +1060,193 @@ export default {
   }
 };
 </script>
+<style scoped>
+.sop-temp-form {
+  max-height: 72vh;
+  overflow-y: auto;
+  padding-right: 6px;
+}
+
+.form-section {
+  margin-bottom: 24px;
+  padding: 28px 32px 16px;
+  background: #fafbfc;
+  border: 1px solid #ebeef5;
+  border-radius: 12px;
+}
+
+.form-section:last-child {
+  margin-bottom: 4px;
+}
+
+.form-section__title {
+  display: flex;
+  align-items: baseline;
+  gap: 14px;
+  margin: 0 0 24px;
+  padding-left: 12px;
+  border-left: 4px solid #409eff;
+  font-size: 16px;
+  font-weight: 600;
+  color: #1f2d3d;
+  line-height: 1.3;
+  letter-spacing: 0.02em;
+}
+
+.form-section__hint {
+  font-size: 13px;
+  font-weight: 400;
+  color: #909399;
+}
+
+.w-full {
+  width: 100%;
+}
+
+.num-input {
+  width: 100%;
+}
+
+.num-input >>> .el-input__inner {
+  text-align: left;
+}
+
+.dept-treeselect {
+  width: 100%;
+  line-height: 40px;
+}
+
+.capability-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+  gap: 16px;
+  margin-bottom: 4px;
+}
+
+.capability-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px;
+  min-height: 88px;
+  padding: 20px 18px;
+  background: #fff;
+  border: 1px solid #e4e7ed;
+  border-radius: 10px;
+}
+
+.capability-item__meta {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  min-width: 0;
+}
+
+.capability-item__name {
+  font-size: 15px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.capability-item__desc {
+  font-size: 12px;
+  color: #909399;
+  line-height: 1.45;
+}
+
+.sip-task-row {
+  margin-top: 18px;
+}
+
+.remind-list {
+  margin: 4px 0 8px 100px;
+}
+
+.remind-list__label {
+  margin-bottom: 12px;
+  font-size: 14px;
+  font-weight: 500;
+  color: #606266;
+}
+
+.remind-grid {
+  display: grid;
+  grid-template-columns: 1fr;
+  gap: 14px;
+}
+
+.remind-row {
+  display: flex;
+  align-items: flex-start;
+  gap: 12px;
+  padding: 14px 16px;
+  background: #fff;
+  border: 1px solid #e4e7ed;
+  border-radius: 10px;
+}
+
+.remind-row__index {
+  flex: 0 0 58px;
+  margin-top: 8px;
+  font-size: 13px;
+  font-weight: 500;
+  color: #606266;
+}
+
+.remind-row__time {
+  flex: 0 0 130px;
+}
+
+.remind-row__desc {
+  flex: 1;
+  min-width: 0;
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 12px;
+}
+</style>
+
+<style>
+.sop-temp-dialog {
+  border-radius: 12px;
+  overflow: hidden;
+}
+
+.sop-temp-dialog .el-dialog__header {
+  padding: 24px 32px 18px;
+  border-bottom: 1px solid #ebeef5;
+}
+
+.sop-temp-dialog .el-dialog__title {
+  font-size: 18px;
+  font-weight: 600;
+  color: #1f2d3d;
+  letter-spacing: 0.02em;
+}
+
+.sop-temp-dialog .el-dialog__body {
+  padding: 28px 32px 18px;
+}
+
+.sop-temp-dialog .el-dialog__footer {
+  padding: 18px 32px 24px;
+  border-top: 1px solid #ebeef5;
+}
+
+.sop-temp-dialog .el-form-item {
+  margin-bottom: 22px;
+}
+
+.sop-temp-dialog .el-input__inner,
+.sop-temp-dialog .el-textarea__inner {
+  border-radius: 6px;
+}
+
+.sop-temp-dialog .el-button--medium {
+  min-width: 104px;
+  padding: 11px 24px;
+}
+</style>