Browse Source

短信记录及发送界面

庄逸洲 4 years ago
parent
commit
738f9f7b4f

+ 11 - 0
src/api/sms/index.js View File

@@ -0,0 +1,11 @@
1
+import request from '@/utils/request'
2
+
3
+export function fetchBatchSendRecords(page) {
4
+    return request({
5
+        url: "/api/sms/records",
6
+        method: "get",
7
+        params: {
8
+            page: page,
9
+        }
10
+    })
11
+}

BIN
src/assets/img/message_preview_bg.png View File


BIN
src/assets/img/message_preview_bg@2x.png View File


+ 2 - 0
src/lang/en.js View File

@@ -120,6 +120,8 @@ export default {
120 120
     activityShare: "activity share",
121 121
     activityPreview: "activity preview",
122 122
     activitySignupList: "activity sign up list",
123
+    SMSManage: "sms manage",
124
+    SMSSend: "sms send",
123 125
   },
124 126
   navbar: {
125 127
     logOut: 'Log Out',

+ 2 - 0
src/lang/zh.js View File

@@ -169,6 +169,8 @@ export default {
169 169
     activityShare: "活动分享",
170 170
     activityPreview: "活动预览",
171 171
     activitySignupList: "报名列表",
172
+    SMSManage: "短信管理",
173
+    SMSSend: "群发短信",
172 174
   },
173 175
   navbar: {
174 176
     logOut: '退出登录',

+ 0 - 11
src/router/modules/marketing_tool.js View File

@@ -61,17 +61,6 @@ export default {
61 61
         noCache: true,
62 62
       }
63 63
     },
64
-    // {
65
-    //   path: "/activity/preview",
66
-    //   hidden: true,
67
-    //   is_menu: false,
68
-    //   component: () => import("@/scrm_pages/marketing_tool/activity_preview"),
69
-    //   name: "activityPreview",
70
-    //   meta: {
71
-    //     title: "activityPreview",
72
-    //     noCache: true,
73
-    //   }
74
-    // },
75 64
     {
76 65
       path: "/activity/signupusers",
77 66
       hidden: true,

+ 16 - 0
src/router/modules/member.js View File

@@ -30,5 +30,21 @@ export default {
30 30
       name: 'memberCardlist',
31 31
       meta: { title: '会员卡管理', noCache: true }
32 32
     },
33
+
34
+    // SMS
35
+    {
36
+      path: '/sms',
37
+      component: () => import('@/scrm_pages/sms/sms_manage'),
38
+      name: 'SMSManage',
39
+      meta: { title: 'SMSManage', noCache: true }
40
+    },
41
+    {
42
+      path: '/sms/send',
43
+      component: () => import('@/scrm_pages/sms/sms_send'),
44
+      hidden: true,
45
+      is_menu: false,
46
+      name: 'SMSSend',
47
+      meta: { title: 'SMSSend', noCache: true }
48
+    },
33 49
   ]
34 50
 }

+ 121 - 0
src/scrm_pages/sms/sms_manage.vue View File

@@ -0,0 +1,121 @@
1
+<template>
2
+    <div class="main-contain">
3
+        <div class="position">
4
+            <bread-crumb :crumbs="crumbs"></bread-crumb>
5
+            <el-button
6
+                :disabled="$store.getters.xt_user.subscibe.state==3?true:false"
7
+                @click="sendAction"
8
+                style="float:right;"
9
+                type="primary"
10
+                icon="el-icon-message"
11
+                size="small"
12
+            >群发短信</el-button>
13
+        </div>
14
+        <div class="app-container" style="padding: 0 5px; background-color: #f6f8f9;">
15
+            <div style="background-color: #fff; padding: 5px 0;">
16
+                <el-table :data="records">
17
+                    <el-table-column label="发送内容" align="center" prop="full_content" :show-overflow-tooltip="true" min-width="48%">
18
+                    </el-table-column>
19
+                    <el-table-column label="发送状态" align="center" min-width="15%">
20
+                        <template slot-scope="scope">
21
+                            {{ send_status_text(scope.row.status) }}
22
+                        </template>
23
+                    </el-table-column>
24
+                    <el-table-column label="发送人数/到达条数" align="center" min-width="22%">
25
+                        <template slot-scope="scope">
26
+                            {{ scope.row.total_count }}/{{ scope.row.success_count }}
27
+                        </template>
28
+                    </el-table-column>
29
+                    <el-table-column label="审核状态" align="center" min-width="15%">
30
+                        <template slot-scope="scope">
31
+                            {{ review_status_text(scope.row.status) }}
32
+                        </template>
33
+                    </el-table-column>
34
+                </el-table>
35
+                <div style="padding-top: 15px; padding-left: 10px;">
36
+                    <el-pagination :total="record_total" :current-page.sync="current_page" :page-size="10" layout="total, prev, pager, next" @current-change="getSendRecords()"></el-pagination>
37
+                </div>
38
+            </div>
39
+        </div>
40
+    </div>
41
+</template>
42
+
43
+<script>
44
+import BreadCrumb from "@/scrm_pages/components/bread-crumb"
45
+import { fetchBatchSendRecords } from "@/api/sms"
46
+
47
+export default {
48
+    name: "SMSManage",
49
+    components: {
50
+        BreadCrumb,
51
+    },
52
+    data() {
53
+        return {
54
+            crumbs: [
55
+                { path: false, name: "客户管理" },
56
+                { path: false, name: "短信管理" },
57
+            ],
58
+
59
+            record_total: 0,
60
+            current_page: 1,
61
+            records: [],
62
+        }
63
+    },
64
+    mounted() {
65
+        this.getSendRecords()
66
+    },
67
+    methods: {
68
+        getSendRecords: function() {
69
+            fetchBatchSendRecords(this.current_page).then(rs => {
70
+                var resp = rs.data
71
+                if (resp.state == 1) {
72
+                    this.records = resp.data.records
73
+                    this.record_total = resp.data.total
74
+
75
+                } else {
76
+                    this.$message.error(resp.msg)
77
+                }
78
+
79
+            }).catch(err => {
80
+                this.$message.error(err)
81
+            })
82
+        },
83
+        sendAction: function() {
84
+            // var targets = [
85
+            //     { id: 1, name: "会员1", mobile: "12346789098" },
86
+            //     { id: 2, name: "会员2", mobile: "12346789098" },
87
+            //     { id: 3, name: "会员3", mobile: "12346789098" },
88
+            //     { id: 4, name: "会员4", mobile: "12346789098" },
89
+            //     { id: 5, name: "会员5", mobile: "12346789098" },
90
+            // ]
91
+            // this.$store.dispatch("SMSSetTargets", targets)
92
+            this.$router.push({path:'/sms/send'})
93
+        },
94
+
95
+        send_status_text: function(status) {
96
+            if (status == 4) {
97
+                return "已发送"
98
+            } else if (status == 5) {
99
+                return "发送失败"
100
+            } else {
101
+                return "未发送"
102
+            }
103
+        },
104
+        review_status_text: function(status) {
105
+            if (status == 1) {
106
+                return "待审核"
107
+            } else if (status == 2) {
108
+                return "未通过"
109
+            } else {
110
+                return "通过"
111
+            }
112
+        },
113
+    },
114
+}
115
+</script>
116
+
117
+<style lang="scss" scoped>
118
+
119
+</style>
120
+
121
+

+ 378 - 0
src/scrm_pages/sms/sms_send.vue View File

@@ -0,0 +1,378 @@
1
+<template>
2
+    <div class="main-contain">
3
+        <div class="position">
4
+            <bread-crumb :crumbs="crumbs"></bread-crumb>
5
+        </div>
6
+        <div class="app-container" style="padding: 0 5px; background-color: #f6f8f9;">
7
+            <div class="main-panel">
8
+                <div class="preview-panel">
9
+                    <div class="form-title">
10
+                        <img class="icon" src="@/assets/img/sa_06.png" />
11
+                        <span>预览</span>
12
+                    </div>
13
+                    <div class="form-content">
14
+                        <div class="preview">
15
+                            <div class="bg">
16
+                                <div class="scroll">
17
+                                    <div class="fake-time">9:41</div>
18
+                                    <div class="content">
19
+                                        {{ content_preview }}
20
+                                    </div>
21
+                                </div>
22
+                            </div>
23
+                        </div>
24
+                    </div>
25
+                </div>
26
+                <div class="edit-panel">
27
+                    <div class="form-title">
28
+                        <img class="icon" src="@/assets/img/sa_7.png" />
29
+                        <span>短信编辑</span>
30
+                    </div>
31
+                    <div class="form-content">
32
+                        <div class="input-panel">
33
+                            <el-input v-model="sms_content" type="textarea" :rows="4" resize="none"></el-input>
34
+                            <div class="hint-text">已输入{{ total_content_length }}字(含签名和尾缀)按{{ sms_charge_count }}条短信计费</div>
35
+                        </div>
36
+                        <div v-if="designated_targets.length == 0" class="filters-panel">
37
+                            <div class="send-all-panel">
38
+                                <div class="text">
39
+                                    <p class="title">推送全部客户</p>
40
+                                    <p class="desc">将该短信推送给系统里的所有客户</p>
41
+                                </div>
42
+                                <div class="btn">
43
+                                    <el-button type="primary">发送</el-button>
44
+                                </div>
45
+                            </div>
46
+                            <div class="tag-filter-panel">
47
+                                <p class="title">按标签推送</p>
48
+                                <p class="desc">将该短信推送给相应标签客户</p>
49
+                                <div class="tag-panel">
50
+                                    <div v-for="(tag, index) in tags" class="item-panel" :key="index">
51
+                                        <el-tag effect="plain">{{ tag.name }}</el-tag>
52
+                                    </div>
53
+                                </div>
54
+                                <p class="hint-text">*选择标签发送可以帮助您精准营销</p>
55
+                                <div class="send">
56
+                                    <p class="text">已为您筛选 {{ tag_filtered_count }} 位客户</p>
57
+                                    <div class="btn">
58
+                                        <el-button type="primary">发送</el-button>
59
+                                    </div>
60
+                                </div>
61
+                            </div>
62
+                        </div>
63
+                        <div class="designated-targets-panel" v-else>
64
+                            <div class="send-panel">
65
+                                <div class="title">
66
+                                    推送指定客户
67
+                                </div>
68
+                                <div class="btn">
69
+                                    <el-button type="primary">发送</el-button>
70
+                                </div>
71
+                            </div>
72
+                            <el-table :data="designated_targets" border max-height="500px">
73
+                                <el-table-column label="客户" prop="name" align="center"></el-table-column>
74
+                                <el-table-column label="手机号" prop="mobile" align="center"></el-table-column>
75
+                            </el-table>
76
+                        </div>
77
+                    </div>
78
+                </div>
79
+            </div>
80
+        </div>
81
+    </div>
82
+</template>
83
+
84
+<script>
85
+import BreadCrumb from "@/scrm_pages/components/bread-crumb"
86
+
87
+export default {
88
+    name: "SMSSend",
89
+    components: {
90
+        BreadCrumb,
91
+    },
92
+    data() {
93
+        return {
94
+            crumbs: [
95
+                { path: false, name: "短信管理" },
96
+                { path: false, name: "群发短信" },
97
+            ],
98
+
99
+            sms_content: "",
100
+            tags: [],
101
+
102
+            designated_targets: [],
103
+        }
104
+    },
105
+    computed: {
106
+        content_preview: function() {
107
+            return "【酷医聚客】" + this.sms_content + "退订回TD"
108
+        },
109
+        total_content_length: function() {
110
+            return this.content_preview.length
111
+        },
112
+        sms_charge_count: function() {
113
+            return parseInt(Math.ceil(this.total_content_length / 67.0))
114
+        },
115
+        tag_filtered_count: function() {
116
+            return 0
117
+        },
118
+    },
119
+    mounted() {
120
+        this.designated_targets = this.$store.getters.sms_designated_targets.targets
121
+        this.$store.dispatch("SMSClearTargets")
122
+    },
123
+}
124
+</script>
125
+
126
+<style lang="scss" scoped>
127
+.main-panel {
128
+    width: 100%;
129
+    display: flex;
130
+    min-height: 600px;
131
+    height: auto;
132
+    box-sizing: border-box;
133
+
134
+    .preview-panel {
135
+        flex: 3;
136
+        margin-right: 1rem;
137
+        background-color: #fff;
138
+
139
+        .form-content {
140
+            width: 100%;
141
+            padding: 14px 30px 14px 30px;
142
+
143
+            .preview {
144
+                width: 320px;
145
+                height: 568px;
146
+                background: #fff;
147
+                position: relative;
148
+                margin: 0 auto;
149
+
150
+                .bg {
151
+                    position: relative;
152
+                    padding-top: 85px;
153
+                    padding-bottom: 40px;
154
+                    width: 320px;
155
+                    height: 568px;
156
+                    background: url(../../assets/img/message_preview_bg.png) 50% no-repeat;
157
+                    background-image: -webkit-image-set(url(../../assets/img/message_preview_bg.png) 1x,url(../../assets/img/message_preview_bg@2x.png) 2x);
158
+                    -webkit-box-sizing: border-box;
159
+                    -moz-box-sizing: border-box;
160
+                    box-sizing: border-box;
161
+
162
+                    .scroll {
163
+                        height: 430px;
164
+                        overflow-x: hidden;
165
+                        overflow-y: auto;
166
+
167
+                        .fake-time {
168
+                            margin: auto;
169
+                            width: 310px;
170
+                            line-height: 40px;
171
+                            font-size: 12px;
172
+                            text-align: center;
173
+                            color: #999;
174
+                            background: #fff;
175
+                        }
176
+
177
+                        .content {
178
+                            display: inline-block;
179
+                            position: relative;
180
+                            left: 20px;
181
+                            margin-bottom: 12px;
182
+                            padding: 8px 15px;
183
+                            max-width: 260px;
184
+                            word-break: break-all;
185
+                            font-size: 16px;
186
+                            line-height: 1.25;
187
+                            color: #000;
188
+                            background: #e5e5ea;
189
+                            border-radius: 17px;
190
+                            -webkit-box-sizing: border-box;
191
+                            -moz-box-sizing: border-box;
192
+                            box-sizing: border-box;
193
+                        }
194
+                        .content::before {
195
+                            content: "";
196
+                            position: absolute;
197
+                            bottom: -2px;
198
+                            left: -7px;
199
+                            height: 20px;
200
+                            border-left: 20px solid #e5e5ea;
201
+                            border-bottom-right-radius: 16px 14px;
202
+                            -webkit-transform: translateY(-2px);
203
+                            -moz-transform: translateY(-2px);
204
+                            -ms-transform: translateY(-2px);
205
+                            transform: translateY(-2px);
206
+                        }
207
+                        .content::after {
208
+                            content: "";
209
+                            position: absolute;
210
+                            bottom: -2px;
211
+                            left: 20px;
212
+                            width: 10px;
213
+                            height: 20px;
214
+                            background: #fff;
215
+                            border-bottom-right-radius: 10px;
216
+                            -webkit-transform: translate(-30px,-2px);
217
+                            -moz-transform: translate(-30px,-2px);
218
+                            -ms-transform: translate(-30px,-2px);
219
+                            transform: translate(-30px,-2px);
220
+                        }
221
+                    }
222
+                    .scroll::-webkit-scrollbar {
223
+                        width: 5px;
224
+                    }
225
+                }
226
+            }
227
+        }
228
+    }
229
+
230
+    .edit-panel {
231
+        flex: 4;
232
+        padding-bottom: 80px;
233
+        background-color: #fff;
234
+
235
+        .form-content {
236
+            width: 100%;
237
+            padding: 14px 30px 14px 30px;
238
+
239
+            .input-panel {
240
+                padding-top: 10px;
241
+
242
+                .hint-text {
243
+                    margin-top: 5px;
244
+                    font-size: 12px;
245
+                    color: #a8b3ba;
246
+                    line-height: 30px;
247
+                }
248
+            }
249
+
250
+            .filters-panel {
251
+                margin-top: 50px;
252
+
253
+                .send-all-panel {
254
+                    display: flex;
255
+                    justify-content: space-between;
256
+                    align-items: center;
257
+
258
+                    .text {
259
+                        flex: 1;
260
+
261
+                        .title {
262
+                            font-size: 16px;
263
+                            color: #485b6d;
264
+                            font-weight: bold;
265
+                            height: 30px;
266
+                            display: -webkit-box;
267
+                            -webkit-box-orient: vertical;
268
+                            -webkit-line-clamp: 2;
269
+                            overflow: hidden;
270
+                        }
271
+                        .desc {
272
+                            font-size: 14px;
273
+                            color: #7b8a97;
274
+                        }
275
+                    }
276
+                    .btn {
277
+                        text-align: right;
278
+                    }
279
+                }
280
+                .tag-filter-panel {
281
+                    margin-top: 50px;
282
+
283
+                    .title {
284
+                        font-size: 16px;
285
+                        color: #485b6d;
286
+                        font-weight: bold;
287
+                        height: 30px;
288
+                        display: -webkit-box;
289
+                        -webkit-box-orient: vertical;
290
+                        -webkit-line-clamp: 2;
291
+                        overflow: hidden;
292
+                    }
293
+                    .desc {
294
+                        font-size: 14px;
295
+                        color: #7b8a97;
296
+                    }
297
+                    .tag-panel {
298
+                        margin-top: 15px;
299
+                        padding: 10px 15px 5px;
300
+                        background: #f4f7fa;
301
+
302
+                        .item-panel {
303
+                            display: inline-block;
304
+                            margin-bottom: 5px;
305
+                        }
306
+                    }
307
+                    .hint-text {
308
+                        font-size: 12px;
309
+                        color: #a8b3ba;
310
+                        line-height: 30px;
311
+                        margin-top: 10px;
312
+                    }
313
+                    .send {
314
+                        display: flex;
315
+                        justify-content: space-between;
316
+                        align-items: center;
317
+                        margin-top: 15px;
318
+
319
+                        .text {
320
+                            flex: 1;
321
+                            color: #7b8a97;
322
+                            font-size: 14px;
323
+                        }
324
+                        .btn {
325
+                            text-align: right;
326
+                        }
327
+                    }
328
+                }
329
+            }
330
+
331
+            .designated-targets-panel {
332
+                margin-top: 20px;
333
+
334
+                .send-panel {
335
+                    display: flex;
336
+                    justify-content: space-between;
337
+                    align-items: center;
338
+                    padding-bottom: 10px;
339
+
340
+                    .title {
341
+                        flex: 1;
342
+                        font-size: 16px;
343
+                        color: #485b6d;
344
+                        font-weight: bold;
345
+                        height: 100%;
346
+                    }
347
+                    .btn {
348
+                        text-align: right;
349
+                    }
350
+                }
351
+            }
352
+        }
353
+    }
354
+}
355
+</style>
356
+
357
+<style lang="scss" scoped>
358
+.form-title {
359
+    width: 100%;
360
+    height: 60px;
361
+    padding: 20px 0 20px 30px;
362
+    border-bottom: 1px #dee2e5 solid;
363
+
364
+    .icon {
365
+        margin-top: -4px;
366
+        vertical-align: middle;
367
+    }
368
+
369
+    span {
370
+        font-size: 18px;
371
+        font-weight: 500;
372
+        color: #485b6d;
373
+        margin-left: 10px;
374
+    }
375
+}
376
+</style>
377
+
378
+

+ 2 - 0
src/store/getters.js View File

@@ -16,5 +16,7 @@ const getters = {
16 16
   xt_user: state => state.xt_user,
17 17
 
18 18
   xt_role_temps: state => state.xt_role_temps,
19
+
20
+  sms_designated_targets: state => state.sms_designated_targets,
19 21
 }
20 22
 export default getters

+ 2 - 0
src/store/index.js View File

@@ -7,6 +7,7 @@ import xt_user from './modules/xt_user'
7 7
 import xt_permission from './modules/xt_permission'
8 8
 import members from './modules/members'
9 9
 import xt_role_temps from './modules/role_temps'
10
+import sms_designated_targets from "./modules/sms_designated_targets"
10 11
 
11 12
 import getters from './getters'
12 13
 
@@ -21,6 +22,7 @@ const store = new Vuex.Store({
21 22
     members,
22 23
     xt_permission,
23 24
     xt_role_temps,
25
+    sms_designated_targets,
24 26
   },
25 27
   getters
26 28
 })

+ 27 - 0
src/store/modules/sms_designated_targets.js View File

@@ -0,0 +1,27 @@
1
+const sms_designated_targets = {
2
+    state: {
3
+        targets: [],
4
+    },
5
+    mutations: {
6
+        SET_TARGETS: (state, targets) => {
7
+            state.targets = targets
8
+        },
9
+        CLEART_TARGETS: (state) => {
10
+            state.targets = []
11
+        },
12
+    },
13
+    actions: {
14
+        SMSSetTargets({ commit }, targets) {
15
+            if (targets == null || targets == undefined) {
16
+                commit("CLEART_TARGETS")
17
+            } else {
18
+                commit("SET_TARGETS", targets)
19
+            }
20
+        },
21
+        SMSClearTargets({ commit }) {
22
+            commit("CLEART_TARGETS")
23
+        },
24
+    }
25
+}
26
+
27
+export default sms_designated_targets