Преглед на файлове

Merge branch 'master' of http://git.shengws.com/zhangbj/scrm-vue

xiaoming_global преди 5 години
родител
ревизия
0227f2ddea

+ 8 - 1
src/api/kefu/tencentim.js Целия файл

@@ -5,4 +5,11 @@ export function getusersig() {
5 5
     url: '/api/tencent/usersig', 
6 6
     method: 'get',
7 7
   })
8
-}
8
+}
9
+export function GetAllMembers() {
10
+  return request({
11
+    url: '/api/members/all', 
12
+    method: 'get',
13
+  })
14
+}
15
+

+ 68 - 0
src/api/sms/index.js Целия файл

@@ -0,0 +1,68 @@
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
+}
12
+
13
+export function sendsInitData(action, id) {
14
+    var param = {}
15
+    if (action != null && action != undefined) {
16
+        param.action = action
17
+    }
18
+    if (id != null && id != undefined) {
19
+        param.id = id
20
+    }
21
+    return request({
22
+        url: "/api/sms/sendinit",
23
+        method: "get",
24
+        params: param,
25
+    })
26
+}
27
+
28
+export function tagFilterCustomerCount(tag_ids_str) {
29
+    return request({
30
+        url: "/api/sms/tagfiltercount",
31
+        method: "get",
32
+        params: {
33
+            tags: tag_ids_str,
34
+        },
35
+    })
36
+}
37
+
38
+export function send2AllCustomers(content) {
39
+    return request({
40
+        url: "/api/sms/send2all",
41
+        method: "post",
42
+        params: {
43
+            content: content,
44
+        }
45
+    })
46
+}
47
+
48
+export function send2TagCustomers(content, tag_ids) {
49
+    return request({
50
+        url: "/api/sms/send2tag",
51
+        method: "post",
52
+        params: {
53
+            content: content,
54
+            tags: tag_ids,
55
+        }
56
+    })
57
+}
58
+
59
+export function send2SpecificCustomers(content, customer_ids) {
60
+    return request({
61
+        url: "/api/sms/send2specific",
62
+        method: "post",
63
+        params: {
64
+            content: content,
65
+            ids: customer_ids,
66
+        }
67
+    })
68
+}

BIN
src/assets/img/message_preview_bg.png Целия файл


BIN
src/assets/img/message_preview_bg@2x.png Целия файл


+ 2 - 0
src/lang/en.js Целия файл

@@ -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 Целия файл

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

+ 2 - 2
src/router/index.js Целия файл

@@ -84,9 +84,9 @@ var _asy_router_map = [
84 84
   article,
85 85
   marketing_tool,
86 86
   org,
87
-  role,
88 87
   system,
89
-  site
88
+  site,
89
+  role,
90 90
 ]
91 91
 
92 92
 var is_asy_router = process.env.NODE_ENV === 'production' // true; 设置为 true 强制进行路由验证

+ 0 - 11
src/router/modules/marketing_tool.js Целия файл

@@ -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 Целия файл

@@ -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
 }

+ 24 - 9
src/router/modules/site.js Целия файл

@@ -4,17 +4,32 @@ export default {
4 4
   path: '',
5 5
   component: Layout,
6 6
   redirect: '/site',
7
+  alwaysShow: true,
8
+  // name: '微网站',
9
+  meta: {
10
+    title: '微网站',
11
+    icon: 'dashboard'
12
+  },
7 13
   children: [{
8
-    path: '/site',
9
-    component: () => import('@/scrm_pages/site/index'),
10
-    name: 'site',
11
-    meta: {
12
-      title: '微网站',
13
-      icon: 'dashboard',
14
-      noCache: true
14
+      path: '/site',
15
+      component: () => import('@/scrm_pages/site/index'),
16
+      name: 'site',
17
+      unfinished: true,
18
+      meta: {
19
+        title: '微网站',
20
+        noCache: true,
21
+      }
22
+    },
23
+    {
24
+      path: '/site/preview',
25
+      component: () => import('@/scrm_pages/site/preview'),
26
+      name: '预览',
27
+      meta: {
28
+        title: 'preview'
29
+      },
30
+      hidden: true,
31
+      is_menu: false
15 32
     }
16
-  },
17
-  { path: '/site/preview', component: () => import('@/scrm_pages/site/preview'), name: '预览', meta: { title: 'preview' }, hidden: true, is_menu: false }
18 33
 
19 34
   ]
20 35
 }

+ 4 - 3
src/scrm_pages/contactBox/index.vue Целия файл

@@ -642,13 +642,14 @@ export default {
642 642
 
643 643
                 newMsg = newMsgList[j];
644 644
                 formId = newMsg.getSession().id();
645
-                if (formId.indexOf("AdminKeFu")<0) {
646
-                    continue;
647
-                }else {
645
+                if (formId.indexOf("AdminKeFu")>-1) {
646
+                    
648 647
                     if (newMsg.getSession().id() == this.selToID) {//为当前聊天对象的消息
649 648
                         this.addMsg(newMsg);
650 649
                     }
651 650
                     msgList.push(newMsg.elems[0].content.text);
651
+                }else if(formId.indexOf("User")>-1){
652
+                    this.$message.success("有的新的消息,请到客服管理查看")
652 653
                 }
653 654
                 
654 655
             }

+ 57 - 9
src/scrm_pages/home/index.vue Целия файл

@@ -10,6 +10,21 @@
10 10
        </li>
11 11
      </ul>
12 12
    </div>
13
+   <div class="notice">
14
+     <el-table ref="notice_table" :data="notices" @row-click="expandRow">
15
+       <el-table-column
16
+        type="selection"
17
+        width="30">
18
+       </el-table-column>
19
+       <el-table-column type="expand" width="20">
20
+         <template slot-scope="scope">
21
+           <p class="notice_expand_content">{{ scope.row.content }}</p>
22
+           <p class="notice_expand_publisher">酷医聚客团队</p>
23
+         </template>
24
+       </el-table-column>
25
+       <el-table-column prop="title" label="所有通知"></el-table-column>
26
+     </el-table>
27
+   </div>
13 28
   </div>
14 29
 </div>
15 30
 
@@ -21,16 +36,34 @@ export default {
21 36
     return {
22 37
       numbers: [
23 38
         { num: '0', name: '新增数量', time: '昨日: 0' },
24
-        { num: '1', name: '总粉丝', time: '昨日: 0' },
25
-        { num: '1', name: '今日新增会员', time: '昨日: 0' },
26
-        { num: '1', name: '总会员', time: '昨日: 1' },
27
-        { num: '1', name: '商品浏览数', time: '昨日: 1' },
28
-        { num: '111', name: '文章浏览量', time: '昨日: 0' },
29
-        { num: '150', name: '活动浏览量', time: '昨日: 0' },
30
-        { num: '120', name: '新增订单', time: '昨日: 0' }
31
-      ]
39
+        { num: '0', name: '总粉丝', time: '昨日: 0' },
40
+        { num: '0', name: '今日新增会员', time: '昨日: 0' },
41
+        { num: '0', name: '总会员', time: '昨日: 0' },
42
+        { num: '0', name: '商品浏览数', time: '昨日: 0' },
43
+        { num: '0', name: '文章浏览量', time: '昨日: 0' },
44
+        { num: '0', name: '活动浏览量', time: '昨日: 0' },
45
+        { num: '0', name: '新增订单', time: '昨日: 0' }
46
+      ],
47
+
48
+      expanding_row: null,
49
+      notices: [
50
+        // { id: 1, title: "第一个通知", content: "第一个通知第一个通知第一个通知第一个通知第一个通知第一个通知第一个通知第一个通知第一个通知第一个通知" }, 
51
+        // { id: 2, title: "第二个通知", content: "第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知第二个通知" }, 
52
+      ],
32 53
     }
33
-  }
54
+  },
55
+  methods: {
56
+    expandRow: function(row, column, event) {
57
+      if (this.expanding_row == null || this.expanding_row.id != row.id) {
58
+        this.$refs.notice_table.toggleRowExpansion(this.expanding_row, false)
59
+        this.$refs.notice_table.toggleRowExpansion(row, true)
60
+        this.expanding_row = row
61
+      } else {
62
+        this.$refs.notice_table.toggleRowExpansion(this.expanding_row, false)
63
+        this.expanding_row = null
64
+      }
65
+    }
66
+  },
34 67
 }
35 68
 </script>
36 69
 
@@ -65,5 +98,20 @@ export default {
65 98
       }
66 99
     }
67 100
   }
101
+
102
+  .notice {
103
+    box-shadow: 4px 4px 8px #dde7f2;
104
+    margin-top: 20px;
105
+
106
+    .notice_expand_content {
107
+      color: #89949b;
108
+      font-size: 15px;
109
+      line-height: 25px;
110
+    }
111
+    .notice_expand_publisher {
112
+      font-size: 14px;
113
+      margin-top: 10px;
114
+    }
115
+  }
68 116
 }
69 117
 </style>

+ 860 - 92
src/scrm_pages/kefu/index.vue Целия файл

@@ -3,107 +3,141 @@
3 3
     <div class="position">
4 4
       <bread-crumb :crumbs='crumbs'></bread-crumb>
5 5
     </div>
6
-    <div class="app-container">
7
-        <div class="customer_plate white-bg">
8
-            <div class="aside">
9
-                <div class="customer_search clearfix">
10
-                <div class="super_searchbox">
11
-                    <span class="search_icon"></span>
12
-                    <input type="text" class="cus_search_input" v-model="searchname" placeholder="请输入客户名称">
13
-                </div>
14
-                </div>
15
-                <div class="client_list_box">
16
-                <div class="client_list">
17
-                    <ul class="users-list">
18
-                    <li v-for="(patient, key) in patientsMap" v-show="searchname == '' || (patient.name.indexOf(searchname)>=0)" :class="thisPatient && patient.user_id==thisPatient.user_id?'Active':''" :key="key" :id="'sessDiv_User_'+patient.user_id" class="item" :data-username="patient.name" @click="chooseThisUser(patient)">
19
-                        <span :id="'badgeDiv_User_'+patient.user_id" class="circle-count" style="" v-show="patient.unread_msg_count"><i class="count">{{patient.unread_msg_count}}</i></span>
20
-                        <figure :id="'faceImg_User_'+patient.user_id" class="session-pic"><img :src="patient.avatar"></figure>
21
-                        <h3 :id="'nameDiv_User_'+patient.user_id" class="nick">{{patient.name}}</h3>
22
-                        <!-- <span class="square-close" title="退出接待" :data-toid="'User_'+patient.user_id">×</span> -->
23
-                    </li>
24
-                    </ul>
25
-                </div>
26
-                </div>
6
+    <div class="app-container"  
7
+      v-loading="loading"
8
+      :element-loading-text="loadingtext">
9
+
10
+      <div class="customer_plate white-bg">
11
+        <div class="aside">
12
+          <div class="customer_search clearfix">
13
+            <div class="super_searchbox">
14
+              <span class="search_icon"></span>
15
+              <input type="text" class="cus_search_input" v-model="searchname" placeholder="请输入客户名称">
27 16
             </div>
17
+          </div>
18
+          <div class="client_list_box">
19
+            <div class="client_list">
20
+              <ul class="users-list">
21
+                <li v-for="(customer, key) in customersMap" v-show="searchname == '' || (customer.name.indexOf(searchname)>=0)" :class="thisCustomer && customer.user_id==thisCustomer.user_id?'Active':''" :key="key" :id="'sessDiv_User_'+customer.user_id" class="item" :data-username="customer.name" @click="chooseThisUser(customer)">
22
+                  <span :id="'badgeDiv_User_'+customer.user_id" class="circle-count" style="" v-show="customer.unread_msg_count"><i class="count">{{customer.unread_msg_count}}</i></span>
23
+                  <figure :id="'faceImg_User_'+customer.user_id" class="session-pic"><img :src="customer.avatar"></figure>
24
+                  <h3 :id="'nameDiv_User_'+customer.user_id" class="nick">{{customer.name}}</h3>
25
+                  <!-- <span class="square-close" title="退出接待" :data-toid="'User_'+customer.user_id">×</span> -->
26
+                </li>
27
+              </ul>
28
+            </div>
29
+          </div>
30
+        </div>
28 31
 
29
-            <div class="left_content">
30
-                <div class="conversation-admin">
31
-                <span class="nickname">
32
-                    <span></span>
33
-                </span>
34
-                </div>
35
-                <div class="message-list-wrap">
36
-                <div class="message-list-item">
37
-                    <ul class="message-list">
38
-                    
39
-                    <li v-for="(messageitem, key) in messageBox" :key="key" :id="'id_'+messageitem.id" class="message-item clearfix">
40
-                        <div class="time"><span v-html="messageitem.time"></span></div>
41
-                        <div class="user " :class="messageitem.float">
42
-                            <figure class="bg-pic circle-bg-pic">
43
-                            <div class="bg-pic-content">
44
-                                <img :src="messageitem.avatar">
45
-                            </div>
46
-                            </figure>
32
+        <div class="left_content">
33
+          <div class="conversation-admin">
34
+            <span class="nickname">
35
+              <span></span>
36
+            </span>
37
+          </div>
38
+          <div class="message-list-wrap">
39
+            <div class="message-list-item">
40
+              <ul class="message-list">
41
+              
42
+                <li v-for="(messageitem, key) in messageBox" :key="key" :id="'id_'+messageitem.id" class="message-item clearfix">
43
+                    <div class="time"><span v-html="messageitem.time"></span></div>
44
+                    <div class="user " :class="messageitem.float">
45
+                      <figure class="bg-pic circle-bg-pic">
46
+                        <div class="bg-pic-content">
47
+                          <img :src="messageitem.avatar">
47 48
                         </div>
48
-                        <div class="message-body " :class="messageitem.float"><div class="inner" v-html="messageitem.message"></div></div>
49
-                    </li>
50
-
51
-                    </ul>
52
-                </div>
53
-                <div class="no_session">
54
-                    <span class="no_session_pic"></span>
55
-                    <p class="no_session_txt">没有选中会话哦!</p>
56
-                </div>
57
-                </div>
58
-                <div class="send_box" v-loading="loadingbox" :element-loading-text="loadingboxtext">
59
-                <div class="inputer_actions">
60
-                    <!-- <div class="face">
61
-                    <span class="face_icon" @click="showEmotionDialog"></span>
62
-                    </div> -->
63
-                    <div class="picture">
64
-                    <span class="picture_icon" @click="selectPicClick"></span>
65
-                    </div>
66
-                </div>
67
-                <div id="wl_faces_box" class="wl_faces_box" v-bind:style="{left:'0px', display:facesBoxDisplay}">
68
-                    <div class="wl_faces_content">
69
-                    <div class="title">
70
-                        <ul>
71
-                        <li class="title_name">常用表情</li>
72
-                        <li class="wl_faces_close">
73
-                            <span @click="facesBoxDisplay='none'">&nbsp;</span>
74
-                        </li>
75
-                        </ul>
49
+                      </figure>
76 50
                     </div>
77
-                    <div id="wl_faces_main" class="wl_faces_main">
78
-                        <ul id="emotionUL">
79
-                        <li v-for="(emotion, key) in emotionList" :key="key" @click="chooseThisFace(emotion)">
80
-                            <img :src="emotion.src" :id="emotion.id" alt="" style="cursor:pointer;">
81
-                        </li>
82
-                        </ul>
83
-                    </div>
84
-                    </div>
85
-                    <div class="wlf_icon"></div>
86
-                </div>
87
-                <div class="inputer-area">
88
-                    <textarea
89
-                    maxlength="500" v-model="message"
90
-                    class="message-textarea transparent-txta"
91
-                    placeholder
92
-                    id="message"
93
-                    ></textarea>
94
-                </div>
95
-                <div class="send_box_btn">
96
-                    <button class="send_btn" id="sendMessaeg" @click="sendMessage">发送</button>
51
+                    <div class="message-body " :class="messageitem.float"><div class="inner" v-html="messageitem.message"></div></div>
52
+                </li>
53
+
54
+              </ul>
55
+            </div>
56
+            <div class="no_session">
57
+              <span class="no_session_pic"></span>
58
+              <p class="no_session_txt">没有选中会话哦!</p>
59
+            </div>
60
+          </div>
61
+          <div class="send_box" v-loading="loadingbox" :element-loading-text="loadingboxtext">
62
+            <div class="inputer_actions">
63
+              <!-- <div class="face">
64
+                <span class="face_icon" @click="showEmotionDialog"></span>
65
+              </div> -->
66
+              <div class="picture">
67
+                <span class="picture_icon" @click="selectPicClick"></span>
68
+              </div>
69
+            </div>
70
+            <div id="wl_faces_box" class="wl_faces_box" v-bind:style="{left:'0px', display:facesBoxDisplay}">
71
+              <div class="wl_faces_content">
72
+                <div class="title">
73
+                  <ul>
74
+                    <li class="title_name">常用表情</li>
75
+                    <li class="wl_faces_close">
76
+                      <span @click="facesBoxDisplay='none'">&nbsp;</span>
77
+                    </li>
78
+                  </ul>
97 79
                 </div>
80
+                <div id="wl_faces_main" class="wl_faces_main">
81
+                  <ul id="emotionUL">
82
+                    <li v-for="(emotion, key) in emotionList" :key="key" @click="chooseThisFace(emotion)">
83
+                      <img :src="emotion.src" :id="emotion.id" alt="" style="cursor:pointer;">
84
+                    </li>
85
+                  </ul>
98 86
                 </div>
87
+              </div>
88
+              <div class="wlf_icon"></div>
89
+            </div>
90
+            <div class="inputer-area">
91
+              <textarea
92
+                maxlength="500" v-model="message"
93
+                class="message-textarea transparent-txta"
94
+                placeholder
95
+                id="message"
96
+              ></textarea>
99 97
             </div>
98
+            <div class="send_box_btn">
99
+              <button class="send_btn" id="sendMessaeg" @click="sendMessage">发送</button>
100
+            </div>
101
+          </div>
102
+        </div>
103
+      </div>
104
+
105
+      <el-dialog title="发送图片" :visible.sync="dialogPicModal">
106
+        <el-form ref="form" :model="form" label-width="80px">
107
+          <el-form-item label="选择">
108
+            <input type="file" name="filebox" id="upd_pic" @change="fileOnChange(this)">
109
+
110
+          </el-form-item>
111
+          <el-form-item label="预览">
112
+          <img :src="dialogImageUrl"  alt="" style="width:100%">
113
+          </el-form-item>
114
+          <el-form-item label="进度">
115
+            <el-progress :text-inside="true" :stroke-width="18" :percentage="percentage" status="success"></el-progress>
116
+          </el-form-item>
117
+        </el-form>
118
+        <div slot="footer" class="dialog-footer">
119
+          <el-button type="primary" @click="uploadPic">发送</el-button>
100 120
         </div>
121
+      </el-dialog>
122
+
123
+
101 124
     </div>
102 125
   </div>
103 126
 </template>
104 127
 
105 128
 <script>
106
-  import BreadCrumb from '../components/bread-crumb'
129
+import BreadCrumb from '../components/bread-crumb'
130
+import {getusersig,GetAllMembers} from "@/api/kefu/tencentim";
131
+
132
+// let webim = require('../../../static/im/sdk/sdk/webim');
133
+import webim from 'webim-tencent'
134
+// let webim = webimSDK.webim;
135
+let selType =  webim.SESSION_TYPE.C2C;
136
+let reqMsgCount = 15; //每次请求的历史消息(c2c获取群)条数
137
+var msgList = [];
138
+var getPrePageC2CHistroyMsgInfoMap = {};
139
+var kgetPrePageC2CHistroyMsgInfoMap = {};
140
+
107 141
   export default {
108 142
     name: 'commentList',
109 143
     components:{
@@ -115,15 +149,749 @@
115 149
           { path: false, name: '客服管理' },
116 150
           { path: false, name: '咨询管理' }
117 151
         ],
152
+        
153
+        imloginFlag:false,
154
+        customer_id:0,
118 155
         searchname:'',
119
-        patientsMap:{},
120
-        thisPatient:null,
156
+        facesBoxDisplay:'none',
157
+        emotionList:[],
158
+        percentage:0,
159
+        dialogImageUrl:'',
160
+        form:{name:'', region:''},
161
+        dialogPicModal:false,
162
+        loading:false,
163
+        loadingtext:'加载中...',
164
+        loadingbox:true,
165
+        loadingboxtext:'请选择用户',
166
+        selToID:'',
167
+        useradmin:{
168
+          usersig: '',
169
+          Indentifier: '',
170
+          appIDAt3rd:'',
171
+          IdentifierNick:'',
172
+          HeadURL:'',
173
+        },
174
+        loginInfo: {
175
+            sdkAppID: '', //用户所属应用id,必填
176
+            appIDAt3rd: '',
177
+            identifier: '', //当前用户ID,必须是否字符串类型,必填
178
+            accountType: '', //用户所属应用帐号类型,必填
179
+            userSig: '', //当前用户身份凭证,必须是字符串类型,必填
180
+            identifierNick: '', //当前用户昵称,不用填写,登录接口会返回用户的昵称,如果没有设置,则返回用户的id
181
+            headurl: '', //当前用户默认头像,选填,如果设置过头像,则可以通过拉取个人资料接口来得到头像信息
182
+        },
183
+        loginOptions:{},
184
+        userSigFlag:false,
185
+        userListFlag:false,
186
+        customers:[],
187
+        customersUnique:[],
188
+        customersMap:{},
189
+        thisCustomer:null,
190
+        selSess:null,
191
+        message:'',
121 192
         messageBox:[],
122 193
       }
123 194
     },
124
-    methods:{},
195
+    methods: {
196
+      
197
+      bindScrollHistoryEvent(axtion){
198
+        var msgflow = document.getElementsByClassName("message-list-item")[0];
199
+        var _this = this;
200
+        if (axtion=='init') {
201
+          msgflow.onscroll = function () {
202
+                if (msgflow.scrollTop == 0) {
203
+                    msgflow.scrollTop = 10;
204
+                    if (selType == webim.SESSION_TYPE.C2C) {
205
+                        _this.getPrePageC2CHistoryMsgs();
206
+                    } 
207
+                }
208
+            }
209
+        }else {
210
+          
211
+              msgflow.onscroll = null;
212
+        }
213
+      },
214
+      selectPicClick() {
215
+        this.dialogPicModal = true;
216
+      },  
217
+      chooseThisFace(face) {
218
+        this.message = this.message + face.id;
219
+        this.facesBoxDisplay='none';
220
+      },
221
+      onProgressCallBack(loadedSize, totalSize) {
222
+          this.percentage = (loadedSize / totalSize) * 100;
223
+      },
224
+      uploadPic(){
225
+        var _this = this;
226
+        var uploadFiles = document.getElementById('upd_pic');
227
+        var file = uploadFiles.files[0];
228
+        var businessType;//业务类型,1-发群图片,2-向好友发图片
229
+        if (selType != webim.SESSION_TYPE.C2C) {//向好友发图片
230
+          this.$message.error('不支持群聊');
231
+          return; 
232
+        } 
233
+        businessType = webim.UPLOAD_PIC_BUSSINESS_TYPE.C2C_MSG;
234
+        //封装上传图片请求
235
+        var opt = {
236
+            'file': file, //图片对象
237
+            'onProgressCallBack': this.onProgressCallBack, //上传图片进度条回调函数
238
+            //'abortButton': document.getElementById('upd_abort'), //停止上传图片按钮
239
+            'To_Account': this.selToID, //接收者
240
+            'businessType': businessType//业务类型
241
+        };
242
+        //上传图片
243
+        webim.uploadPic(opt,
244
+            function (resp) {
245
+                //上传成功发送图片
246
+                _this.sendPic(resp, file.name);
247
+                
248
+            },
249
+            function (err) {
250
+                alert(err.ErrorInfo);
251
+            }
252
+        );
253
+      },
254
+      sendPic(images, imgName) {
255
+        var _this = this;
256
+          if (!this.selToID) {
257
+              alert("请先选择对话用户");
258
+              return;
259
+          }
260
+
261
+          if (!this.selSess) {
262
+              this.selSess = new webim.Session(selType, this.selToID, this.selToID, this.thisCustomer.avatar, Math.round(new Date().getTime() / 1000));
263
+          }
264
+          var msg = new webim.Msg(this.selSess, true, -1, -1, -1, this.loginInfo.identifier, 0, this.loginInfo.identifierNick);
265
+          var images_obj = new webim.Msg.Elem.Images(images.File_UUID);
266
+          for (var i in images.URL_INFO) {
267
+              var img = images.URL_INFO[i];
268
+              var newImg;
269
+              var type;
270
+              switch (img.PIC_TYPE) {
271
+                  case 1://原图
272
+                      type = 1;//原图
273
+                      break;
274
+                  case 2://小图(缩略图)
275
+                      type = 3;//小图
276
+                      break;
277
+                  case 4://大图
278
+                      type = 2;//大图
279
+                      break;
280
+              }
281
+              newImg = new webim.Msg.Elem.Images.Image(type, img.PIC_Size, img.PIC_Width, img.PIC_Height, img.DownUrl);
282
+              images_obj.addImage(newImg);
283
+          }
284
+          msg.addImage(images_obj);
285
+          //if(imgName){
286
+          //    var data=imgName;//通过自定义消息中的data字段保存图片名称
287
+          //    var custom_obj = new webim.Msg.Elem.Custom(data, '', '');
288
+          //    msg.addCustom(custom_obj);
289
+          //}
290
+          //调用发送图片消息接口
291
+          webim.sendMsg(msg, function (resp) {
292
+              if (selType == webim.SESSION_TYPE.C2C) {//私聊时,在聊天窗口手动添加一条发的消息,群聊时,长轮询接口会返回自己发的消息
293
+                  _this.addMsg(msg);
294
+              }
295
+          }, function (err) {
296
+              alert(err.ErrorInfo);
297
+          });
298
+      },
299
+      showEmotionDialog(){
300
+        this.facesBoxDisplay = 'block';
301
+      },
302
+      fileOnChange(){
303
+        var file = document.querySelector('input[name=filebox]').files[0]
304
+        if (!window.File || !window.FileList || !window.FileReader) {
305
+            alert("您的浏览器不支持File Api");
306
+            return;
307
+        }
308
+
309
+        var fileSize = file.size;
310
+
311
+        //先检查图片类型和大小
312
+        if (!this.checkPic(file, fileSize)) {
313
+            return;
314
+        }
315
+
316
+        var _this = this;
317
+        //预览图片
318
+        var reader = new FileReader();
319
+        reader.onload = (function (file) {
320
+            return function (e) {
321
+              _this.dialogImageUrl = this.result;
322
+            };
323
+        })(file);
324
+        //预览图片
325
+        reader.readAsDataURL(file);
326
+      },
327
+      
328
+      checkPic(obj, fileSize) {
329
+          var picExts = 'jpg|jpeg|png|bmp|gif|webp';
330
+          var photoExt = obj.name.substr(obj.name.lastIndexOf(".") + 1).toLowerCase();//获得文件后缀名
331
+          var pos = picExts.indexOf(photoExt);
332
+          if (pos < 0) {
333
+            this.$message.error('您选中的文件不是图片,请重新选择');
334
+              return false;
335
+          }
336
+          fileSize = Math.round(fileSize / 1024 * 100) / 100; //单位为KB
337
+          if (fileSize > 30 * 1024) {
338
+            this.$message.error('您选择的图片大小超过限制(最大为30M),请重新选择');
339
+              return false;
340
+          }
341
+          return true;
342
+      },
343
+      chooseThisUser(customer) {
344
+        if(customer.user_id<=0) {
345
+          this.$message.error('选择对象还未关联用户,无法进行对话');
346
+          return false;
347
+        }
348
+        this.thisCustomer = customer;
349
+        this.selToID = "User_" + customer.user_id;
350
+        this.onSelSess(selType, this.selToID);
351
+        this.loadingbox = false;
352
+      },
353
+      sendMessage(){
354
+        if(this.thisCustomer == null) {
355
+          this.$message.error('请先选择对话用户');
356
+          return false;
357
+        }
358
+        var message = this.message.trim();
359
+        var msgLen = webim.Tool.getStrBytes(message);
360
+        if(message == '') {
361
+          this.$message.error('不能发送空消息');
362
+          return false;
363
+        }
364
+        var maxLen, errInfo;
365
+        if (selType == webim.SESSION_TYPE.C2C) {
366
+          maxLen = webim.MSG_MAX_LENGTH.C2C;
367
+          errInfo = "消息长度超出限制(最多" + Math.round(maxLen / 3) + "汉字)";
368
+        } else {
369
+          maxLen = webim.MSG_MAX_LENGTH.GROUP;
370
+          errInfo = "消息长度超出限制(最多" + Math.round(maxLen / 3) + "汉字)";
371
+        }
372
+        if (msgLen > maxLen) {
373
+          this.$message.error(errInfo);
374
+          return false;
375
+        }
376
+        this.handleMsgSend(message);
377
+
378
+      },
379
+      webimlogin(){
380
+        let _this = this;
381
+        let listeners = {
382
+          "onConnNotify": this.onConnNotify //监听连接状态回调变化事件,必填
383
+          ,
384
+          "jsonpCallback": this.jsonpCallback //IE9(含)以下浏览器用到的jsonp回调函数,
385
+          ,
386
+          "onMsgNotify": this.onMsgNotify //监听新消息(私聊,普通群(非直播聊天室)消息,全员推送消息)事件,必填
387
+          ,
388
+          "onProfileSystemNotifys": this.onProfileSystemNotifys //监听资料系统(自己或好友)通知事件,选填
389
+          ,
390
+          "onKickedEventCall": this.onKickedEventCall //被其他登录实例踢下线
391
+          ,
392
+          "onC2cEventNotifys": this.onC2cEventNotifys //监听C2C系统消息通道
393
+        };
394
+
395
+        webim.login(this.loginInfo, listeners, this.loginOptions, 
396
+          function(resp){
397
+            _this.$message.success('登录成功');
398
+            _this.imloginFlag = true;
399
+          },
400
+          function(err) {
401
+            _this.$message.error('登录错误');
402
+          }
403
+        );
404
+      
405
+      },
406
+      onConnNotify(){},
407
+      jsonpCallback(){},
408
+      onMsgNotify(newMsgList){
409
+        //监听新消息事件
410
+        var msgList = [];
411
+        var dateStart = null;
412
+        var dateEnd = null;
413
+
414
+        //console.warn(newMsgList);
415
+        var sess, newMsg, formId;
416
+        //获取所有聊天会话
417
+        var sessMap = webim.MsgStore.sessMap();
418
+
419
+        for (var j in newMsgList) {//遍历新消息
420
+
421
+            newMsg = newMsgList[j];
422
+            formId = newMsg.getSession().id();
423
+            if (formId.indexOf("User")<0) {
424
+                continue;
425
+            }else {
426
+                // if (!this.selToID) {//没有聊天对象
427
+                //     this.selToID = newMsg.getSession().id();
428
+                //     selType = newMsg.getSession().type();
429
+                //     this.selSess = newMsg.getSession();
430
+                //     if(this.selToID in this.customersMap) {
431
+                //       this.thisCustomer = this.customersMap[this.selToID];
432
+                //     }
433
+                // }
434
+                if (newMsg.getSession().id() == this.selToID) {//为当前聊天对象的消息
435
+                    this.addMsg(newMsg);
436
+                }
437
+                msgList.push(newMsg.elems[0].content.text);
438
+            }
439
+
440
+            
441
+        }
442
+
443
+
444
+        for (var i in sessMap) {
445
+            sess = sessMap[i];
446
+            if (this.selToID != sess.id() && sess.id() in this.customersMap) {//更新其他聊天对象的未读消息数
447
+              var unread = sess.unread();
448
+              if(unread>99) {
449
+                unread = '99+';
450
+              }
451
+              // var custimerItem = this.customersMap[sess.id()];
452
+              // custimerItem.unread_msg_count = unread;
453
+              var thisdiv = document.getElementById('badgeDiv_'+ sess.id());
454
+              thisdiv.innerHTML = '<i class="count">' + unread + '</i>';
455
+              thisdiv.style.display = 'block';
456
+              this.$set(this.customersMap[sess.id()], 'unread_msg_count', unread);
457
+            }
458
+        }
459
+    
460
+      },
461
+      onProfileSystemNotifys(){},
462
+      onKickedEventCall(){},
463
+      onC2cEventNotifys(){},
464
+      initUnreadMsgCount() {
465
+          var sess;
466
+          var sessMap = webim.MsgStore.sessMap();
467
+          for (var i in sessMap) {
468
+              sess = sessMap[i];
469
+              if (this.selToID != sess.id() && sess.id() in this.customersMap) {//更新其他聊天对象的未读消息数
470
+                var unread = sess.unread();
471
+                if(unread>99) {
472
+                  unread = '99+';
473
+                }
474
+                // var custimerItem = this.customersMap[sess.id()];
475
+                // custimerItem.unread_msg_count = unread;
476
+                
477
+                var thisdiv = document.getElementById('badgeDiv_'+ sess.id());
478
+                thisdiv.innerHTML = '<i class="count">' + unread + '</i>';
479
+                thisdiv.style.display = 'block';
480
+                this.$set(this.customersMap[sess.id()], 'unread_msg_count', unread);
481
+                
482
+                // this.$set(this.customersMap, sess.id(), custimerItem);
483
+              }
484
+          }
485
+      },
486
+      getusersig() {
487
+        getusersig().then(response => {
488
+          if (response.data.state == 0) {
489
+            this.$message.error(response.data.msg);
490
+            return false;
491
+          } else {
492
+            this.useradmin = {
493
+              usersig: response.data.data.sig,
494
+              Indentifier: response.data.data.Indentifier,
495
+              appIDAt3rd:response.data.data.appIDAt3rd,
496
+              IdentifierNick:response.data.data.IdentifierNick,
497
+              HeadURL:response.data.data.HeadURL,
498
+            } 
499
+          
500
+            this.loginInfo.appIDAt3rd = this.useradmin.appIDAt3rd
501
+            this.loginInfo.identifier = this.useradmin.Indentifier
502
+            this.loginInfo.userSig = this.useradmin.usersig
503
+            this.loginInfo.identifierNick = this.useradmin.IdentifierNick
504
+            this.loginInfo.headurl = this.useradmin.HeadURL
505
+            this.loginInfo.sdkAppID = response.data.data.sdkAppID
506
+            this.loginInfo.accountType = response.data.data.accountType
507
+            this.userSigFlag = true;
508
+          }
509
+        });
510
+        this.bindWechatDialog = true
511
+      },
512
+
513
+      GetAllMembers(){
514
+        GetAllMembers().then(response=>{
515
+          if (response.data.state == 1) { 
516
+            this.customers = response.data.data.members;
517
+            for (const key in this.customers) {
518
+              var custimerItem = this.customers[key];
519
+              var mapKey = "User_" + custimerItem.user_id;
520
+              if(custimerItem.user_id==0 || mapKey in this.customersMap) {
521
+                continue;
522
+              }
523
+              custimerItem['sortno'] = -1;
524
+              custimerItem['unread_msg_count'] = '';
525
+              this.$set(this.customersMap, mapKey, custimerItem);
526
+              this.customersUnique.push(custimerItem);
527
+            }
528
+            this.userListFlag = true;
529
+          } else {
530
+
531
+          }
532
+        });
533
+      },
534
+      addMsg(msg, prepage) {
535
+        var messageitem = {
536
+          id:'',
537
+          message:'',
538
+          avatar:'',
539
+          time:'',
540
+          float:''
541
+        };
542
+        var isSelfSend, fromAccount, fromAccountNick, fromAccountImage, sessType, subType;
543
+
544
+        //webim.SESSION_TYPE.GROUP-群聊,
545
+        //webim.SESSION_TYPE.C2C-私聊,
546
+        sessType = msg.getSession().type();
547
+        isSelfSend = msg.getIsSend();//消息是否为自己发的
548
+        fromAccount = msg.getFromAccount();
549
+        if (!fromAccount) {
550
+            return;
551
+        }
552
+        if (isSelfSend) {//如果是自己发的消息
553
+          messageitem.avatar = this.loginInfo.headurl;
554
+          messageitem.float = "fr";
555
+        } else {
556
+          messageitem.avatar = this.thisCustomer.avatar;
557
+          messageitem.float = "fl";
558
+        }
559
+        messageitem.id = msg.random;
560
+        messageitem.time = webim.Tool.formatText2Html(webim.Tool.formatTimeStamp(msg.getTime()));
561
+        messageitem.message = this.convertMsgtoHtml(msg)
562
+        this.messageBox.push(messageitem);
563
+
564
+        var msgflow = document.getElementsByClassName("message-list-item")[0];
565
+        if (prepage) {
566
+            //300ms后,等待图片加载完,滚动条自动滚动到底部
567
+            if (msgflow.scrollTop == 0) {
568
+                setTimeout(function () {
569
+                    msgflow.scrollTop = 0;
570
+                }, 300);
571
+            }
572
+        } else {
573
+            //300ms后,等待图片加载完,滚动条自动滚动到底部
574
+            setTimeout(function () {
575
+                msgflow.scrollTop = msgflow.scrollHeight;
576
+            }, 300);
577
+        }
578
+
579
+      },
580
+      convertMsgtoHtml(msg) {
581
+          var html = "",
582
+              elems, elem, type, content;
583
+          elems = msg.getElems(); //获取消息包含的元素数组
584
+          var count = elems.length;
585
+          for (var i = 0; i < count; i++) {
586
+              elem = elems[i];
587
+              type = elem.getType();//获取元素类型
588
+              content = elem.getContent();//获取元素对象
589
+              switch (type) {
590
+                  case webim.MSG_ELEMENT_TYPE.TEXT:
591
+                      var eleHtml = this.convertTextMsgToHtml(content);
592
+                      //转义,防XSS
593
+                      html += webim.Tool.formatText2Html(eleHtml);
594
+                      break;
595
+                  case webim.MSG_ELEMENT_TYPE.FACE:
596
+                      html += this.convertFaceMsgToHtml(content);
597
+                      break;
598
+                  case webim.MSG_ELEMENT_TYPE.IMAGE:
599
+                      if (i <= count - 2) {
600
+                          var customMsgElem = elems[i + 1];//获取保存图片名称的自定义消息elem
601
+                          var imgName = customMsgElem.getContent().getData();//业务可以自定义保存字段,demo中采用data字段保存图片文件名
602
+                          html += this.convertImageMsgToHtml(content, imgName);
603
+                          i++;//下标向后移一位
604
+                      } else {
605
+                          html += this.convertImageMsgToHtml(content);
606
+                      }
607
+                      break;
608
+                  case webim.MSG_ELEMENT_TYPE.SOUND:
609
+                      html += this.convertSoundMsgToHtml(content);
610
+                      break;
611
+                  case webim.MSG_ELEMENT_TYPE.FILE:
612
+                      html += this.convertFileMsgToHtml(content);
613
+                      break;
614
+                  case webim.MSG_ELEMENT_TYPE.LOCATION:
615
+                      html += this.convertLocationMsgToHtml(content);
616
+                      break;
617
+                  case webim.MSG_ELEMENT_TYPE.CUSTOM:
618
+                      var eleHtml = this.convertCustomMsgToHtml(content);
619
+                      //转义,防XSS
620
+                      html += webim.Tool.formatText2Html(eleHtml);
621
+                      break;
622
+                  case webim.MSG_ELEMENT_TYPE.GROUP_TIP:
623
+                      var eleHtml = this.convertGroupTipMsgToHtml(content);
624
+                      //转义,防XSS
625
+                      html += webim.Tool.formatText2Html(eleHtml);
626
+                      break;
627
+                  default:
628
+                      webim.Log.error('未知消息元素类型: elemType=' + type);
629
+                      break;
630
+              }
631
+          }
632
+          return html;
633
+      },
634
+      convertTextMsgToHtml(content) {
635
+          return content.getText();
636
+      },
637
+      //解析表情消息元素
638
+      convertFaceMsgToHtml(content) {
639
+          var faceUrl = null;
640
+          var data = content.getData();
641
+          var index = webim.EmotionDataIndexs[data];
642
+
643
+          var emotion = webim.Emotions[index];
644
+          if (emotion && emotion[1]) {
645
+              faceUrl = emotion[1];
646
+          }
647
+          if (faceUrl) {
648
+              return "<img src='" + faceUrl + "'/>";
649
+          } else {
650
+              return data;
651
+          }
652
+      },
653
+      //解析图片消息元素
654
+      convertImageMsgToHtml(content, imageName) {
655
+          var smallImage = content.getImage(webim.IMAGE_TYPE.SMALL);//小图
656
+          var bigImage = content.getImage(webim.IMAGE_TYPE.LARGE);//大图
657
+          var oriImage = content.getImage(webim.IMAGE_TYPE.ORIGIN);//原图
658
+          if (!bigImage) {
659
+              bigImage = smallImage;
660
+          }
661
+          if (!oriImage) {
662
+              oriImage = smallImage;
663
+          }
664
+          return "<img name='" + imageName + "' src='" + smallImage.getUrl() + "#" + bigImage.getUrl() + "#" + oriImage.getUrl() + "' style='CURSOR: hand' id='" + content.getImageId() + "' bigImgUrl='" + bigImage.getUrl() + "' />";
665
+      },
666
+      onSelSess(sess_type, to_id) {
667
+        var _this = this;
668
+        if(this.selToID != to_id) {
669
+          this.message = '';
670
+        }
671
+        if (sess_type == webim.SESSION_TYPE.GROUP) {
672
+          this.$message.error("不支持群聊");
673
+          return;
674
+        }
675
+        this.messageBox = [];
676
+        this.bindScrollHistoryEvent('reset');
677
+        this.getLastC2CHistoryMsgs(function (msgList) {
678
+            _this.getHistoryMsgCallback(msgList);
679
+            _this.bindScrollHistoryEvent('init');
680
+            //绑定滚动操作
681
+        }, function (err) {
682
+            alert(err.ErrorInfo);
683
+        });
684
+      },
685
+      getHistoryMsgCallback(msgList, prepage) {
686
+          var msg;
687
+          var prepage = prepage || false;
688
+
689
+          //如果是加载前几页的消息,消息体需要prepend,所以先倒排一下
690
+          if (prepage) {
691
+              msgList.reverse();
692
+          }
693
+
694
+          for (var j in msgList) {//遍历新消息
695
+              msg = msgList[j];
696
+              if (msg.getSession().id() == this.selToID) {//为当前聊天对象的消息
697
+                  this.selSess = msg.getSession();
698
+                  //在聊天窗体中新增一条消息
699
+                  this.addMsg(msg, prepage);
700
+                  var thisdiv = document.getElementById('badgeDiv_'+ this.selToID);
701
+                  thisdiv.innerHTML = '<i class="count"></i>';
702
+                  thisdiv.style.display = 'none';
703
+              }
704
+          }
705
+          //消息已读上报,并将当前会话的消息设置成自动已读
706
+          webim.setAutoRead(this.selSess, true, true);
707
+      },
708
+      getPrePageC2CHistoryMsgs  (cbOk, cbError) {
709
+        var _this = this;
710
+          if (selType == webim.SESSION_TYPE.GROUP) {
711
+            _this.$message.error("当前的聊天类型为群聊天,不能进行拉取好友历史消息操作");
712
+              return;
713
+          }
714
+          var tempInfo = getPrePageC2CHistroyMsgInfoMap[_this.selToID];//获取下一次拉取的c2c消息时间和消息Key
715
+          var lastMsgTime;
716
+          var msgKey;
717
+          if (tempInfo) {
718
+              lastMsgTime = tempInfo.LastMsgTime;
719
+              msgKey = tempInfo.MsgKey;
720
+          } else {
721
+              _this.$message.error("获取下一次拉取的c2c消息时间和消息Key为空");
722
+              return;
723
+          }
724
+          var options = {
725
+              'Peer_Account': _this.selToID, //好友帐号
726
+              'MaxCnt': reqMsgCount, //拉取消息条数
727
+              'LastMsgTime': lastMsgTime, //最近的消息时间,即从这个时间点向前拉取历史消息
728
+              'MsgKey': msgKey
729
+          };
730
+          webim.getC2CHistoryMsgs(
731
+              options,
732
+              function (resp) {
733
+                  var complete = resp.Complete;//是否还有历史消息可以拉取,1-表示没有,0-表示有
734
+                  if (resp.MsgList.length == 0) {
735
+                      webim.Log.warn("没有历史消息了:data=" + JSON.stringify(options));
736
+                      return;
737
+                  }
738
+                  getPrePageC2CHistroyMsgInfoMap[_this.selToID] = {//保留服务器返回的最近消息时间和消息Key,用于下次向前拉取历史消息
739
+                      'LastMsgTime': resp.LastMsgTime,
740
+                      'MsgKey': resp.MsgKey
741
+                  };
742
+                  if (cbOk) {
743
+                      cbOk(resp.MsgList);
744
+                  } else {
745
+                      _this.getHistoryMsgCallback(resp.MsgList, true);
746
+                  }
747
+              },
748
+              cbError
749
+          );
750
+      },
751
+      getLastC2CHistoryMsgs (cbOk, cbError) {
752
+        var _this = this;
753
+          var lastMsgTime = 0;//第一次拉取好友历史消息时,必须传0
754
+          var msgKey = '';
755
+          var options = {
756
+              'Peer_Account': this.selToID, //好友帐号
757
+              'MaxCnt': reqMsgCount, //拉取消息条数
758
+              'LastMsgTime': lastMsgTime, //最近的消息时间,即从这个时间点向前拉取历史消息
759
+              'MsgKey': msgKey
760
+          };
761
+          this.selSess = null;
762
+          webim.MsgStore.delSessByTypeId(selType, this.selToID);
763
+
764
+          webim.getC2CHistoryMsgs(
765
+              options,
766
+              function (resp) {
767
+                  var complete = resp.Complete;//是否还有历史消息可以拉取,1-表示没有,0-表示有
768
+
769
+                  if (resp.MsgList.length == 0) {
770
+                      webim.Log.warn("没有历史消息了:data=" + JSON.stringify(options));
771
+                      return;
772
+                  }
773
+                  getPrePageC2CHistroyMsgInfoMap[_this.selToID] = {//保留服务器返回的最近消息时间和消息Key,用于下次向前拉取历史消息
774
+                      'LastMsgTime': resp.LastMsgTime,
775
+                      'MsgKey': resp.MsgKey
776
+                  };
777
+                  
778
+                  if (cbOk)
779
+                      cbOk(resp.MsgList);
780
+              },
781
+              cbError
782
+          );
783
+      },
784
+      handleMsgSend(msgContent) {
785
+          
786
+          var selSess = new webim.Session(selType, this.selToID, this.selToID, this.thisCustomer.avatar, Math.round(new Date().getTime() / 1000));
787
+          
788
+          var isSend = true; //是否为自己发送
789
+          var seq = -1; //消息序列,-1表示sdk自动生成,用于去重
790
+          var random = Math.round(Math.random() * 4294967296); //消息随机数,用于去重
791
+          var msgTime = Math.round(new Date().getTime() / 1000); //消息时间戳
792
+          var subType; //消息子类型
793
+          if (selType == webim.SESSION_TYPE.C2C) {
794
+              subType = webim.C2C_MSG_SUB_TYPE.COMMON;
795
+          } else {
796
+              subType = webim.GROUP_MSG_SUB_TYPE.COMMON;
797
+          }
798
+          var msg = new webim.Msg(selSess, isSend, seq, random, msgTime, this.loginInfo.identifier, subType, this.loginInfo.identifierNick);
799
+
800
+          var text_obj, face_obj, tmsg, emotionIndex, emotion, restMsgIndex;
801
+          //解析文本和表情
802
+          var expr = /\[[^[\]]{1,3}\]/mg;
803
+          var emotions = msgContent.match(expr);
804
+          if (!emotions || emotions.length < 1) {
805
+              text_obj = new webim.Msg.Elem.Text(msgContent);
806
+              msg.addText(text_obj);
807
+          } else {
808
+              for (var i = 0; i < emotions.length; i++) {
809
+                  tmsg = msgContent.substring(0, msgContent.indexOf(emotions[i]));
810
+                  if (tmsg) {
811
+                      text_obj = new webim.Msg.Elem.Text(tmsg);
812
+                      msg.addText(text_obj);
813
+                  }
814
+                  emotionIndex = webim.EmotionDataIndexs[emotions[i]];
815
+                  emotion = webim.Emotions[emotionIndex];
816
+
817
+                  if (emotion) {
818
+                      face_obj = new webim.Msg.Elem.Face(emotionIndex, emotions[i]);
819
+                      msg.addFace(face_obj);
820
+                  } else {
821
+                      text_obj = new webim.Msg.Elem.Text(emotions[i]);
822
+                      msg.addText(text_obj);
823
+                  }
824
+                  restMsgIndex = msgContent.indexOf(emotions[i]) + emotions[i].length;
825
+                  msgContent = msgContent.substring(restMsgIndex);
826
+              }
827
+              if (msgContent) {
828
+                  text_obj = new webim.Msg.Elem.Text(msgContent);
829
+                  msg.addText(text_obj);
830
+              }
831
+          }
832
+
833
+          msg.PushInfo = {
834
+              "PushFlag": 0,
835
+              "Desc": '测试离线推送内容', //离线推送内容
836
+              "Ext": '测试离线推送透传内容', //离线推送透传内容
837
+              "AndroidInfo": {
838
+                  "Sound": "android.mp3" //离线推送声音文件路径。
839
+              },
840
+              "ApnsInfo": {
841
+                  "Sound": "apns.mp3", //离线推送声音文件路径。
842
+                  "BadgeMode": 1
843
+              }
844
+          };
845
+
846
+          msg.PushInfoBoolean = true; //是否开启离线推送push同步
847
+          msg.sending = 1;
848
+          msg.originContent = msgContent;
849
+          // turnoffFaces_box();
850
+          var _this = this;
851
+
852
+          webim.sendMsg(msg, function (resp) {
853
+            _this.addMsg(msg);
854
+            _this.message = '';
855
+          }, function (err) {
856
+              console.log(err)
857
+          });
858
+      },
859
+      
860
+    },
861
+    watch:{
862
+      userSigFlag(newValue){
863
+        if(this.userSigFlag) {
864
+          this.webimlogin();
865
+        }
866
+      },
867
+      userListFlag(newValue){
868
+        if(this.userListFlag && this.imloginFlag) {
869
+          this.initUnreadMsgCount();
870
+        }
871
+
872
+      },
873
+      imloginFlag(newvalue) {
874
+        if(this.userListFlag && this.imloginFlag) {
875
+          this.initUnreadMsgCount();
876
+        }
877
+      }
878
+    },
125 879
     created(){
126
-    }
880
+      
881
+      this.GetAllMembers();
882
+      this.getusersig();
883
+
884
+      this.emotionList = [];
885
+
886
+      for (var index in webim.Emotions) {
887
+        var item = {
888
+          id:webim.Emotions[index][0],
889
+          src:webim.Emotions[index][1],
890
+        };
891
+        this.emotionList.push(item);
892
+      }
893
+
894
+    },
127 895
   }
128 896
 </script>
129 897
 

+ 4 - 1
src/scrm_pages/marketing_tool/activity_share.vue Целия файл

@@ -24,7 +24,7 @@
24 24
                     <p class="desc">以短信方式推送给客户</p>
25 25
                 </div>
26 26
                 <div class="btn-panel">
27
-                    <el-button type="primary">短信推送</el-button>
27
+                    <el-button type="primary" @click="smsAction">短信推送</el-button>
28 28
                 </div>
29 29
             </div>
30 30
             <div class="link-share-panel">
@@ -114,6 +114,9 @@ export default {
114 114
             document.body.removeChild(input);
115 115
 
116 116
             this.$message.success("已复制链接")
117
+        },
118
+        smsAction: function() {
119
+            this.$router.push({ path: "/sms/send", query: { action: 1, id: this.activity_id } })
117 120
         }
118 121
     },
119 122
 }

+ 87 - 76
src/scrm_pages/members/components/CreateMemberForm.vue Целия файл

@@ -30,8 +30,49 @@
30 30
                 </el-row>
31 31
                 <el-row>
32 32
                     <el-col :span="12">
33
-                        <el-form-item label="城市:" required prop="city">
34
-                            <el-cascader
33
+                        <el-form-item label="城市:" required prop="city_id">
34
+                            <el-select
35
+                                v-model="form.province_id"
36
+                                style="width:32%"
37
+                                placeholder="选择地址"
38
+                                @change="changeProvince">
39
+                                <el-option
40
+                                v-for="item in provinceOptions"
41
+                                :key="item.id"
42
+                                :label="item.name"
43
+                                :value="item.id">
44
+                                </el-option>
45
+                            </el-select>
46
+                            
47
+                            <el-select
48
+                                v-model="form.city_id"
49
+                                style="width:32%"
50
+                                placeholder="选择地址"
51
+                                @change="changeCity"
52
+                                v-show="cityOptions.length>0"
53
+                                >
54
+                                <el-option
55
+                                v-for="item in cityOptions"
56
+                                :key="item.id"
57
+                                :label="item.name"
58
+                                :value="item.id">
59
+                                </el-option>
60
+                            </el-select>
61
+                            
62
+                            <el-select
63
+                                v-model="form.district_id"
64
+                                style="width:32%"
65
+                                placeholder="选择地址"
66
+                                v-show="distictOptions.length>0"
67
+                                >
68
+                                <el-option
69
+                                v-for="item in distictOptions"
70
+                                :key="item.id"
71
+                                :label="item.name"
72
+                                :value="item.id">
73
+                                </el-option>
74
+                            </el-select>
75
+                            <!-- <el-cascader
35 76
                                 v-model="form.city"
36 77
                                 :options="cityOptions"
37 78
                                 @active-item-change="handleChangeCity"
@@ -42,7 +83,7 @@
42 83
                                     label: 'name',
43 84
                                     children: 'cities'
44 85
                                 }"
45
-                            ></el-cascader>
86
+                            ></el-cascader> -->
46 87
                         </el-form-item>
47 88
                     </el-col>
48 89
                     <el-col :span="12">
@@ -179,7 +220,7 @@ export default {
179 220
                 mobile: [{required: true, message: "请填写手机号",},{ validator: checkMobileRule}],
180 221
                 gender: [{required: true, message: "请选择性别",},],
181 222
                 birthday: [{required: true, message: "请选择生日",},],
182
-                city: [{required: true, message: "请选择城市",},],
223
+                city_id: [{required: true, message: "请选择城市",},],
183 224
                 illness: [{required: true, message: "请选择病种",},],
184 225
                 ill_date: [{required: true, message: "请选择患病时间",},],
185 226
                 treat_type: [{required: true, message: "请选择治疗方式",},],
@@ -189,14 +230,16 @@ export default {
189 230
                 {id:1, name:'男'},
190 231
                 {id:2, name:'女'},
191 232
             ],
233
+            provinceOptions:[],
192 234
             cityOptions:[],
235
+            distictOptions:[],
193 236
             form:{
194 237
                 name:'',
195 238
                 mobile:'',
196 239
                 gender:0,
197 240
                 birthday:'',
198 241
                 city:[],
199
-                province_id:0,
242
+                province_id:'',
200 243
                 city_id:0,
201 244
                 district_id:0,
202 245
                 illness:[],
@@ -212,7 +255,7 @@ export default {
212 255
     },
213 256
     methods:{
214 257
         open:function(){
215
-            this.GetDistrictsByUpid();
258
+            this.GetProvince(0);
216 259
             this.resetForm("memberForm");
217 260
             this.createMemberFormVisible = true;
218 261
         },
@@ -227,6 +270,9 @@ export default {
227 270
             // }
228 271
             this.$refs[formName].validate((valid) => {
229 272
                 if (valid) {
273
+                    if (this.form.district_id == '') {
274
+                        this.form.district_id = this.form.city_id;
275
+                    }
230 276
                     CreateMember(this.form).then(response=>{
231 277
                         var res = response.data;
232 278
                         if(res.state === 1) {
@@ -264,83 +310,48 @@ export default {
264 310
                 this.form.avatar = 'https://images.shengws.com/201809182128111.png';
265 311
             }
266 312
         },
267
-        handleChangeCity:function(val) {
268
-            this.GetDistrictsByUpid(val);
313
+        changeProvince(id) {
314
+            this.form.city_id = '';
315
+            this.form.district_id = '';
316
+            this.GetCity(id);
317
+            this.distictOptions = [];
269 318
         },
270
-        handleSelectedCity:function(val) {
271
-            this.form.province_id = val[0];
272
-            this.form.city_id = val[1];
273
-            this.form.district_id = val[2];
319
+        changeCity(id){
320
+            this.form.district_id = '';
321
+            this.GetDistrict(id);
274 322
         },
275
-        GetDistrictsByUpid:function(val) {  
276
-            let idArea
277
-            let sizeArea
278
-            if (!val) {
279
-                idArea = 0
280
-                sizeArea = 0
281
-            } else if (val.length === 1) {
282
-                idArea = val[0]
283
-                sizeArea = val.length // 3:一级 4:二级 6:三级
284
-            } else if (val.length === 2) {
285
-                idArea = val[1]
286
-                sizeArea = val.length // 3:一级 4:二级 6:三级
287
-            }
288
-            
289
-            GetDistrictsByUpid({id:idArea}).then(response=>{
323
+        GetProvince:function(id) {  
324
+            GetDistrictsByUpid({id:id}).then(response=>{
290 325
                 var res = response.data;
291 326
                 if (res.state===1) {
292
-                    var citys = res.data.citys;
293
-                    if (sizeArea === 0) { // 初始化 加载一级 省
294
-                        this.cityOptions = citys.map((value, i) => {
295
-                            return {
296
-                                id: value.id,
297
-                                name: value.name,
298
-                                cities: []
299
-                            }
300
-                        })
301
-                    } else if (sizeArea === 1) { // 点击一级 加载二级 市
302
-                        this.cityOptions.map((value, i) => {
303
-                            if (value.id === val[0]) {
304
-                                if (!value.cities.length) {
305
-                                    value.cities = citys.map((value, i) => {
306
-                                        return {
307
-                                            id: value.id,
308
-                                            name: value.name,
309
-                                            cities: []
310
-                                        }
311
-                                    })
312
-                                }
313
-                            }
314
-                        })
315
-                    } else if (sizeArea === 2) { // 点击二级 加载三级 区
316
-                        this.cityOptions.map((value, i) => {
317
-                            if (value.id === val[0]) {
318
-                                value.cities.map((value, i) => {
319
-                                    if (value.id === val[1]) {
320
-                                        if (!value.cities.length) {
321
-                                            if (citys.length) {
322
-                                                value.cities = citys.map((value, i) => {
323
-                                                    return {
324
-                                                        id: value.id,
325
-                                                        name: value.name
326
-                                                    }
327
-                                                })
328
-                                            }else {
329
-                                                value.cities = [
330
-                                                    {id:value.id, name:value.name}
331
-                                                ];
332
-                                            }
333
-                                            
334
-                                        }
335
-                                    }
336
-                                })
337
-                            }
338
-                        })
339
-                    }
327
+                    this.provinceOptions = res.data.citys;
328
+                }else {
329
+                    this.$message.error(res.msg);
330
+                }
331
+            }).catch(e=>{
332
+            });
333
+        },
334
+        GetCity:function(id) {  
335
+            GetDistrictsByUpid({id:id}).then(response=>{
336
+                var res = response.data;
337
+                if (res.state===1) {
338
+                    this.cityOptions = res.data.citys;
340 339
                 }else {
341 340
                     this.$message.error(res.msg);
342 341
                 }
343
-            }).catch(e=>{});
342
+            }).catch(e=>{
343
+            });
344
+        },
345
+        GetDistrict:function(id) {  
346
+            GetDistrictsByUpid({id:id}).then(response=>{
347
+                var res = response.data;
348
+                if (res.state===1) {
349
+                    this.distictOptions = res.data.citys;
350
+                }else {
351
+                    this.$message.error(res.msg);
352
+                }
353
+            }).catch(e=>{
354
+            });
344 355
         },
345 356
     }
346 357
 }

+ 88 - 79
src/scrm_pages/members/components/EditMemberForm.vue Целия файл

@@ -31,7 +31,48 @@
31 31
                 <el-row>
32 32
                     <el-col :span="12">
33 33
                         <el-form-item label="城市:" required prop="city">
34
-                            <el-cascader
34
+                            <el-select
35
+                                v-model="form.province_id"
36
+                                style="width:32%"
37
+                                placeholder="选择地址"
38
+                                @change="changeProvince">
39
+                                <el-option
40
+                                v-for="item in provinceOptions"
41
+                                :key="item.id"
42
+                                :label="item.name"
43
+                                :value="item.id">
44
+                                </el-option>
45
+                            </el-select>
46
+                            
47
+                            <el-select
48
+                                v-model="form.city_id"
49
+                                style="width:32%"
50
+                                placeholder="选择地址"
51
+                                @change="changeCity"
52
+                                v-show="cityOptions.length>0"
53
+                                >
54
+                                <el-option
55
+                                v-for="item in cityOptions"
56
+                                :key="item.id"
57
+                                :label="item.name"
58
+                                :value="item.id">
59
+                                </el-option>
60
+                            </el-select>
61
+                            
62
+                            <el-select
63
+                                v-model="form.district_id"
64
+                                style="width:32%"
65
+                                placeholder="选择地址"
66
+                                v-show="distictOptions.length>0"
67
+                                >
68
+                                <el-option
69
+                                v-for="item in distictOptions"
70
+                                :key="item.id"
71
+                                :label="item.name"
72
+                                :value="item.id">
73
+                                </el-option>
74
+                            </el-select>
75
+                            <!-- <el-cascader
35 76
                                 v-model="form.city"
36 77
                                 :options="cityOptions"
37 78
                                 @active-item-change="handleChangeCity"
@@ -42,7 +83,7 @@
42 83
                                     label: 'name',
43 84
                                     children: 'cities'
44 85
                                 }"
45
-                            ></el-cascader>
86
+                            ></el-cascader> -->
46 87
                         </el-form-item>
47 88
                     </el-col>
48 89
                     <el-col :span="12">
@@ -186,21 +227,21 @@ export default {
186 227
                 {id:1, name:'男'},
187 228
                 {id:2, name:'女'},
188 229
             ],
230
+            provinceOptions:[],
189 231
             cityOptions:[],
232
+            distictOptions:[],
190 233
             illnessMap:{},
234
+
191 235
         }
192 236
     },
193 237
     methods:{
194 238
         open:function(){
195
-            let city = [];
196
-            this.GetDistrictsByUpid();
239
+            this.GetProvince(0);
197 240
             if (this.form.province_id>0) {
198
-                city.push(this.form.province_id);
199
-                this.GetDistrictsByUpid(city);
241
+                this.GetCity(this.form.province_id);
200 242
             }
201
-            if (this.form.province_id>0 && this.form.city_id>0) {
202
-                city.push(this.form.city_id);
203
-                this.GetDistrictsByUpid(city);
243
+            if (this.form.city_id>0) {
244
+                this.GetDistrict(this.form.city_id);
204 245
             }
205 246
             this.form.birthday = this.form.birthday?this.form.birthday:'';
206 247
             this.form.ill_date = this.form.ill_date?this.form.ill_date:'';
@@ -230,6 +271,9 @@ export default {
230 271
             // }
231 272
             this.$refs[formName].validate((valid) => {
232 273
                 if (valid) {
274
+                    if (this.form.district_id == '') {
275
+                        this.form.district_id = this.form.city_id;
276
+                    }
233 277
                     EditMember(this.form.id, this.form).then(response=>{
234 278
                         var res = response.data;
235 279
                         if(res.state === 1) {
@@ -268,83 +312,48 @@ export default {
268 312
                 this.form.avatar = 'https://images.shengws.com/201809182128111.png';
269 313
             }
270 314
         },
271
-        handleChangeCity:function(val) {
272
-            this.GetDistrictsByUpid(val);
315
+        changeProvince(id) {
316
+            this.form.city_id = '';
317
+            this.form.district_id = '';
318
+            this.GetCity(id);
319
+            this.distictOptions = [];
273 320
         },
274
-        handleSelectedCity:function(val) {
275
-            this.form.province_id = val[0];
276
-            this.form.city_id = val[1];
277
-            this.form.district_id = val[2];
321
+        changeCity(id){
322
+            this.form.district_id = '';
323
+            this.GetDistrict(id);
278 324
         },
279
-        GetDistrictsByUpid:function(val) {  
280
-            let idArea
281
-            let sizeArea
282
-            if (!val) {
283
-                idArea = 0
284
-                sizeArea = 0
285
-            } else if (val.length === 1) {
286
-                idArea = val[0]
287
-                sizeArea = val.length // 3:一级 4:二级 6:三级
288
-            } else if (val.length === 2) {
289
-                idArea = val[1]
290
-                sizeArea = val.length // 3:一级 4:二级 6:三级
291
-            }
292
-            
293
-            GetDistrictsByUpid({id:idArea}).then(response=>{
325
+        GetProvince:function(id) {  
326
+            GetDistrictsByUpid({id:id}).then(response=>{
294 327
                 var res = response.data;
295 328
                 if (res.state===1) {
296
-                    var citys = res.data.citys;
297
-                    if (sizeArea === 0) { // 初始化 加载一级 省
298
-                        this.cityOptions = citys.map((value, i) => {
299
-                            return {
300
-                                id: value.id,
301
-                                name: value.name,
302
-                                cities: []
303
-                            }
304
-                        })
305
-                    } else if (sizeArea === 1) { // 点击一级 加载二级 市
306
-                        this.cityOptions.map((value, i) => {
307
-                            if (value.id === val[0]) {
308
-                                if (!value.cities.length) {
309
-                                    value.cities = citys.map((value, i) => {
310
-                                        return {
311
-                                            id: value.id,
312
-                                            name: value.name,
313
-                                            cities: []
314
-                                        }
315
-                                    })
316
-                                }
317
-                            }
318
-                        })
319
-                    } else if (sizeArea === 2) { // 点击二级 加载三级 区
320
-                        this.cityOptions.map((value, i) => {
321
-                            if (value.id === val[0]) {
322
-                                value.cities.map((value, i) => {
323
-                                    if (value.id === val[1]) {
324
-                                        if (!value.cities.length) {
325
-                                            if (citys.length) {
326
-                                                value.cities = citys.map((value, i) => {
327
-                                                    return {
328
-                                                        id: value.id,
329
-                                                        name: value.name
330
-                                                    }
331
-                                                })
332
-                                            }else {
333
-                                                value.cities = [
334
-                                                    {id:value.id, name:value.name}
335
-                                                ];
336
-                                            }
337
-                                            
338
-                                        }
339
-                                    }
340
-                                })
341
-                            }
342
-                        })
343
-                    }
329
+                    this.provinceOptions = res.data.citys;
330
+                }else {
331
+                    this.$message.error(res.msg);
332
+                }
333
+            }).catch(e=>{
334
+            });
335
+        },
336
+        GetCity:function(id) {  
337
+            GetDistrictsByUpid({id:id}).then(response=>{
338
+                var res = response.data;
339
+                if (res.state===1) {
340
+                    this.cityOptions = res.data.citys;
341
+                }else {
342
+                    this.$message.error(res.msg);
343
+                }
344
+            }).catch(e=>{
345
+            });
346
+        },
347
+        GetDistrict:function(id) {  
348
+            GetDistrictsByUpid({id:id}).then(response=>{
349
+                var res = response.data;
350
+                if (res.state===1) {
351
+                    this.distictOptions = res.data.citys;
344 352
                 }else {
345 353
                     this.$message.error(res.msg);
346 354
                 }
347
-            }).catch(e=>{});
355
+            }).catch(e=>{
356
+            });
348 357
         },
349 358
     }
350 359
 }

+ 6 - 3
src/scrm_pages/members/components/EditMemberTagsForm.vue Целия файл

@@ -11,7 +11,7 @@
11 11
                                 style="width:100%"
12 12
                                 placeholder="请选择标签">
13 13
                                 <el-option
14
-                                v-for="item in tagOptions"
14
+                                v-for="item in tags"
15 15
                                 :key="item.id"
16 16
                                 :label="item.tag_name"
17 17
                                 :value="item.id">
@@ -34,8 +34,6 @@
34 34
 </template>
35 35
 
36 36
 <script>
37
-import {GetDistrictsByUpid} from  "@/api/district";
38
-import {checkMobile} from "@/utils/tools";
39 37
 import {EditMemberTags} from "@/api/member/member";
40 38
 
41 39
 export default {
@@ -78,10 +76,15 @@ export default {
78 76
     data(){
79 77
         return {
80 78
             editMemberTagFormVisible:false,
79
+            tags:[],
81 80
         }
82 81
     },
83 82
     methods:{
84 83
         open:function(){
84
+            this.tags = [];
85
+            for (const index in this.tagOptions) {
86
+                this.tags.push(this.tagOptions[index]);
87
+            }
85 88
             this.editMemberTagFormVisible = true;
86 89
         },
87 90
         resetForm(formName) {

+ 14 - 5
src/scrm_pages/members/members.vue Целия файл

@@ -222,7 +222,6 @@
222 222
                     this.total = res.data.total;
223 223
                     if(typeof(res.data.cards) != 'undefined') {
224 224
                         this.levelCards = res.data.cards;
225
-                        console.log("levelCard是什么",levelCards)
226 225
                     }
227 226
                     if(typeof(res.data.tags) != 'undefined') {
228 227
                         this.tagOptions = res.data.tags;
@@ -237,13 +236,22 @@
237 236
             }).catch(e=>{});
238 237
         },
239 238
         openSendMessage(){
240
-          
241
-          this.$message.error("功能开发中...");
242
-          return false;
243 239
           if (this.selectedMembers.length==0) {
244
-            this.$message.error("请选择要发送短信的会员");
240
+            this.$message.error("请选择会员");
241
+            return false;
242
+          }
243
+          var targets = [];
244
+          for (const index in this.selectedMembers) {
245
+            if(this.selectedMembers[index].mobile.length>0) {
246
+              targets.push({id:this.selectedMembers[index].id,name:this.selectedMembers[index].name,name:this.selectedMembers[index].name,mobile:this.selectedMembers[index].mobile});
247
+            }
248
+          }
249
+          if (targets.length==0) {
250
+            this.$message.error("由于所选的全部会员里,均无手机号,无法发送短信");
245 251
             return false;
246 252
           }
253
+            this.$store.dispatch("SMSSetTargets", targets)
254
+          this.$router.push({path:'/sms/send'})
247 255
           
248 256
         },
249 257
         openAddMembersTags() {
@@ -339,6 +347,7 @@
339 347
           this.$refs.editMemberTagsForm.open();
340 348
         },
341 349
         openEdit(row, index){
350
+          this.memberData.tags= [];
342 351
           this.memberData.illness = [];
343 352
           this.memberData.city = [];
344 353
           this.memberData.birthday = '';

+ 113 - 0
src/scrm_pages/sms/sms_manage.vue Целия файл

@@ -0,0 +1,113 @@
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
+            this.$router.push({path:'/sms/send'})
85
+        },
86
+
87
+        send_status_text: function(status) {
88
+            if (status == 4) {
89
+                return "已发送"
90
+            } else if (status == 5) {
91
+                return "发送失败"
92
+            } else {
93
+                return "未发送"
94
+            }
95
+        },
96
+        review_status_text: function(status) {
97
+            if (status == 1) {
98
+                return "待审核"
99
+            } else if (status == 2) {
100
+                return "未通过"
101
+            } else {
102
+                return "通过"
103
+            }
104
+        },
105
+    },
106
+}
107
+</script>
108
+
109
+<style lang="scss" scoped>
110
+
111
+</style>
112
+
113
+

+ 545 - 0
src/scrm_pages/sms/sms_send.vue Целия файл

@@ -0,0 +1,545 @@
1
+<template>
2
+    <div class="main-contain" v-loading="sending">
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" maxlength="500"></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" @click="sendToAllCustomersAction">发送</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="tagEffect(tag)" @click="selectTagAction(tag)" @close="cancelSelectTagAction(tag)" :closable="isTagSelected(tag)">{{ tag.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" @click="sendToTagCustomersAction">发送</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" @click="sendToDesignatedCustomersAction">发送</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
+import { sendsInitData, tagFilterCustomerCount, send2AllCustomers, send2TagCustomers, send2SpecificCustomers } from "@/api/sms"
87
+
88
+export default {
89
+    name: "SMSSend",
90
+    components: {
91
+        BreadCrumb,
92
+    },
93
+    data() {
94
+        return {
95
+            crumbs: [
96
+                { path: false, name: "短信管理" },
97
+                { path: false, name: "群发短信" },
98
+            ],
99
+
100
+            sending: false,
101
+            sms_content: "",
102
+            tags: [],
103
+            selecting_tags: [],
104
+            tag_filtered_count: 0,
105
+
106
+            action: 0,
107
+            id: 0,
108
+            designated_targets: [],
109
+        }
110
+    },
111
+    computed: {
112
+        content_preview: function() {
113
+            return "【酷医聚客】" + this.sms_content + "退订回TD"
114
+        },
115
+        total_content_length: function() {
116
+            return this.content_preview.length
117
+        },
118
+        sms_charge_count: function() {
119
+            return parseInt(Math.ceil(this.total_content_length / 67.0))
120
+        },
121
+    },
122
+    mounted() {
123
+        this.designated_targets = this.$store.getters.sms_designated_targets.targets
124
+        if (this.designated_targets.length > 0) {
125
+            this.$store.dispatch("SMSClearTargets")
126
+        } else {
127
+            var action = this.$route.query.action
128
+            var id = this.$route.query.id
129
+            if (action != undefined && id != undefined) {
130
+                this.action = action
131
+                this.id = id
132
+            }
133
+
134
+            this.getInitData()
135
+        }
136
+    },
137
+    methods: {
138
+        getInitData: function() {
139
+            sendsInitData(this.action, this.id).then(rs => {
140
+                var resp = rs.data
141
+                if (resp.state == 1) {
142
+                    this.tags = resp.data.tags
143
+                    this.sms_content = resp.data.default_content
144
+
145
+                } else {
146
+                    this.$message.error(resp.msg)
147
+                }
148
+
149
+            }).catch(err => {
150
+                this.$message.error(err)
151
+            })
152
+        },
153
+        getTagCustomerCount: function() {
154
+            var tag_ids = []
155
+            for (let index = 0; index < this.selecting_tags.length; index++) {
156
+                tag_ids.push(this.selecting_tags[index].id)
157
+            }
158
+            if (tag_ids.length == 0) {
159
+                this.tag_filtered_count = 0
160
+                return
161
+            }
162
+            tagFilterCustomerCount(tag_ids.join(",")).then(rs => {
163
+                var resp = rs.data
164
+                if (resp.state == 1) {
165
+                    this.tag_filtered_count = resp.data.count
166
+                } else {
167
+                    console.log(resp.msg)
168
+                }
169
+            }).catch(err => {
170
+                console.log(err)
171
+            })
172
+        },
173
+
174
+        tagEffect: function(tag) {
175
+            if (this.isTagSelected(tag)) {
176
+                return "dark"
177
+            } else {
178
+                return "plain"
179
+            }
180
+        },
181
+        isTagSelected: function(tag) {
182
+            for (let index = 0; index < this.selecting_tags.length; index++) {
183
+                if (this.selecting_tags[index].id == tag.id) {
184
+                    return true
185
+                }
186
+            }
187
+            return false
188
+        },
189
+        selectTagAction: function(tag) {
190
+            if (!this.isTagSelected(tag)) {
191
+                this.selecting_tags.push(tag)
192
+                this.getTagCustomerCount()
193
+            }
194
+        },
195
+        cancelSelectTagAction: function(tag) {
196
+            for (let index = 0; index < this.selecting_tags.length; index++) {
197
+                if (this.selecting_tags[index].id == tag.id) {
198
+                    this.selecting_tags.splice(index, 1)
199
+                    this.getTagCustomerCount()
200
+                    break
201
+                }
202
+            }
203
+        },
204
+
205
+        isSMSContentValid: function() {
206
+            if (this.sms_content.length == 0) {
207
+                this.$message.error("请编辑短信内容")
208
+                return false
209
+            }
210
+            if (this.sms_content.indexOf("【") >= 0 || this.sms_content.indexOf("】") >= 0) {
211
+                this.$message.error("短信内容不得包含【】")
212
+                return false
213
+            }
214
+            return true
215
+        },
216
+        sendToAllCustomersAction: function() {
217
+            if (!this.isSMSContentValid()) {
218
+                return
219
+            }
220
+            this.sending = true
221
+            send2AllCustomers(this.sms_content).then(rs => {
222
+                this.sending = false
223
+                var resp = rs.data
224
+                if (resp.state == 1) {
225
+                    this.$message.success("发送成功")
226
+                } else {
227
+                    this.$message.error(resp.msg)
228
+                }
229
+
230
+            }).catch(err => {
231
+                this.sending = false
232
+                this.$message.error(err)
233
+            })
234
+        },
235
+        sendToTagCustomersAction: function() {
236
+            if (!this.isSMSContentValid()) {
237
+                return
238
+            }
239
+            if (this.selecting_tags.length == 0) {
240
+                this.$message.error("请选择标签")
241
+                return
242
+            }
243
+
244
+            var tag_ids = []
245
+            for (let index = 0; index < this.selecting_tags.length; index++) {
246
+                tag_ids.push(this.selecting_tags[index].id)
247
+            }
248
+
249
+            this.sending = true
250
+            send2TagCustomers(this.sms_content, tag_ids.join(",")).then(rs => {
251
+                this.sending = false
252
+                var resp = rs.data
253
+                if (resp.state == 1) {
254
+                    this.$message.success("发送成功")
255
+                } else {
256
+                    this.$message.error(resp.msg)
257
+                }
258
+
259
+            }).catch(err => {
260
+                this.sending = false
261
+                this.$message.error(err)
262
+            })
263
+        },
264
+        sendToDesignatedCustomersAction: function() {
265
+            if (!this.isSMSContentValid()) {
266
+                return
267
+            }
268
+            var customer_ids = []
269
+            for (let index = 0; index < this.designated_targets.length; index++) {
270
+                customer_ids.push(this.designated_targets[index].id)
271
+            }
272
+
273
+            this.sending = true
274
+            send2SpecificCustomers(this.sms_content, customer_ids.join(",")).then(rs => {
275
+                this.sending = false
276
+                var resp = rs.data
277
+                if (resp.state == 1) {
278
+                    this.$message.success("发送成功")
279
+                } else {
280
+                    this.$message.error(resp.msg)
281
+                }
282
+
283
+            }).catch(err => {
284
+                this.sending = false
285
+                this.$message.error(err)
286
+            })
287
+        },
288
+    },
289
+}
290
+</script>
291
+
292
+<style lang="scss" scoped>
293
+.main-panel {
294
+    width: 100%;
295
+    display: flex;
296
+    min-height: 600px;
297
+    height: auto;
298
+    box-sizing: border-box;
299
+
300
+    .preview-panel {
301
+        flex: 3;
302
+        margin-right: 1rem;
303
+        background-color: #fff;
304
+
305
+        .form-content {
306
+            width: 100%;
307
+            padding: 14px 30px 14px 30px;
308
+
309
+            .preview {
310
+                width: 320px;
311
+                height: 568px;
312
+                background: #fff;
313
+                position: relative;
314
+                margin: 0 auto;
315
+
316
+                .bg {
317
+                    position: relative;
318
+                    padding-top: 85px;
319
+                    padding-bottom: 40px;
320
+                    width: 320px;
321
+                    height: 568px;
322
+                    background: url(../../assets/img/message_preview_bg.png) 50% no-repeat;
323
+                    background-image: -webkit-image-set(url(../../assets/img/message_preview_bg.png) 1x,url(../../assets/img/message_preview_bg@2x.png) 2x);
324
+                    -webkit-box-sizing: border-box;
325
+                    -moz-box-sizing: border-box;
326
+                    box-sizing: border-box;
327
+
328
+                    .scroll {
329
+                        height: 430px;
330
+                        overflow-x: hidden;
331
+                        overflow-y: auto;
332
+
333
+                        .fake-time {
334
+                            margin: auto;
335
+                            width: 310px;
336
+                            line-height: 40px;
337
+                            font-size: 12px;
338
+                            text-align: center;
339
+                            color: #999;
340
+                            background: #fff;
341
+                        }
342
+
343
+                        .content {
344
+                            display: inline-block;
345
+                            position: relative;
346
+                            left: 20px;
347
+                            margin-bottom: 12px;
348
+                            padding: 8px 15px;
349
+                            max-width: 260px;
350
+                            word-break: break-all;
351
+                            font-size: 16px;
352
+                            line-height: 1.25;
353
+                            color: #000;
354
+                            background: #e5e5ea;
355
+                            border-radius: 17px;
356
+                            -webkit-box-sizing: border-box;
357
+                            -moz-box-sizing: border-box;
358
+                            box-sizing: border-box;
359
+                        }
360
+                        .content::before {
361
+                            content: "";
362
+                            position: absolute;
363
+                            bottom: -2px;
364
+                            left: -7px;
365
+                            height: 20px;
366
+                            border-left: 20px solid #e5e5ea;
367
+                            border-bottom-right-radius: 16px 14px;
368
+                            -webkit-transform: translateY(-2px);
369
+                            -moz-transform: translateY(-2px);
370
+                            -ms-transform: translateY(-2px);
371
+                            transform: translateY(-2px);
372
+                        }
373
+                        .content::after {
374
+                            content: "";
375
+                            position: absolute;
376
+                            bottom: -2px;
377
+                            left: 20px;
378
+                            width: 10px;
379
+                            height: 20px;
380
+                            background: #fff;
381
+                            border-bottom-right-radius: 10px;
382
+                            -webkit-transform: translate(-30px,-2px);
383
+                            -moz-transform: translate(-30px,-2px);
384
+                            -ms-transform: translate(-30px,-2px);
385
+                            transform: translate(-30px,-2px);
386
+                        }
387
+                    }
388
+                    .scroll::-webkit-scrollbar {
389
+                        width: 5px;
390
+                    }
391
+                }
392
+            }
393
+        }
394
+    }
395
+
396
+    .edit-panel {
397
+        flex: 4;
398
+        padding-bottom: 80px;
399
+        background-color: #fff;
400
+
401
+        .form-content {
402
+            width: 100%;
403
+            padding: 14px 30px 14px 30px;
404
+
405
+            .input-panel {
406
+                padding-top: 10px;
407
+
408
+                .hint-text {
409
+                    margin-top: 5px;
410
+                    font-size: 12px;
411
+                    color: #a8b3ba;
412
+                    line-height: 30px;
413
+                }
414
+            }
415
+
416
+            .filters-panel {
417
+                margin-top: 50px;
418
+
419
+                .send-all-panel {
420
+                    display: flex;
421
+                    justify-content: space-between;
422
+                    align-items: center;
423
+
424
+                    .text {
425
+                        flex: 1;
426
+
427
+                        .title {
428
+                            font-size: 16px;
429
+                            color: #485b6d;
430
+                            font-weight: bold;
431
+                            height: 30px;
432
+                            display: -webkit-box;
433
+                            -webkit-box-orient: vertical;
434
+                            -webkit-line-clamp: 2;
435
+                            overflow: hidden;
436
+                        }
437
+                        .desc {
438
+                            font-size: 14px;
439
+                            color: #7b8a97;
440
+                        }
441
+                    }
442
+                    .btn {
443
+                        text-align: right;
444
+                    }
445
+                }
446
+                .tag-filter-panel {
447
+                    margin-top: 50px;
448
+
449
+                    .title {
450
+                        font-size: 16px;
451
+                        color: #485b6d;
452
+                        font-weight: bold;
453
+                        height: 30px;
454
+                        display: -webkit-box;
455
+                        -webkit-box-orient: vertical;
456
+                        -webkit-line-clamp: 2;
457
+                        overflow: hidden;
458
+                    }
459
+                    .desc {
460
+                        font-size: 14px;
461
+                        color: #7b8a97;
462
+                    }
463
+                    .tag-panel {
464
+                        margin-top: 15px;
465
+                        padding: 10px 15px 5px;
466
+                        background: #f4f7fa;
467
+
468
+                        .item-panel {
469
+                            display: inline-block;
470
+                            margin-bottom: 5px;
471
+                            margin-right: 5px;
472
+                        }
473
+                    }
474
+                    .hint-text {
475
+                        font-size: 12px;
476
+                        color: #a8b3ba;
477
+                        line-height: 30px;
478
+                        margin-top: 10px;
479
+                    }
480
+                    .send {
481
+                        display: flex;
482
+                        justify-content: space-between;
483
+                        align-items: center;
484
+                        margin-top: 15px;
485
+
486
+                        .text {
487
+                            flex: 1;
488
+                            color: #7b8a97;
489
+                            font-size: 14px;
490
+                        }
491
+                        .btn {
492
+                            text-align: right;
493
+                        }
494
+                    }
495
+                }
496
+            }
497
+
498
+            .designated-targets-panel {
499
+                margin-top: 20px;
500
+
501
+                .send-panel {
502
+                    display: flex;
503
+                    justify-content: space-between;
504
+                    align-items: center;
505
+                    padding-bottom: 10px;
506
+
507
+                    .title {
508
+                        flex: 1;
509
+                        font-size: 16px;
510
+                        color: #485b6d;
511
+                        font-weight: bold;
512
+                        height: 100%;
513
+                    }
514
+                    .btn {
515
+                        text-align: right;
516
+                    }
517
+                }
518
+            }
519
+        }
520
+    }
521
+}
522
+</style>
523
+
524
+<style lang="scss" scoped>
525
+.form-title {
526
+    width: 100%;
527
+    height: 60px;
528
+    padding: 20px 0 20px 30px;
529
+    border-bottom: 1px #dee2e5 solid;
530
+
531
+    .icon {
532
+        margin-top: -4px;
533
+        vertical-align: middle;
534
+    }
535
+
536
+    span {
537
+        font-size: 18px;
538
+        font-weight: 500;
539
+        color: #485b6d;
540
+        margin-left: 10px;
541
+    }
542
+}
543
+</style>
544
+
545
+

+ 2 - 0
src/store/getters.js Целия файл

@@ -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 Целия файл

@@ -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 Целия файл

@@ -0,0 +1,27 @@
1
+const sms_designated_targets = {
2
+    state: {
3
+        targets: [ /* { id, name, mobile } */ ],
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

+ 19 - 7
src/views/layout/components/Sidebar/SidebarItem.vue Целия файл

@@ -17,12 +17,21 @@
17 17
         <template v-for="child in item.children" v-if="!child.hidden">
18 18
           <sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :item="child" :key="child.path" :base-path="resolvePath(child.path)"></sidebar-item>
19 19
 
20
-          <router-link v-else :to="resolvePath(child.path)" :key="child.name">
21
-            <el-menu-item :index="resolvePath(child.path)">
22
-              <svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon>
23
-              <span v-if="child.meta&&child.meta.title" slot="title">{{generateTitle(child.meta.title)}}</span>
24
-            </el-menu-item>
25
-          </router-link>
20
+          <template v-else>
21
+            <!-- <router-link :key="child.name" :to="resolvePath(child.path)"> -->
22
+              <el-menu-item v-if="child.unfinished == true" :key="child.name" @click.native="unfinishAction()"  :index="resolvePath(child.path)">
23
+                <svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon>
24
+                <span v-if="child.meta&&child.meta.title" slot="title">{{generateTitle(child.meta.title)}}</span>
25
+              </el-menu-item>
26
+            <!-- </router-link> -->
27
+            <router-link v-else :to="resolvePath(child.path)" :key="child.name">
28
+              <el-menu-item :index="resolvePath(child.path)">
29
+                <svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon>
30
+                <span v-if="child.meta&&child.meta.title" slot="title">{{generateTitle(child.meta.title)}}</span>
31
+              </el-menu-item>
32
+            </router-link>
33
+          </template>
34
+          
26 35
         </template>
27 36
       </el-submenu>
28 37
 
@@ -74,7 +83,10 @@ export default {
74 83
     resolvePath(...paths) {
75 84
       return path.resolve(this.basePath, ...paths)
76 85
     },
77
-    generateTitle
86
+    generateTitle,
87
+    unfinishAction: function() {
88
+      this.$message.error("功能开发中")
89
+    },
78 90
   }
79 91
 }
80 92
 </script>