Browse Source

feat: 花生平台 - 数据循环统计 - 前端页面

zhengxy 2 years ago
parent
commit
322456d4d9

+ 4 - 3
project/src/components/dataBoard/loseUserTrends.vue

@@ -1,9 +1,9 @@
1 1
 <template>
2 2
   <div class="loseUserTrends-wrap" v-loading="pageLoading">
3
-    <div class="screenBox flex">
4
-      <div /><!-- 占位 -->
3
+    <!-- <div class="screenBox flex">
4
+      <div />
5 5
       <el-button type="primary" size="mini" @click="onClickExport">导出Excel</el-button>
6
-    </div>
6
+    </div> -->
7 7
     <!-- S 筛选区 -->
8 8
     <div class="screenBox filter-wrap">
9 9
       <!-- 日期 -->
@@ -21,6 +21,7 @@
21 21
         </div>
22 22
         <el-button size="mini" type="primary" plain @click="onClickSearch">确定</el-button>
23 23
         <el-button size="mini" plain @click="onClickReset">重置</el-button>
24
+        <el-button type="primary" size="mini" @click="onClickExport">导出Excel</el-button>
24 25
       </div>
25 26
     </div>
26 27
     <!-- E 筛选区 -->

+ 348 - 0
project/src/components/dataBoard/regRangeReportHS.vue

@@ -0,0 +1,348 @@
1
+<template>
2
+  <div v-loading="loading">
3
+    <!-- <div class="screenBox flex">
4
+      <div />
5
+      <el-button type="primary" size="mini" @click="init(1, 'export')">导出Excel</el-button>
6
+    </div> -->
7
+    <div class="screenBox flex">
8
+      <div class="flex">
9
+        <date-picker :reset="reset" title="自定义" :quickFlag='true' :afferent_time="default_time" :clearFlag='false'
10
+          @changeTime="changeTime"></date-picker>
11
+        <!-- 收益截止日期 -->
12
+        <div style="margin-left: 30px;" class="common-screen-item">
13
+          <label class="common-screen-label">收益截止日期</label>
14
+          <el-date-picker v-model="closing_date" value-format="yyyy-MM-dd" type="date" placeholder="选择日期" size="small" style="width:150px" @change="onChangeClosingDate" />
15
+        </div>
16
+        <!-- 花生账户 -->
17
+        <selfChannel :reset="reset" title="账户" type="adqAccount" labelWidth @channelDefine="onChangeAccountHS" />
18
+      </div>
19
+    </div>
20
+    <!-- S 新增区间筛选项 -->
21
+    <div class="screenBox filter-wrap">
22
+      <input-range v-model="firstDayRoi" label="首日ROI" />
23
+      <input-range v-model="firstOrderCost " label="下单成本" />
24
+      <input-range v-model="perFollowCost" label="企微成本" />
25
+      <input-range v-model="totalRoi" label="回本率" />
26
+      <input-range v-model="paid" label="消耗" />
27
+      <div>
28
+        <el-button size="mini" type="primary" plain @click="init(1);handleGetUpdateTime();">确定</el-button>
29
+        <el-button size="mini" plain @click="handleReset">重置</el-button>
30
+        <el-button type="primary" size="mini" @click="init(1, 'export')">导出Excel</el-button>
31
+      </div>
32
+    </div>
33
+    <!-- S 新增区间筛选项 -->
34
+    <!-- S 数据更新时间 -->
35
+    <div class="update-time"><i class="el-icon-warning-outline" />数据更新时间:{{ updateTime || '-' }}</div>
36
+    <!-- E 数据更新时间 -->
37
+    <!-- S 汇总表 summaryTable -->
38
+    <ux-grid style="margin-top:10px" ref="summaryTable" :border="false" @row-click="() => { return }" :header-cell-style="() => { return { backgroundColor: '#FFFFFF !important', border: 'none!important' } }" show-footer-overflow="tooltip" show-overflow="tooltip" size="mini">
39
+      <ux-table-column v-for="item in summaryTableCol" :key="item.column + item.name" :resizable="true" :field="item.column" :title="item.name" :min-width="item.min_width ? item.min_width : 140" :fixed="item.fixed ? item.fixed : ''" align="center">
40
+        <template #header>
41
+          <div class="flex-align-jus-center">
42
+            {{ item.name }}
43
+            <el-tooltip v-if="item.notes" :content="item.notes" placement="top">
44
+              <div><i class="el-icon-question"></i></div>
45
+            </el-tooltip>
46
+          </div>
47
+        </template>
48
+        <template v-slot="{ row }">
49
+          <span>{{ (row[item.column] || row[item.column] == 0) ? $formatNum(row[item.column]) : '-' }}</span>
50
+        </template>
51
+      </ux-table-column>
52
+    </ux-grid>
53
+    <!-- E 汇总表 summaryTable -->
54
+
55
+    <!-- S 明细表 detailsTable -->
56
+    <ux-grid style="margin-top:10px" ref="detailsTable" :border="false" @row-click="() => { return }" :header-cell-style="() => { return { backgroundColor: '#FFFFFF !important', border: 'none!important' } }" :height="height" show-footer-overflow="tooltip" show-overflow="tooltip" size="mini">
57
+      <ux-table-column v-for="item in detailsTableCol" :key="item.column + item.name" :resizable="true" :field="item.column" :title="item.name" :min-width="item.min_width ? item.min_width : 140" :fixed="item.fixed ? item.fixed : ''" align="center">
58
+        <template #header>
59
+          <div class="flex-align-jus-center">
60
+            {{ item.name }}
61
+            <el-tooltip v-if="item.notes" :content="item.notes" placement="top">
62
+              <div><i class="el-icon-question"></i></div>
63
+            </el-tooltip>
64
+            <div v-if="item.enable_to_sort" class="sort-wrap">
65
+              <i class="el-icon-caret-top" :class="{ 'active': sort_field === item.column && sort_type === 'asc' }" @click="onClickSort(item.column, 'asc')" />
66
+              <i class="el-icon-caret-bottom" :class="{ 'active': sort_field === item.column && sort_type === 'desc' }" @click="onClickSort(item.column, 'desc')" />
67
+            </div>
68
+          </div>
69
+        </template>
70
+        <template v-slot="{ row }">
71
+          <span>{{ (row[item.column] || row[item.column] == 0) ? $formatNum(row[item.column]) : '-' }}</span>
72
+        </template>
73
+      </ux-table-column>
74
+    </ux-grid>
75
+    <div class="pagination" v-show="total > 0">
76
+      <el-pagination background :current-page="page" @current-change="handleCurrentChange" layout="prev, pager, next"
77
+        :page-count='Number(pages)'>
78
+      </el-pagination>
79
+    </div>
80
+    <!-- E 明细表 detailsTable -->
81
+  </div>
82
+</template>
83
+<script>
84
+import datePicker from '@/components/assembly/screen/datePicker.vue'
85
+import inputRange from '@/components/dataBoard/inputRange.vue'
86
+import switchMpAdq from '@/components/assembly/screen/switchMpAdq.vue'
87
+import selfChannel from '@/components/assembly/screen/channel.vue'
88
+
89
+export default {
90
+  components: { datePicker, inputRange, switchMpAdq, selfChannel },
91
+  data () {
92
+    return {
93
+      height: '',
94
+
95
+      loading: false,
96
+      page: 1,
97
+      pages: 0,
98
+      total: 0,
99
+      page_size: 20,
100
+      default_time: [this.$getDay(-30, false), this.$getDay(0, false)],
101
+      time: [],
102
+
103
+      summaryTableCol: [],
104
+      detailsTableCol: [],
105
+
106
+      firstDayRoi: ['', ''], // 首日ROI(范围)
107
+      firstOrderCost: ['', ''], // 下单成本(范围)
108
+      perFollowCost: ['', ''], // 企微成本(范围)
109
+      totalRoi: ['', ''], // 回本率(范围)
110
+      paid: ['', ''], // 消耗(范围)
111
+      reset: false,
112
+      closing_date: '', // 收益截止日期
113
+      account_id_hs: '', // 花生账户
114
+      sort_field: 'date', // 排序字段 默认 时间 & 降序
115
+      sort_type: 'desc', // 升序/降序
116
+      updateTime: '', // 数据更新时间
117
+    }
118
+  },
119
+  created () {
120
+    this.height = document.documentElement.clientHeight - 200 > 400 ? document.documentElement.clientHeight - 200 : 400
121
+    this.time = this.default_time
122
+    this.init(1)
123
+    this.handleGetUpdateTime()
124
+  },
125
+  methods: {
126
+    // 获取"数据更新时间"
127
+    async handleGetUpdateTime() {
128
+      console.log('handleGetUpdateTime => ')
129
+      try {
130
+        const params = { type: '' }
131
+
132
+        params.type = 'data_cycle_adq' // mock
133
+
134
+        const { data: res = {} } = await this.$axios.get(`${this.URL.BASEURL}${this.URL.dataBoard_uptime}`, { params })
135
+        if (res && res.errno == 0) {
136
+          this.updateTime = res.rst.uptime
137
+        } else if (res.errno != 4002) {
138
+          this.$message.warning(res.err)
139
+          this.updateTime = ''
140
+        }
141
+      } catch (error) {
142
+        this.updateTime = ''
143
+      }
144
+    },
145
+    handleReset() {
146
+      this.reset = !this.reset
147
+      this.closing_date = ''
148
+      this.account_id_hs = ''
149
+      this.time = this.default_time
150
+      this.firstDayRoi = ['', ''] // 首日ROI(范围)
151
+      this.firstOrderCost = ['', ''] // 下单成本(范围)
152
+      this.perFollowCost = ['', ''] // 企微成本(范围)
153
+      this.totalRoi = ['', ''] // 回本率(范围)
154
+      this.paid = ['', ''] // 消耗(范围)
155
+      this.sort_field = 'date'
156
+      this.sort_type = 'desc'
157
+      this.init(1)
158
+      this.handleGetUpdateTime()
159
+    },
160
+    changeTime (time) {//筛选时间变化
161
+      if (!time || time && time.length == 0) {
162
+        this.time = []
163
+      } else {
164
+        this.time = time
165
+      }
166
+      this.init(1)
167
+      this.handleGetUpdateTime()
168
+    },
169
+    async init(page, type) {
170
+      console.log('init => ')
171
+      try {
172
+        if (type != 'export') {
173
+          this.page = page ? page : this.page;
174
+        } else {
175
+          if (this.total == 0) return this.$message.warning('暂无数据可导出')
176
+        }
177
+        this.loading = true
178
+        const { data: res = {} } = await this.$axios.get(`${this.URL.BASEURL}${this.URL.dataBoard_dataCycleList}`, {
179
+          params: {
180
+            page: type == 'export' ? 1 : this.page,
181
+            page_size: type == 'export' ? this.$store.state.exportNumber : this.page_size,
182
+            begin_date: this.time[0],
183
+            end_date: this.time[1],
184
+            first_order_cost_min: this.firstOrderCost[0],
185
+            first_order_cost_max: this.firstOrderCost[1],
186
+            per_follow_cost_min: this.perFollowCost[0],
187
+            per_follow_cost_max: this.perFollowCost[1],
188
+            total_roi_min: this.totalRoi[0],
189
+            total_roi_max: this.totalRoi[1],
190
+            first_day_roi_min: this.firstDayRoi[0],
191
+            first_day_roi_max: this.firstDayRoi[1],
192
+            closing_date: this.closing_date,
193
+            account_id_hs: this.account_id_hs,
194
+            paid_min: this.paid[0],
195
+            paid_max: this.paid[1],
196
+            sort_field: this.sort_field,
197
+            sort_type: this.sort_type,
198
+
199
+            order_type: 2, // mock
200
+          }
201
+        })
202
+
203
+        if (res && res.errno == 0) {
204
+          if (type == 'export') {
205
+            this.exportEvent(res.rst.data)
206
+          } else {
207
+            res.rst.data.list.head[0].fixed = 'left' // 第一列固定左侧
208
+            res.rst.data.overview.head[0].fixed = 'left' // 第一列固定左侧
209
+            this.summaryTableCol = Object.freeze(res.rst.data.overview.head || [])
210
+            this.detailsTableCol = Object.freeze(res.rst.data.list.head || [])
211
+            await this.$nextTick()
212
+            const summaryTableList = Array.isArray(res.rst.data.overview.list) ? res.rst.data.overview.list : [res.rst.data.overview.list]
213
+            const detailsTableList = Array.isArray(res.rst.data.list.list) ? res.rst.data.list.list : [res.rst.data.list.list]
214
+            this.$refs.summaryTable.reloadData(summaryTableList)
215
+            this.$refs.detailsTable.reloadData(detailsTableList)
216
+            this.total = res.rst.pageInfo.total;
217
+            this.pages = res.rst.pageInfo.pages;
218
+          }
219
+        } else if (res.errno != 4002) {
220
+          this.$message.warning(res.err)
221
+          this.handleSetEmptyTable()
222
+        }
223
+      } catch (error) {
224
+        console.log(error)
225
+        this.$message.warning('操作失败')
226
+        this.handleSetEmptyTable()
227
+      } finally {
228
+        this.loading = false
229
+      }
230
+    },
231
+    // 清空表数据
232
+    handleSetEmptyTable() {
233
+      this.$refs.summaryTable.reloadData([])
234
+      this.$refs.detailsTable.reloadData([])
235
+      this.total = 0;
236
+      this.pages = 0;
237
+    },
238
+    handleCurrentChange (val) {
239
+      this.init(val)
240
+      this.handleGetUpdateTime()
241
+    },
242
+    // 监听“收益截止日期”筛选变化
243
+    onChangeClosingDate(val) {
244
+      this.closing_date = val || ''
245
+      this.init(1)
246
+      this.handleGetUpdateTime()
247
+    },
248
+    // 监听“花生账户”筛选变化
249
+    onChangeAccountHS(val) {
250
+      this.account_id_hs = val || ''
251
+      this.init(1)
252
+      this.handleGetUpdateTime()
253
+    },
254
+    // 监听排序变化
255
+    onClickSort(sort_field, sort_type) {
256
+      // sort_type:升序asc、降序desc
257
+      if (this.sort_field === sort_field) {
258
+        if (this.sort_type === sort_type) {
259
+          // 点击的是当前排序字段 && 是当前排序类型 => 重置 取消排序
260
+          this.sort_field = 'date'
261
+          this.sort_type = 'desc'
262
+        } else {
263
+          // 点击的是当前排序字段 && 非当前排序类型 => 设置排序类型
264
+          this.sort_type = sort_type
265
+        }
266
+      } else {
267
+        // 点击的不是当前排序字段 => 设置排序字段和类型
268
+        this.sort_field = sort_field
269
+        this.sort_type = sort_type
270
+      }
271
+      // 后端排序 => 获取最新数据
272
+      this.init(1)
273
+      this.handleGetUpdateTime()
274
+    },
275
+    exportEvent (data) {
276
+      const tHeader = data.list.head.map(v => v.name)
277
+      const filterVal = data.list.head.map(v => v.column)
278
+      const tableDatas = [data.overview.list, ...data.list.list]
279
+      const excelDatas = [
280
+        {
281
+          tHeader: tHeader, // sheet表一头部
282
+          filterVal: filterVal, // 表一的数据字段
283
+          tableDatas: tableDatas, // 表一的整体json数据
284
+          sheetName: '', // 表一的sheet名字
285
+        },
286
+      ]
287
+      this.$exportOrder({ excelDatas, name: `数据循环统计(导出时间:${this.$getDay(0)})` })
288
+    }
289
+  }
290
+}
291
+</script>
292
+<style lang="scss" scoped>
293
+.screenBox {
294
+  position: relative;
295
+  background: #fff;
296
+  padding: 5px 20px;
297
+}
298
+.update-time {
299
+  margin-top: 10px;
300
+  padding-left: 20px;
301
+  display: flex;
302
+  align-items: center;
303
+  font-size: 12px;
304
+  color: #999;
305
+  i {
306
+    font-size: 14px;
307
+    font-weight: 600;
308
+    margin-right: 4px;
309
+  }
310
+}
311
+.ml-10 {
312
+  margin-left: 10px;
313
+}
314
+.filter-wrap {
315
+  display: flex;
316
+  align-items: center;
317
+  flex-wrap: wrap;
318
+  padding-bottom: 10px;
319
+  & > div {
320
+    margin: 0 10px 10px 0;
321
+  }
322
+  & > button {
323
+    margin: -10px 0 0 10px;
324
+  }
325
+  .el-button+.el-button {
326
+    margin-left: 5px;
327
+  }
328
+}
329
+.summaryTable {
330
+  margin-top: 10px;
331
+}
332
+.sort-wrap {
333
+  display: flex;
334
+  flex-direction: column;
335
+  i {
336
+    cursor: pointer;
337
+    &.active {
338
+      color: #32B38A;
339
+    }
340
+  }
341
+  i:first-child {
342
+    margin-bottom: -3px;
343
+  }
344
+  i:last-child {
345
+    margin-top: -3px;
346
+  }
347
+}
348
+</style>

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

@@ -39,6 +39,7 @@ const operateDayRetrieve = () => import(/* webpackChunkName: 'operateDayRetrieve
39 39
 const populariz = () => import(/* webpackChunkName: 'populariz' */ '@/components/dataBoard/populariz/index.vue')
40 40
 const throwPerson = () => import(/* webpackChunkName: 'throwPerson' */ '@/components/dataBoard/throwPerson/index.vue')
41 41
 const regRangeReport = () => import(/* webpackChunkName: 'regRangeReport' */ '@/components/dataBoard/regRangeReport.vue')
42
+const regRangeReportHS = () => import(/* webpackChunkName: 'regRangeReportHS' */ '@/components/dataBoard/regRangeReportHS.vue')
42 43
 const dramaManage = () => import(/* webpackChunkName: 'dramaManage' */ '@/components/dataBoard/dramaManage.vue')
43 44
 const charge = () => import(/* webpackChunkName: 'charge' */ '@/components/orderManage/charge.vue')
44 45
 const wxAccountList = () => import(/* webpackChunkName: 'wxAccountList' */ '@/components/dataBoard/wxAccount/list.vue')
@@ -687,6 +688,17 @@ export var allRouter = [
687 688
         }
688 689
       },
689 690
       {
691
+        path: 'regRangeReportHS',
692
+        name: 'regRangeReportHS',
693
+        component: regRangeReportHS,
694
+        meta: {
695
+          keepAlive: false,
696
+          isLogin: true,
697
+          title: '花生数据循环统计',
698
+          isData: true
699
+        }
700
+      },
701
+      {
690 702
         path: 'dramaManage',
691 703
         name: 'dramaManage',
692 704
         component: dramaManage,