Browse Source

feat: 系统消息 - 交互逻辑&接口联调

zhengxy 1 year ago
parent
commit
50de3ceb7c

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

@@ -412,6 +412,8 @@ var api = {
412 412
   media_changeGroup: '/api/media/changeGroup', // 批量移动素材到指定素材组
413 413
   media_delMedia: '/api/media/delMedia', // 批量删除素材
414 414
   media_mediaSel: '/api/media/mediaSel', // 群发/朋友圈/欢迎语 选择素材
415
+
416
+  system_sendNoticeList: '/api/massMsg/sendNoticeList', // 系统管理 - 系统消息
415 417
 };
416 418
 
417 419
 export { api };

+ 66 - 14
project/src/components/Index/header.vue

@@ -67,16 +67,19 @@
67 67
       <div class="rightLogin" v-if="$cookie.getCookie('isLogin')">
68 68
         <!-- 系统消息 -->
69 69
         <div class="system-msg-wrap">
70
-          <el-popover placement="bottom" width="200" trigger="hover" @show="onShowSystemMsg">
71
-            <div class="system-msg-pop">
70
+          <el-popover placement="bottom" width="240" trigger="hover" @show="onShowSystemMsg">
71
+            <div class="system-msg-pop" v-loading="systemMsgLoading">
72 72
               <div class="system-msg-list">
73
-                <div class="system-msg-item" title="消息消息消息消息消息消息消息消息消息">消息消息消息消息消息消息消息消息消息</div>
74
-                <div class="system-msg-item" title="消息2">消息2</div>
75
-                <div class="system-msg-item" title="消息3">消息3</div>
76
-                <div class="system-msg-item" title="消息4">消息4</div>
77
-                <div class="system-msg-item" title="消息5">消息5</div>
73
+                <template v-if="systemMsgList && systemMsgList.length">
74
+                  <el-tooltip v-for="(msg, idx) in systemMsgList" :key="idx" effect="dark" :content="msg.notice" placement="left">
75
+                    <div class="system-msg-item">
76
+                      {{ msg.notice }}
77
+                    </div>
78
+                  </el-tooltip>
79
+                  <div class="system-more-btn" @click="onClickMsgMore">查看更多</div>
80
+                </template>
81
+                <div v-else class="system-msg-item none">暂无系统消息</div>
78 82
               </div>
79
-              <div class="system-more-btn" @click="onClickMsgMore">查看更多</div>
80 83
             </div>
81 84
             <i slot="reference" class="el-icon-message-solid msg-icon" />
82 85
           </el-popover>
@@ -135,6 +138,9 @@ export default {
135 138
       reversalAnimationFlag: false,
136 139
       pageDataBoard: this.$route.meta.isData,
137 140
       system_enterprise: [],//系统管理员
141
+
142
+      systemMsgLoading: false,
143
+      systemMsgList: [],
138 144
     }
139 145
   },
140 146
   watch: {
@@ -241,15 +247,56 @@ export default {
241 247
     },
242 248
 
243 249
     // 监听显示系统消息弹框
244
-    onShowSystemMsg() {
245
-      console.log('onShowSystemMsg => ')
250
+    async onShowSystemMsg() {
251
+      const sys_group_id = this.$cookie.getCookie('isSuperManage') == 1 ? sessionStorage.getItem('company_session_defaultCorp_level_1').toString() : ''
252
+      try {
253
+        this.systemMsgLoading = true
254
+        const params = {
255
+          sys_group_id,
256
+          corp_id: '',
257
+          page: 1,
258
+          page_size: 5,
259
+        }
260
+        const url = `${this.URL.BASEURL}${this.URL.system_sendNoticeList}`
261
+        const { data: res = {} } = await this.$axios.get(url, { params })
262
+        if (res && res.errno == 0) {
263
+          this.systemMsgList = Array.isArray(res.rst.data) ? res.rst.data : []
264
+        } else if (res.errno != 4002) {
265
+          this.$message.warning(res.err)
266
+          this.systemMsgList = []
267
+        }
268
+      } catch (error) {
269
+        console.log(error)
270
+        this.systemMsgList = []
271
+      } finally {
272
+        this.systemMsgLoading = false
273
+      }
246 274
     },
247 275
     // 监听点击“系统消息 - 查看更多”
248 276
     onClickMsgMore() {
249
-      console.log('onClickMsgMore => ',)
250
-      this.$router.push({
251
-        path: '/systemMsg'
252
-      })
277
+      try {
278
+        // 当前用户菜单权限
279
+        const permissions = this.$store.state.helper_side_list
280
+        // 获取当前用户是否有“系统消息”菜单权限
281
+        const isAuthorized = permissions.some(menu => {
282
+          if (menu.route === 'systemMsg') {
283
+            return true
284
+          } else if (Array.isArray(menu.sub) && menu.sub.length) {
285
+            return menu.sub.some(s => s.route === 'systemMsg')
286
+          } else {
287
+            return false
288
+          }
289
+        })
290
+        if (isAuthorized) { // 有权限 => 跳转
291
+          this.$router.push({
292
+            path: '/systemMsg'
293
+          })
294
+        } else { // 无权限 => 提示
295
+          this.$message.warning('当前用户暂无“系统消息”菜单权限')
296
+        }
297
+      } catch (error) {
298
+        console.log('error => ', error)
299
+      }
253 300
     },
254 301
   },
255 302
 }
@@ -523,6 +570,11 @@ export default {
523 570
       &:first-child {
524 571
         padding-top: 0;
525 572
       }
573
+      &.none {
574
+        padding-top: 10px;
575
+        text-align: center;
576
+        border: none;
577
+      }
526 578
     }
527 579
   }
528 580
   .system-more-btn {

+ 32 - 207
project/src/components/manage/systemMsg.vue

@@ -2,8 +2,10 @@
2 2
   <div class="customerStaff-wrap" v-loading="pageLoading">
3 3
     <!-- S 筛选区 -->
4 4
     <div class="screenBox" style="padding-right: 100px;">
5
+      <!-- 日期 -->
6
+      <datePicker :reset="reset" title="自定义" :quickFlag="true" :afferent_time="default_time" :clearFlag="false" @changeTime="onChangeTime" />
5 7
       <!-- 企微主体 -->
6
-      <div class="common-screen-item">
8
+      <div style="margin-right: 30px;" class="common-screen-item">
7 9
         <label class="common-screen-label">企微主体</label>
8 10
         <!-- 系统管理员 -->
9 11
         <el-cascader v-if="$cookie.getCookie('isSuperManage') == 1" v-model="system_enterprise" size="small" :options="enterpriseList" :props="{value:'self_id',label:'self_name',children:'manage_corp_list'}" @change="onChangeCorpidSystem" clearable filterable placeholder="请选择" class="select-cls cascader" />
@@ -13,21 +15,11 @@
13 15
         </el-select>
14 16
       </div>
15 17
       <!-- 成员 -->
16
-      <selfInputV2 v-model="filter.user_name" label_name="成员" placeholder="请输入" @change="onChangeUserName" />
17
-      <!-- 客服状态 -->
18
-      <selfChannel title="客服状态" type="customerStaffStatus" :reset="reset" placeholder="请选择" @channelDefine="onChangeStatus" />
19
-      <!-- 激活状态 -->
20
-      <selfChannel title="激活状态" type="isActive" :reset="reset" placeholder="请选择" @channelDefine="onChangeActiveStatus" />
21
-      <!-- 运营人员 -->
22
-      <selfChannel title="运营人员" type="circleCreate" :reset="reset" placeholder="请选择" @channelDefine="onChangeOperatorUid" />
18
+      <selfInputV2 style="margin-left: -20px;"  :labelWidth="true" v-model="filter.user_name" label_name="成员" placeholder="请输入" @change="onChangeUserName" />
23 19
 
24 20
       <div class="reset" @click="onClickReset">重置</div>
25
-      <el-button v-if="isCanExport" class="export-btn" type="primary" size="mini" @click="onClickExport">导出Excel</el-button>
26 21
     </div>
27 22
     <!-- E 筛选区 -->
28
-    <!-- S 数据更新时间 -->
29
-    <!-- <div class="update-time"><i class="el-icon-warning-outline" />数据更新时间:{{ updateTime || '-' }}</div> -->
30
-    <!-- E 数据更新时间 -->
31 23
     <!-- S 明细表 detailsTable -->
32 24
     <div v-loading="detailLoading">
33 25
       <ux-grid class="detailsTable" ref="detailsTable" :border="false" @row-click="() => { return }" :header-cell-style="getHeaderCellStyle" show-footer-overflow="tooltip" show-overflow="tooltip" size="mini" :height="height">
@@ -38,27 +30,10 @@
38 30
               <el-tooltip v-if="item.notes" :content="item.notes" placement="top">
39 31
                 <div><i class="el-icon-question" /></div>
40 32
               </el-tooltip>
41
-              <div v-if="item.enable_to_sort" class="sort-wrap">
42
-                <i class="el-icon-caret-top" :class="{ 'active': filter.sort_field === item.column && filter.sort_type === 'asc' }" @click="onClickSort(item.column, 'asc')" />
43
-                <i class="el-icon-caret-bottom" :class="{ 'active': filter.sort_field === item.column && filter.sort_type === 'desc' }" @click="onClickSort(item.column, 'desc')" />
44
-              </div>
45 33
             </div>
46 34
           </template>
47 35
           <template v-slot="{ row }">
48
-            <!-- 客服状态 -->
49
-            <span v-if="item.column === 'status'">{{ handleGetStatus(row['status']) }}</span>
50
-            <!-- 激活状态 -->
51
-            <span v-else-if="item.column === 'active_status'">{{ handleGetActiveStatus(row['active_status']) }}</span>
52
-            <!-- 激活状态 -->
53
-            <span v-else-if="item.column === 'operator_name'">{{ row['operator_name'] || '-' }}</span>
54
-            <!-- 其他字段 -->
55
-            <span v-else>{{ (row[item.column] || row[item.column] == 0) ? $formatNum(row[item.column]) : '-' }}</span>
56
-          </template>
57
-        </ux-table-column>
58
-
59
-        <ux-table-column v-if="detailsTableCol && detailsTableCol.length" :width="120" fixed="right" align="center" title="操作">
60
-          <template v-slot="{ row }">
61
-            <span class="c-00B38A pointer">客服状态</span>
36
+            <span>{{ (row[item.column] || row[item.column] == 0) ? $formatNum(row[item.column]) : '-' }}</span>
62 37
           </template>
63 38
         </ux-table-column>
64 39
       </ux-grid>
@@ -73,22 +48,28 @@
73 48
 <script>
74 49
 import selfChannel from '@/components/assembly/screen/channel.vue'
75 50
 import selfInputV2 from '@/components/assembly/screen/inputV2.vue'
76
-import { customerStaffStatusMap, kfActiveStatusMap } from '@/assets/js/staticTypes'
51
+import datePicker from '@/components/assembly/screen/datePicker.vue'
77 52
 
78 53
 export default {
79 54
   components: {
80 55
     selfChannel,
81 56
     selfInputV2,
57
+    datePicker,
82 58
   },
83 59
   data() {
84
-    return{
60
+    const DEFAULT_TIME = [this.$getDay(-30, false), this.$getDay(0, false)]
61
+    return {
85 62
       reset: false,
63
+      default_time: DEFAULT_TIME,
86 64
       pageLoading: false,
87 65
       height: '',
88
-
89 66
       detailLoading: false,
90
-      detailsTableCol: [],
91
-
67
+      detailsTableCol: [
68
+        { column: 'create_time', name: '时间', 'min_width': 140, fixed: 'left', },
69
+        { column: 'corp_name', name: '企微主体', 'min_width': 200, fixed: '', },
70
+        { column: 'user_name', name: '成员', 'min_width': 140, fixed: '', },
71
+        { column: 'notice', name: '消息内容', 'min_width': 400, fixed: '', },
72
+      ],
92 73
       pagination: {
93 74
         page: 1,
94 75
         page_size: 20,
@@ -101,84 +82,38 @@ export default {
101 82
       enterprise: {}, // 当前选择的企微信息
102 83
 
103 84
       filter: {
104
-        sort_field: 'daily_new_contact_cnt', // 排序字段 - 默认值 daily_new_contact_cnt
105
-        sort_type: 'desc', // 升序/降序
85
+        sys_group_id: this.$cookie.getCookie('isSuperManage') == 1 ? sessionStorage.getItem('company_session_defaultCorp_level_1').toString() : '',
86
+        time: DEFAULT_TIME, // 自定义日期
106 87
         corpid: '', // 企微主体
107 88
         user_name: '', // 成员
108
-        status: '', // 客服状态
109
-        operator_uid: '', // 运营人员
110
-        active_status: '', // 激活状态
111 89
       },
112
-
113
-      updateTime: '', // 数据更新时间
114 90
     }
115 91
   },
116
-  computed: {
117
-    // 是否有“导出”权限
118
-    isCanExport() {
119
-      return !!this.$store.state.dataBoardAuth.can_export
120
-    },
121
-  },
122 92
   created() {
123 93
     this.initTableHeight()
124 94
     this.handleInitCorpOptions()
125 95
     this.handleGetData()
126 96
   },
127 97
   methods: {
128
-    handleGetStatus(status) {
129
-      return customerStaffStatusMap.get(status) || '-'
130
-    },
131
-    handleGetActiveStatus(active_status) {
132
-      return kfActiveStatusMap.get(active_status) || '-'
133
-    },
134 98
     handleGetData() {
135 99
       this.handleGetList()
136
-      // this.handleGetUpdateTime()
137
-    },
138
-    // 获取"数据更新时间"
139
-    async handleGetUpdateTime() {
140
-      try {
141
-        const params = { type: '' }
142
-        const { data: res = {} } = await this.$axios.get(`${this.URL.BASEURL}${this.URL.dataBoard_uptime}`, { params })
143
-        if (res && res.errno == 0) {
144
-          this.updateTime = res.rst.uptime
145
-        } else if (res.errno != 4002) {
146
-          this.$message.warning(res.err)
147
-          this.updateTime = ''
148
-        }
149
-      } catch (error) {
150
-        this.updateTime = ''
151
-      }
152 100
     },
153 101
     // 获取列表数据
154 102
     async handleGetList() {
155 103
       try {
156 104
         this.detailLoading = true
157 105
         const params = {
106
+          sys_group_id: this.filter.sys_group_id,
107
+          start_date: this.filter.time[0],
108
+          end_date: this.filter.time[1],
158 109
           corp_id: this.filter.corpid,
159 110
           user_name: this.filter.user_name,
160
-          status: this.filter.status,
161
-          operator_uid: this.filter.operator_uid,
162
-          active_status: this.filter.active_status,
163
-
164
-          sort_field: this.filter.sort_field,
165
-          sort_type: this.filter.sort_type,
166 111
           page: this.pagination.page,
167 112
           page_size: this.pagination.page_size,
168 113
         }
169
-        const url = `${this.URL.BASEURL}${this.URL.dataBoard_customerStaff_list}`
114
+        const url = `${this.URL.BASEURL}${this.URL.system_sendNoticeList}`
170 115
         const { data: res = {} } = await this.$axios.get(url, { params })
171 116
         if (res && res.errno == 0) {
172
-          res.rst.extra[0].fixed = 'left' // 前1列固定左侧
173
-          const detailsTableCol = []
174
-          res.rst.extra.forEach(item => {
175
-            if (item.name && item.name.length > 6) { // 长字符宽度
176
-              item['min_width'] = item.name.length * 25
177
-            }
178
-            detailsTableCol.push(item) // 收集普通表头
179
-          })
180
-          this.detailsTableCol = Object.freeze(detailsTableCol)
181
-          await this.$nextTick()
182 117
           const detailsTableList = Array.isArray(res.rst.data) ? res.rst.data : []
183 118
           this.$refs.detailsTable.reloadData(detailsTableList)
184 119
           this.pagination.total = res.rst.pageInfo.total
@@ -198,153 +133,40 @@ export default {
198 133
         this.detailLoading = false
199 134
       }
200 135
     },
201
-    // 监听排序变化
202
-    onClickSort(sort_field, sort_type) {
203
-      // sort_type:升序asc、降序desc
204
-      if (this.filter.sort_field === sort_field) {
205
-        if (this.filter.sort_type === sort_type) {
206
-          // 点击的是当前排序字段 && 是当前排序类型 => 重置 取消排序
207
-          this.filter.sort_field = 'daily_new_contact_cnt'
208
-          this.filter.sort_type = 'desc'
209
-        } else {
210
-          // 点击的是当前排序字段 && 非当前排序类型 => 设置排序类型
211
-          this.filter.sort_type = sort_type
212
-        }
213
-      } else {
214
-        // 点击的不是当前排序字段 => 设置排序字段和类型
215
-        this.filter.sort_field = sort_field
216
-        this.filter.sort_type = sort_type
217
-      }
218
-      // 后端排序 => 获取最新数据
219
-      this.pagination.page = 1
220
-      this.handleGetData()
221
-    },
222 136
     // 监听当前页变化
223 137
     handleCurrentChange(currentPage) {
224 138
       this.pagination.page = currentPage
225 139
       this.handleGetData()
226 140
     },
227
-    // 监听客服输入变化
228
-    onChangeUserName(val) {
229
-      this.filter.user_name = val
230
-      this.pagination.page = 1
231
-      this.handleGetData()
232
-    },
233
-    // 监听“客服状态”变化
234
-    onChangeStatus(val) {
235
-      this.filter.status = val
141
+    // 监听时间筛选变化
142
+    onChangeTime(time) {
143
+      this.filter.time = Array.isArray(time) ? time : []
236 144
       this.pagination.page = 1
237 145
       this.handleGetData()
238 146
     },
239
-    // 监听“运营人员”变化
240
-    onChangeOperatorUid(val) {
241
-      this.filter.operator_uid = val
242
-      this.pagination.page = 1
243
-      this.handleGetData()
244
-    },
245
-    // 监听“激活状态”变化
246
-    onChangeActiveStatus(val) {
247
-      this.filter.active_status = val
147
+    // 监听客服输入变化
148
+    onChangeUserName(val) {
149
+      this.filter.user_name = val
248 150
       this.pagination.page = 1
249 151
       this.handleGetData()
250 152
     },
251 153
     // 监听点击"重置"按钮
252 154
     onClickReset() {
253 155
       this.reset = !this.reset
254
-
156
+      this.filter.time = this.default_time
255 157
       this.system_enterprise = []
256 158
       this.enterprise = {}
257 159
       this.filter.corpid = ''
258 160
       this.filter.user_name = ''
259
-      this.filter.status = ''
260
-      this.filter.operator_uid = ''
261
-      this.filter.active_status = ''
262
-
263
-      this.filter.sort_field = 'daily_new_contact_cnt'
264
-      this.filter.sort_type = 'desc'
265 161
       this.pagination.page = 1
266 162
       this.handleGetData()
267 163
     },
268
-    // 监听点击"导出"按钮
269
-    async onClickExport() {
270
-      if (!this.pagination.total) return this.$message.warning('暂无数据可导出')
271
-      try {
272
-        this.pageLoading = true
273
-        const url = {
274
-          detail: `${this.URL.BASEURL}${this.URL.dataBoard_customerStaff_list}`,
275
-        }
276
-        const params = {
277
-          corp_id: this.filter.corpid,
278
-          user_name: this.filter.user_name,
279
-          status: this.filter.status,
280
-          operator_uid: this.filter.operator_uid,
281
-          active_status: this.filter.active_status,
282
-        }
283
-        const [{ data: detailRes = {} }] = await Promise.all([
284
-          this.$axios.get(url.detail, {
285
-            params: {
286
-              ...params,
287
-              sort_field: this.filter.sort_field,
288
-              sort_type: this.filter.sort_type,
289
-              page: 1,
290
-              page_size: this.$store.state.exportNumber,
291
-            }
292
-          })
293
-        ])
294
-        if (detailRes && detailRes.errno == 0) {
295
-          this.handleExport({
296
-            detailData: detailRes.rst,
297
-          })
298
-        } else if (detailRes.errno != 4002) {
299
-          this.$message.warning(detailRes.err)
300
-        }
301
-      } catch (error) {
302
-        console.log(error)
303
-        this.$message.warning('导出失败,请重试')
304
-      } finally {
305
-        this.pageLoading = false
306
-      }
307
-    },
308
-    // 执行导出逻辑
309
-    handleExport({ detailData = {} }) {
310
-      let tHeader = []
311
-      let filterVal = []
312
-      let tableDatas = []
313
-
314
-      detailData.data.forEach(item => {
315
-        item['status'] = this.handleGetStatus(item['status'])
316
-        item['active_status'] = this.handleGetActiveStatus(item['active_status'])
317
-      })
318
-
319
-      tHeader = [
320
-        ...detailData.extra.map(d => `${d.name}`),
321
-      ]
322
-
323
-      filterVal = [
324
-        ...detailData.extra.map(d => d.column),
325
-      ]
326
-
327
-      tableDatas = [
328
-        ...detailData.data,
329
-      ]
330
-
331
-      const excelDatas = [
332
-        {
333
-          tHeader, // sheet表一头部
334
-          filterVal, // 表一的数据字段
335
-          tableDatas, // 表一的整体json数据
336
-          sheetName: ''// 表一的sheet名字
337
-        }
338
-      ]
339
-      this.$exportOrder({ excelDatas, name: `客服数据统计(导出时间:${this.$getDay(0)})` })
340
-    },
341 164
     getHeaderCellStyle() {
342 165
       return { backgroundColor: '#FFFFFF !important', border: 'none!important' }
343 166
     },
344 167
     initTableHeight() {
345 168
       this.height = document.documentElement.clientHeight - 240 > 400 ? document.documentElement.clientHeight - 240 : 400
346 169
     },
347
-
348 170
     // S 企微主体数据
349 171
     onChangeCorpidSystem(val) {//二级联选择器
350 172
       if (val.length < 1) {
@@ -411,6 +233,9 @@ export default {
411 233
     // padding: 5px 20px 26px;
412 234
     padding: 5px 20px 10px;
413 235
     position: relative;
236
+    .common-screen-item {
237
+      width: auto;
238
+    }
414 239
     .export-btn {
415 240
       position: absolute;
416 241
       top: 17px;