Browse Source

feat: copy 客户阶段配置 代码

zhengxy 2 years ago
parent
commit
bb2211906f

+ 6 - 0
project/src/assets/config/interface_api.js

@@ -320,6 +320,12 @@ var api = {
320 320
   smartPush_senderListExport: "/api/CircleMassMsg/senderListExport", // 企微助手 - 智能推送 - 推送记录 - 详情&成员详情导出
321 321
   smartPush_massSendCustList: "/api/CircleMassMsg/massSendCustList", // 企微助手 - 智能推送 - 推送记录 - 详情&客户详情列表
322 322
   smartPush_massSendCustListExport: "/api/CircleMassMsg/massSendCustListExport", // 企微助手 - 智能推送 - 推送记录 - 详情&客户详情导出
323
+
324
+  //客户阶段配置
325
+  phaseList: "/api/CustomerStage/stageRuleList",
326
+  phaseDel: "/api/CustomerStage/stageRuleDel",
327
+  phaseSort: "/api/CustomerStage/stageRuleSort",
328
+  phaseSet: "/api/CustomerStage/setStageRule",
323 329
 };
324 330
 
325 331
 export { api };

+ 201 - 0
project/src/components/phaseConfig/addEditDialog.vue

@@ -0,0 +1,201 @@
1
+<template>
2
+  <div>
3
+    <el-dialog class="dialogCon_permission" :title="dialogTitle" :visible.sync="dialogVisible" :append-to-body="true" width="600px" @close="closeEvent">
4
+      <div class="dialogCon">
5
+        <div class="itemBox" style="margin-top: 0">
6
+          <div class="name"><em>*</em>阶段名称:</div>
7
+          <div class="ipt">
8
+            <el-input size="small" v-model="iptName"  placeholder="请输入阶段名称" style="width: 100%"></el-input>
9
+          </div>
10
+        </div>
11
+        <div class="itemBox">
12
+          <div class="name">阶段描述:</div>
13
+          <div class="ipt">
14
+            <el-input size="small" v-model="iptDesc"  placeholder="请输入阶段描述" style="width: 100%"></el-input>
15
+          </div>
16
+        </div>
17
+        <div class="itemBox" style="align-items: baseline">
18
+          <div class="name"><em>*</em>阶段规则:</div>
19
+          <div class="ipt phaseBox">
20
+            <div class="flex-row bMar15">
21
+              <div class="chooseItem" v-for="item in phaseList" :key="item.value"
22
+                   :class="item.value == phaseVal ? 'acItem' : ''"
23
+                   @click.stop="phaseVal = item.value"
24
+                   >
25
+                <span class="checkBox" @click.stop="switchCheckBox(item.value)"
26
+                                     :class="choosePhaseArr.indexOf(item.value)>-1 ? 'acCheck' : ''"></span>
27
+                {{item.label}}
28
+              </div>
29
+            </div>
30
+            <div>
31
+              <addTime v-show="phaseVal==1"></addTime>
32
+              <screen-pay :reset='resetFlag' title="" v-show="phaseVal==2"></screen-pay>
33
+              <enterprise-tag :reset='resetFlag' title="" v-show="phaseVal==3"></enterprise-tag>
34
+            </div>
35
+
36
+
37
+          </div>
38
+        </div>
39
+      </div>
40
+      <div slot="footer" class="dialog-footer">
41
+        <el-button size="mini" @click="closeEvent">取 消</el-button>
42
+        <el-button size="mini" type="primary" @click="confirmEvent()">确 定</el-button>
43
+      </div>
44
+    </el-dialog>
45
+  </div>
46
+</template>
47
+<script>
48
+  import screenPay from './pay.vue'
49
+  import addTime from './addTime.vue'
50
+  import enterpriseTag from '@/components/assembly/screen/enterpriseTag.vue'
51
+  export default {
52
+    name: 'addEditDialog',
53
+    components: { screenPay,addTime,enterpriseTag},
54
+    props: ["dialogVisible","echoObj"],
55
+    data () {
56
+      return {
57
+        dialogTitle:'阶段配置',
58
+        iptName:'',
59
+        iptDesc:'',
60
+        phaseVal:1,
61
+        resetFlag:false,
62
+        phaseList:[
63
+          {label:'客户添加时间',value:1},
64
+          {label:'付费情况',value:2},
65
+          {label:'标签',value:3},
66
+        ],
67
+        choosePhaseArr:[],
68
+        ruleList:[
69
+          {type:'1',condition:'test'}
70
+        ],//传给后台的格式和回显的规则
71
+      }
72
+    },
73
+    created () {
74
+
75
+
76
+    },
77
+    watch: {
78
+      dialogVisible(isShow) {
79
+        // 弹框显示 => 初始化表单数据
80
+        if (isShow) {
81
+          console.log(this.echoObj);
82
+          if(this.echoObj.rule_id){ //编辑
83
+            this.iptName = this.echoObj.stage_title
84
+            this.iptDesc = this.echoObj.stage_desc
85
+          }else{ //新增
86
+            this.iptName = ''
87
+            this.iptDesc = ''
88
+            this.phaseVal = 1
89
+          }
90
+        }
91
+      },
92
+    },
93
+    methods: {
94
+      // 添加编辑 规则
95
+      addEditEvent(){
96
+
97
+      },
98
+      // 删除
99
+      deleEvent(){
100
+
101
+      },
102
+      //弹框关闭
103
+      closeEvent(){
104
+        this.$emit('close')
105
+      },
106
+      //选择规则
107
+      switchCheckBox(val){
108
+        let idx = this.choosePhaseArr.indexOf(val)
109
+        if(idx > -1){
110
+          this.choosePhaseArr.splice(idx,1)
111
+        }else{
112
+          this.phaseVal = val
113
+          this.choosePhaseArr.push(val)
114
+        }
115
+      },
116
+      //确定
117
+      confirmEvent(){
118
+        if(this.iptName==''){
119
+          this.$message({
120
+            message: '规则标题必填',
121
+            type: "warning"
122
+          })
123
+          return
124
+        }
125
+        this.$axios.post(this.URL.BASEURL + this.URL.phaseSet, {
126
+          rule_id: this.echoObj ? this.echoObj.rule_id : '',
127
+          stage_title: this.iptName,
128
+          stage_desc:this.iptDesc,
129
+          stage_rule:this.ruleList
130
+        }).then((res) => {
131
+          var res = res.data
132
+          if (res && res.errno == 0) {
133
+            this.$message({
134
+              message: '添加成功',
135
+              type: "success"
136
+            })
137
+            this.$emit('close',true)
138
+          } else if (res.errno != 4002) {
139
+            this.$message({
140
+              message: res.err,
141
+              type: "warning"
142
+            })
143
+          }
144
+        }).catch((err) => {
145
+        });
146
+      }
147
+
148
+    }
149
+  }
150
+</script>
151
+<style lang="scss">
152
+  .dialogCon {
153
+    .itemBox {
154
+      display: flex;
155
+      align-items: center;
156
+      margin-bottom: 10px;
157
+      .name {
158
+        width: 80px;
159
+        flex-wrap: nowrap;
160
+        text-align: right;
161
+        margin-right: 10px;
162
+        flex-shrink: 0;
163
+        em {
164
+          color: red;
165
+        }
166
+      }
167
+      .ipt{
168
+        width: 100%;
169
+      }
170
+      .phaseBox{
171
+        padding: 20px;
172
+        background-color: #f8f8f8;
173
+        .chooseItem{
174
+          display: flex;
175
+          align-items: center;
176
+          cursor: pointer;
177
+          margin-right: 25px;
178
+          .checkBox{
179
+            display: inline-block;
180
+            width: 16px;
181
+            height: 16px;
182
+            border-radius: 3px;
183
+            border: 1px solid #d8d8d8;
184
+            margin-right: 6px;
185
+          }
186
+        }
187
+        .acItem{
188
+          color: #00B38A;
189
+          font-weight: 600;
190
+        }
191
+        .acCheck{
192
+          background-color: #00B38A;
193
+          border: 1px solid #00B38A!important;
194
+        }
195
+      }
196
+    }
197
+  }
198
+  .dialog-footer {
199
+    margin-top: 40px;
200
+  }
201
+</style>

+ 45 - 0
project/src/components/phaseConfig/addTime.vue

@@ -0,0 +1,45 @@
1
+<template>
2
+  <div class="timeLineBox " style="margin: 10px 0;">
3
+    <el-radio v-model="timeline" :label="24">24小时</el-radio>
4
+    <el-radio v-model="timeline" :label="168">7天</el-radio>
5
+    <el-radio v-model="timeline" :label="360">15天</el-radio>
6
+    <el-radio v-model="timeline" :label="-1">自定义</el-radio>
7
+    <date-picker v-if="timeline == -1" title="" width="355px" :timeFlag="true"
8
+                 :afferent_time='afferent_time' @changeTime="changeTime"
9
+                 style="margin:6px 0 8px 0">
10
+    </date-picker>
11
+    <!-- :reset='resetFlag'-->
12
+  </div>
13
+</template>
14
+<script>
15
+  import datePicker from '@/components/assembly/screen/datePicker.vue'
16
+  export default {
17
+    name: 'addTime',
18
+    components: { datePicker },
19
+    props: ["afferent_time"],
20
+    data () {
21
+      return {
22
+        timeline:24,
23
+        resetFlag:false
24
+      }
25
+    },
26
+    created () {
27
+
28
+
29
+    },
30
+    methods: {
31
+      //
32
+      changeEvent(){
33
+
34
+      },
35
+      changeTime(){
36
+
37
+      }
38
+
39
+
40
+    }
41
+  }
42
+</script>
43
+<style lang="scss">
44
+
45
+</style>

+ 136 - 0
project/src/components/phaseConfig/index.vue

@@ -0,0 +1,136 @@
1
+<template>
2
+  <div>
3
+    <div class="screenBox">
4
+      <el-button type="primary" size="small" @click="addEditEvent">阶段配置</el-button>
5
+      <span class="f12 lMar8">*上下拖动调整阶段排列顺序</span>
6
+    </div>
7
+
8
+    <el-table ref="phaseTable" :height="height" :data="tableData" tooltip-effect="dark" style="width: 100%;margin-top:10px" v-loading="loading">
9
+      <el-table-column min-width='160' v-for="(item,index) in desCol" :key="index+'des'" :prop="item.prop" :label="item.label" show-overflow-tooltip align="center" :fixed="item.prop=='date'?'left':false">
10
+        <template slot-scope="scope">
11
+          <template v-if="item.prop == 'enable'">
12
+            <span v-if="scope.row.enable==1" class="c-448AFF">启用</span>
13
+            <span v-else class="c-F03F5C">禁用</span>
14
+          </template>
15
+          <span v-else>{{scope.row[item.prop]}}</span>
16
+        </template>
17
+      </el-table-column>
18
+      <el-table-column label="操作" min-width="160" align="center">
19
+        <template slot-scope="scope">
20
+          <el-button type="primary" size="mini" @click="addEditEvent(scope.row)">编辑</el-button>
21
+          <el-popconfirm title="确定删除吗?" @confirm="deleEvent(scope.row.rule_id)">
22
+            <el-button slot="reference" type="danger" size="mini">删除</el-button>
23
+          </el-popconfirm>
24
+        </template>
25
+      </el-table-column>
26
+    </el-table>
27
+
28
+    <div class="pagination" v-show="total>0">
29
+      <el-pagination background :current-page="page" @current-change="handleCurrentChange" layout="prev, pager, next" :page-count='Number(pages)'>
30
+      </el-pagination>
31
+    </div>
32
+
33
+    <!--阶段配置弹框-->
34
+    <addEditDialog
35
+      :dialogVisible="dialogVisible"
36
+      :echoObj="echoObj"
37
+      @close="closeEvent"
38
+    />
39
+  </div>
40
+</template>
41
+<script>
42
+  import addEditDialog from './addEditDialog.vue'
43
+  export default {
44
+    name: 'phaseConfig',
45
+    components: { addEditDialog },
46
+    data () {
47
+      return {
48
+        desCol: [
49
+          { prop: "sort_order", label: "排序", min_width: 180, fixed: 'left' },
50
+          { prop: "stage_title", label: "客户阶段", min_width: 180, },
51
+          { prop: "stage_desc", label: "阶段描述", min_width: 180, },
52
+          { prop: "stage_rule", label: "阶段规则", min_width: 180, },
53
+        ],
54
+        height:'',
55
+        tableData:[],
56
+        dialogVisible:false,
57
+        total:0,
58
+        page:1,
59
+        pages:0,
60
+        page_size:20,
61
+        loading:false,
62
+        echoObj:{}
63
+      }
64
+    },
65
+    created () {
66
+      this.height = document.documentElement.clientHeight - 280 > 400 ? document.documentElement.clientHeight - 280 : 400
67
+      this.init()
68
+    },
69
+    methods: {
70
+      // 添加编辑 规则
71
+      addEditEvent( row ){
72
+        this.dialogVisible = true
73
+        this.echoObj = row
74
+      },
75
+      // 删除
76
+      deleEvent(id){
77
+        this.$axios.post(this.URL.BASEURL + this.URL.phaseDel, {
78
+          rule_id: id,
79
+        }).then((res) => {
80
+          var res = res.data
81
+          if (res && res.errno == 0) {
82
+            this.init(1)
83
+          } else if (res.errno != 4002) {
84
+            this.$message({
85
+              message: res.err,
86
+              type: "warning"
87
+            })
88
+          }
89
+        }).catch((err) => {
90
+        });
91
+      },
92
+      // 分页
93
+      handleCurrentChange(){
94
+
95
+      },
96
+      //关闭弹框
97
+      closeEvent(flag){
98
+        this.dialogVisible = false
99
+        if(flag){
100
+          this.init(1)
101
+        }
102
+      },
103
+      //列表
104
+      init(page){
105
+        this.page = page ? page : this.page;
106
+        this.loading = true
107
+        this.$axios.get(this.URL.BASEURL + this.URL.phaseList, {
108
+          params: {
109
+            page: this.page,
110
+            page_size: this.page_size
111
+          }
112
+        }).then((res) => {
113
+          var res = res.data
114
+          this.loading = false
115
+          if (res && res.errno == 0) {
116
+            this.tableData = res.rst.data;
117
+            this.total = res.rst.pageInfo.total;
118
+            this.pages = res.rst.pageInfo.pages;
119
+          } else if (res.errno != 4002) {
120
+            this.$message({
121
+              message: res.err,
122
+              type: "warning"
123
+            })
124
+          }
125
+        }).catch((err) => {
126
+          this.loading = false
127
+        });
128
+      }
129
+
130
+
131
+    }
132
+  }
133
+</script>
134
+<style lang="scss">
135
+
136
+</style>

+ 217 - 0
project/src/components/phaseConfig/pay.vue

@@ -0,0 +1,217 @@
1
+<template>
2
+  <div class="common-screen-item pay">
3
+    <label class="common-screen-label" v-if="title&&title!=''">{{title}}</label>
4
+    <el-popover placement="bottom" trigger="click" v-model="visible" @hide="visibleHide">
5
+      <div :style="width?'width:'+width:''" :class="['common-screen-self-box','common-input-select',valueObj.radio!=null&&clearable?'common-input-select-hover':'']" slot="reference">
6
+        <div :class="['common-screen-self-con',valueObj.radio!=0&&valueObj.radio==null?'common-screen-self-placeholder':'']" :style="width?'text-align: left;padding-left: 15px !important;':''">
7
+          <div class="common-screen-self-con-div">
8
+            <span>{{valueObj.radio==0?'未付费':valueObj.radio==1?(valueObj.minValue==''&&valueObj.maxValue==''?'已付费':(valueObj.minValue==''&&valueObj.maxValue!=''?`已付费(${valueObj.maxValue}次)以下`:(valueObj.minValue!=''&&valueObj.maxValue==''?`已付费(${valueObj.minValue}次)以上`:`已付费(${valueObj.minValue} ~ ${valueObj.maxValue}次)`))):'请选择'}}</span>
9
+          </div>
10
+          <i class="el-icon-circle-close other-icon" @click.stop="clear"></i>
11
+        </div>
12
+        <div class="common-screen-self-icon">
13
+          <i class="el-icon-arrow-down"></i>
14
+        </div>
15
+      </div>
16
+      <div class="popover_con">
17
+        <div :class="['item',radio==0?'active':'']" @click="payChange(0)">未付费</div>
18
+        <div :class="['item',radio==1?'active':'']" @click="payChange(1)">已付费</div>
19
+        <template v-if="radio==1">
20
+          <div class="custom">
21
+            <p>自定义充值次数</p>
22
+            <div class="inputBox">
23
+              <input type="number" placeholder="充值次数" v-model="valueObj.minValue">
24
+              <span> - </span>
25
+              <input type="number" placeholder="充值次数" v-model="valueObj.maxValue">
26
+              <span>次</span>
27
+            </div>
28
+          </div>
29
+
30
+          <div class="custom">
31
+            <p>自定义充值金额</p>
32
+            <div class="inputBox">
33
+              <input type="number" placeholder="充值金额" v-model="minMoneyValue">
34
+              <span> - </span>
35
+              <input type="number" placeholder="充值金额" v-model="maxMoneyValue">
36
+              <span>次</span>
37
+            </div>
38
+          </div>
39
+
40
+          <div class="buttons">
41
+            <el-button type="primary" size="mini" @click="customPay">确定</el-button>
42
+          </div>
43
+        </template>
44
+      </div>
45
+    </el-popover>
46
+  </div>
47
+</template>
48
+<script>
49
+export default {
50
+  props: {
51
+    title: {
52
+      type: String,
53
+      default: '付费情况'
54
+    },
55
+    clearable: {
56
+      type: Boolean,
57
+      default: true
58
+    },
59
+    reset: {
60
+      type: Boolean,
61
+      default: false
62
+    },
63
+    width: {
64
+      type: String,
65
+    },
66
+    afferent_obj: {//接口传入数据 {pay_status: 1 , pay_num_min: '' , pay_num_max: '',}
67
+      type: Object,
68
+      default: () => { }
69
+    },
70
+  },
71
+  watch: {
72
+    reset () {
73
+      this.radio = null
74
+      this.valueObj.radio = null
75
+      this.valueObj.minValue = '';
76
+      this.valueObj.maxValue = '';
77
+      this.minMoneyValue = '';
78
+      this.maxMoneyValue = '';
79
+    }
80
+  },
81
+  data () {
82
+    return {
83
+      visible: false,
84
+      radio: null,
85
+      valueObj: {
86
+        radio: null,
87
+        minValue: '',
88
+        maxValue: '',
89
+      },
90
+      minMoneyValue: '',
91
+      maxMoneyValue: '',
92
+    }
93
+  },
94
+  mounted () {
95
+    if (this.afferent_obj) {
96
+      this.valueObj = {
97
+        radio: this.afferent_obj.pay_status,
98
+        minValue: this.afferent_obj.pay_num_min || this.afferent_obj.pay_num_min == 0 ? this.afferent_obj.pay_num_min : '',
99
+        maxValue: this.afferent_obj.pay_num_max || this.afferent_obj.pay_num_max == 0 ? this.afferent_obj.pay_num_max : '',
100
+      }
101
+      this.radio = this.afferent_obj.pay_status
102
+      this.minMoneyValue = ''
103
+      this.maxMoneyValue = ''
104
+    }
105
+  },
106
+  methods: {
107
+    visibleHide () {
108
+      if (this.radio == 1) {
109
+        if (this.valueObj.minValue == '' || this.valueObj.maxValue == '' || this.valueObj.maxValue < this.valueObj.minValue) {
110
+          return
111
+        }
112
+        this.valueObj.radio = 1;
113
+        this.$emit('payChange', this.valueObj)
114
+        this.visible = false
115
+      }
116
+    },
117
+    clear () {
118
+      if (this.valueObj.radio == null) {
119
+        this.visible = !this.visible
120
+        return
121
+      }
122
+      this.radio = null
123
+      this.valueObj.radio = null
124
+      this.valueObj.minValue = '';
125
+      this.valueObj.maxValue = '';
126
+      this.$emit('payChange', this.valueObj)
127
+    },
128
+    payChange (type) {
129
+      this.radio = type;
130
+      if (type == 0) {
131
+        this.valueObj = {
132
+          radio: type,
133
+          minValue: '',
134
+          maxValue: '',
135
+        }
136
+        this.visible = false
137
+        this.$emit('payChange', this.valueObj)
138
+      }
139
+    },
140
+    customPay () {
141
+      if (this.valueObj.maxValue && this.valueObj.minValue && this.valueObj.maxValue < this.valueObj.minValue) {
142
+        this.$message({
143
+          message: '请正确填写自定义次数',
144
+          type: "warning"
145
+        })
146
+        return
147
+      }
148
+      this.valueObj.radio = 1;
149
+      this.$emit('payChange', this.valueObj)
150
+      this.visible = false
151
+    }
152
+  }
153
+}
154
+</script>
155
+<style lang="scss" scoped>
156
+.popover_con {
157
+  width: 250px;
158
+  .item {
159
+    font-size: 14px;
160
+    padding: 0 20px;
161
+    position: relative;
162
+    white-space: nowrap;
163
+    overflow: hidden;
164
+    text-overflow: ellipsis;
165
+    color: #606266;
166
+    height: 34px;
167
+    line-height: 34px;
168
+    -webkit-box-sizing: border-box;
169
+    box-sizing: border-box;
170
+    cursor: pointer;
171
+    &.active {
172
+      color: #00b38a;
173
+      font-weight: bold;
174
+    }
175
+    &:hover {
176
+      background-color: #f5f7fa;
177
+    }
178
+  }
179
+}
180
+.custom {
181
+  border-top: 1px dotted #cecece;
182
+  margin: 20px;
183
+  margin-top: 10px;
184
+  p {
185
+    color: #383e47;
186
+    font-size: 13px;
187
+    line-height: 18px;
188
+    margin-top: 14px;
189
+  }
190
+  .inputBox {
191
+    display: flex;
192
+    align-items: center;
193
+    margin-top: 15px;
194
+    input {
195
+      width: 80px;
196
+      height: 30px;
197
+      background: #ffffff;
198
+      border-radius: 4px;
199
+      border: 1px solid #b7b9c6;
200
+      font-size: 12px;
201
+      padding-left: 8px;
202
+      line-height: 30px;
203
+    }
204
+    span {
205
+      font-size: 13px;
206
+      line-height: 18px;
207
+      color: #383e47;
208
+      margin: 0 10px;
209
+    }
210
+  }
211
+}
212
+.buttons {
213
+  text-align: right;
214
+  padding-bottom: 10px;
215
+  padding-right: 20px;
216
+}
217
+</style>

+ 14 - 0
project/src/router/allRouter.js

@@ -77,6 +77,9 @@ const platformPromote = () => import(/* webpackChunkName: 'platformPromote' */ '
77 77
 // 企微助手 - 客户分析
78 78
 const customerAnalysis = () => import(/* webpackChunkName: 'customerAnalysis' */ '@/components/customerAnalysis/index.vue')
79 79
 
80
+// 客户管理 - 客户阶段配置
81
+const phaseConfig = () => import(/* webpackChunkName: 'phaseConfig' */ '@/components/phaseConfig/index.vue')
82
+
80 83
 // name与菜单配置的页面路由一致
81 84
 // meta下isData:true为数据看板,否则为助手
82 85
 export var allRouter = [
@@ -703,6 +706,17 @@ export var allRouter = [
703 706
           title: '客户分析'
704 707
         }
705 708
       },
709
+      {
710
+        path: 'phaseConfig',
711
+        name: 'phaseConfig',
712
+        component: phaseConfig,
713
+        meta: {
714
+          keepAlive: false,
715
+          isLogin: true,
716
+          title: '客户阶段配置',
717
+          isData: false
718
+        }
719
+      },
706 720
     ]
707 721
   }
708 722
 ]