Bladeren bron

个微SOP

吴树波 1 dag geleden
bovenliggende
commit
eef4b8122c

+ 1 - 0
.cursorignore

@@ -0,0 +1 @@
+# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)

+ 53 - 0
src/api/wx/wxSop.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询个微SOP列表
+export function listWxSop(query) {
+  return request({
+    url: '/wx/wxSop/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询个微SOP详细
+export function getWxSop(id) {
+  return request({
+    url: '/wx/wxSop/' + id,
+    method: 'get'
+  })
+}
+
+// 新增个微SOP
+export function addWxSop(data) {
+  return request({
+    url: '/wx/wxSop',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改个微SOP
+export function updateWxSop(data) {
+  return request({
+    url: '/wx/wxSop',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除个微SOP
+export function delWxSop(id) {
+  return request({
+    url: '/wx/wxSop/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出个微SOP
+export function exportWxSop(query) {
+  return request({
+    url: '/wx/wxSop/export',
+    method: 'get',
+    params: query
+  })
+}

+ 53 - 0
src/api/wx/wxSopLogs.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询个微发送记录列表
+export function listWxSopLogs(query) {
+  return request({
+    url: '/wx/wxSopLogs/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询个微发送记录详细
+export function getWxSopLogs(id) {
+  return request({
+    url: '/wx/wxSopLogs/' + id,
+    method: 'get'
+  })
+}
+
+// 新增个微发送记录
+export function addWxSopLogs(data) {
+  return request({
+    url: '/wx/wxSopLogs',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改个微发送记录
+export function updateWxSopLogs(data) {
+  return request({
+    url: '/wx/wxSopLogs',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除个微发送记录
+export function delWxSopLogs(id) {
+  return request({
+    url: '/wx/wxSopLogs/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出个微发送记录
+export function exportWxSopLogs(query) {
+  return request({
+    url: '/wx/wxSopLogs/export',
+    method: 'get',
+    params: query
+  })
+}

+ 53 - 0
src/api/wx/wxSopUser.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询个微营期列表
+export function listWxSopUser(query) {
+  return request({
+    url: '/wx/wxSopUser/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询个微营期详细
+export function getWxSopUser(id) {
+  return request({
+    url: '/wx/wxSopUser/' + id,
+    method: 'get'
+  })
+}
+
+// 新增个微营期
+export function addWxSopUser(data) {
+  return request({
+    url: '/wx/wxSopUser',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改个微营期
+export function updateWxSopUser(data) {
+  return request({
+    url: '/wx/wxSopUser',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除个微营期
+export function delWxSopUser(id) {
+  return request({
+    url: '/wx/wxSopUser/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出个微营期
+export function exportWxSopUser(query) {
+  return request({
+    url: '/wx/wxSopUser/export',
+    method: 'get',
+    params: query
+  })
+}

+ 53 - 0
src/api/wx/wxSopUserInfo.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询个微营期详情列表
+export function listWxSopUserInfo(query) {
+  return request({
+    url: '/wx/wxSopUserInfo/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询个微营期详情详细
+export function getWxSopUserInfo(id) {
+  return request({
+    url: '/wx/wxSopUserInfo/' + id,
+    method: 'get'
+  })
+}
+
+// 新增个微营期详情
+export function addWxSopUserInfo(data) {
+  return request({
+    url: '/wx/wxSopUserInfo',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改个微营期详情
+export function updateWxSopUserInfo(data) {
+  return request({
+    url: '/wx/wxSopUserInfo',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除个微营期详情
+export function delWxSopUserInfo(id) {
+  return request({
+    url: '/wx/wxSopUserInfo/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出个微营期详情
+export function exportWxSopUserInfo(query) {
+  return request({
+    url: '/wx/wxSopUserInfo/export',
+    method: 'get',
+    params: query
+  })
+}

+ 678 - 0
src/views/wx/wxSop/index.vue

@@ -0,0 +1,678 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="名称" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="筛选方式" prop="filterType">
+        <el-select
+          v-model="queryParams.filterType"
+          placeholder="请选择筛选方式"
+          clearable
+          size="small"
+        >
+          <el-option
+            v-for="item in filterTypeOptions"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="模板" prop="tempId">
+        <el-select
+          v-model="queryParams.tempId"
+          placeholder="请选择模板"
+          clearable
+          filterable
+          size="small"
+          style="width: 200px;"
+        >
+          <el-option
+            v-for="item in tempList"
+            :key="item.id"
+            :label="item.name"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="固定营期" prop="isFixed">
+        <el-select
+          v-model="queryParams.isFixed"
+          placeholder="请选择固定营期"
+          clearable
+          size="small"
+        >
+          <el-option
+            v-for="item in isFixedOptions"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="营期时间" prop="startTime">
+        <el-date-picker clearable size="small"
+          v-model="queryParams.startTime"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择营期时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['wx:wxSop:add']"
+        >新增</el-button>
+      </el-col>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="wxSopList" @selection-change="handleSelectionChange">
+      <el-table-column label="id" align="center" prop="id" />
+      <el-table-column label="名称" align="center" prop="name" />
+      <el-table-column label="筛选方式" align="center" prop="filterType">
+        <template slot-scope="scope">
+          <dict-tag :options="filterTypeOptions" :value="scope.row.filterType" />
+        </template>
+      </el-table-column>
+      <el-table-column label="模板" align="center" prop="tempId">
+        <template slot-scope="scope">
+          <span
+            v-for="item in tempList"
+            :key="item.id"
+            v-if="String(item.id) === String(scope.row.tempId)"
+          >
+            {{ item.name }}
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column label="是否固定营期" align="center" prop="isFixed">
+        <template slot-scope="scope">
+          <dict-tag :options="isFixedOptions" :value="scope.row.isFixed" />
+        </template>
+      </el-table-column>
+      <el-table-column label="过期时间(小时)" align="center" prop="expiryTime">
+        <template slot-scope="scope">
+          {{ scope.row.expiryTime }} 小时
+        </template>
+      </el-table-column>
+      <el-table-column label="营期开始时间" align="center" prop="startTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['wx:wxSop:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['wx:wxSop:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改个微SOP对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="70%" append-to-body class="wx-sop-dialog">
+      <div class="wx-sop-dialog-content">
+        <el-form ref="form" :model="form" :rules="rules" label-width="130px" class="wx-sop-form">
+          <!-- 基本信息 -->
+          <div class="form-section">
+            <div class="section-title">
+              <i class="el-icon-document"></i>
+              <span>基本信息</span>
+            </div>
+            <el-form-item label="规则名称" prop="name">
+              <el-input v-model="form.name" placeholder="请输入规则名称" clearable maxlength="50" show-word-limit />
+            </el-form-item>
+            <el-form-item label="模板" prop="tempId">
+              <el-select
+                v-model="form.tempId"
+                placeholder="请选择模板"
+                filterable
+                clearable
+                style="width: 100%;"
+                v-loading="tempListLoading"
+                @focus="loadTempList"
+              >
+                <el-option
+                  v-for="item in tempList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+              <Tip title="选择想要发送的模板规则" />
+            </el-form-item>
+          </div>
+
+          <!-- 筛选规则 -->
+          <div class="form-section">
+            <div class="section-title">
+              <i class="el-icon-s-operation"></i>
+              <span>筛选规则</span>
+            </div>
+            <el-form-item label="筛选方式" prop="filterType">
+              <el-radio-group v-model="form.filterType">
+                <el-radio :label="'0'">按标签</el-radio>
+                <el-radio :label="'1'">按群聊</el-radio>
+              </el-radio-group>
+              <Tip :title="'标签:按标签筛选客户进入SOP;群聊:按群聊筛选进入SOP'" />
+            </el-form-item>
+            <template v-if="form.filterType === '0'">
+              <el-form-item label="选择的标签" prop="selectTags">
+                <el-select
+                  v-model="selectTagsList"
+                  multiple
+                  filterable
+                  clearable
+                  placeholder="请选择进入SOP的标签"
+                  style="width: 100%;"
+                  @change="onSelectTagsChange"
+                >
+                  <el-option
+                    v-for="item in tagsOptions"
+                    :key="item.dictValue"
+                    :label="item.dictLabel"
+                    :value="item.dictValue"
+                  />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="排除的标签" prop="excludeTags">
+                <el-select
+                  v-model="excludeTagsList"
+                  multiple
+                  filterable
+                  clearable
+                  placeholder="请选择不想进入SOP的标签"
+                  style="width: 100%;"
+                  @change="onExcludeTagsChange"
+                >
+                  <el-option
+                    v-for="item in tagsOptions"
+                    :key="item.dictValue"
+                    :label="item.dictLabel"
+                    :value="item.dictValue"
+                  />
+                </el-select>
+              </el-form-item>
+            </template>
+            <el-form-item label="执行账号" prop="accountIds">
+              <el-button
+                type="primary"
+                plain
+                icon="el-icon-user"
+                size="small"
+                @click="openQwUserSelect"
+              >
+                选择账号
+                <el-tag v-if="selectedQwUsers && selectedQwUsers.length" type="success" size="mini" style="margin-left: 6px;">
+                  {{ selectedQwUsers.length }}
+                </el-tag>
+              </el-button>
+              <div v-if="selectedQwUsers && selectedQwUsers.length" class="selected-tags">
+                <el-tag
+                  v-for="item in selectedQwUsers"
+                  :key="item.id"
+                  size="mini"
+                  closable
+                  @close="removeQwUser(item)"
+                >
+                  {{ item.wxNickName || item.wxNo || item.companyUserName }}
+                </el-tag>
+              </div>
+              <Tip title="从企微账号列表中选择用于执行个微SOP的账号" />
+            </el-form-item>
+          </div>
+
+          <!-- 时间与营期 -->
+          <div class="form-section">
+            <div class="section-title">
+              <i class="el-icon-time"></i>
+              <span>时间与营期</span>
+            </div>
+            <el-row :gutter="24">
+              <el-col :span="12">
+                <el-form-item label="开始时间" prop="startTime">
+                  <el-date-picker
+                    clearable
+                    size="small"
+                    v-model="form.startTime"
+                    type="date"
+                    value-format="yyyy-MM-dd"
+                    placeholder="选择开始时间"
+                    style="width: 100%;"
+                  />
+                  <Tip title="SOP开始发送时间" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="过期时间" prop="expiryTime">
+                  <div class="expiry-time-wrap">
+                    <el-input-number
+                      v-model="form.expiryTime"
+                      :min="1"
+                      :max="100"
+                      controls-position="right"
+                      style="flex: 1;"
+                    />
+                    <span class="unit-text">小时</span>
+                  </div>
+                  <Tip title="待发送消息超过此时间未发送将自动作废" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-form-item label="固定营期" prop="isFixed">
+              <el-radio-group v-model="form.isFixed">
+                <el-radio :label="'0'">否</el-radio>
+                <el-radio :label="'1'">是</el-radio>
+              </el-radio-group>
+              <Tip title="固定营期:不管什么时候进入SOP的客户,营期时间都为任务开始时间" />
+            </el-form-item>
+          </div>
+
+          <!-- 其他 -->
+          <div class="form-section">
+            <div class="section-title">
+              <i class="el-icon-edit-outline"></i>
+              <span>其他</span>
+            </div>
+            <el-form-item label="备注" prop="remark">
+              <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注(选填)" maxlength="200" show-word-limit />
+            </el-form-item>
+          </div>
+        </el-form>
+      </div>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="cancel">取 消</el-button>
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+      </div>
+
+      <qw-user-select @success="selectQwUserFun" ref="qwUserSelect" />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listWxSop, getWxSop, delWxSop, addWxSop, updateWxSop, exportWxSop } from "@/api/wx/wxSop";
+import { listSopTemp } from "@/api/qw/sopTemp";
+import Tip from "@/components/Tip";
+import QwUserSelect from "@/views/components/QwUserSelect.vue";
+
+export default {
+  name: "WxSop",
+  components: { Tip, QwUserSelect },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 模板下拉数据
+      tempList: [],
+      tempListLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 个微SOP表格数据
+      wxSopList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 选择的企微账号
+      selectedQwUsers: [],
+      // 标签库(与线索客户打标签共用 crm_customer_tag)
+      tagsOptions: [],
+      selectTagsList: [],
+      excludeTagsList: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        name: null,
+        filterType: null,
+        selectTags: null,
+        excludeTags: null,
+        tempId: null,
+        isFixed: null,
+        expiryTime: null,
+        startTime: null,
+      },
+      // 本地字典(筛选方式、是否固定营期)
+      filterTypeOptions: [
+        { dictValue: "0", dictLabel: "按标签筛选" },
+        { dictValue: "1", dictLabel: "按群聊筛选" },
+      ],
+      isFixedOptions: [
+        { dictValue: "0", dictLabel: "否" },
+        { dictValue: "1", dictLabel: "是" },
+      ],
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        name: [{ required: true, message: "请输入规则名称", trigger: "blur" }],
+        tempId: [{ required: true, message: "请选择模板", trigger: "change" }]
+      }
+    };
+  },
+  created() {
+    this.getList();
+    this.loadTempList();
+    this.getDicts("crm_customer_tag").then((response) => {
+      this.tagsOptions = response.data || [];
+    });
+  },
+  methods: {
+    loadTempList() {
+      this.tempListLoading = true;
+      listSopTemp()
+        .then(response => {
+          this.tempList = response.rows || [];
+        })
+        .finally(() => {
+          this.tempListLoading = false;
+        });
+    },
+    onSelectTagsChange(val) {
+      this.form.selectTags = Array.isArray(val) ? val.join(",") : "";
+    },
+    onExcludeTagsChange(val) {
+      this.form.excludeTags = Array.isArray(val) ? val.join(",") : "";
+    },
+    syncTagsFromForm() {
+      this.selectTagsList = this.form.selectTags ? String(this.form.selectTags).split(",").filter(Boolean) : [];
+      this.excludeTagsList = this.form.excludeTags ? String(this.form.excludeTags).split(",").filter(Boolean) : [];
+    },
+    /** 查询个微SOP列表 */
+    getList() {
+      this.loading = true;
+      listWxSop(this.queryParams).then(response => {
+        this.wxSopList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        name: null,
+        filterType: "0",
+        selectTags: null,
+        excludeTags: null,
+        tempId: null,
+        tempName: null,
+        isFixed: "0",
+        accountIds: null,
+        expiryTime: 24,
+        startTime: null,
+        createTime: null,
+        createBy: null,
+        updateTime: null,
+        updateBy: null,
+        remark: null
+      };
+      this.selectTagsList = [];
+      this.excludeTagsList = [];
+      this.resetForm("form");
+    },
+    // 打开企微账号选择
+    openQwUserSelect() {
+      this.$nextTick(() => {
+        this.$refs.qwUserSelect && this.$refs.qwUserSelect.setRows(this.selectedQwUsers);
+      });
+    },
+    // 选择企微账号回调(数据源与外呼任务分配账号一致)
+    selectQwUserFun(e) {
+      this.selectedQwUsers = e.rows || [];
+      const ids = e.ids || [];
+      this.form.accountIds = ids.join(",");
+      this.$forceUpdate();
+    },
+    removeQwUser(item) {
+      this.selectedQwUsers = this.selectedQwUsers.filter((u) => u.id !== item.id);
+      this.form.accountIds = this.selectedQwUsers.map((u) => u.id).join(",");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加个微SOP";
+      this.loadTempList();
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getWxSop(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改个微SOP";
+        // 兼容后端返回 number,确保 radio 正常回显
+        this.form.filterType =
+          this.form.filterType === null || typeof this.form.filterType === "undefined"
+            ? "0"
+            : String(this.form.filterType);
+        this.form.isFixed =
+          this.form.isFixed === null || typeof this.form.isFixed === "undefined"
+            ? "0"
+            : String(this.form.isFixed);
+        this.syncTagsFromForm();
+        // 回显执行账号
+        this.selectedQwUsers = response.data.selectedQwUsers || [];
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          const payload = this.buildSubmitPayload();
+          if (this.form.id != null) {
+            updateWxSop(payload).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addWxSop(payload).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 构建提交数据,确保与后端 WxSop 字段一致 */
+    buildSubmitPayload() {
+      const f = this.form;
+      return {
+        id: f.id,
+        name: f.name,
+        filterType: f.filterType != null ? parseInt(f.filterType, 10) : 0,
+        selectTags: f.selectTags || null,
+        excludeTags: f.excludeTags || null,
+        tempId: f.tempId != null ? String(f.tempId) : null,
+        isFixed: f.isFixed != null ? parseInt(f.isFixed, 10) : 0,
+        expiryTime: f.expiryTime != null ? parseInt(f.expiryTime, 10) : 24,
+        startTime: f.startTime || null,
+        remark: f.remark || null,
+        accountIds: f.accountIds || null
+      };
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除个微SOP编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delWxSop(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有个微SOP数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportWxSop(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.wx-sop-dialog-content {
+  padding: 8px 16px 24px;
+  max-height: 70vh;
+  overflow-y: auto;
+}
+
+.wx-sop-form {
+  .form-section {
+    background: #fafbfc;
+    border-radius: 10px;
+    padding: 24px 28px;
+    margin-bottom: 24px;
+    border: 1px solid #ebeef5;
+  }
+
+  .form-section:last-of-type {
+    margin-bottom: 0;
+  }
+
+  .section-title {
+    display: flex;
+    align-items: center;
+    gap: 10px;
+    font-size: 15px;
+    font-weight: 600;
+    color: #303133;
+    margin-bottom: 20px;
+    padding-bottom: 14px;
+    border-bottom: 1px solid #e4e7ed;
+  }
+
+  .section-title i {
+    font-size: 18px;
+    color: #409eff;
+  }
+
+  ::v-deep .el-form-item {
+    margin-bottom: 22px;
+  }
+
+  ::v-deep .el-form-item:last-child {
+    margin-bottom: 0;
+  }
+
+  .expiry-time-wrap {
+    display: flex;
+    align-items: center;
+    gap: 10px;
+    width: 100%;
+  }
+
+  .unit-text {
+    color: #909399;
+    font-size: 13px;
+    flex-shrink: 0;
+  }
+
+  .selected-tags {
+    margin-top: 12px;
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px;
+  }
+
+  .selected-tags .el-tag {
+    margin: 0;
+  }
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 16px;
+  padding: 16px 0 8px;
+}
+</style>

+ 481 - 0
src/views/wx/wxSopLogs/index.vue

@@ -0,0 +1,481 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="消息类型0个人1群" prop="type">
+        <el-select v-model="queryParams.type" placeholder="请选择消息类型0个人1群" clearable size="small">
+          <el-option label="请选择字典生成" value="" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="任务ID" prop="sopId">
+        <el-input
+          v-model="queryParams.sopId"
+          placeholder="请输入任务ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="营期ID" prop="sopUserId">
+        <el-input
+          v-model="queryParams.sopUserId"
+          placeholder="请输入营期ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="发送类型" prop="sendType">
+        <el-select v-model="queryParams.sendType" placeholder="请选择发送类型" clearable size="small">
+          <el-option label="请选择字典生成" value="" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="生成类型(0自动1手动)" prop="generateType">
+        <el-select v-model="queryParams.generateType" placeholder="请选择生成类型(0自动1手动)" clearable size="small">
+          <el-option label="请选择字典生成" value="" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="发送账号ID" prop="accountId">
+        <el-input
+          v-model="queryParams.accountId"
+          placeholder="请输入发送账号ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="发送对象ID" prop="wxContactId">
+        <el-input
+          v-model="queryParams.wxContactId"
+          placeholder="请输入发送对象ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="发送对象名称" prop="wxContactName">
+        <el-input
+          v-model="queryParams.wxContactName"
+          placeholder="请输入发送对象名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="发送群聊ID" prop="wxRoomId">
+        <el-input
+          v-model="queryParams.wxRoomId"
+          placeholder="请输入发送群聊ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="发送群聊名称" prop="wxRoomName">
+        <el-input
+          v-model="queryParams.wxRoomName"
+          placeholder="请输入发送群聊名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="小程序ID" prop="fsUserId">
+        <el-input
+          v-model="queryParams.fsUserId"
+          placeholder="请输入小程序ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="发送状态0待发送1发送成功2发送失败3消息作废" prop="sendStatus">
+        <el-select v-model="queryParams.sendStatus" placeholder="请选择发送状态0待发送1发送成功2发送失败3消息作废" clearable size="small">
+          <el-option label="请选择字典生成" value="" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="发送备注" prop="sendRemark">
+        <el-input
+          v-model="queryParams.sendRemark"
+          placeholder="请输入发送备注"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="发送排序" prop="sendSort">
+        <el-input
+          v-model="queryParams.sendSort"
+          placeholder="请输入发送排序"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="消息过期时间" prop="expirationTime">
+        <el-date-picker clearable size="small"
+          v-model="queryParams.expirationTime"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择消息过期时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['wx:wxSopLogs:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['wx:wxSopLogs:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['wx:wxSopLogs:remove']"
+        >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['wx:wxSopLogs:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="wxSopLogsList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="id" align="center" prop="id" />
+      <el-table-column label="消息类型0个人1群" align="center" prop="type" />
+      <el-table-column label="任务ID" align="center" prop="sopId" />
+      <el-table-column label="营期ID" align="center" prop="sopUserId" />
+      <el-table-column label="发送类型" align="center" prop="sendType" />
+      <el-table-column label="生成类型(0自动1手动)" align="center" prop="generateType" />
+      <el-table-column label="发送账号ID" align="center" prop="accountId" />
+      <el-table-column label="发送对象ID" align="center" prop="wxContactId" />
+      <el-table-column label="发送对象名称" align="center" prop="wxContactName" />
+      <el-table-column label="发送群聊ID" align="center" prop="wxRoomId" />
+      <el-table-column label="发送群聊名称" align="center" prop="wxRoomName" />
+      <el-table-column label="小程序ID" align="center" prop="fsUserId" />
+      <el-table-column label="发送状态0待发送1发送成功2发送失败3消息作废" align="center" prop="sendStatus" />
+      <el-table-column label="发送备注" align="center" prop="sendRemark" />
+      <el-table-column label="发送排序" align="center" prop="sendSort" />
+      <el-table-column label="消息过期时间" align="center" prop="expirationTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.expirationTime, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['wx:wxSopLogs:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['wx:wxSopLogs:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改个微发送记录对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="消息类型0个人1群" prop="type">
+          <el-select v-model="form.type" placeholder="请选择消息类型0个人1群">
+            <el-option label="请选择字典生成" value="" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="任务ID" prop="sopId">
+          <el-input v-model="form.sopId" placeholder="请输入任务ID" />
+        </el-form-item>
+        <el-form-item label="营期ID" prop="sopUserId">
+          <el-input v-model="form.sopUserId" placeholder="请输入营期ID" />
+        </el-form-item>
+        <el-form-item label="发送类型" prop="sendType">
+          <el-select v-model="form.sendType" placeholder="请选择发送类型">
+            <el-option label="请选择字典生成" value="" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="生成类型(0自动1手动)" prop="generateType">
+          <el-select v-model="form.generateType" placeholder="请选择生成类型(0自动1手动)">
+            <el-option label="请选择字典生成" value="" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="发送账号ID" prop="accountId">
+          <el-input v-model="form.accountId" placeholder="请输入发送账号ID" />
+        </el-form-item>
+        <el-form-item label="发送对象ID" prop="wxContactId">
+          <el-input v-model="form.wxContactId" placeholder="请输入发送对象ID" />
+        </el-form-item>
+        <el-form-item label="发送对象名称" prop="wxContactName">
+          <el-input v-model="form.wxContactName" placeholder="请输入发送对象名称" />
+        </el-form-item>
+        <el-form-item label="发送群聊ID" prop="wxRoomId">
+          <el-input v-model="form.wxRoomId" placeholder="请输入发送群聊ID" />
+        </el-form-item>
+        <el-form-item label="发送群聊名称" prop="wxRoomName">
+          <el-input v-model="form.wxRoomName" placeholder="请输入发送群聊名称" />
+        </el-form-item>
+        <el-form-item label="小程序ID" prop="fsUserId">
+          <el-input v-model="form.fsUserId" placeholder="请输入小程序ID" />
+        </el-form-item>
+        <el-form-item label="发送状态0待发送1发送成功2发送失败3消息作废">
+          <el-radio-group v-model="form.sendStatus">
+            <el-radio label="1">请选择字典生成</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="发送备注" prop="sendRemark">
+          <el-input v-model="form.sendRemark" placeholder="请输入发送备注" />
+        </el-form-item>
+        <el-form-item label="发送排序" prop="sendSort">
+          <el-input v-model="form.sendSort" placeholder="请输入发送排序" />
+        </el-form-item>
+        <el-form-item label="消息过期时间" prop="expirationTime">
+          <el-date-picker clearable size="small"
+            v-model="form.expirationTime"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择消息过期时间">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" placeholder="请输入备注" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listWxSopLogs, getWxSopLogs, delWxSopLogs, addWxSopLogs, updateWxSopLogs, exportWxSopLogs } from "@/api/wx/wxSopLogs";
+
+export default {
+  name: "WxSopLogs",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 个微发送记录表格数据
+      wxSopLogsList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        type: null,
+        sopId: null,
+        sopUserId: null,
+        sendType: null,
+        generateType: null,
+        accountId: null,
+        wxContactId: null,
+        wxContactName: null,
+        wxRoomId: null,
+        wxRoomName: null,
+        fsUserId: null,
+        sendStatus: null,
+        sendRemark: null,
+        sendSort: null,
+        expirationTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询个微发送记录列表 */
+    getList() {
+      this.loading = true;
+      listWxSopLogs(this.queryParams).then(response => {
+        this.wxSopLogsList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        type: null,
+        sopId: null,
+        sopUserId: null,
+        sendType: null,
+        generateType: null,
+        accountId: null,
+        wxContactId: null,
+        wxContactName: null,
+        wxRoomId: null,
+        wxRoomName: null,
+        fsUserId: null,
+        sendStatus: 0,
+        sendRemark: null,
+        sendSort: null,
+        expirationTime: null,
+        createTime: null,
+        createBy: null,
+        updateTime: null,
+        updateBy: null,
+        remark: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加个微发送记录";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getWxSopLogs(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改个微发送记录";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateWxSopLogs(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addWxSopLogs(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除个微发送记录编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delWxSopLogs(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有个微发送记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportWxSopLogs(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>

+ 350 - 0
src/views/wx/wxSopUser/index.vue

@@ -0,0 +1,350 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="类型(0个人1群聊)" prop="type">
+        <el-select v-model="queryParams.type" placeholder="请选择类型(0个人1群聊)" clearable size="small">
+          <el-option label="请选择字典生成" value="" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="任务ID" prop="sopId">
+        <el-input
+          v-model="queryParams.sopId"
+          placeholder="请输入任务ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="个微账号ID" prop="accountId">
+        <el-input
+          v-model="queryParams.accountId"
+          placeholder="请输入个微账号ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="营期时间" prop="startTime">
+        <el-date-picker clearable size="small"
+          v-model="queryParams.startTime"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择营期时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="群聊ID" prop="chatId">
+        <el-input
+          v-model="queryParams.chatId"
+          placeholder="请输入群聊ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
+          <el-option label="请选择字典生成" value="" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['wx:wxSopUser:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['wx:wxSopUser:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['wx:wxSopUser:remove']"
+        >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['wx:wxSopUser:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="wxSopUserList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="id" align="center" prop="id" />
+      <el-table-column label="类型(0个人1群聊)" align="center" prop="type" />
+      <el-table-column label="任务ID" align="center" prop="sopId" />
+      <el-table-column label="个微账号ID" align="center" prop="accountId" />
+      <el-table-column label="营期时间" align="center" prop="startTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="群聊ID" align="center" prop="chatId" />
+      <el-table-column label="状态" align="center" prop="status" />
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['wx:wxSopUser:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['wx:wxSopUser:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改个微营期对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="类型(0个人1群聊)" prop="type">
+          <el-select v-model="form.type" placeholder="请选择类型(0个人1群聊)">
+            <el-option label="请选择字典生成" value="" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="任务ID" prop="sopId">
+          <el-input v-model="form.sopId" placeholder="请输入任务ID" />
+        </el-form-item>
+        <el-form-item label="个微账号ID" prop="accountId">
+          <el-input v-model="form.accountId" placeholder="请输入个微账号ID" />
+        </el-form-item>
+        <el-form-item label="营期时间" prop="startTime">
+          <el-date-picker clearable size="small"
+            v-model="form.startTime"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择营期时间">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="群聊ID" prop="chatId">
+          <el-input v-model="form.chatId" placeholder="请输入群聊ID" />
+        </el-form-item>
+        <el-form-item label="状态">
+          <el-radio-group v-model="form.status">
+            <el-radio label="1">请选择字典生成</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" placeholder="请输入备注" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listWxSopUser, getWxSopUser, delWxSopUser, addWxSopUser, updateWxSopUser, exportWxSopUser } from "@/api/wx/wxSopUser";
+
+export default {
+  name: "WxSopUser",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 个微营期表格数据
+      wxSopUserList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        type: null,
+        sopId: null,
+        accountId: null,
+        startTime: null,
+        chatId: null,
+        status: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询个微营期列表 */
+    getList() {
+      this.loading = true;
+      listWxSopUser(this.queryParams).then(response => {
+        this.wxSopUserList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        type: null,
+        sopId: null,
+        accountId: null,
+        startTime: null,
+        chatId: null,
+        status: 0,
+        createTime: null,
+        createBy: null,
+        updateTime: null,
+        updateBy: null,
+        remark: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加个微营期";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getWxSopUser(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改个微营期";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateWxSopUser(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addWxSopUser(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除个微营期编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delWxSopUser(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有个微营期数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportWxSopUser(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>

+ 412 - 0
src/views/wx/wxSopUserInfo/index.vue

@@ -0,0 +1,412 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="任务ID" prop="sopId">
+        <el-input
+          v-model="queryParams.sopId"
+          placeholder="请输入任务ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="营期ID" prop="sopUserId">
+        <el-input
+          v-model="queryParams.sopUserId"
+          placeholder="请输入营期ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="联系人ID" prop="wxContactId">
+        <el-input
+          v-model="queryParams.wxContactId"
+          placeholder="请输入联系人ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="小程序ID" prop="fsUserId">
+        <el-input
+          v-model="queryParams.fsUserId"
+          placeholder="请输入小程序ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="是否7天都没有看课 0否 1是" prop="isDaysNotStudy">
+        <el-input
+          v-model="queryParams.isDaysNotStudy"
+          placeholder="请输入是否7天都没有看课 0否 1是"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="总完课天数" prop="finishCout">
+        <el-input
+          v-model="queryParams.finishCout"
+          placeholder="请输入总完课天数"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="最近完课时间" prop="finishTime">
+        <el-date-picker clearable size="small"
+          v-model="queryParams.finishTime"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择最近完课时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="连续完课天数" prop="finishCourseDays">
+        <el-input
+          v-model="queryParams.finishCourseDays"
+          placeholder="请输入连续完课天数"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="客户评级的等级" prop="grade">
+        <el-input
+          v-model="queryParams.grade"
+          placeholder="请输入客户评级的等级"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="禁用状态 0 正常 1禁用" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择禁用状态 0 正常 1禁用" clearable size="small">
+          <el-option label="请选择字典生成" value="" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['wx:wxSopUserInfo:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['wx:wxSopUserInfo:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['wx:wxSopUserInfo:remove']"
+        >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['wx:wxSopUserInfo:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="wxSopUserInfoList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="id" align="center" prop="id" />
+      <el-table-column label="任务ID" align="center" prop="sopId" />
+      <el-table-column label="营期ID" align="center" prop="sopUserId" />
+      <el-table-column label="联系人ID" align="center" prop="wxContactId" />
+      <el-table-column label="小程序ID" align="center" prop="fsUserId" />
+      <el-table-column label="是否7天都没有看课 0否 1是" align="center" prop="isDaysNotStudy" />
+      <el-table-column label="总完课天数" align="center" prop="finishCout" />
+      <el-table-column label="最近完课时间" align="center" prop="finishTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.finishTime, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="连续完课天数" align="center" prop="finishCourseDays" />
+      <el-table-column label="客户评级的等级" align="center" prop="grade" />
+      <el-table-column label="禁用状态 0 正常 1禁用" align="center" prop="status" />
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['wx:wxSopUserInfo:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['wx:wxSopUserInfo:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改个微营期详情对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="任务ID" prop="sopId">
+          <el-input v-model="form.sopId" placeholder="请输入任务ID" />
+        </el-form-item>
+        <el-form-item label="营期ID" prop="sopUserId">
+          <el-input v-model="form.sopUserId" placeholder="请输入营期ID" />
+        </el-form-item>
+        <el-form-item label="联系人ID" prop="wxContactId">
+          <el-input v-model="form.wxContactId" placeholder="请输入联系人ID" />
+        </el-form-item>
+        <el-form-item label="小程序ID" prop="fsUserId">
+          <el-input v-model="form.fsUserId" placeholder="请输入小程序ID" />
+        </el-form-item>
+        <el-form-item label="是否7天都没有看课 0否 1是" prop="isDaysNotStudy">
+          <el-input v-model="form.isDaysNotStudy" placeholder="请输入是否7天都没有看课 0否 1是" />
+        </el-form-item>
+        <el-form-item label="总完课天数" prop="finishCout">
+          <el-input v-model="form.finishCout" placeholder="请输入总完课天数" />
+        </el-form-item>
+        <el-form-item label="最近完课时间" prop="finishTime">
+          <el-date-picker clearable size="small"
+            v-model="form.finishTime"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择最近完课时间">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="连续完课天数" prop="finishCourseDays">
+          <el-input v-model="form.finishCourseDays" placeholder="请输入连续完课天数" />
+        </el-form-item>
+        <el-form-item label="客户评级的等级" prop="grade">
+          <el-input v-model="form.grade" placeholder="请输入客户评级的等级" />
+        </el-form-item>
+        <el-form-item label="禁用状态 0 正常 1禁用">
+          <el-radio-group v-model="form.status">
+            <el-radio label="1">请选择字典生成</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" placeholder="请输入备注" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listWxSopUserInfo, getWxSopUserInfo, delWxSopUserInfo, addWxSopUserInfo, updateWxSopUserInfo, exportWxSopUserInfo } from "@/api/wx/wxSopUserInfo";
+
+export default {
+  name: "WxSopUserInfo",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 个微营期详情表格数据
+      wxSopUserInfoList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        sopId: null,
+        sopUserId: null,
+        wxContactId: null,
+        fsUserId: null,
+        isDaysNotStudy: null,
+        finishCout: null,
+        finishTime: null,
+        finishCourseDays: null,
+        grade: null,
+        status: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询个微营期详情列表 */
+    getList() {
+      this.loading = true;
+      listWxSopUserInfo(this.queryParams).then(response => {
+        this.wxSopUserInfoList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        sopId: null,
+        sopUserId: null,
+        wxContactId: null,
+        fsUserId: null,
+        isDaysNotStudy: null,
+        finishCout: null,
+        finishTime: null,
+        finishCourseDays: null,
+        grade: null,
+        status: 0,
+        createTime: null,
+        createBy: null,
+        updateTime: null,
+        updateBy: null,
+        remark: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加个微营期详情";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getWxSopUserInfo(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改个微营期详情";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateWxSopUserInfo(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addWxSopUserInfo(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除个微营期详情编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delWxSopUserInfo(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有个微营期详情数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportWxSopUserInfo(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>