Procházet zdrojové kódy

feat: 投手adq数据看板

zhengxy před 1 rokem
rodič
revize
94f3e99391

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

@@ -308,6 +308,7 @@ var api = {
308 308
   dataBoard_officialAccount_summary: "/api/statistics/publicAccountDataSummary", // 公众号数据 - 汇总
309 309
   dataBoard_officialAccount_list: "/api/statistics/publicAccountDataList", // 公众号数据 - 列表
310 310
   dataBoard_recharge_list: "/api/statistics/cumulativeRecoveryData", // 充值数据 - 列表
311
+  dataBoard_pitcherAdqAccountData: "/api/statistics/pitcherAdqAccountData", // 投手ADQ数据统计 - 列表
311 312
   dataBoard_customerStaff_list: "/api/statistics/customerServiceData", // 客服数据统计 - 列表
312 313
   dataBoard_customerStaff_setStatus: "/api/user/updateUserStatus", // 客服数据统计 - 更新客服状态
313 314
   dataBoard_chatGroupData_list: "/api/chatGroup/chatGroupStatistic", // 客户群数据 - 列表

+ 350 - 0
project/src/components/dataBoard/pitcherAdq.vue

@@ -0,0 +1,350 @@
1
+<template>
2
+  <div class="recharge-wrap" v-loading="pageLoading">
3
+      <!-- S 筛选区 -->
4
+      <div class="screenBox">
5
+        <div class="flex-align-center" style="flex-wrap:wrap;margin-right:60px;">
6
+          <!-- 日期 -->
7
+          <datePicker style="margin-right:30px;" title="自定义" :quickFlag="true" :afferent_time="default_time" :clearFlag="false" :reset="reset" @changeTime="onChangeTime" />
8
+        </div>
9
+        <el-button v-if="isCanExport" class="export-btn" type="primary" size="mini" @click="onClickExport">导出Excel</el-button>
10
+      </div>
11
+      <!-- E 筛选区 -->
12
+      <!-- S 数据更新时间 -->
13
+      <!-- <div class="update-time"><i class="el-icon-warning-outline" />数据更新时间:{{ updateTime || '-' }}</div> -->
14
+      <!-- E 数据更新时间 -->
15
+      <!-- S 明细表 detailsTable -->
16
+      <div v-loading="detailLoading">
17
+        <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">
18
+          <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">
19
+            <template #header>
20
+              <div class="flex-align-jus-center">
21
+                {{ item.name }}
22
+                <el-tooltip v-if="item.notes" :content="item.notes" placement="top">
23
+                  <div><i class="el-icon-question" /></div>
24
+                </el-tooltip>
25
+                <div v-if="item.enable_to_sort" class="sort-wrap">
26
+                  <i class="el-icon-caret-top" :class="{ 'active': filter.sort_field === item.column && filter.sort_type === 'asc' }" @click="onClickSort(item.column, 'asc')" />
27
+                  <i class="el-icon-caret-bottom" :class="{ 'active': filter.sort_field === item.column && filter.sort_type === 'desc' }" @click="onClickSort(item.column, 'desc')" />
28
+                </div>
29
+              </div>
30
+            </template>
31
+            <template v-slot="{ row }">
32
+              <span v-if="item.column == 'pitcher_name'">{{row[item.column] || '-' }}</span>
33
+              <span v-else>{{ (row[item.column] || row[item.column] == 0) ? $formatNum(row[item.column]) : '-' }}</span>
34
+            </template>
35
+          </ux-table-column>
36
+        </ux-grid>
37
+        <!-- <div class="pagination" v-show="pagination.total > 0">
38
+          <el-pagination background :current-page="pagination.page" @current-change="handleCurrentChange" layout="prev, pager, next, sizes" :page-sizes="[20, 50, 100]" @size-change="handleSizeChange" :page-count='Number(pagination.pages)' />
39
+        </div> -->
40
+      </div>
41
+      <!-- E 明细表 detailsTable -->
42
+  </div>
43
+</template>
44
+
45
+<script>
46
+import datePicker from '@/components/assembly/screen/datePicker.vue'
47
+
48
+export default {
49
+  components: {
50
+    datePicker,
51
+  },
52
+  data() {
53
+    const DEFAULT_TIME = [this.$getDay(-30, false), this.$getDay(0, false)]
54
+    return{
55
+      default_time: DEFAULT_TIME,
56
+      reset: false,
57
+      pageLoading: false,
58
+      height: '',
59
+
60
+      detailLoading: false,
61
+      detailsTableCol: [],
62
+
63
+      pagination: {
64
+        page: 1,
65
+        page_size: 20,
66
+        pages: 0,
67
+        total: 0,
68
+      },
69
+      filter: {
70
+        time: DEFAULT_TIME, // 自定义日期
71
+        sort_field: '', // 排序字段 - 默认值
72
+        sort_type: '', // 升序/降序
73
+      },
74
+      updateTime: '', // 数据更新时间
75
+      isHasData: false,
76
+    }
77
+  },
78
+  computed: {
79
+    // 是否有“导出”权限
80
+    isCanExport() {
81
+      return !!this.$store.state.dataBoardAuth.can_export
82
+    },
83
+  },
84
+  created() {
85
+    this.initTableHeight()
86
+    this.handleGetData()
87
+  },
88
+  methods:{
89
+    handleGetData() {
90
+      this.handleGetList()
91
+      // this.handleGetUpdateTime()
92
+    },
93
+    // 获取"数据更新时间"
94
+    // async handleGetUpdateTime() {
95
+    //   try {
96
+    //     const params = { type: 'cumulative_recovery_data' }
97
+    //     const { data: res = {} } = await this.$axios.get(`${this.URL.BASEURL}${this.URL.dataBoard_uptime}`, { params })
98
+    //     if (res && res.errno == 0) {
99
+    //       this.updateTime = res.rst.uptime
100
+    //     } else if (res.errno != 4002) {
101
+    //       this.$message.warning(res.err)
102
+    //       this.updateTime = ''
103
+    //     }
104
+    //   } catch (error) {
105
+    //     this.updateTime = ''
106
+    //   }
107
+    // },
108
+    // 获取列表数据
109
+    async handleGetList() {
110
+      try {
111
+        this.detailLoading = true
112
+        const params = {
113
+          start_date: this.filter.time[0],
114
+          end_date: this.filter.time[1],
115
+          sort_field: this.filter.sort_field,
116
+          sort_type: this.filter.sort_type,
117
+          // page: this.pagination.page,
118
+          // page_size: this.pagination.page_size,
119
+        }
120
+        const url = `${this.URL.BASEURL}${this.URL.dataBoard_pitcherAdqAccountData}`
121
+        const { data: res = {} } = await this.$axios.get(url, { params })
122
+        if (res && res.errno == 0) {
123
+          res.rst.extra[0].fixed = 'left' // 前1列固定左侧
124
+          const detailsTableCol = []
125
+          res.rst.extra.forEach(item => {
126
+            if (item.name && item.name.length > 6) { // 长字符宽度
127
+              item['min_width'] = item.name.length * 15
128
+            }
129
+            detailsTableCol.push(item) // 收集普通表头
130
+          })
131
+          this.detailsTableCol = Object.freeze(detailsTableCol)
132
+          await this.$nextTick()
133
+          const detailsTableList = Array.isArray(res.rst.data) ? res.rst.data : []
134
+          this.$refs.detailsTable.reloadData(detailsTableList)
135
+          // this.pagination.total = res.rst.pageInfo.total
136
+          // this.pagination.pages = res.rst.pageInfo.pages
137
+          this.isHasData = !!detailsTableList.length
138
+        } else if (res.errno != 4002) {
139
+          this.$message.warning(res.err)
140
+          this.$refs.detailsTable.reloadData([])
141
+          // this.pagination.total = 0
142
+          // this.pagination.pages = 0
143
+          this.isHasData = false
144
+        }
145
+      } catch (error) {
146
+        console.log(error)
147
+        this.$refs.detailsTable.reloadData([])
148
+        // this.pagination.total = 0
149
+        // this.pagination.pages = 0
150
+        this.isHasData = false
151
+      } finally {
152
+        this.detailLoading = false
153
+      }
154
+    },
155
+    // 监听"时间"筛选变化
156
+    onChangeTime(time) {
157
+      this.filter.time = Array.isArray(time) ? time : []
158
+      // this.pagination.page = 1
159
+      this.handleGetData()
160
+    },
161
+    // 监听排序变化
162
+    onClickSort(sort_field, sort_type) {
163
+      // sort_type:升序asc、降序desc
164
+      if (this.filter.sort_field === sort_field) {
165
+        if (this.filter.sort_type === sort_type) {
166
+          // 点击的是当前排序字段 && 是当前排序类型 => 重置 取消排序
167
+          this.filter.sort_field = ''
168
+          this.filter.sort_type = ''
169
+        } else {
170
+          // 点击的是当前排序字段 && 非当前排序类型 => 设置排序类型
171
+          this.filter.sort_type = sort_type
172
+        }
173
+      } else {
174
+        // 点击的不是当前排序字段 => 设置排序字段和类型
175
+        this.filter.sort_field = sort_field
176
+        this.filter.sort_type = sort_type
177
+      }
178
+      // 后端排序 => 获取最新数据
179
+      // this.pagination.page = 1
180
+      this.handleGetData()
181
+    },
182
+    // 监听当前页变化
183
+    // handleCurrentChange(currentPage) {
184
+    //   this.pagination.page = currentPage
185
+    //   this.handleGetData()
186
+    // },
187
+    // handleSizeChange(page_size) {
188
+    //   this.pagination.page_size = page_size
189
+    //   this.pagination.page = 1
190
+    //   this.handleGetData()
191
+    // },
192
+    // 监听点击"重置"按钮
193
+    onClickReset() {
194
+      this.reset = !this.reset
195
+      this.filter.time = this.default_time
196
+      this.filter.sort_field = ''
197
+      this.filter.sort_type = ''
198
+      // this.pagination.page = 1
199
+      this.handleGetData()
200
+    },
201
+    // 监听点击"导出"按钮
202
+    async onClickExport() {
203
+      // if (!this.pagination.total) return this.$message.warning('暂无数据可导出')
204
+      if (!this.isHasData) return this.$message.warning('暂无数据可导出')
205
+      try {
206
+        this.pageLoading = true
207
+        const url = {
208
+          detail: `${this.URL.BASEURL}${this.URL.dataBoard_pitcherAdqAccountData}`,
209
+        }
210
+        const params = {
211
+          start_date: this.filter.time[0],
212
+          end_date: this.filter.time[1],
213
+        }
214
+        const [{ data: detailRes = {} }] = await Promise.all([
215
+          this.$axios.get(url.detail, {
216
+            params: {
217
+              ...params,
218
+              sort_field: this.filter.sort_field,
219
+              sort_type: this.filter.sort_type,
220
+              // page: 1,
221
+              // page_size: this.$store.state.exportNumber,
222
+            }
223
+          })
224
+        ])
225
+        if (detailRes && detailRes.errno == 0) {
226
+          this.handleExport({
227
+            detailData: detailRes.rst,
228
+          })
229
+        } else if (detailRes.errno != 4002) {
230
+          this.$message.warning(detailRes.err)
231
+        }
232
+      } catch (error) {
233
+        console.log(error)
234
+        this.$message.warning('导出失败,请重试')
235
+      } finally {
236
+        this.pageLoading = false
237
+      }
238
+    },
239
+    // 执行导出逻辑
240
+    handleExport({ detailData = {} }) {
241
+      let tHeader = []
242
+      let filterVal = []
243
+      let tableDatas = []
244
+
245
+      tHeader = [
246
+        ...detailData.extra.map(d => `${d.name}`),
247
+      ]
248
+
249
+      filterVal = [
250
+        ...detailData.extra.map(d => d.column),
251
+      ]
252
+
253
+      tableDatas = [
254
+        ...detailData.data,
255
+      ]
256
+
257
+      const excelDatas = [
258
+        {
259
+          tHeader, // sheet表一头部
260
+          filterVal, // 表一的数据字段
261
+          tableDatas, // 表一的整体json数据
262
+          sheetName: ''// 表一的sheet名字
263
+        }
264
+      ]
265
+      this.$exportOrder({ excelDatas, name: `投手ADQ数据统计(导出时间:${this.$getDay(0)})` })
266
+    },
267
+    getHeaderCellStyle() {
268
+      return { backgroundColor: '#FFFFFF !important', border: 'none!important' }
269
+    },
270
+    initTableHeight() {
271
+      this.height = document.documentElement.clientHeight - 180 > 400 ? document.documentElement.clientHeight - 180 : 400
272
+    },
273
+  },
274
+}
275
+</script>
276
+
277
+<style lang="scss" scoped>
278
+.recharge-wrap {
279
+  position: relative;
280
+  min-height: calc(100vh - 70px);
281
+  .empty-wrap {
282
+    position: absolute;
283
+    top: 50%;
284
+    left: 50%;
285
+    transform: translate(-50%, -50%);
286
+  }
287
+  .screenBox {
288
+    background: #fff;
289
+    // padding: 5px 20px 26px;
290
+    padding: 5px 20px 10px;
291
+    position: relative;
292
+    .export-btn {
293
+      position: absolute;
294
+      top: 17px;
295
+      right: 4px;
296
+    }
297
+    .reset {
298
+      width: 80px;
299
+      height: 30px;
300
+      background: #00b38a;
301
+      border-radius: 100px 3px 3px 3px;
302
+      border: 1px solid #d2d2d2;
303
+      color: #ffffff;
304
+      font-size: 14px;
305
+      line-height: 30px;
306
+      text-align: center;
307
+      letter-spacing: 2px;
308
+      cursor: pointer;
309
+      position: absolute;
310
+      right: 0;
311
+      bottom: 0;
312
+    }
313
+  }
314
+  .update-time {
315
+    margin-top: 10px;
316
+    padding-left: 20px;
317
+    display: flex;
318
+    align-items: center;
319
+    font-size: 12px;
320
+    color: #999;
321
+    i {
322
+      font-size: 14px;
323
+      font-weight: 600;
324
+      margin-right: 4px;
325
+    }
326
+  }
327
+  .summaryTable {
328
+    margin-top: 10px;
329
+  }
330
+  .detailsTable {
331
+    margin-top: 10px;
332
+  }
333
+  .sort-wrap {
334
+    display: flex;
335
+    flex-direction: column;
336
+    i {
337
+      cursor: pointer;
338
+      &.active {
339
+        color: #32B38A;
340
+      }
341
+    }
342
+    i:first-child {
343
+      margin-bottom: -3px;
344
+    }
345
+    i:last-child {
346
+      margin-top: -3px;
347
+    }
348
+  }
349
+}
350
+</style>

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

@@ -86,6 +86,8 @@ const operator = () => import(/* webpackChunkName: 'operator' */ '@/components/d
86 86
 const officialAccount = () => import(/* webpackChunkName: 'officialAccount' */ '@/components/dataBoard/officialAccount.vue')
87 87
 // 数据看板 - 充值数据
88 88
 const recharge = () => import(/* webpackChunkName: 'recharge' */ '@/components/dataBoard/recharge.vue')
89
+// 数据看板 - 充值数据
90
+const pitcherAdq = () => import(/* webpackChunkName: 'pitcherAdq' */ '@/components/dataBoard/pitcherAdq.vue')
89 91
 // 数据看板 - 客服数据统计
90 92
 const customerStaff = () => import(/* webpackChunkName: 'customerStaff' */ '@/components/dataBoard/customerStaff/index.vue')
91 93
 // 数据看板 - 导出列表(离线导出任务列表)
@@ -766,6 +768,17 @@ export var allRouter = [
766 768
         }
767 769
       },
768 770
       {
771
+        path: 'pitcherAdq',
772
+        name: 'pitcherAdq',
773
+        component: pitcherAdq,
774
+        meta: {
775
+          keepAlive: false,
776
+          isLogin: true,
777
+          title: '投手ADQ数据统计',
778
+          isData: true
779
+        }
780
+      },
781
+      {
769 782
         path: 'customerStaff',
770 783
         name: 'customerStaff',
771 784
         component: customerStaff,