Quellcode durchsuchen

feat: 客服数据统计

zhengxy vor 1 Jahr
Ursprung
Commit
f15e97ad79

+ 85 - 58
project/src/components/dataBoard/customerStaff.vue

@@ -1,78 +1,87 @@
1 1
 <template>
2 2
   <div class="customerStaff-wrap" v-loading="pageLoading">
3
-      <!-- S 筛选区 -->
4
-      <div class="screenBox" style="padding-right: 100px;">
5
-        <!-- 企微主体 -->
6
-        <div class="common-screen-item">
7
-          <label class="common-screen-label">企微主体</label>
8
-          <!-- 系统管理员 -->
9
-          <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" />
10
-          <!-- 非系统管理员 -->
11
-          <el-select v-else v-model="filter.corpid" size="small" filterable placeholder="请选择" @change="onChangeCorpid" clearable class="select-cls">
12
-            <el-option v-for="(item, index) in enterpriseList" :key="index+'enterpriseList'" :label="item.corp_name?item.corp_name:item.corp_full_name?item.corp_full_name:item.corpid" :value="item.corpid" />
13
-          </el-select>
14
-        </div>
15
-        <!-- 成员 -->
16
-        <selfInputV2 v-model="filter.keyword" label_name="成员" placeholder="请输入" @change="onChangeKeyword" />
17
-        <!-- 客服状态 -->
18
-        <selfChannel title="客服状态" type="customerStaffStatus" :reset="reset" placeholder="请选择" @channelDefine="onChangeCuStStatus" />
19
-        <!-- 激活状态 -->
20
-        <selfChannel title="激活状态" type="isActive" :reset="reset" placeholder="请选择" @channelDefine="onChangeActiveStatus" />
21
-        <!-- 运营人员 -->
22
-        <selfChannel title="运营人员" type="circleCreate" :reset="reset" placeholder="请选择" @channelDefine="onChangeOperatorId" />
23
-
24
-        <div class="reset" @click="onClickReset">重置</div>
25
-        <el-button v-if="isCanExport" class="export-btn" type="primary" size="mini" @click="onClickExport">导出Excel</el-button>
3
+    <!-- S 筛选区 -->
4
+    <div class="screenBox" style="padding-right: 100px;">
5
+      <!-- 企微主体 -->
6
+      <div class="common-screen-item">
7
+        <label class="common-screen-label">企微主体</label>
8
+        <!-- 系统管理员 -->
9
+        <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" />
10
+        <!-- 非系统管理员 -->
11
+        <el-select v-else v-model="filter.corpid" size="small" filterable placeholder="请选择" @change="onChangeCorpid" clearable class="select-cls">
12
+          <el-option v-for="(item, index) in enterpriseList" :key="index+'enterpriseList'" :label="item.corp_name?item.corp_name:item.corp_full_name?item.corp_full_name:item.corpid" :value="item.corpid" />
13
+        </el-select>
26 14
       </div>
27
-      <!-- E 筛选区 -->
28
-      <!-- S 数据更新时间 -->
29
-      <div class="update-time"><i class="el-icon-warning-outline" />数据更新时间:{{ updateTime || '-' }}</div>
30
-      <!-- E 数据更新时间 -->
31
-      <!-- S 明细表 detailsTable -->
32
-      <div v-loading="detailLoading">
33
-        <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">
34
-          <ux-table-column v-for="(item, idx) in detailsTableCol" :key="item.column + item.name" :resizable="true" :field="item.column" :title="item.name" :min-width="item.min_width ? item.min_width : 160" :fixed="item.fixed ? item.fixed : ''" align="center">
35
-            <template #header>
36
-              <div class="flex-align-jus-center">
37
-                {{ item.name }}
38
-                <el-tooltip v-if="item.notes" :content="item.notes" placement="top">
39
-                  <div><i class="el-icon-question" /></div>
40
-                </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>
15
+      <!-- 成员 -->
16
+      <selfInputV2 v-model="filter.keyword" label_name="成员" placeholder="请输入" @change="onChangeKeyword" />
17
+      <!-- 客服状态 -->
18
+      <selfChannel title="客服状态" type="customerStaffStatus" :reset="reset" placeholder="请选择" @channelDefine="onChangeCuStStatus" />
19
+      <!-- 激活状态 -->
20
+      <selfChannel title="激活状态" type="isActive" :reset="reset" placeholder="请选择" @channelDefine="onChangeActiveStatus" />
21
+      <!-- 运营人员 -->
22
+      <selfChannel title="运营人员" type="circleCreate" :reset="reset" placeholder="请选择" @channelDefine="onChangeOperatorId" />
23
+
24
+      <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
+    </div>
27
+    <!-- E 筛选区 -->
28
+    <!-- S 数据更新时间 -->
29
+    <div class="update-time"><i class="el-icon-warning-outline" />数据更新时间:{{ updateTime || '-' }}</div>
30
+    <!-- E 数据更新时间 -->
31
+    <!-- S 明细表 detailsTable -->
32
+    <div v-loading="detailLoading">
33
+      <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">
34
+        <ux-table-column v-for="(item, idx) in detailsTableCol" :key="item.column + item.name" :resizable="true" :field="item.column" :title="item.name" :min-width="item.min_width ? item.min_width : 160" :fixed="item.fixed ? item.fixed : ''" align="center">
35
+          <template #header>
36
+            <div class="flex-align-jus-center">
37
+              {{ item.name }}
38
+              <el-tooltip v-if="item.notes" :content="item.notes" placement="top">
39
+                <div><i class="el-icon-question" /></div>
40
+              </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')" />
45 44
               </div>
46
-            </template>
47
-            <template v-slot="{ row }">
48
-              <span>{{ (row[item.column] || row[item.column] == 0) ? $formatNum(row[item.column]) : '-' }}</span>
49
-            </template>
50
-          </ux-table-column>
45
+            </div>
46
+          </template>
47
+          <template v-slot="{ row }">
48
+            <span>{{ (row[item.column] || row[item.column] == 0) ? $formatNum(row[item.column]) : '-' }}</span>
49
+          </template>
50
+        </ux-table-column>
51 51
 
52
-          <ux-table-column :width="120" fixed="right" align="center" title="操作">
53
-            <template v-slot="{ row }">
54
-              <span class="c-00B38A pointer" @click="onClickStatus(row)">客服状态</span>
55
-            </template>
56
-          </ux-table-column>
57
-        </ux-grid>
58
-        <div class="pagination" v-show="pagination.total > 0">
59
-          <el-pagination background :current-page="pagination.page" @current-change="handleCurrentChange" layout="prev, pager, next" :page-count='Number(pagination.pages)' />
60
-        </div>
52
+        <ux-table-column :width="120" fixed="right" align="center" title="操作">
53
+          <template v-slot="{ row }">
54
+            <span class="c-00B38A pointer" @click="onClickStatus(row)">客服状态</span>
55
+          </template>
56
+        </ux-table-column>
57
+      </ux-grid>
58
+      <div class="pagination" v-show="pagination.total > 0">
59
+        <el-pagination background :current-page="pagination.page" @current-change="handleCurrentChange" layout="prev, pager, next" :page-count='Number(pagination.pages)' />
61 60
       </div>
62
-      <!-- E 明细表 detailsTable -->
61
+    </div>
62
+    <!-- E 明细表 detailsTable -->
63
+
64
+    <!-- S 设置客服状态 -->
65
+    <setStatusDialog
66
+      :dialogVisible="setStatusDialogVisible"
67
+      :currentCustomer="currentCustomer"
68
+      @confirm="handleSetStatusConfirm"
69
+      @cancel="handleSetStatusCancel"
70
+    />
71
+    <!-- E 设置客服状态 -->
63 72
   </div>
64 73
 </template>
65 74
 
66 75
 <script>
67 76
 import selfChannel from '@/components/assembly/screen/channel.vue'
68 77
 import selfInputV2 from '@/components/assembly/screen/inputV2.vue'
69
-import selfCustomerservice from '@/components/assembly/screen/customerService.vue'
78
+import setStatusDialog from './setStatusDialog.vue'
70 79
 
71 80
 export default {
72 81
   components: {
73 82
     selfChannel,
74 83
     selfInputV2,
75
-    selfCustomerservice,
84
+    setStatusDialog,
76 85
   },
77 86
   data() {
78 87
     const DEFAULT_TIME = [this.$getDay(-30, false), this.$getDay(0, false)]
@@ -107,6 +116,9 @@ export default {
107 116
         is_active: '', // 激活状态
108 117
       },
109 118
       updateTime: '', // 数据更新时间
119
+
120
+      setStatusDialogVisible: false,
121
+      currentCustomer: {},
110 122
     }
111 123
   },
112 124
   computed: {
@@ -171,6 +183,10 @@ export default {
171 183
           })
172 184
           this.detailsTableCol = Object.freeze(detailsTableCol)
173 185
           await this.$nextTick()
186
+          // mock
187
+          res.rst.data[0].status = 2
188
+          res.rst.data[1].status = 1
189
+          // mock
174 190
           const detailsTableList = Array.isArray(res.rst.data) ? res.rst.data : []
175 191
           this.$refs.detailsTable.reloadData(detailsTableList)
176 192
           this.pagination.total = res.rst.pageInfo.total
@@ -389,6 +405,17 @@ export default {
389 405
 
390 406
     onClickStatus(row) {
391 407
       console.log('row => ', row)
408
+      this.currentCustomer = {...row}
409
+      this.setStatusDialogVisible = true
410
+    },
411
+    handleSetStatusConfirm() {
412
+      this.currentCustomer = {}
413
+      this.setStatusDialogVisible = false
414
+      this.handleGetData()
415
+    },
416
+    handleSetStatusCancel() {
417
+      this.currentCustomer = {}
418
+      this.setStatusDialogVisible = false
392 419
     },
393 420
   },
394 421
 }

+ 158 - 0
project/src/components/dataBoard/customerStaff/setStatusDialog.vue

@@ -0,0 +1,158 @@
1
+<template>
2
+  <el-dialog
3
+    :visible.sync="dialogVisible"
4
+    :before-close="handleCancel"
5
+    class="setStatus-dialog"
6
+    :title="dialogTitle"
7
+    width="450px"
8
+  >
9
+    <div class="form-wrap" v-loading="loading">
10
+      <div class="form-item">
11
+        <span class="lable">请选择</span>
12
+        <selfChannel ref="statusEl" title="" type="customerStaffStatus" placeholder="状态" :labelWidth="true" width="240px" :afferent_value="form.status" @channelDefine="onChangeStatus" />
13
+
14
+
15
+      </div>
16
+    </div>
17
+    <div slot="footer" class="dialog-footer">
18
+      <el-button size="mini" @click="handleCancel">取 消</el-button>
19
+      <el-button size="mini" type="primary" @click="handleConfirm" :disabled="loading">确 定</el-button>
20
+    </div>
21
+  </el-dialog>
22
+</template>
23
+
24
+<script>
25
+import selfChannel from '@/components/assembly/screen/channel.vue'
26
+
27
+export default {
28
+  name: "setStatusDialog",
29
+  components: {
30
+    selfChannel,
31
+  },
32
+  props: {
33
+    // 控制弹框是否显示
34
+    dialogVisible: {
35
+      type: Boolean,
36
+      default: () => false
37
+    },
38
+    // 当前客服信息
39
+    currentCustomer: {
40
+      type: Object,
41
+      default: () => ({})
42
+    },
43
+  },
44
+  data() {
45
+    return {
46
+      loading: false,
47
+      form: {
48
+        status: '',
49
+      }
50
+    }
51
+  },
52
+  computed: {
53
+    // 弹框标题
54
+    dialogTitle() {
55
+      return '客服状态'
56
+    }
57
+  },
58
+  watch: {
59
+    dialogVisible(isShow) {
60
+      // 弹框显示 => 初始化表单数据
61
+      if (isShow) {
62
+        this.handleInitData()
63
+      } else {
64
+        this.form.status = ''
65
+      }
66
+    },
67
+  },
68
+  methods: {
69
+    // 获取弹框表单数据
70
+    async handleInitData() {
71
+      this.loading = false
72
+      const { status } = this.currentCustomer
73
+      this.form.status = status || ''
74
+      console.log('this.form.status => ', this.form.status)
75
+      await this.$nextTick()
76
+      if (this.form.status) {
77
+        this.$refs.statusEl && this.$refs.statusEl.init_afferent()
78
+      } else {
79
+        this.$refs.statusEl && this.$refs.statusEl.clear()
80
+      }
81
+    },
82
+    async handleConfirm() {
83
+      try {
84
+        // 表单校验
85
+        await this.handleFormValidate()
86
+        const url = `${this.URL.BASEURL}${this.URL.miniProManage_addSmallApp}`
87
+        const params = { ...this.form }
88
+        console.log('url => ', url)
89
+        console.log('params => ', params)
90
+        return
91
+
92
+        this.loading = true
93
+        const { data: res = {} } = await this.$axios.post(url, params)
94
+        if (res && res.errno == 0) {
95
+          this.$message.success('操作成功')
96
+          this.$emit('confirm')
97
+        } else if (res.errno != 4002) {
98
+          this.$message.warning(res.err || '操作失败')
99
+        }
100
+      } catch (error) {
101
+        console.log(error)
102
+      } finally {
103
+        this.loading = false
104
+      }
105
+    },
106
+    handleCancel() {
107
+      this.$emit('cancel')
108
+    },
109
+    // 执行表单校验
110
+    handleFormValidate() {
111
+      return new Promise((resolve, reject) => {
112
+        const { status } = this.form
113
+        if (!status) {
114
+          this.$message.warning('请选择客服状态')
115
+          reject('表单校验未通过')
116
+        } else {
117
+          resolve('表单校验通过')
118
+        }
119
+      })
120
+    },
121
+    onChangeStatus(val) {
122
+      this.form.status = val
123
+    },
124
+  },
125
+};
126
+</script>
127
+
128
+<style lang="scss" scoped>
129
+.setStatus-dialog {
130
+  .form-wrap {
131
+    padding: 0 10px;
132
+    .form-item {
133
+      display: flex;
134
+      align-items: center;
135
+      margin-top: 16px;
136
+      &:first-child {
137
+        margin-top: 0;
138
+      }
139
+      .lable {
140
+        width: 82px;
141
+        margin-right: 16px;
142
+        font-weight: 500;
143
+        flex-shrink: 0;
144
+        text-align: right;
145
+        em {
146
+          color: red;
147
+        }
148
+      }
149
+      .el-select {
150
+        width: 100%;
151
+      }
152
+    }
153
+  }
154
+  .dialog-footer {
155
+    text-align: center;
156
+  }
157
+}
158
+</style>

+ 1 - 1
project/src/router/allRouter.js

@@ -78,7 +78,7 @@ const officialAccount = () => import(/* webpackChunkName: 'officialAccount' */ '
78 78
 // 数据看板 - 充值数据
79 79
 const recharge = () => import(/* webpackChunkName: 'recharge' */ '@/components/dataBoard/recharge.vue')
80 80
 // 数据看板 - 客服数据统计
81
-const customerStaff = () => import(/* webpackChunkName: 'customerStaff' */ '@/components/dataBoard/customerStaff.vue')
81
+const customerStaff = () => import(/* webpackChunkName: 'customerStaff' */ '@/components/dataBoard/customerStaff/index.vue')
82 82
 
83 83
 // 平台账号管理
84 84
 const accountManage = () => import(/* webpackChunkName: 'accountManage' */ '@/components/manage/accountManage/accountManage.vue')