Procházet zdrojové kódy

部门发课可优先走主备小程序

xw před 3 týdny
rodič
revize
734fed66d1

+ 481 - 0
src/components/RandomTextListEditor/index.vue

@@ -0,0 +1,481 @@
+<template>
+  <div class="random-text-list-editor">
+    <div class="random-text-list-editor__summary" @click="openDrawer">
+      <div class="random-text-list-editor__summary-main">
+        <i class="el-icon-document" />
+        <span>文本文本 <strong>{{ item.randomValueList.length }}</strong> 条</span>
+      </div>
+      <el-button type="text" icon="el-icon-edit">
+        编辑文本
+      </el-button>
+    </div>
+
+    <el-drawer
+      :title="drawerTitle"
+      :visible.sync="drawerVisible"
+      direction="rtl"
+      size="520px"
+      append-to-body
+      :wrapper-closable="true"
+      custom-class="random-text-drawer"
+      @open="handleDrawerOpen"
+    >
+      <div class="random-text-drawer__body">
+        <div class="random-text-drawer__toolbar">
+          <span class="random-text-drawer__count">共 {{ item.randomValueList.length }} 条内容</span>
+          <el-button
+            v-if="!disabled"
+            type="primary"
+            size="mini"
+            icon="el-icon-plus"
+            @click="handleAdd"
+          >
+            新增文本
+          </el-button>
+        </div>
+
+        <div class="random-text-drawer__list">
+          <div
+            v-for="(randomValue, randomIndex) in item.randomValueList"
+            :key="randomIndex"
+            class="random-text-drawer__item"
+            :class="{ 'is-active': editingIndex === randomIndex }"
+            @click="selectItem(randomIndex)"
+          >
+            <div class="random-text-drawer__item-main">
+              <span class="random-text-drawer__label">文本 {{ randomIndex + 1 }}</span>
+              <span class="random-text-drawer__preview">{{ getPreview(randomValue) }}</span>
+            </div>
+            <el-button
+              v-if="!disabled && item.randomValueList.length > minCount"
+              type="text"
+              icon="el-icon-delete"
+              class="random-text-drawer__delete"
+              @click.stop="handleRemove(randomIndex)"
+            />
+          </div>
+        </div>
+
+        <div v-if="editingIndex !== -1" class="random-text-drawer__editor">
+          <div class="random-text-drawer__editor-title">编辑文本 {{ editingIndex + 1 }}</div>
+          <el-input
+            :disabled="disabled"
+            v-model="item.randomValueList[editingIndex]"
+            type="textarea"
+            :rows="6"
+            placeholder="请输入文本内容"
+            @keydown.native="handleKeydown($event, editingIndex)"
+            :ref="`textarea-${refId}-${editingIndex}`"
+          />
+          <div class="random-text-drawer__actions">
+            <el-link
+              v-if="!disabled"
+              type="primary"
+              @click="toggleSalesCall(editingIndex)"
+            >
+              {{ item.randomSalesCallAdded && item.randomSalesCallAdded[editingIndex] ? `移除${salesCallTag}` : `插入${salesCallTag}` }}
+            </el-link>
+            <el-link
+              v-if="!disabled"
+              type="primary"
+              @click="toggleUserCall(editingIndex)"
+            >
+              {{ item.randomUserNameCallAdded && item.randomUserNameCallAdded[editingIndex] ? `移除${userCallTag}` : `插入${userCallTag}` }}
+            </el-link>
+          </div>
+        </div>
+        <div v-else class="random-text-drawer__empty-editor">
+          请先选择一条文本进行编辑
+        </div>
+      </div>
+
+      <div class="random-text-drawer__footer">
+        <el-button @click="drawerVisible = false">关闭</el-button>
+      </div>
+    </el-drawer>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'RandomTextListEditor',
+  props: {
+    item: {
+      type: Object,
+      required: true
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    salesCallTag: {
+      type: String,
+      default: '#客户称呼#'
+    },
+    userCallTag: {
+      type: String,
+      default: '#用户姓名#'
+    },
+    minCount: {
+      type: Number,
+      default: 2
+    },
+    refId: {
+      type: String,
+      required: true
+    }
+  },
+  data() {
+    return {
+      drawerVisible: false,
+      editingIndex: 0
+    }
+  },
+  computed: {
+    drawerTitle() {
+      return `文本编辑(共${this.item.randomValueList.length}条)`
+    }
+  },
+  watch: {
+    'item.randomValueList.length'(length) {
+      if (this.editingIndex >= length) {
+        this.editingIndex = Math.max(0, length - 1)
+      }
+    }
+  },
+  mounted() {
+    this.initFields()
+  },
+  methods: {
+    openDrawer() {
+      this.drawerVisible = true
+    },
+    handleDrawerOpen() {
+      this.initFields()
+      if (this.item.randomValueList.length > 0 && this.editingIndex === -1) {
+        this.editingIndex = 0
+      }
+    },
+    initFields() {
+      const item = this.item
+      if (!item) {
+        return
+      }
+      if (!Array.isArray(item.randomValueList)) {
+        const initialValue = item.value || ''
+        this.$set(item, 'randomValueList', initialValue ? [initialValue] : ['', ''])
+      }
+      if (!Array.isArray(item.randomSalesCallAdded)) {
+        this.$set(item, 'randomSalesCallAdded', item.randomValueList.map(() => false))
+      }
+      if (!Array.isArray(item.randomUserNameCallAdded)) {
+        this.$set(item, 'randomUserNameCallAdded', item.randomValueList.map(() => false))
+      }
+    },
+    getPreview(text) {
+      const value = (text || '').replace(/\s+/g, ' ').trim()
+      if (!value) {
+        return '暂无内容'
+      }
+      return value.length > 36 ? `${value.slice(0, 36)}...` : value
+    },
+    selectItem(randomIndex) {
+      this.editingIndex = randomIndex
+      this.$nextTick(() => {
+        const textarea = this.getTextarea(randomIndex)
+        if (textarea) {
+          textarea.focus()
+        }
+      })
+    },
+    handleAdd() {
+      this.initFields()
+      this.item.randomValueList.push('')
+      this.item.randomSalesCallAdded.push(false)
+      this.item.randomUserNameCallAdded.push(false)
+      this.editingIndex = this.item.randomValueList.length - 1
+      this.$nextTick(() => {
+        const textarea = this.getTextarea(this.editingIndex)
+        if (textarea) {
+          textarea.focus()
+        }
+      })
+    },
+    handleRemove(randomIndex) {
+      if (!this.item.randomValueList || this.item.randomValueList.length <= this.minCount) {
+        return
+      }
+      this.$confirm('确定删除当前文本文本?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.item.randomValueList.splice(randomIndex, 1)
+        this.item.randomSalesCallAdded.splice(randomIndex, 1)
+        this.item.randomUserNameCallAdded.splice(randomIndex, 1)
+        if (this.editingIndex === randomIndex) {
+          this.editingIndex = Math.min(randomIndex, this.item.randomValueList.length - 1)
+        } else if (this.editingIndex > randomIndex) {
+          this.editingIndex -= 1
+        }
+      }).catch(() => {})
+    },
+    getTextarea(randomIndex) {
+      const ref = this.$refs[`textarea-${this.refId}-${randomIndex}`]
+      return ref && (Array.isArray(ref) ? ref[0] : ref)?.$refs?.textarea
+    },
+    toggleSalesCall(randomIndex) {
+      this.initFields()
+      const textarea = this.getTextarea(randomIndex)
+      const cursorPosition = textarea ? textarea.selectionStart : (this.item.randomValueList[randomIndex] || '').length
+
+      if (this.item.randomSalesCallAdded[randomIndex]) {
+        this.$set(
+          this.item.randomValueList,
+          randomIndex,
+          (this.item.randomValueList[randomIndex] || '').replace(new RegExp(this.escapeRegExp(this.salesCallTag), 'g'), '')
+        )
+        this.$set(this.item.randomSalesCallAdded, randomIndex, false)
+      } else {
+        const currentValue = this.item.randomValueList[randomIndex] || ''
+        this.$set(
+          this.item.randomValueList,
+          randomIndex,
+          currentValue.slice(0, cursorPosition) + this.salesCallTag + currentValue.slice(cursorPosition)
+        )
+        this.$set(this.item.randomSalesCallAdded, randomIndex, true)
+      }
+    },
+    toggleUserCall(randomIndex) {
+      this.initFields()
+      const textarea = this.getTextarea(randomIndex)
+      const cursorPosition = textarea ? textarea.selectionStart : (this.item.randomValueList[randomIndex] || '').length
+
+      if (this.item.randomUserNameCallAdded[randomIndex]) {
+        this.$set(
+          this.item.randomValueList,
+          randomIndex,
+          (this.item.randomValueList[randomIndex] || '').replace(new RegExp(this.escapeRegExp(this.userCallTag), 'g'), '')
+        )
+        this.$set(this.item.randomUserNameCallAdded, randomIndex, false)
+      } else {
+        const currentValue = this.item.randomValueList[randomIndex] || ''
+        this.$set(
+          this.item.randomValueList,
+          randomIndex,
+          currentValue.slice(0, cursorPosition) + this.userCallTag + currentValue.slice(cursorPosition)
+        )
+        this.$set(this.item.randomUserNameCallAdded, randomIndex, true)
+      }
+    },
+    handleKeydown(event, randomIndex) {
+      if (event.key !== 'Backspace' && event.key !== 'Delete') {
+        return
+      }
+      this.initFields()
+      const value = this.item.randomValueList[randomIndex] || ''
+      const cursorPosition = event.target.selectionStart
+      const tags = [this.salesCallTag, this.userCallTag]
+
+      tags.forEach(tag => {
+        const tagIndex = value.indexOf(tag)
+        if (tagIndex !== -1) {
+          const tagEnd = tagIndex + tag.length
+          if ((event.key === 'Backspace' && cursorPosition > tagIndex && cursorPosition <= tagEnd) ||
+            (event.key === 'Delete' && cursorPosition >= tagIndex && cursorPosition < tagEnd)) {
+            event.preventDefault()
+            this.$set(this.item.randomValueList, randomIndex, value.replace(tag, ''))
+            if (tag === this.salesCallTag) {
+              this.$set(this.item.randomSalesCallAdded, randomIndex, false)
+            }
+            if (tag === this.userCallTag) {
+              this.$set(this.item.randomUserNameCallAdded, randomIndex, false)
+            }
+          }
+        }
+      })
+    },
+    escapeRegExp(text) {
+      return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+    }
+  }
+}
+</script>
+
+<style scoped>
+.random-text-list-editor__summary {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 10px 12px;
+  border: 1px dashed #dcdfe6;
+  border-radius: 4px;
+  background: #f8fafc;
+  cursor: pointer;
+  transition: border-color 0.2s, background 0.2s;
+}
+
+.random-text-list-editor__summary:hover {
+  border-color: #409eff;
+  background: #f0f7ff;
+}
+
+.random-text-list-editor__summary-main {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  color: #606266;
+  font-size: 13px;
+}
+
+.random-text-list-editor__summary-main i {
+  color: #409eff;
+  font-size: 16px;
+}
+
+.random-text-list-editor__summary-main strong {
+  color: #409eff;
+  font-weight: 600;
+}
+
+.random-text-drawer__body {
+  display: flex;
+  flex-direction: column;
+  height: calc(100vh - 140px);
+  padding: 0 20px;
+}
+
+.random-text-drawer__toolbar {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 12px;
+  flex-shrink: 0;
+}
+
+.random-text-drawer__count {
+  color: #909399;
+  font-size: 13px;
+}
+
+.random-text-drawer__list {
+  flex: 0 0 auto;
+  max-height: 240px;
+  overflow-y: auto;
+  border: 1px solid #ebeef5;
+  border-radius: 4px;
+  margin-bottom: 16px;
+}
+
+.random-text-drawer__item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 8px;
+  padding: 10px 12px;
+  border-bottom: 1px solid #ebeef5;
+  cursor: pointer;
+  transition: background 0.2s;
+}
+
+.random-text-drawer__item:last-child {
+  border-bottom: none;
+}
+
+.random-text-drawer__item:hover {
+  background: #f5f7fa;
+}
+
+.random-text-drawer__item.is-active {
+  background: #ecf5ff;
+  border-left: 3px solid #409eff;
+  padding-left: 9px;
+}
+
+.random-text-drawer__item-main {
+  flex: 1;
+  min-width: 0;
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+
+.random-text-drawer__label {
+  flex-shrink: 0;
+  color: #303133;
+  font-size: 13px;
+  font-weight: 500;
+  min-width: 48px;
+}
+
+.random-text-drawer__preview {
+  flex: 1;
+  color: #909399;
+  font-size: 12px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.random-text-drawer__item.is-active .random-text-drawer__preview {
+  color: #606266;
+}
+
+.random-text-drawer__delete {
+  color: #f56c6c;
+  flex-shrink: 0;
+}
+
+.random-text-drawer__editor {
+  flex: 1;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+}
+
+.random-text-drawer__editor-title {
+  color: #303133;
+  font-size: 14px;
+  font-weight: 500;
+  margin-bottom: 10px;
+  flex-shrink: 0;
+}
+
+.random-text-drawer__actions {
+  margin-top: 10px;
+  flex-shrink: 0;
+}
+
+.random-text-drawer__actions .el-link + .el-link {
+  margin-left: 20px;
+}
+
+.random-text-drawer__empty-editor {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: #c0c4cc;
+  font-size: 13px;
+  border: 1px dashed #ebeef5;
+  border-radius: 4px;
+}
+
+.random-text-drawer__footer {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  padding: 12px 20px;
+  border-top: 1px solid #ebeef5;
+  background: #fff;
+  text-align: right;
+}
+</style>
+
+<style>
+.random-text-drawer .el-drawer__body {
+  padding: 0;
+  overflow: hidden;
+}
+</style>

+ 246 - 92
src/views/company/companyDept/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
+  <div class="app-container dept-page">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" class="dept-query-form">
       <el-form-item label="部门名称" prop="deptName">
         <el-input
           v-model="queryParams.deptName"
@@ -29,7 +29,7 @@
     <el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
-         plain
+          plain
           type="primary"
           icon="el-icon-plus"
           size="mini"
@@ -47,18 +47,40 @@
       row-key="deptId"
       default-expand-all
       :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+      class="dept-table"
     >
-
-      <el-table-column prop="deptId" label="部门编号" width="260"></el-table-column>
-      <el-table-column prop="deptName" label="部门名称" width="260"></el-table-column>
-      <el-table-column prop="orderNum" label="排序" width="200"></el-table-column>
-      <el-table-column prop="status" label="状态" :formatter="statusFormat" width="100"></el-table-column>
-      <el-table-column label="创建时间" align="center" prop="createTime" width="200">
+      <el-table-column prop="deptName" label="部门名称" min-width="220" show-overflow-tooltip />
+      <el-table-column prop="deptId" label="部门编号" width="100" align="center" />
+      <el-table-column prop="orderNum" label="排序" width="70" align="center" />
+      <el-table-column prop="status" label="状态" width="80" align="center">
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.status === '0' ? 'success' : 'info'" size="mini">
+            {{ statusFormat(scope.row) }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="主要小程序" min-width="160" align="center">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.miniAppMaster" type="primary" size="mini" effect="plain">
+            {{ getMiniAppName(scope.row.miniAppMaster) }}
+          </el-tag>
+          <span v-else class="text-muted">跟随公司</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="备用小程序" min-width="160" align="center">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.miniAppServer" type="warning" size="mini" effect="plain">
+            {{ getMiniAppName(scope.row.miniAppServer) }}
+          </el-tag>
+          <span v-else class="text-muted">跟随公司</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="170">
         <template slot-scope="scope">
           <span>{{ parseTime(scope.row.createTime) }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+      <el-table-column label="操作" align="center" width="200" fixed="right">
         <template slot-scope="scope">
           <el-button
             size="mini"
@@ -79,6 +101,7 @@
             size="mini"
             type="text"
             icon="el-icon-delete"
+            class="danger-text"
             @click="handleDelete(scope.row)"
             v-hasPermi="['company:dept:remove']"
           >删除</el-button>
@@ -86,56 +109,112 @@
       </el-table-column>
     </el-table>
 
-    <!-- 添加或修改部门对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
-      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
-        <el-row>
-          <el-col :span="24" v-if="form.parentId !== 0">
-            <el-form-item label="上级部门" prop="parentId">
-              <treeselect v-model="form.parentId" :options="deptOptions" :normalizer="normalizer" placeholder="选择上级部门" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="部门名称" prop="deptName">
-              <el-input v-model="form.deptName" placeholder="请输入部门名称" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="显示排序" prop="orderNum">
-              <el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="负责人" prop="leader">
-              <el-input v-model="form.leader" placeholder="请输入负责人" maxlength="20" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="联系电话" prop="phone">
-              <el-input v-model="form.phone" placeholder="请输入联系电话" maxlength="11" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="邮箱" prop="email">
-              <el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
-            </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-row>
+    <el-dialog :title="title" :visible.sync="open" width="720px" append-to-body class="dept-dialog">
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px" class="dept-form">
+        <div class="form-section">
+          <div class="form-section-title">基本信息</div>
+          <el-row :gutter="20">
+            <el-col :span="24" v-if="form.parentId !== 0">
+              <el-form-item label="上级部门" prop="parentId">
+                <treeselect
+                  v-model="form.parentId"
+                  :options="deptOptions"
+                  :normalizer="normalizer"
+                  placeholder="选择上级部门"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="部门名称" prop="deptName">
+                <el-input v-model="form.deptName" placeholder="请输入部门名称" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="显示排序" prop="orderNum">
+                <el-input-number v-model="form.orderNum" controls-position="right" :min="0" style="width: 100%" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="负责人" prop="leader">
+                <el-input v-model="form.leader" placeholder="请输入负责人" maxlength="20" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="联系电话" prop="phone">
+                <el-input v-model="form.phone" placeholder="请输入联系电话" maxlength="11" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="邮箱" prop="email">
+                <el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
+              </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-row>
+        </div>
+
+        <div class="form-section">
+          <div class="form-section-title">小程序配置</div>
+          <p class="form-section-tip">不选择时,SOP 发课将使用公司级主备小程序配置</p>
+          <el-row :gutter="20">
+            <el-col :span="24">
+              <el-form-item label="主要小程序">
+                <el-select
+                  v-model="form.miniAppMaster"
+                  placeholder="请选择主要小程序"
+                  clearable
+                  filterable
+                  style="width: 100%"
+                >
+                  <el-option
+                    v-for="item in companyMiniAppList"
+                    :key="item.id"
+                    :label="item.name"
+                    :value="item.appid || item.appId"
+                  >
+                    <span>{{ item.name }}</span>
+                    <span class="option-appid">{{ item.appid || item.appId }}</span>
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="24">
+              <el-form-item label="备用小程序">
+                <el-select
+                  v-model="form.miniAppServer"
+                  placeholder="请选择备用小程序"
+                  clearable
+                  filterable
+                  style="width: 100%"
+                >
+                  <el-option
+                    v-for="item in companyMiniAppList"
+                    :key="'backup-' + item.id"
+                    :label="item.name"
+                    :value="item.appid || item.appId"
+                  >
+                    <span>{{ item.name }}</span>
+                    <span class="option-appid">{{ item.appid || item.appId }}</span>
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
       </el-form>
       <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitForm">确 定</el-button>
         <el-button @click="cancel">取 消</el-button>
+        <el-button type="primary" @click="submitForm">确 定</el-button>
       </div>
     </el-dialog>
   </div>
@@ -143,6 +222,7 @@
 
 <script>
 import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from "@/api/company/companyDept";
+import { getCompanyMiniAppList } from "@/api/company/companyConfig";
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
 
@@ -151,28 +231,20 @@ export default {
   components: { Treeselect },
   data() {
     return {
-      // 遮罩层
       loading: true,
-      // 显示搜索条件
       showSearch: true,
-      // 表格树数据
       deptList: [],
-      // 部门树选项
       deptOptions: [],
-      // 弹出层标题
       title: "",
-      // 是否显示弹出层
       open: false,
-      // 状态数据字典
       statusOptions: [],
-      // 查询参数
       queryParams: {
         deptName: undefined,
         status: undefined
       },
-      // 表单参数
       form: {},
-      // 表单校验
+      companyMiniAppList: [],
+      miniAppNameMap: {},
       rules: {
         parentId: [
           { required: true, message: "上级部门不能为空", trigger: "blur" }
@@ -186,7 +258,7 @@ export default {
         email: [
           {
             type: "email",
-            message: "'请输入正确的邮箱地址",
+            message: "请输入正确的邮箱地址",
             trigger: ["blur", "change"]
           }
         ],
@@ -202,12 +274,34 @@ export default {
   },
   created() {
     this.getList();
+    this.loadCompanyMiniAppList();
     this.getDicts("sys_normal_disable").then(response => {
       this.statusOptions = response.data;
     });
   },
   methods: {
-    /** 查询部门列表 */
+    loadCompanyMiniAppList() {
+      getCompanyMiniAppList().then(res => {
+        this.companyMiniAppList = res.data || [];
+        const map = {};
+        this.companyMiniAppList.forEach(item => {
+          const appId = item.appid || item.appId;
+          if (appId) {
+            map[appId] = item.name || appId;
+          }
+        });
+        this.miniAppNameMap = map;
+      }).catch(() => {
+        this.companyMiniAppList = [];
+        this.miniAppNameMap = {};
+      });
+    },
+    getMiniAppName(appId) {
+      if (!appId) {
+        return "";
+      }
+      return this.miniAppNameMap[appId] || appId;
+    },
     getList() {
       this.loading = true;
       listDept(this.queryParams).then(response => {
@@ -215,7 +309,6 @@ export default {
         this.loading = false;
       });
     },
-    /** 转换部门数据结构 */
     normalizer(node) {
       if (node.children && !node.children.length) {
         delete node.children;
@@ -226,16 +319,13 @@ export default {
         children: node.children
       };
     },
-    // 字典状态字典翻译
-    statusFormat(row, column) {
+    statusFormat(row) {
       return this.selectDictLabel(this.statusOptions, row.status);
     },
-    // 取消按钮
     cancel() {
       this.open = false;
       this.reset();
     },
-    // 表单重置
     reset() {
       this.form = {
         deptId: undefined,
@@ -245,44 +335,43 @@ export default {
         leader: undefined,
         phone: undefined,
         email: undefined,
-        status: "0"
+        status: "0",
+        miniAppMaster: undefined,
+        miniAppServer: undefined
       };
       this.resetForm("form");
     },
-    /** 搜索按钮操作 */
     handleQuery() {
       this.getList();
     },
-    /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
       this.handleQuery();
     },
-    /** 新增按钮操作 */
     handleAdd(row) {
       this.reset();
+      this.loadCompanyMiniAppList();
       if (row != undefined) {
         this.form.parentId = row.deptId;
       }
       this.open = true;
       this.title = "添加部门";
       listDept().then(response => {
-	        this.deptOptions = this.handleTree(response.data, "deptId");
+        this.deptOptions = this.handleTree(response.data, "deptId");
       });
     },
-    /** 修改按钮操作 */
     handleUpdate(row) {
       this.reset();
+      this.loadCompanyMiniAppList();
       getDept(row.deptId).then(response => {
         this.form = response.data;
         this.open = true;
         this.title = "修改部门";
       });
       listDeptExcludeChild(row.deptId).then(response => {
-	        this.deptOptions = this.handleTree(response.data, "deptId");
+        this.deptOptions = this.handleTree(response.data, "deptId");
       });
     },
-    /** 提交按钮 */
     submitForm: function() {
       this.$refs["form"].validate(valid => {
         if (valid) {
@@ -306,19 +395,84 @@ export default {
         }
       });
     },
-    /** 删除按钮操作 */
     handleDelete(row) {
       this.$confirm('是否确认删除名称为"' + row.deptName + '"的数据项?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(function() {
-          return delDept(row.deptId);
-        }).then(() => {
-          this.getList();
-          this.msgSuccess("删除成功");
-        }).catch(function() {});
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return delDept(row.deptId);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(function() {});
     }
   }
 };
 </script>
+
+<style scoped lang="scss">
+.dept-page {
+  .dept-table {
+    width: 100%;
+  }
+
+  .text-muted {
+    color: #c0c4cc;
+    font-size: 12px;
+  }
+
+  .danger-text {
+    color: #f56c6c;
+  }
+}
+
+.dept-dialog {
+  ::v-deep .el-dialog__body {
+    padding: 16px 24px 8px;
+  }
+}
+
+.dept-form {
+  .form-section {
+    margin-bottom: 8px;
+
+    &:not(:last-child) {
+      padding-bottom: 4px;
+      border-bottom: 1px solid #ebeef5;
+      margin-bottom: 16px;
+    }
+  }
+
+  .form-section-title {
+    font-size: 14px;
+    font-weight: 600;
+    color: #303133;
+    margin-bottom: 12px;
+    padding-left: 8px;
+    border-left: 3px solid #409eff;
+    line-height: 1;
+  }
+
+  .form-section-tip {
+    margin: 0 0 12px 8px;
+    font-size: 12px;
+    color: #909399;
+    line-height: 1.5;
+  }
+
+  ::v-deep .el-form-item {
+    margin-bottom: 18px;
+  }
+
+  ::v-deep .el-form-item__label {
+    white-space: nowrap;
+  }
+}
+
+.option-appid {
+  float: right;
+  color: #909399;
+  font-size: 12px;
+}
+</style>

+ 10 - 140
src/views/qw/sopTemp/updateSopTemp.vue

@@ -352,60 +352,14 @@
                                               </span>
                                             </div>
 
-                                            <div v-if="setList.isRandomSend == 1">
-                                              <div
-                                                v-for="(randomValue, randomIndex) in setList.randomValueList"
-                                                :key="randomIndex"
-                                                style="margin-bottom: 12px; padding: 10px; border: 1px dashed #dcdfe6; border-radius: 4px;"
-                                              >
-                                                <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
-                                                  <span style="color: #606266; font-size: 13px;">文案 {{ randomIndex + 1 }}</span>
-                                                  <el-button
-                                                    v-if="setList.randomValueList.length > 2 && roles.includes('edit_sop_temp_content') && formType != 3"
-                                                    type="text"
-                                                    icon="el-icon-delete"
-                                                    style="color: #f56c6c;"
-                                                    @click="removeRandomValue(setList, randomIndex)"
-                                                  />
-                                                </div>
-                                                <el-input
-                                                  :disabled="formType == 3 || !roles.includes('edit_sop_temp_content')"
-                                                  v-model="setList.randomValueList[randomIndex]"
-                                                  type="textarea"
-                                                  :rows="3"
-                                                  placeholder="请输入文案内容"
-                                                  style="width: 90%;"
-                                                  @keydown.native="handleRandomKeydown($event, index, contentIndex, setIndex, randomIndex)"
-                                                  :ref="`textarea-${index}-${contentIndex}-${setIndex}-${randomIndex}`"
-                                                />
-                                                <div style="margin-top: 8px;">
-                                                  <el-link
-                                                    v-if="roles.includes('edit_sop_temp_content')"
-                                                    type="primary"
-                                                    @click="toggleRandomSalesCall(index, contentIndex, setIndex, randomIndex)"
-                                                  >
-                                                    {{ setList.randomSalesCallAdded && setList.randomSalesCallAdded[randomIndex] ? '移除#销售称呼#' : '添加#销售称呼#' }}
-                                                  </el-link>
-                                                  <el-link
-                                                    v-if="roles.includes('edit_sop_temp_content')"
-                                                    type="primary"
-                                                    style="margin-left: 20px;"
-                                                    @click="toggleRandomUserNameCall(index, contentIndex, setIndex, randomIndex)"
-                                                  >
-                                                    {{ setList.randomUserNameCallAdded && setList.randomUserNameCallAdded[randomIndex] ? '移除#客户称呼#' : '添加#客户称呼#' }}
-                                                  </el-link>
-                                                </div>
-                                              </div>
-                                              <el-link
-                                                v-if="roles.includes('edit_sop_temp_content') && formType != 3"
-                                                type="primary"
-                                                icon="el-icon-plus"
-                                                :underline="false"
-                                                @click="addRandomValue(setList)"
-                                              >
-                                                添加文案
-                                              </el-link>
-                                            </div>
+                                            <RandomTextListEditor
+                                              v-if="setList.isRandomSend == 1"
+                                              :item="setList"
+                                              :disabled="formType == 3 || !roles.includes('edit_sop_temp_content')"
+                                              :ref-id="`temp-${index}-${contentIndex}-${setIndex}`"
+                                              sales-call-tag="#销售称呼#"
+                                              user-call-tag="#客户称呼#"
+                                            />
 
                                             <template v-else>
                                               <el-input :disabled="formType == 3 || !roles.includes('edit_sop_temp_content')"
@@ -997,13 +951,14 @@ import {listToLiveNoEnd} from "@/api/live/live";
 import ImageUpload from "@/views/qw/sop/ImageUpload";
 import userVideo from "@/views/qw/userVideo/userVideo.vue";
 import {listReward} from "@/api/qw/luckyBag";
+import RandomTextListEditor from "@/components/RandomTextListEditor/index.vue";
 import {
   getRoles,
 } from "@/api/qw/sop";
 
 export default {
   name: "updateSopTemp",
-  components: {ImageUpload, userVideo, draggable},
+  components: {ImageUpload, userVideo, draggable, RandomTextListEditor},
   data() {
     return {
       queryParams1: {
@@ -2113,91 +2068,6 @@ export default {
         this.$set(setList, 'value', firstValue);
       }
     },
-    addRandomValue(setList) {
-      this.initRandomSendFields(setList);
-      setList.randomValueList.push('');
-      setList.randomSalesCallAdded.push(false);
-      setList.randomUserNameCallAdded.push(false);
-    },
-    removeRandomValue(setList, randomIndex) {
-      if (!setList.randomValueList || setList.randomValueList.length <= 2) {
-        return;
-      }
-      setList.randomValueList.splice(randomIndex, 1);
-      setList.randomSalesCallAdded.splice(randomIndex, 1);
-      setList.randomUserNameCallAdded.splice(randomIndex, 1);
-    },
-    toggleRandomSalesCall(itemIndex, contentIndex, setIndex, randomIndex) {
-      const setItem = this.setting[itemIndex].content[contentIndex].setting[setIndex];
-      this.initRandomSendFields(setItem);
-      const salesCall = '#销售称呼#';
-      const refKey = `textarea-${itemIndex}-${contentIndex}-${setIndex}-${randomIndex}`;
-      const textarea = this.$refs[refKey] && this.$refs[refKey][0]?.$refs?.textarea;
-      const cursorPosition = textarea ? textarea.selectionStart : (setItem.randomValueList[randomIndex] || '').length;
-
-      if (setItem.randomSalesCallAdded[randomIndex]) {
-        this.$set(setItem.randomValueList, randomIndex, (setItem.randomValueList[randomIndex] || '').replace(new RegExp(salesCall, 'g'), ''));
-        this.$set(setItem.randomSalesCallAdded, randomIndex, false);
-      } else {
-        const currentValue = setItem.randomValueList[randomIndex] || '';
-        this.$set(
-          setItem.randomValueList,
-          randomIndex,
-          currentValue.slice(0, cursorPosition) + salesCall + currentValue.slice(cursorPosition)
-        );
-        this.$set(setItem.randomSalesCallAdded, randomIndex, true);
-      }
-    },
-    toggleRandomUserNameCall(itemIndex, contentIndex, setIndex, randomIndex) {
-      const setItem = this.setting[itemIndex].content[contentIndex].setting[setIndex];
-      this.initRandomSendFields(setItem);
-      const userCall = '#客户称呼#';
-      const refKey = `textarea-${itemIndex}-${contentIndex}-${setIndex}-${randomIndex}`;
-      const textarea = this.$refs[refKey] && this.$refs[refKey][0]?.$refs?.textarea;
-      const cursorPosition = textarea ? textarea.selectionStart : (setItem.randomValueList[randomIndex] || '').length;
-
-      if (setItem.randomUserNameCallAdded[randomIndex]) {
-        this.$set(setItem.randomValueList, randomIndex, (setItem.randomValueList[randomIndex] || '').replace(new RegExp(userCall, 'g'), ''));
-        this.$set(setItem.randomUserNameCallAdded, randomIndex, false);
-      } else {
-        const currentValue = setItem.randomValueList[randomIndex] || '';
-        this.$set(
-          setItem.randomValueList,
-          randomIndex,
-          currentValue.slice(0, cursorPosition) + userCall + currentValue.slice(cursorPosition)
-        );
-        this.$set(setItem.randomUserNameCallAdded, randomIndex, true);
-      }
-    },
-    handleRandomKeydown(event, itemIndex, contentIndex, setIndex, randomIndex) {
-      if (event.key !== 'Backspace' && event.key !== 'Delete') {
-        return;
-      }
-      const setItem = this.setting[itemIndex].content[contentIndex].setting[setIndex];
-      this.initRandomSendFields(setItem);
-      const textarea = event.target;
-      const value = setItem.randomValueList[randomIndex] || '';
-      const cursorPosition = textarea.selectionStart;
-      const tags = ['#销售称呼#', '#客户称呼#'];
-
-      tags.forEach(tag => {
-        const tagIndex = value.indexOf(tag);
-        if (tagIndex !== -1) {
-          const tagEnd = tagIndex + tag.length;
-          if ((event.key === 'Backspace' && cursorPosition > tagIndex && cursorPosition <= tagEnd) ||
-            (event.key === 'Delete' && cursorPosition >= tagIndex && cursorPosition < tagEnd)) {
-            event.preventDefault();
-            this.$set(setItem.randomValueList, randomIndex, value.replace(tag, ''));
-            if (tag === '#销售称呼#') {
-              this.$set(setItem.randomSalesCallAdded, randomIndex, false);
-            }
-            if (tag === '#客户称呼#') {
-              this.$set(setItem.randomUserNameCallAdded, randomIndex, false);
-            }
-          }
-        }
-      });
-    },
     normalizeSettingRandomFields(setting) {
       if (!setting || (setting.contentType != 1 && setting.contentType != '1' && setting.contentType != 15 && setting.contentType != '15')) {
         return;

+ 9 - 113
src/views/qw/sopUserLogsInfo/sendMsgOpenTool.vue

@@ -148,44 +148,13 @@
                         </span>
                       </div>
 
-                      <div v-if="item.isRandomSend == 1">
-                        <div
-                          v-for="(randomValue, randomIndex) in item.randomValueList"
-                          :key="randomIndex"
-                          style="margin-bottom: 12px; padding: 10px; border: 1px dashed #dcdfe6; border-radius: 4px;"
-                        >
-                          <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
-                            <span style="color: #606266; font-size: 13px;">文案 {{ randomIndex + 1 }}</span>
-                            <el-button
-                              v-if="item.randomValueList.length > 2"
-                              type="text"
-                              icon="el-icon-delete"
-                              style="color: #f56c6c;"
-                              @click="removeRandomValue(item, randomIndex)"
-                            />
-                          </div>
-                          <el-input
-                            v-model="item.randomValueList[randomIndex]"
-                            type="textarea"
-                            :rows="3"
-                            placeholder="请输入文案内容"
-                            style="width: 90%;"
-                            @keydown.native="handleRandomKeydown($event, index, randomIndex)"
-                            :ref="`textarea-${index}-${randomIndex}`"
-                          />
-                          <div style="margin-top: 8px;">
-                            <el-link type="primary" @click="toggleRandomSalesCall(index, randomIndex)">
-                              {{ item.randomSalesCallAdded && item.randomSalesCallAdded[randomIndex] ? '移除#销售称呼#' : '添加#销售称呼#' }}
-                            </el-link>
-                            <el-link type="primary" style="margin-left: 20px;" @click="toggleRandomUserNameCall(index, randomIndex)">
-                              {{ item.randomUserNameCallAdded && item.randomUserNameCallAdded[randomIndex] ? '移除#客户称呼#' : '添加#客户称呼#' }}
-                            </el-link>
-                          </div>
-                        </div>
-                        <el-link type="primary" icon="el-icon-plus" :underline="false" @click="addRandomValue(item)">
-                          添加文案
-                        </el-link>
-                      </div>
+                      <RandomTextListEditor
+                        v-if="item.isRandomSend == 1"
+                        :item="item"
+                        :ref-id="`setting-${index}`"
+                        sales-call-tag="#销售称呼#"
+                        user-call-tag="#客户称呼#"
+                      />
 
                       <template v-else>
                         <el-input
@@ -463,11 +432,12 @@ import {courseList, videoList} from "@/api/qw/sop";
 import userVideo from "@/views/qw/userVideo/userVideo";
 import {listToLiveNoEnd} from "@/api/live/live";
 import {listReward} from "@/api/qw/luckyBag";
+import RandomTextListEditor from "@/components/RandomTextListEditor/index.vue";
 
 
 export default {
   name: "sendMsgOpenTool",
-  components: {ImageUpload,userVideo},
+  components: {ImageUpload, userVideo, RandomTextListEditor},
   data() {
     return {
       queryParams1: {
@@ -939,80 +909,6 @@ export default {
         this.$set(item, 'value', firstValue);
       }
     },
-    addRandomValue(item) {
-      this.initRandomSendFields(item);
-      item.randomValueList.push('');
-      item.randomSalesCallAdded.push(false);
-      item.randomUserNameCallAdded.push(false);
-    },
-    removeRandomValue(item, randomIndex) {
-      if (!item.randomValueList || item.randomValueList.length <= 2) {
-        return;
-      }
-      item.randomValueList.splice(randomIndex, 1);
-      item.randomSalesCallAdded.splice(randomIndex, 1);
-      item.randomUserNameCallAdded.splice(randomIndex, 1);
-    },
-    toggleRandomSalesCall(index, randomIndex) {
-      const item = this.setting[index];
-      this.initRandomSendFields(item);
-      const salesCall = '#销售称呼#';
-      const textarea = this.$refs[`textarea-${index}-${randomIndex}`] && this.$refs[`textarea-${index}-${randomIndex}`][0]?.$refs?.textarea;
-      const cursorPosition = textarea ? textarea.selectionStart : (item.randomValueList[randomIndex] || '').length;
-
-      if (item.randomSalesCallAdded[randomIndex]) {
-        this.$set(item.randomValueList, randomIndex, (item.randomValueList[randomIndex] || '').replace(new RegExp(salesCall, 'g'), ''));
-        this.$set(item.randomSalesCallAdded, randomIndex, false);
-      } else {
-        const currentValue = item.randomValueList[randomIndex] || '';
-        this.$set(item.randomValueList, randomIndex, currentValue.slice(0, cursorPosition) + salesCall + currentValue.slice(cursorPosition));
-        this.$set(item.randomSalesCallAdded, randomIndex, true);
-      }
-    },
-    toggleRandomUserNameCall(index, randomIndex) {
-      const item = this.setting[index];
-      this.initRandomSendFields(item);
-      const userCall = '#客户称呼#';
-      const textarea = this.$refs[`textarea-${index}-${randomIndex}`] && this.$refs[`textarea-${index}-${randomIndex}`][0]?.$refs?.textarea;
-      const cursorPosition = textarea ? textarea.selectionStart : (item.randomValueList[randomIndex] || '').length;
-
-      if (item.randomUserNameCallAdded[randomIndex]) {
-        this.$set(item.randomValueList, randomIndex, (item.randomValueList[randomIndex] || '').replace(new RegExp(userCall, 'g'), ''));
-        this.$set(item.randomUserNameCallAdded, randomIndex, false);
-      } else {
-        const currentValue = item.randomValueList[randomIndex] || '';
-        this.$set(item.randomValueList, randomIndex, currentValue.slice(0, cursorPosition) + userCall + currentValue.slice(cursorPosition));
-        this.$set(item.randomUserNameCallAdded, randomIndex, true);
-      }
-    },
-    handleRandomKeydown(event, index, randomIndex) {
-      if (event.key !== 'Backspace' && event.key !== 'Delete') {
-        return;
-      }
-      const item = this.setting[index];
-      this.initRandomSendFields(item);
-      const value = item.randomValueList[randomIndex] || '';
-      const cursorPosition = event.target.selectionStart;
-      const tags = ['#销售称呼#', '#客户称呼#'];
-
-      tags.forEach(tag => {
-        const tagIndex = value.indexOf(tag);
-        if (tagIndex !== -1) {
-          const tagEnd = tagIndex + tag.length;
-          if ((event.key === 'Backspace' && cursorPosition > tagIndex && cursorPosition <= tagEnd) ||
-            (event.key === 'Delete' && cursorPosition >= tagIndex && cursorPosition < tagEnd)) {
-            event.preventDefault();
-            this.$set(item.randomValueList, randomIndex, value.replace(tag, ''));
-            if (tag === '#销售称呼#') {
-              this.$set(item.randomSalesCallAdded, randomIndex, false);
-            }
-            if (tag === '#客户称呼#') {
-              this.$set(item.randomUserNameCallAdded, randomIndex, false);
-            }
-          }
-        }
-      });
-    },
     prepareSettingForSave(item) {
       if (!item) {
         return;

+ 9 - 113
src/views/qw/sopUserLogsInfo/sopUserLogsInfoDetails.vue

@@ -344,44 +344,13 @@
                         </span>
                       </div>
 
-                      <div v-if="item.isRandomSend == 1">
-                        <div
-                          v-for="(randomValue, randomIndex) in item.randomValueList"
-                          :key="randomIndex"
-                          style="margin-bottom: 12px; padding: 10px; border: 1px dashed #dcdfe6; border-radius: 4px;"
-                        >
-                          <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
-                            <span style="color: #606266; font-size: 13px;">文案 {{ randomIndex + 1 }}</span>
-                            <el-button
-                              v-if="item.randomValueList.length > 2"
-                              type="text"
-                              icon="el-icon-delete"
-                              style="color: #f56c6c;"
-                              @click="removeRandomValue(item, randomIndex)"
-                            />
-                          </div>
-                          <el-input
-                            v-model="item.randomValueList[randomIndex]"
-                            type="textarea"
-                            :rows="3"
-                            placeholder="请输入文案内容"
-                            style="width: 90%;"
-                            @keydown.native="handleRandomKeydown($event, index, randomIndex)"
-                            :ref="`textarea-${index}-${randomIndex}`"
-                          />
-                          <div style="margin-top: 8px;">
-                            <el-link type="primary" @click="toggleRandomSalesCall(index, randomIndex)">
-                              {{ item.randomSalesCallAdded && item.randomSalesCallAdded[randomIndex] ? '移除#客服称呼#' : '添加#客服称呼#' }}
-                            </el-link>
-                            <el-link type="primary" style="margin-left: 20px;" @click="toggleRandomUserNameCall(index, randomIndex)">
-                              {{ item.randomUserNameCallAdded && item.randomUserNameCallAdded[randomIndex] ? '移除#客户称呼#' : '添加#客户称呼#' }}
-                            </el-link>
-                          </div>
-                        </div>
-                        <el-link type="primary" icon="el-icon-plus" :underline="false" @click="addRandomValue(item)">
-                          添加文案
-                        </el-link>
-                      </div>
+                      <RandomTextListEditor
+                        v-if="item.isRandomSend == 1"
+                        :item="item"
+                        :ref-id="`setting-${index}`"
+                        sales-call-tag="#客服称呼#"
+                        user-call-tag="#客户称呼#"
+                      />
 
                       <template v-else>
                         <el-input
@@ -735,12 +704,13 @@ import {listTag} from "@/api/qw/tag";
 import {searchTags} from "../../../api/qw/tag";
 import userVideo from "@/views/qw/userVideo/userVideo.vue";
 import PaginationMore from "../../../components/PaginationMore/index.vue";
+import RandomTextListEditor from "@/components/RandomTextListEditor/index.vue";
 import {listToLiveNoEnd} from "@/api/live/live";
 import {listReward} from "@/api/qw/luckyBag";
 
 export default {
   name: "sopUserLogsInfoDetails",
-  components: {PaginationMore, userVideo, ImageUpload},
+  components: {PaginationMore, userVideo, ImageUpload, RandomTextListEditor},
   data() {
     return {
       selectExTags: [], // 排除标签选择数组
@@ -1511,80 +1481,6 @@ export default {
         this.$set(item, 'value', firstValue);
       }
     },
-    addRandomValue(item) {
-      this.initRandomSendFields(item);
-      item.randomValueList.push('');
-      item.randomSalesCallAdded.push(false);
-      item.randomUserNameCallAdded.push(false);
-    },
-    removeRandomValue(item, randomIndex) {
-      if (!item.randomValueList || item.randomValueList.length <= 2) {
-        return;
-      }
-      item.randomValueList.splice(randomIndex, 1);
-      item.randomSalesCallAdded.splice(randomIndex, 1);
-      item.randomUserNameCallAdded.splice(randomIndex, 1);
-    },
-    toggleRandomSalesCall(index, randomIndex) {
-      const item = this.setting[index];
-      this.initRandomSendFields(item);
-      const salesCall = '#客服称呼#';
-      const textarea = this.$refs[`textarea-${index}-${randomIndex}`] && this.$refs[`textarea-${index}-${randomIndex}`][0]?.$refs?.textarea;
-      const cursorPosition = textarea ? textarea.selectionStart : (item.randomValueList[randomIndex] || '').length;
-
-      if (item.randomSalesCallAdded[randomIndex]) {
-        this.$set(item.randomValueList, randomIndex, (item.randomValueList[randomIndex] || '').replace(new RegExp(salesCall, 'g'), ''));
-        this.$set(item.randomSalesCallAdded, randomIndex, false);
-      } else {
-        const currentValue = item.randomValueList[randomIndex] || '';
-        this.$set(item.randomValueList, randomIndex, currentValue.slice(0, cursorPosition) + salesCall + currentValue.slice(cursorPosition));
-        this.$set(item.randomSalesCallAdded, randomIndex, true);
-      }
-    },
-    toggleRandomUserNameCall(index, randomIndex) {
-      const item = this.setting[index];
-      this.initRandomSendFields(item);
-      const userCall = '#客户称呼#';
-      const textarea = this.$refs[`textarea-${index}-${randomIndex}`] && this.$refs[`textarea-${index}-${randomIndex}`][0]?.$refs?.textarea;
-      const cursorPosition = textarea ? textarea.selectionStart : (item.randomValueList[randomIndex] || '').length;
-
-      if (item.randomUserNameCallAdded[randomIndex]) {
-        this.$set(item.randomValueList, randomIndex, (item.randomValueList[randomIndex] || '').replace(new RegExp(userCall, 'g'), ''));
-        this.$set(item.randomUserNameCallAdded, randomIndex, false);
-      } else {
-        const currentValue = item.randomValueList[randomIndex] || '';
-        this.$set(item.randomValueList, randomIndex, currentValue.slice(0, cursorPosition) + userCall + currentValue.slice(cursorPosition));
-        this.$set(item.randomUserNameCallAdded, randomIndex, true);
-      }
-    },
-    handleRandomKeydown(event, index, randomIndex) {
-      if (event.key !== 'Backspace' && event.key !== 'Delete') {
-        return;
-      }
-      const item = this.setting[index];
-      this.initRandomSendFields(item);
-      const value = item.randomValueList[randomIndex] || '';
-      const cursorPosition = event.target.selectionStart;
-      const tags = ['#客服称呼#', '#客户称呼#'];
-
-      tags.forEach(tag => {
-        const tagIndex = value.indexOf(tag);
-        if (tagIndex !== -1) {
-          const tagEnd = tagIndex + tag.length;
-          if ((event.key === 'Backspace' && cursorPosition > tagIndex && cursorPosition <= tagEnd) ||
-            (event.key === 'Delete' && cursorPosition >= tagIndex && cursorPosition < tagEnd)) {
-            event.preventDefault();
-            this.$set(item.randomValueList, randomIndex, value.replace(tag, ''));
-            if (tag === '#客服称呼#') {
-              this.$set(item.randomSalesCallAdded, randomIndex, false);
-            }
-            if (tag === '#客户称呼#') {
-              this.$set(item.randomUserNameCallAdded, randomIndex, false);
-            }
-          }
-        }
-      });
-    },
     prepareSettingForSave(item) {
       if (!item) {
         return;