Browse Source

开发静态页面

liuxiaona 2 years ago
parent
commit
2e89c137cc

+ 102 - 0
.idea/workspace.xml

1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project version="4">
3
+  <component name="ChangeListManager">
4
+    <list default="true" id="326b77ef-a0bc-4d8a-bd1c-34c4541ee400" name="Changes" comment="">
5
+      <change afterPath="$PROJECT_DIR$/project/src/components/dataBoard/playletFansActTrend.vue" afterDir="false" />
6
+      <change afterPath="$PROJECT_DIR$/project/src/components/dataBoard/thePublicTrend.vue" afterDir="false" />
7
+      <change beforePath="$PROJECT_DIR$/project/src/router/allRouter.js" beforeDir="false" afterPath="$PROJECT_DIR$/project/src/router/allRouter.js" afterDir="false" />
8
+    </list>
9
+    <option name="SHOW_DIALOG" value="false" />
10
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
11
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
12
+    <option name="LAST_RESOLUTION" value="IGNORE" />
13
+  </component>
14
+  <component name="FileTemplateManagerImpl">
15
+    <option name="RECENT_TEMPLATES">
16
+      <list>
17
+        <option value="Vue Single File Component" />
18
+      </list>
19
+    </option>
20
+  </component>
21
+  <component name="Git.Settings">
22
+    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
23
+  </component>
24
+  <component name="GitSEFilterConfiguration">
25
+    <file-type-list>
26
+      <filtered-out-file-type name="LOCAL_BRANCH" />
27
+      <filtered-out-file-type name="REMOTE_BRANCH" />
28
+      <filtered-out-file-type name="TAG" />
29
+      <filtered-out-file-type name="COMMIT_BY_MESSAGE" />
30
+    </file-type-list>
31
+  </component>
32
+  <component name="ProjectId" id="28Q2iXOGC8uIIgXdokLl2xoSuGY" />
33
+  <component name="ProjectViewState">
34
+    <option name="hideEmptyMiddlePackages" value="true" />
35
+    <option name="showExcludedFiles" value="false" />
36
+    <option name="showLibraryContents" value="true" />
37
+  </component>
38
+  <component name="PropertiesComponent">
39
+    <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
40
+    <property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
41
+    <property name="WebServerToolWindowFactoryState" value="false" />
42
+    <property name="last_opened_file_path" value="$PROJECT_DIR$/project/src/components/dataBoard" />
43
+    <property name="nodejs_package_manager_path" value="npm" />
44
+    <property name="ts.external.directory.path" value="C:\Program Files (x86)\webstorm\WebStorm 2021.2.1\plugins\JavaScriptLanguage\jsLanguageServicesImpl\external" />
45
+    <property name="vue.rearranger.settings.migration" value="true" />
46
+  </component>
47
+  <component name="RecentsManager">
48
+    <key name="CopyFile.RECENT_KEYS">
49
+      <recent name="C:\projectCode\playlet\project\src\components\dataBoard" />
50
+      <recent name="C:\projectCode\playlet\project\src\components\detials" />
51
+    </key>
52
+  </component>
53
+  <component name="RunManager">
54
+    <configuration name="dev" type="js.build_tools.npm" nameIsGenerated="true">
55
+      <package-json value="$PROJECT_DIR$/project/package.json" />
56
+      <command value="run" />
57
+      <scripts>
58
+        <script value="dev" />
59
+      </scripts>
60
+      <node-interpreter value="project" />
61
+      <envs />
62
+      <method v="2" />
63
+    </configuration>
64
+  </component>
65
+  <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
66
+  <component name="TaskManager">
67
+    <task active="true" id="Default" summary="Default task">
68
+      <changelist id="326b77ef-a0bc-4d8a-bd1c-34c4541ee400" name="Changes" comment="" />
69
+      <created>1651134619686</created>
70
+      <option name="number" value="Default" />
71
+      <option name="presentableId" value="Default" />
72
+      <updated>1651134619686</updated>
73
+      <workItem from="1651134622442" duration="12757000" />
74
+      <workItem from="1651197530707" duration="20257000" />
75
+      <workItem from="1651715435371" duration="498000" />
76
+      <workItem from="1651720937846" duration="3607000" />
77
+      <workItem from="1651801746039" duration="13012000" />
78
+      <workItem from="1651888568336" duration="927000" />
79
+      <workItem from="1651894000029" duration="4978000" />
80
+      <workItem from="1652061471731" duration="10722000" />
81
+      <workItem from="1652166348383" duration="10980000" />
82
+      <workItem from="1652234936568" duration="14227000" />
83
+      <workItem from="1652406821567" duration="7210000" />
84
+    </task>
85
+    <servers />
86
+  </component>
87
+  <component name="TypeScriptGeneratedFilesManager">
88
+    <option name="version" value="3" />
89
+  </component>
90
+  <component name="Vcs.Log.Tabs.Properties">
91
+    <option name="TAB_STATES">
92
+      <map>
93
+        <entry key="MAIN">
94
+          <value>
95
+            <State />
96
+          </value>
97
+        </entry>
98
+      </map>
99
+    </option>
100
+    <option name="oldMeFiltersMigrated" value="true" />
101
+  </component>
102
+</project>

+ 171 - 0
project/src/components/dataBoard/playletFansActTrend.vue

1
+<template>
2
+  <div v-loading="loading">
3
+    <div class="screenBox flex" style="padding-left: 10px">
4
+      <div class="flex">
5
+        <self-channel title="公众号名称" type='thePublic' @channelDefine="(val)=>{account_id = val;init(1)}"></self-channel>
6
+        <date-picker title="起止时间" :quickFlag='false' :afferent_time="default_time" :clearFlag='false' @changeTime="changeTime"></date-picker>
7
+      </div>
8
+      <el-button type="primary" size="mini" @click="init(1,'export')">导出Excel</el-button>
9
+    </div>
10
+    <ux-grid ref="plxTable"
11
+             :border="false"
12
+             @row-click="()=>{return}"
13
+             :header-cell-style="()=>{return { backgroundColor: '#FFFFFF !important', border: 'none!important' }}"
14
+             :height="height"
15
+             show-footer-overflow="tooltip"
16
+             show-overflow="tooltip"
17
+             class="tMar10"
18
+             size="mini">
19
+      <ux-table-column
20
+        v-for="item in desCol"
21
+        :key="item.prop"
22
+        :resizable="true"
23
+        :field="item.prop"
24
+        :title="item.label"
25
+        :min-width="item.min_width?item.min_width:120"
26
+        :fixed="item.fixed?item.fixed:''"
27
+        align="center">
28
+        <template #header>
29
+          <div
30
+            :class="['flex-align-jus-center',item.sort?'pointer':'',sort_field==item.prop?'sortFieldStyle':'']"
31
+            @click="item.sort?sortFieldEvent(item.prop):''">
32
+            {{item.label}}
33
+            <i class="el-icon-caret-bottom" v-if="item.sort"></i>
34
+            <el-tooltip v-if="item.notes" :content="item.notes" placement="top">
35
+              <div><i class="el-icon-question"></i></div>
36
+            </el-tooltip>
37
+          </div>
38
+        </template>
39
+        <template v-slot="{ row }">
40
+          <span :class="sort_field==item.prop?'sortFieldStyle':''">{{row[item.prop]||row[item.prop]==0?$formatNum(row[item.prop]):'-' }}</span>
41
+        </template>
42
+      </ux-table-column>
43
+    </ux-grid>
44
+    <div class="pagination" v-show="total>0">
45
+      <el-pagination background :current-page="page" @current-change="handleCurrentChange" layout="prev, pager, next" :page-count='Number(pages)'>
46
+      </el-pagination>
47
+    </div>
48
+  </div>
49
+</template>
50
+<script>
51
+import datePicker from '@/components/assembly/screen/datePicker.vue'
52
+import selfChannel from '@/components/assembly/screen/channel.vue'
53
+export default {
54
+  components: { datePicker, selfChannel },
55
+  data () {
56
+    return {
57
+      loading: false,
58
+      page: 1,
59
+      pages: 0,
60
+      total: 0,
61
+      page_size: 20,
62
+      sort_field: 'charge_total',
63
+      default_time: [this.$getDay(-7, false), this.$getDay(0, false)],
64
+      time: [],
65
+      account_id: '',
66
+      height: '',
67
+      desCol: [
68
+        { prop: "account_name", label: "时间", fixed: 'left' },
69
+        { prop: "account_name", label: "公众号", fixed: 'left' },
70
+        { prop: "promoter_name", label: "当天消耗" },
71
+        { prop: "start_paid", label: "当日新用户累计充值" },
72
+        { prop: "end_paid", label: "回本率(%)" },
73
+        { prop: 'paid_total', label: "企微关注数",  },
74
+        { prop: 'charge_total', label: "企微关注成本",  },
75
+        { prop: 'margin_rate', label: "首日用户成本",  notes: '首日用户成本=当天消耗/当天充值人数' },
76
+        { prop: 'cost_recovery_rate', label: "累计用户成本", notes: '累计用户成本=当天消耗/累计充值人数' },
77
+      ]
78
+    }
79
+  },
80
+  created () {
81
+    this.time = this.default_time
82
+    this.height = document.documentElement.clientHeight - 220 > 400 ? document.documentElement.clientHeight - 220 : 400
83
+    this.init(1)
84
+  },
85
+  methods: {
86
+    changeTime (time) {//筛选时间变化
87
+      if (!time || time && time.length == 0) {
88
+        this.time = []
89
+      } else {
90
+        this.time = time
91
+      }
92
+      this.init(1)
93
+    },
94
+    init (page, type) {
95
+      if (type != 'export') {
96
+        this.page = page ? page : this.page;
97
+      } else {
98
+        if (this.total == 0) {
99
+          this.$message({
100
+            message: '暂无数据可导出',
101
+            type: "warning"
102
+          })
103
+          return
104
+        }
105
+      }
106
+      this.loading = true
107
+      this.$axios.get(this.URL.BASEURL + this.URL.statistics_wechatAccountData, {
108
+        params: {
109
+          sort_field: this.sort_field,
110
+          start_date: this.time[0],
111
+          end_date: this.time[1],
112
+          account_id: this.account_id,
113
+          page: type == 'export' ? 1 : this.page,
114
+          page_size: type == 'export' ? this.$store.state.exportNumber : this.page_size,
115
+        }
116
+      }).then((res) => {
117
+        var res = res.data
118
+        this.loading = false
119
+        if (res && res.errno == 0) {
120
+          if (type == 'export') {
121
+            this.exportEvent(res.rst.data.list)
122
+          } else {
123
+            this.datas = res.rst.data.list // 知道为啥datas不在 data()方法里面定义吗?嘻嘻
124
+            this.$refs.plxTable.reloadData(this.datas)
125
+            this.total = res.rst.pageInfo.total;
126
+            this.pages = res.rst.pageInfo.pages;
127
+          }
128
+        } else if (res.errno != 4002) {
129
+          this.$message({
130
+            message: res.err,
131
+            type: "warning"
132
+          })
133
+        }
134
+      }).catch((err) => {
135
+        this.loading = false
136
+      });
137
+    },
138
+    handleCurrentChange (val) {
139
+      this.init(val)
140
+    },
141
+    sortFieldEvent (type) {
142
+      this.sort_field = type;
143
+      this.init(1)
144
+    },
145
+    exportEvent (data) {
146
+      let list = data;
147
+      let tHeader = this.desCol.map((v) => {
148
+        return v.label;
149
+      })
150
+      let filterVal = this.desCol.map((v) => {
151
+        return v.prop;
152
+      })
153
+      let excelDatas = [
154
+        {
155
+          tHeader: tHeader, // sheet表一头部
156
+          filterVal: filterVal, // 表一的数据字段
157
+          tableDatas: list, // 表一的整体json数据
158
+          sheetName: ''// 表一的sheet名字
159
+        }
160
+      ]
161
+      this.$exportOrder({ excelDatas, name: `公众号数据(导出时间:${this.$getDay(0)})` })
162
+    }
163
+  }
164
+}
165
+</script>
166
+<style lang="scss" scoped>
167
+.screenBox {
168
+  background: #fff;
169
+  padding: 5px 20px;
170
+}
171
+</style>

+ 258 - 0
project/src/components/dataBoard/thePublicTrend.vue

1
+<template>
2
+  <div v-loading="loading">
3
+    <div class="screenBox flex">
4
+      <div class="flex">
5
+        <date-picker title="自定义" :quickFlag='true' :afferent_time="default_time" :clearFlag='false' @changeTime="changeTime"></date-picker>
6
+        <self-channel title="公众号" type='thePublic' @channelDefine="(val)=>{account_id = val;init(1)}"></self-channel>
7
+      </div>
8
+      <el-button type="primary" size="mini" @click="init(1,'export')">导出Excel</el-button>
9
+    </div>
10
+    <div class="dataInfoBox">
11
+      <div class="dataInfoItem" v-for="(item,index) in dataInfoArrs" :key="index">
12
+        <div class="dataItemTitle">
13
+          <img src="@/assets/img/icon/累计消耗@2x.png" style="height:14px" class="titleIcon" alt="">
14
+          <span>{{item.label}}</span>
15
+        </div>
16
+        <div class="dataItem-data">{{dataInfo&&(dataInfo[item.prop]||dataInfo[item.prop]==0)?$formatNum(dataInfo[item.prop]):'-'}}</div>
17
+      </div>
18
+    </div>
19
+    <ux-grid ref="plxTable" :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">
20
+      <ux-table-column v-for="item in desCol" :key="item.prop" :resizable="true" :field="item.prop" :title="item.label" :min-width="item.min_width?item.min_width:120" :fixed="item.fixed?item.fixed:''" align="center">
21
+        <template #header>
22
+          <div :class="['flex-align-jus-center',item.sort?'pointer':'',sort_field==item.prop?'sortFieldStyle':'']" @click="item.sort?sortFieldEvent(item.prop):''">{{item.label}}<i class="el-icon-caret-bottom" v-if="item.sort"></i>
23
+            <el-tooltip v-if="item.notes" :content="item.notes" placement="top">
24
+              <div><i class="el-icon-question"></i></div>
25
+            </el-tooltip>
26
+          </div>
27
+        </template>
28
+        <template v-slot="{ row }">
29
+          <span :class="sort_field==item.prop?'sortFieldStyle':''">{{row[item.prop]||row[item.prop]==0?$formatNum(row[item.prop]):'-' }}</span>
30
+        </template>
31
+      </ux-table-column>
32
+    </ux-grid>
33
+    <div class="pagination" v-show="total>0">
34
+      <el-pagination background :current-page="page" @current-change="handleCurrentChange" layout="prev, pager, next" :page-count='Number(pages)'>
35
+      </el-pagination>
36
+    </div>
37
+  </div>
38
+</template>
39
+<script>
40
+import datePicker from '@/components/assembly/screen/datePicker.vue'
41
+import selfChannel from '@/components/assembly/screen/channel.vue'
42
+export default {
43
+  components: { datePicker, selfChannel },
44
+  data () {
45
+    return {
46
+      loading: false,
47
+      page: 1,
48
+      pages: 0,
49
+      total: 0,
50
+      page_size: 20,
51
+      sort_field: 'charge_total',
52
+      dataInfoArrs:[
53
+        {
54
+          prop:'',
55
+          label:'首日ROI',
56
+        },
57
+        {
58
+          prop:'',
59
+          label:'当天总消耗',
60
+        },
61
+        {
62
+          prop:'',
63
+          label:'企微关注数',
64
+        },
65
+        {
66
+          prop:'',
67
+          label:'企微关注成本',
68
+        },
69
+        {
70
+          prop:'',
71
+          label:'当日总回收',
72
+        },
73
+        {
74
+          prop:'',
75
+          label:'累计充值金额',
76
+        },
77
+        {
78
+          prop:'',
79
+          label:'新增用户',
80
+        },
81
+        {
82
+          prop:'',
83
+          label:'回本率',
84
+        },
85
+        {
86
+          prop:'',
87
+          label:'充值人数',
88
+        },
89
+        {
90
+          prop:'',
91
+          label:'总充值人数',
92
+        },
93
+        {
94
+          prop:'',
95
+          label:'充值次数',
96
+        },
97
+        {
98
+          prop:'',
99
+          label:'总充值次数',
100
+        },
101
+        {
102
+          prop:'',
103
+          label:'充值用户成本',
104
+        },
105
+      ],
106
+      dataInfo: {},
107
+      default_time: [this.$getDay(-7, false), this.$getDay(0, false)],
108
+      time: [],
109
+      account_id: '',
110
+      height: '',
111
+      desCol: [
112
+        { prop: "account_name", label: "时间", fixed: 'left' },
113
+        { prop: "account_name", label: "公众号", fixed: 'left' },
114
+        { prop: "promoter_name", label: "首日ROI" },
115
+        { prop: "start_paid", label: "当天消耗" },
116
+        { prop: "end_paid", label: "企微关注数" },
117
+        { prop: 'paid_total', label: "企微关注成本", },
118
+        { prop: 'charge_total', label: "当日回收",  },
119
+        { prop: 'margin_rate', label: "累计充值金额", },
120
+        { prop: 'cost_recovery_rate', label: "新增用户",  },
121
+        { prop: 'follow_total', label: "回本率(%)", },
122
+        { prop: 'average_follow_paid', label: "充值人数",},
123
+        { prop: 'charge_user_total', label: "总充值人数",  },
124
+        { prop: 'charge_transform', label: "充值次数",  },
125
+        { prop: 'charge_transform_cost', label: "总充值次数",  },
126
+        { prop: 'today', label: "充值用户成本", notes:'充值用户成本=当天消耗/充值人数' },
127
+      ]
128
+    }
129
+  },
130
+  created () {
131
+    this.time = this.default_time
132
+    this.height = document.documentElement.clientHeight - 350 > 400 ? document.documentElement.clientHeight - 350 : 400
133
+    this.init(1)
134
+  },
135
+  methods: {
136
+    changeTime (time) {//筛选时间变化
137
+      if (!time || time && time.length == 0) {
138
+        this.time = []
139
+      } else {
140
+        this.time = time
141
+      }
142
+      this.init(1)
143
+    },
144
+    init (page, type) {
145
+      if (type != 'export') {
146
+        this.page = page ? page : this.page;
147
+      } else {
148
+        if (this.total == 0) {
149
+          this.$message({
150
+            message: '暂无数据可导出',
151
+            type: "warning"
152
+          })
153
+          return
154
+        }
155
+      }
156
+      this.loading = true
157
+      this.$axios.get(this.URL.BASEURL + this.URL.statistics_wechatAccountData, {
158
+        params: {
159
+          sort_field: this.sort_field,
160
+          start_date: this.time[0],
161
+          end_date: this.time[1],
162
+          account_id: this.account_id,
163
+          page: type == 'export' ? 1 : this.page,
164
+          page_size: type == 'export' ? this.$store.state.exportNumber : this.page_size,
165
+        }
166
+      }).then((res) => {
167
+        var res = res.data
168
+        this.loading = false
169
+        if (res && res.errno == 0) {
170
+          if (type == 'export') {
171
+            this.exportEvent(res.rst.data.list)
172
+          } else {
173
+            this.datas = res.rst.data.list // 知道为啥datas不在 data()方法里面定义吗?嘻嘻
174
+            this.$refs.plxTable.reloadData(this.datas)
175
+            this.dataInfo = res.rst.data.total;
176
+            this.total = res.rst.pageInfo.total;
177
+            this.pages = res.rst.pageInfo.pages;
178
+          }
179
+        } else if (res.errno != 4002) {
180
+          this.$message({
181
+            message: res.err,
182
+            type: "warning"
183
+          })
184
+        }
185
+      }).catch((err) => {
186
+        this.loading = false
187
+      });
188
+    },
189
+    handleCurrentChange (val) {
190
+      this.init(val)
191
+    },
192
+    sortFieldEvent (type) {
193
+      this.sort_field = type;
194
+      this.init(1)
195
+    },
196
+    exportEvent (data) {
197
+      let list = data;
198
+      let tHeader = this.desCol.map((v) => {
199
+        return v.label;
200
+      })
201
+      let filterVal = this.desCol.map((v) => {
202
+        return v.prop;
203
+      })
204
+      let excelDatas = [
205
+        {
206
+          tHeader: tHeader, // sheet表一头部
207
+          filterVal: filterVal, // 表一的数据字段
208
+          tableDatas: list, // 表一的整体json数据
209
+          sheetName: ''// 表一的sheet名字
210
+        }
211
+      ]
212
+      this.$exportOrder({ excelDatas, name: `公众号数据(导出时间:${this.$getDay(0)})` })
213
+    }
214
+  }
215
+}
216
+</script>
217
+<style lang="scss" scoped>
218
+.screenBox {
219
+  background: #fff;
220
+  padding: 5px 20px;
221
+}
222
+.dataInfoBox {
223
+  display: flex;
224
+  margin-top: 10px;
225
+  flex-wrap: wrap;
226
+  .dataInfoItem {
227
+    background: #ffffff;
228
+    border-radius: 8px;
229
+    margin-right: 10px;
230
+    margin-bottom: 10px;
231
+    padding: 0 19px;
232
+    height: 70px;
233
+    display: flex;
234
+    flex-direction: column;
235
+    justify-content: center;
236
+    align-items: center;
237
+    .dataItemTitle {
238
+      display: flex;
239
+      align-items: center;
240
+      color: #6f6f6f;
241
+      font-size: 13px;
242
+      line-height: 17px;
243
+      font-weight: bold;
244
+      .titleIcon {
245
+        height: 16px;
246
+        margin-right: 4px;
247
+      }
248
+    }
249
+    .dataItem-data {
250
+      color: #000000;
251
+      font-size: 19px;
252
+      line-height: 28px;
253
+      font-weight: bold;
254
+      margin-top: 2px;
255
+    }
256
+  }
257
+}
258
+</style>

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

27
 const dramaManage = () => import(/* webpackChunkName: 'dramaManage' */ '@/components/dataBoard/dramaManage.vue')
27
 const dramaManage = () => import(/* webpackChunkName: 'dramaManage' */ '@/components/dataBoard/dramaManage.vue')
28
 const charge = () => import(/* webpackChunkName: 'charge' */ '@/components/orderManage/charge.vue')
28
 const charge = () => import(/* webpackChunkName: 'charge' */ '@/components/orderManage/charge.vue')
29
 const wxAccountList = () => import(/* webpackChunkName: 'wxAccountList' */ '@/components/dataBoard/wxAccount/list.vue')
29
 const wxAccountList = () => import(/* webpackChunkName: 'wxAccountList' */ '@/components/dataBoard/wxAccount/list.vue')
30
+const thePublicTrend = () => import(/* webpackChunkName: 'thePublicTrend' */ '@/components/dataBoard/thePublicTrend.vue')
31
+const playletFansActTrend = () => import(/* webpackChunkName: 'playletFansActTrend' */ '@/components/dataBoard/playletFansActTrend.vue')
30
 
32
 
31
 // name与菜单配置的页面路由一致
33
 // name与菜单配置的页面路由一致
32
 // meta下isData:true为数据看板,否则为助手
34
 // meta下isData:true为数据看板,否则为助手
252
         }
254
         }
253
       },
255
       },
254
       {
256
       {
257
+        path: 'thePublicTrend',
258
+        name: 'thePublicTrend',
259
+        component: thePublicTrend,
260
+        meta: {
261
+          keepAlive: false,
262
+          isLogin: true,
263
+          title: '公众号数据趋势',
264
+          isData: true
265
+        }
266
+      },
267
+      {
268
+        path: 'playletFansActTrend',
269
+        name: 'playletFansActTrend',
270
+        component: playletFansActTrend,
271
+        meta: {
272
+          keepAlive: false,
273
+          isLogin: true,
274
+          title: '短剧粉丝激活趋势',
275
+          isData: true
276
+        }
277
+      },
278
+      {
255
         path: 'playletData',
279
         path: 'playletData',
256
         name: 'playletData',
280
         name: 'playletData',
257
         component: playletData,
281
         component: playletData,