Browse Source

回访模板管理

lsw 9 months ago
parent
commit
6cceed9d95

File diff suppressed because it is too large
+ 128 - 129
admin-ui/src/assets/styles/extend.scss


+ 0 - 52
admin-ui/src/assets/styles/ruoyi.scss

@@ -423,58 +423,6 @@ h6 {
     }
   }
 }
-.mbg {
-  background-color: #f1f1f1;
-  border: 1px solid darkgray;
-  padding: 20px;
-  border-radius: 5px;
-  .moptions {
-    margin-bottom: 20px !important;
-    .minp {
-      width: 90%;
-      float: left;
-    }
-    .mdel {
-      float: right;
-      margin-top: 3px;
-      cursor: pointer;
-      color: #f44336;
-      font-size: 15px;
-    }
-    .mcopy {
-      float: right;
-      margin-top: 3px;
-      cursor: pointer;
-      color: #2196f3;
-      font-size: 15px;
-      margin-right: 10px;
-    }
-    .selects {
-      float: left;
-      margin-top: 5px;
-      width: 90%;
-      background-color: white;
-      padding: 10px;
-      border-radius: 3px;
-      overflow: hidden;
-      .el-tag {
-        margin-left: 10px;
-      }
-      .button-new-tag {
-        margin-left: 10px;
-        height: 32px;
-        line-height: 30px;
-        padding-top: 0;
-        padding-bottom: 0;
-      }
-      .input-new-tag {
-        width: 200px;
-        margin-left: 10px;
-        vertical-align: bottom;
-      }
-    }
-  }
-}
 .ellip {
   -webkit-line-clamp: 2;
   text-overflow: ellipsis;

+ 1 - 0
admin-ui/src/views/system/dict/index.vue

@@ -199,6 +199,7 @@ export default {
               this.getList();
             });
           } else {
+            this.form.type = 1;
             addType(this.form).then((response) => {
               this.$modal.msgSuccess('新增成功');
               this.open = false;

+ 199 - 0
admin-ui/src/views/work/follow/template/edit.vue

@@ -0,0 +1,199 @@
+<template>
+  <div class="cmain" style="overflow: hidden">
+    <div class="fv">
+      <div class="f">
+        <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+          <el-form-item label="模板名称" prop="title">
+            <el-input v-model="form.title" placeholder="请输入标题" clearable />
+          </el-form-item>
+          <el-form-item label="问题选项" prop="op">
+            <div class="mbg">
+              <el-form-item :label="'' + (index + 1)" v-for="(item, index) in form.op" :key="item.key" :prop="'op.' + index + '.name'" :rules="rules.dynamic" class="moptions">
+                <el-input class="minp" v-model="item.name" placeholder="请输入内容" @input="input()" clearable>
+                  <el-select v-model="item.ifnull" slot="prepend" placeholder="是否必填" style="width: 90px">
+                    <el-option :label="s" :value="s" v-for="s in nulls" :key="s"></el-option>
+                  </el-select>
+                  <el-select v-model="item.input" slot="append" placeholder="请选择" style="width: 101px" @change="change($event, item)">
+                    <el-option :label="s" :value="s" v-for="s in selects" :key="s"></el-option>
+                  </el-select>
+                </el-input>
+                <!--问题子选项start-->
+                <el-form-item v-if="item.input == '单选' || item.input == '多选' || item.input == '判断'" :prop="'op.' + index + '.selects'" :rules="rules.selects" class="selects">
+                  <el-tag v-for="(tag, index) in item.selects" :key="index" :closable="item.input != '判断'" @close="tagClose(item, tag)">{{ tag.name }}</el-tag>
+                  <el-input class="input-new-tag" v-if="item.show" v-model="item.s_value" :ref="'tag' + index" @keyup.enter.native="confirm(item)" @input="input()" @blur="confirm(item)"></el-input>
+                  <el-button v-else class="button-new-tag" v-show="item.input != '判断'" size="small" @click="showInput(item, index)">+ 输入选项</el-button>
+                </el-form-item>
+                <!--问题子选项end-->
+                <div class="mdel" @click="del(item)">移除</div>
+                <div class="mcopy" @click="copy(item)">复制</div>
+              </el-form-item>
+              <div class="addBtn">
+                <el-button icon="el-icon-plus" @click="add()">添加内容</el-button>
+              </div>
+            </div>
+          </el-form-item>
+        </el-form>
+      </div>
+      <div class="f" style="flex: 0.5">
+        <div class="div-mc">
+          <div class="guise-mobile guise-mobile-2">
+            <div class="guise-mobile-show" ref="scrollbar">
+              <!--手机展示内容放置区-->
+              <div class="mttv">{{ form.title }}</div>
+              <el-divider>开始</el-divider>
+              <div class="items" v-for="(item, index) in form.op" :key="index">
+                <div class="vtitle" style="font-weight: normal; font-size: 14px">
+                  <span class="ifnull" v-if="item.ifnull == '必填'">*</span>
+                  <span class="index">{{ index + 1 }},</span>
+                  <span class="tm">{{ item.name }} ({{ item.input }})</span>
+                </div>
+                <div class="mts">
+                  <input v-if="item.input == '填空'" placeholder="请输入" :disabled="true" />
+                  <textarea v-if="item.input == '多行文本'" :disabled="true" placeholder="请输入"></textarea>
+                  <input type="number" v-if="item.input == '电话'" placeholder="请输入" :disabled="true" />
+                  <div class="mchoice" v-if="item.input == '单选' || item.input == '多选' || item.input == '判断'">
+                    <div v-if="item.selects.length <= 4">
+                      <div v-for="(op, i) in item.selects" :key="op.name">
+                        <div class="op" :class="{ active: op.check }" @click="check(item, op)">{{ op.name }}</div>
+                        <!--如果选项要输入显示手动输入框-->
+                        <textarea v-if="op.input && op.check" v-model="op.s_value" :placeholder="'请输入' + op.name" style="margin-top: 5px; float: left"></textarea>
+                      </div>
+                    </div>
+                    <div v-else>
+                      <span>点击选择</span>
+                      <i class="el-icon-caret-bottom icon"></i>
+                    </div>
+                  </div>
+                  <div v-if="item.input == '附件'"><el-button style="width: 100%" icon="el-icon-upload">选择文件</el-button></div>
+                </div>
+              </div>
+              <el-divider>结束</el-divider>
+              <el-button type="primary" style="width: 100%; background-color: #4581fb; border: 0px">提交</el-button>
+              <!--手机展示内容放置区-->
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="mfooter">
+      <el-button type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="$layer.close(layerid)">取 消</el-button>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      form: { tag: 1, state: 0, op: [] },
+      nulls: ['必填', '非必填'],
+      selects: ['填空', '单选', '多选', '判断', '附件', '多行文本', '电话'],
+      rules: {
+        title: [{ required: true, message: '标题不能为空', trigger: 'blur' }],
+        op: [{ required: true, message: '内容不能为空', trigger: 'blur' }],
+        dynamic: [
+          { required: true, message: '请输入问题名称', trigger: 'blur' },
+          {
+            validator: (rule, value, callback) => {
+              this.util.checkSame(this.form, value, callback);
+            }
+          }
+        ],
+        selects: [{ required: true, message: '请输入问题内容', trigger: 'blur' }]
+      }
+    };
+  },
+  props: {
+    param: {
+      type: Object,
+      default: () => {
+        return {};
+      }
+    },
+    layerid: {
+      type: String
+    }
+  },
+  mounted() {
+    if (this.param.id) {
+      this.ajax({ url: '/work/follow/template/detail/' + this.param.id }).then((response) => {
+        response.data.op = JSON.parse(response.data.op);
+        this.form = response.data;
+      });
+    }
+  },
+  methods: {
+    //添加内容
+    add() {
+      this.form.op.push({ name: '', ifnull: '必填', input: '填空', selects: [], show: false });
+      /* this.$nextTick(() => {
+        let scrollElem = this.$refs.scrollbar;
+        scrollElem.scrollTo({ top: scrollElem.scrollHeight, behavior: 'smooth' });
+      }); */
+    },
+    //删除问题
+    del(item) {
+      this.form.op.splice(this.form.op.indexOf(item), 1);
+    },
+    //复制问题
+    copy(item) {
+      this.form.op.push(JSON.parse(JSON.stringify(item)));
+      this.msgSuccess('已复制');
+      this.$forceUpdate();
+    },
+    //判断只能选择是或否
+    change(e, item) {
+      if (item.input == '判断') {
+        item.selects = [{ name: '是' }, { name: '否' }];
+      } else {
+        item.selects = [];
+      }
+    },
+    //解决input有时无法输入的问题
+    input(e) {
+      this.$forceUpdate();
+    },
+    showInput(item, index) {
+      item.show = true;
+      this.$nextTick(() => {
+        this.$refs['tag' + index][0].$refs.input.focus();
+      });
+      this.$forceUpdate();
+    },
+    //删除问题子选项
+    tagClose(item, tag) {
+      item.selects.splice(item.selects.indexOf(tag), 1);
+    },
+    confirm(item) {
+      if (item.s_value) {
+        item.selects.push({ name: item.s_value });
+        item.selects = this.util.duplicate(item.selects);
+      }
+      item.show = false;
+      item.s_value = '';
+    },
+    submitForm() {
+      this.$refs['form'].validate((valid) => {
+        if (valid) {
+          let data = JSON.parse(JSON.stringify(this.form));
+          data.op = JSON.stringify(data.op);
+          if (this.form.id) {
+            this.ajax({ method: 'post', url: '/work/follow/template/edit', data: data }).then((response) => {
+              this.$modal.msgSuccess('修改成功');
+              this.$layer.close(this.layerid);
+              this.$parent.getList();
+            });
+          } else {
+            this.ajax({ method: 'post', url: '/work/follow/template/add', data: data }).then((response) => {
+              this.$modal.msgSuccess('新增成功');
+              this.$layer.close(this.layerid);
+              this.$parent.getList();
+            });
+          }
+        }
+      });
+    }
+  }
+};
+</script>

+ 113 - 0
admin-ui/src/views/work/follow/template/index.vue

@@ -0,0 +1,113 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" @submit.native.prevent v-show="showSearch">
+      <el-form-item label="关联账号" prop="userId">
+        <el-input v-model="queryParams.userId" placeholder="请输入关联账号" @keyup.enter.native="handleQuery" clearable class="inp" />
+      </el-form-item>
+      <el-form-item label="标题" prop="title">
+        <el-input v-model="queryParams.title" placeholder="请输入标题" @keyup.enter.native="handleQuery" clearable class="inp" />
+      </el-form-item>
+      <el-form-item label="状态" prop="state">
+        <el-input v-model="queryParams.state" placeholder="请输入状态" @keyup.enter.native="handleQuery" clearable class="inp" />
+      </el-form-item>
+      <el-form-item label="部门ID" prop="deptId">
+        <el-input v-model="queryParams.deptId" placeholder="请输入部门ID" @keyup.enter.native="handleQuery" clearable class="inp" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+    <el-row :gutter="10" class="mb8">
+      <el-button type="primary" icon="el-icon-plus" :disabled="ids.length > 0" @click="op('add')" v-hasPermi="['work:template:add']">新增</el-button>
+      <el-button type="success" icon="el-icon-edit" :disabled="ids.length != 1" @click="op('edit', ids)" v-hasPermi="['work:template:edit']">修改</el-button>
+      <el-button type="danger" icon="el-icon-delete" :disabled="ids.length == 0" @click="del" v-hasPermi="['work:template:remove']">删除{{ ids.length > 0 ? '(' + ids.length + ')' : '' }}</el-button>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table :data="response.rows" border @selection-change="selects" height="calc(100vh - 270px)">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="标题" align="left" prop="title" />
+      <el-table-column label="状态" align="center" prop="state" width="140">
+        <template slot-scope="scope">
+          <el-tag type="success" v-if="scope.row.state == 0">启用</el-tag>
+          <el-tag type="danger" v-if="scope.row.state == 1">停用</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="200" />
+      <el-table-column label="操作" align="center" width="200">
+        <template slot-scope="scope">
+          <el-button size="mini" type="text" icon="el-icon-edit" @click="op('edit', scope.row)" v-hasPermi="['work:template:edit']">修改</el-button>
+          <el-button size="mini" type="text" icon="el-icon-delete" @click="del(scope.row)" v-hasPermi="['work:template:remove']">删除</el-button>
+        </template>
+      </el-table-column>
+      <template slot="empty">
+        <el-empty></el-empty>
+      </template>
+    </el-table>
+    <pagination v-if="response.total > 0" :total="response.total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
+  </div>
+</template>
+
+<script>
+import edit from './edit';
+export default {
+  name: 'Template',
+  data() {
+    return {
+      ids: [],
+      showSearch: true,
+      response: {},
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        title: null,
+        content: null,
+        state: null,
+        deptId: null,
+        orderByColumn: 'id', //排序字段
+        isAsc: 'desc' //排序方式
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    getList() {
+      this.ajax({ url: '/work/follow/template/list', data: this.queryParams }).then((response) => {
+        this.response = response;
+      });
+    },
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    resetQuery() {
+      this.resetForm('queryForm');
+      this.handleQuery();
+    },
+    selects(rows) {
+      this.ids = rows.map((item) => item.id);
+    },
+    op(tag, row) {
+      if (tag == 'add') {
+        this.iframe({ obj: edit, param: {}, title: '新增', width: '75%', height: '75%' });
+      }
+      if (tag == 'edit') {
+        const id = row.id || this.ids[0];
+        this.iframe({ obj: edit, param: { id: id }, title: '编辑', width: '75%', height: '80%' });
+      }
+    },
+    del(row) {
+      this.$confirm('是否确认删除选中数据?', '警告', { type: 'warning' }).then(() => {
+        this.get({ url: '/work/follow/template/remove/' + (row.id || this.ids) }).then((response) => {
+          this.$modal.msgSuccess('删除成功');
+          this.getList();
+        });
+      });
+    }
+  }
+};
+</script>

+ 63 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/work/controller/FollowTemplateController.java

@@ -0,0 +1,63 @@
+package com.ruoyi.web.work.controller;
+
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.web.work.domain.FollowTemplate;
+import com.ruoyi.web.work.service.IFollowTemplateService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 回访模板管理
+ *
+ * @author lsw
+ * @date 2024-07-11
+ */
+@RestController
+@RequestMapping("/work/follow/template")
+public class FollowTemplateController extends BaseController {
+    @Autowired
+    private IFollowTemplateService followTemplateService;
+
+    @PreAuthorize("@ss.hasPermi('work:template:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FollowTemplate followTemplate) {
+        startPage();
+        List<FollowTemplate> list = followTemplateService.selectList(followTemplate);
+        return getDataTable(list);
+    }
+
+    @PreAuthorize("@ss.hasPermi('work:template:query')")
+    @GetMapping(value = "/detail/{id}")
+    public AjaxResult detail(@PathVariable("id") Long id) {
+        return AjaxResult.success(followTemplateService.getById(id));
+    }
+
+    @PreAuthorize("@ss.hasPermi('work:template:add')")
+    @Log(title = "回访模板管理", businessType = BusinessType.INSERT)
+    @PostMapping("/add")
+    public AjaxResult add(@RequestBody FollowTemplate followTemplate) {
+        return toAjax(followTemplateService.save(followTemplate));
+    }
+
+    @PreAuthorize("@ss.hasPermi('work:template:edit')")
+    @Log(title = "回访模板管理", businessType = BusinessType.UPDATE)
+    @PostMapping("/edit")
+    public AjaxResult edit(@RequestBody FollowTemplate followTemplate) {
+        return toAjax(followTemplateService.updateById(followTemplate));
+    }
+
+    @PreAuthorize("@ss.hasPermi('work:template:remove')")
+    @Log(title = "回访模板管理", businessType = BusinessType.DELETE)
+    @GetMapping("/remove/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids) {
+        return toAjax(followTemplateService.removeByIds(Arrays.asList(ids)));
+    }
+}

+ 54 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/work/domain/FollowTemplate.java

@@ -0,0 +1,54 @@
+package com.ruoyi.web.work.domain;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+/**
+ * @author lsw
+ * @date 2024-07-11
+ */
+@Data
+@TableName(value = "tb_follow_template")
+@Accessors(chain = true)
+public class FollowTemplate{
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+
+    @ApiModelProperty(value = "关联账号")
+    private Long userId;
+
+    @ApiModelProperty(value = "标题")
+    private String title;
+
+    @ApiModelProperty(value = "内容")
+    private String op;
+
+    @ApiModelProperty(value = "状态:0=正常,1=停用")
+    private Integer state;
+
+    @TableField(fill = FieldFill.INSERT)
+    private String createBy;
+
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    @TableField(fill = FieldFill.UPDATE)
+    private String updateBy;
+
+    @TableField(fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date updateTime;
+
+    @ApiModelProperty(value = "部门ID")
+    private Long deptId;
+
+
+}

+ 13 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/work/mapper/FollowTemplateMapper.java

@@ -0,0 +1,13 @@
+package com.ruoyi.web.work.mapper;
+
+import java.util.List;
+import com.ruoyi.web.work.domain.FollowTemplate;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @author lsw
+ * @date 2024-07-11
+ */
+public interface FollowTemplateMapper extends BaseMapper<FollowTemplate> {
+    List<FollowTemplate> selectList(FollowTemplate followTemplate);
+}

+ 14 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/work/service/IFollowTemplateService.java

@@ -0,0 +1,14 @@
+package com.ruoyi.web.work.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.web.work.domain.FollowTemplate;
+
+import java.util.List;
+
+/**
+ * @author lsw
+ * @date 2024-07-11
+ */
+public interface IFollowTemplateService extends IService<FollowTemplate>{
+    List<FollowTemplate> selectList(FollowTemplate followTemplate);
+}

+ 24 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/work/service/impl/FollowTemplateServiceImpl.java

@@ -0,0 +1,24 @@
+package com.ruoyi.web.work.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.web.work.mapper.FollowTemplateMapper;
+import com.ruoyi.web.work.domain.FollowTemplate;
+import com.ruoyi.web.work.service.IFollowTemplateService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+
+/**
+ * @author lsw
+ * @date 2024-07-11
+ */
+@Service
+public class FollowTemplateServiceImpl extends ServiceImpl<FollowTemplateMapper, FollowTemplate> implements IFollowTemplateService {
+    @Autowired
+    private FollowTemplateMapper followTemplateMapper;
+
+    @Override
+    public List<FollowTemplate> selectList(FollowTemplate followTemplate) {
+        return followTemplateMapper.selectList(followTemplate);
+    }
+}

+ 17 - 0
ruoyi-admin/src/main/resources/mapper/work/FollowTemplateMapper.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.web.work.mapper.FollowTemplateMapper">
+    
+    <select id="selectList" resultType="com.ruoyi.web.work.domain.FollowTemplate">
+        select * from tb_follow_template
+        <where>  
+            <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="title != null  and title != ''"> and title = #{title}</if>
+            <if test="state != null "> and state = #{state}</if>
+            <if test="deptId != null "> and dept_id = #{deptId}</if>
+        </where>
+    </select>
+
+</mapper>

+ 0 - 1
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusMetaObjectHandler.java

@@ -11,7 +11,6 @@ import java.util.Date;
 public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
     @Override
     public void insertFill(MetaObject metaObject) {
-        System.out.println("createTime");
         this.setFieldValByName("createTime", new Date(), metaObject);
         if (SecurityUtils.getLoginUser() != null) {
             if (SecurityUtils.getLoginUser().getDeptId() != null) {

Some files were not shown because too many files changed in this diff