index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. <template>
  2. <div class="Content">
  3. <el-breadcrumb separator-class="el-icon-arrow-right">
  4. <el-breadcrumb-item>新媒体</el-breadcrumb-item>
  5. <el-breadcrumb-item>应用分析</el-breadcrumb-item>
  6. <el-breadcrumb-item>搜索分析</el-breadcrumb-item>
  7. </el-breadcrumb>
  8. <el-card class="box-card">
  9. <el-form
  10. ref="form"
  11. :model="form"
  12. size="small"
  13. :inline="true"
  14. label-width="120px"
  15. class="demo-form-inline"
  16. >
  17. <el-form-item label="日期">
  18. <el-date-picker
  19. v-if="form.date.length"
  20. v-model="form.date"
  21. type="daterange"
  22. :disabled-date="time => disabledDate(time)"
  23. range-separator="-"
  24. start-placeholder="开始日期"
  25. end-placeholder="结束日期"
  26. :clearable="false"
  27. >
  28. </el-date-picker>
  29. </el-form-item>
  30. <el-form-item label="应用">
  31. <el-select
  32. v-model="form.app"
  33. placeholder="请选择时段"
  34. @change="change"
  35. >
  36. <el-option
  37. v-for="item in cycle"
  38. :key="item.value"
  39. :label="item.label"
  40. :value="item.value"
  41. :disabled="item.disabled"
  42. >
  43. </el-option>
  44. </el-select>
  45. </el-form-item>
  46. <el-form-item label="版本">
  47. <el-select
  48. multiple
  49. collapse-tags
  50. clearable
  51. v-model="form.version"
  52. placeholder="请选择版本"
  53. @change="changeversion"
  54. >
  55. <el-option
  56. v-for="item in version"
  57. :key="item.value"
  58. :label="item.label"
  59. :value="item.value"
  60. :disabled="item.disabled"
  61. >
  62. </el-option>
  63. </el-select>
  64. </el-form-item>
  65. <el-form-item label="内容类型">
  66. <el-select
  67. collapse-tags
  68. clearable
  69. v-model="form.content"
  70. placeholder="请选择内容"
  71. >
  72. <el-option
  73. v-for="item in content"
  74. :key="item.value"
  75. :label="item.label"
  76. :value="item.value"
  77. :disabled="item.disabled"
  78. >
  79. </el-option>
  80. </el-select>
  81. </el-form-item>
  82. <el-form-item style="float: right">
  83. <el-button type="primary" @click="onSubmit">查询</el-button>
  84. </el-form-item>
  85. </el-form>
  86. </el-card>
  87. <br />
  88. <el-card class="box-card">
  89. <el-row :gutter="20">
  90. <el-col :span="6" v-for="(v, i) in top" :key="i + v.name">
  91. <div class="libTitle" v-text="v.name"></div>
  92. <el-table
  93. :data="v.list || []"
  94. style="width: 100%"
  95. :header-cell-style="{
  96. backgroundColor: '#f4f5f7',
  97. color: '#606266',
  98. }"
  99. >
  100. <el-table-column
  101. prop="title"
  102. header-align="center"
  103. align="center"
  104. label="标题"
  105. show-overflow-tooltip
  106. />
  107. <el-table-column label="次数" header-align="center" align="center">
  108. <template #default="scope">
  109. <countTo
  110. :startVal="0"
  111. :endVal="scope.row[v.target] || 0"
  112. :duration="100"
  113. ></countTo>
  114. </template>
  115. </el-table-column>
  116. </el-table>
  117. </el-col>
  118. </el-row>
  119. <br />
  120. <el-row>
  121. <el-col :span="8">
  122. </el-col>
  123. <el-col :span="16">
  124. <el-button
  125. size="small"
  126. style="float: right"
  127. type="primary"
  128. @click="onExport"
  129. >
  130. 导出
  131. </el-button>
  132. </el-col>
  133. </el-row>
  134. <br />
  135. <br />
  136. <el-table
  137. :data="table.records || []"
  138. style="width: 100%"
  139. @sort-change="tableChange"
  140. :header-cell-style="{
  141. backgroundColor: '#f4f5f7',
  142. color: '#606266',
  143. }"
  144. >
  145. <el-table-column
  146. prop="contentId"
  147. header-align="center"
  148. align="center"
  149. label="内容ID"
  150. show-overflow-tooltip
  151. >
  152. <template #default="scope">
  153. <span v-text="scope.row.contentId || '-'"></span>
  154. </template>
  155. </el-table-column>
  156. <el-table-column
  157. prop="title"
  158. header-align="center"
  159. align="center"
  160. width="300"
  161. label="标题"
  162. show-overflow-tooltip
  163. >
  164. <template #default="scope">
  165. <span v-text="scope.row.title || '-'"></span>
  166. </template>
  167. </el-table-column>
  168. <el-table-column
  169. show-overflow-tooltip
  170. label="应用名称"
  171. header-align="center"
  172. align="center"
  173. >
  174. <template #default>
  175. <span>{{ form.app || "-" }}</span>
  176. </template>
  177. </el-table-column>
  178. <el-table-column
  179. prop="publishTime"
  180. header-align="center"
  181. align="center"
  182. label="发布时间"
  183. show-overflow-tooltip
  184. >
  185. <template #default="scope">
  186. <span v-text="scope.row.publishTime || '-'"></span>
  187. </template>
  188. </el-table-column>
  189. <el-table-column
  190. label="平均阅读时长"
  191. header-align="center"
  192. align="center"
  193. sortable
  194. :sort-orders="['descending', null]"
  195. prop="cduration"
  196. >
  197. <template #default="scope">
  198. <span v-text="timeFormat(scope.row.cduration)"></span>
  199. </template>
  200. </el-table-column>
  201. <el-table-column
  202. label="阅读次数"
  203. header-align="center"
  204. align="center"
  205. sortable
  206. :sort-orders="['descending', null]"
  207. prop="cpv"
  208. >
  209. <template #default="scope">
  210. <countTo
  211. :startVal="0"
  212. :endVal="scope.row.cpv || 0"
  213. :duration="100"
  214. ></countTo>
  215. </template>
  216. </el-table-column>
  217. <el-table-column
  218. label="点赞次数"
  219. header-align="center"
  220. align="center"
  221. sortable
  222. :sort-orders="['descending', null]"
  223. prop="csupport"
  224. >
  225. <template #default="scope">
  226. <countTo
  227. :startVal="0"
  228. :endVal="scope.row.csupport || 0"
  229. :duration="100"
  230. ></countTo>
  231. </template>
  232. </el-table-column>
  233. <el-table-column
  234. label="收藏次数"
  235. header-align="center"
  236. align="center"
  237. sortable
  238. :sort-orders="['descending', null]"
  239. prop="ccollect"
  240. >
  241. <template #default="scope">
  242. <countTo
  243. :startVal="0"
  244. :endVal="scope.row.ccollect || 0"
  245. :duration="100"
  246. ></countTo>
  247. </template>
  248. </el-table-column>
  249. <el-table-column
  250. label="分享次数"
  251. header-align="center"
  252. align="center"
  253. sortable
  254. :sort-orders="['descending', null]"
  255. prop="cshare"
  256. >
  257. <template #default="scope">
  258. <countTo
  259. :startVal="0"
  260. :endVal="scope.row.cshare || 0"
  261. :duration="100"
  262. ></countTo>
  263. </template>
  264. </el-table-column>
  265. <el-table-column
  266. label="观看用户数"
  267. header-align="center"
  268. align="center"
  269. sortable
  270. :sort-orders="['descending', null]"
  271. prop="cuser"
  272. >
  273. <template #default="scope">
  274. <countTo
  275. :startVal="0"
  276. :endVal="scope.row.cuser || 0"
  277. :duration="100"
  278. ></countTo>
  279. </template>
  280. </el-table-column>
  281. </el-table>
  282. <br />
  283. <el-pagination
  284. layout="prev, pager, next"
  285. :page-size="form.size"
  286. :current-page="form.page"
  287. :total="table.total"
  288. background
  289. @current-change="changePage"
  290. />
  291. </el-card>
  292. </div>
  293. </template>
  294. <script>
  295. // @ is an alias to /src
  296. import {
  297. getRule,
  298. getAppList,
  299. getSearchData,
  300. getContentTop10,
  301. getContentPage,
  302. } from "@/api/index";
  303. import countTo from "@/components/counto/vue-countTo.vue";
  304. import config from "@/config/index";
  305. export default {
  306. name: "Content",
  307. data() {
  308. return {
  309. lastParams: {},
  310. table: {},
  311. form: {
  312. app: "",
  313. version: [],
  314. date: [],
  315. content: "",
  316. },
  317. cycle: [],
  318. showList: [],
  319. version: [],
  320. content: [],
  321. top: [],
  322. };
  323. },
  324. async mounted() {
  325. const { source, appV, contentV, appli, appVLi, contentli } =
  326. await this.getAppListFunc();
  327. const keys = {
  328. value: "mname",
  329. label: "mname",
  330. };
  331. this.cycle = this.verifyList(appli, source, keys, false);
  332. this.version = this.verifyList(appVLi, appV, keys, true);
  333. this.content = this.verifyList(contentli, contentV, keys, true);
  334. this.form = {
  335. // app: (this.cycle[0] || { value: "" }).value,
  336. app: "起点新闻",
  337. version: [(this.version[0] || { value: "" }).value],
  338. content: -1,
  339. date: [new Date(Date.now() - 604800000), new Date(Date.now() - 86400000)],
  340. page: 1,
  341. size: 20,
  342. sortBy: "",
  343. };
  344. this.onSubmit();
  345. },
  346. computed: {},
  347. methods: {
  348. tableChange(e) {
  349. this.form.page = 1;
  350. this.form.sortBy = e.column ? e.column.property : "";
  351. this.lastParams.sortBy = e.column ? e.column.property : "";
  352. this.lastParams.page = 1;
  353. getContentPage(this.lastParams).then(r => (this.table = r || {}));
  354. },
  355. verifyList(list, verify, obj, more) {
  356. if (!obj) return;
  357. let li = list || [];
  358. const out = [];
  359. more && out.push({ value: -1, label: "不限" });
  360. for (let i = 0; i < li.length; i++) {
  361. const v = li[i];
  362. if (verify.length !== 0 && !verify[v.mcode]) continue;
  363. out.push({
  364. value: v[obj.value],
  365. label: v[obj.label],
  366. });
  367. }
  368. return out;
  369. },
  370. changePage(e) {
  371. this.form.page = e;
  372. this.onSubmit();
  373. },
  374. onSubmit() {
  375. this.lastParams = {
  376. app: this.form.app,
  377. start: this.FormData(this.form.date[0]),
  378. end: this.FormData(this.form.date[1]),
  379. version: this.form.version == -1 ? undefined : this.form.version,
  380. contentType: this.form.content == -1 ? undefined : this.form.content,
  381. page: this.form.page,
  382. pageSize: this.form.size,
  383. sortBy: this.form.sortBy,
  384. };
  385. getContentPage(this.lastParams).then(r => (this.table = r || {}));
  386. getContentTop10({
  387. app: this.form.app,
  388. start: this.FormData(this.form.date[0]),
  389. end: this.FormData(this.form.date[1]),
  390. version: this.form.version == -1 ? undefined : this.form.version,
  391. contentType: this.form.content == -1 ? undefined : this.form.content,
  392. })
  393. .then(r => {
  394. this.top = r || [];
  395. });
  396. },
  397. async getAppListFunc() {
  398. const { r, li, appVersion, contentList } = await this.getAppListOri();
  399. let source = { length: 0 },
  400. appli = [];
  401. let appV = { length: 0 },
  402. appVLi = [];
  403. let contentV = { length: 0 },
  404. contentli = [];
  405. let prvList = r.output.data.prvRolectrl || [];
  406. for (let i = 0; i < prvList.length; i++) {
  407. const v = prvList[i];
  408. if (v.controlid == "RMT_SOURCE")
  409. (source[v.detid] = true), (source.length = source.length + 1);
  410. if (v.controlid == "APP_VERSION")
  411. (appV[v.detid] = true), (appV.length = appV.length + 1);
  412. if (v.controlid == "content_TYPE")
  413. (contentV[v.detid] = true), (contentV.length = contentV.length + 1);
  414. }
  415. if (li.status === "0") appli = li.output.data || [];
  416. if (appVersion.length) appVLi = appVersion || [];
  417. if (contentList.length) contentli = contentList || [];
  418. return {
  419. source,
  420. appV,
  421. appli,
  422. contentV,
  423. appVLi,
  424. contentli,
  425. };
  426. },
  427. async getAppListOri() {
  428. const roleid = JSON.parse(
  429. window.parent.localStorage.userinfo || "{}"
  430. ).roleid;
  431. const r = await getRule({
  432. db: "authplat",
  433. exportMark: "0",
  434. menuid: 399,
  435. roleid,
  436. });
  437. // 应用列表
  438. const li = await getAppList({
  439. exportMark: "0",
  440. gcode: "SOURCE",
  441. pageid: 1,
  442. pagesize: 1000,
  443. });
  444. const defaultAppName = "起点新闻";
  445. // 应用版本列表
  446. const appVersion = await getSearchData({
  447. gcode: "APP_VERSION",
  448. source: defaultAppName,
  449. });
  450. // 端列表
  451. let contentList = await getAppList({
  452. exportMark: "0",
  453. gcode: "CONTENT_TYPE",
  454. pageid: 1,
  455. pagesize: 1000,
  456. });
  457. contentList =
  458. contentList.status == "0" ? contentList.output.data || [] : [];
  459. return { r, li, appVersion, contentList };
  460. },
  461. disabledDate(time) {
  462. const first = new Date("2021-06-21 00:00:00");
  463. return (
  464. time.getTime() > Date.now() - 86400000 ||
  465. time.getTime() < first.getTime()
  466. );
  467. },
  468. FormData(date) {
  469. const d = new Date(date || Date.now() - 86400000);
  470. const year = d.getFullYear();
  471. const month =
  472. d.getMonth() <= 8 ? "0" + (d.getMonth() + 1) : d.getMonth() + 1;
  473. const day = d.getDate() <= 9 ? "0" + d.getDate() : d.getDate();
  474. return [year, month, day].join("-");
  475. },
  476. timeFormat(t) {
  477. const Time = t || 0;
  478. const mH = Time % 3600;
  479. let hour = (Time - mH) / 3600;
  480. let min = (mH - (mH % 60)) / 60;
  481. let son = Number(mH % 60).toFixed(0);
  482. hour = hour <= 9 ? "0" + hour : hour;
  483. min = min <= 9 ? "0" + min : min;
  484. son = son <= 9 ? "0" + son : son;
  485. let out = [];
  486. if (hour * 1 > 0) out.push(hour);
  487. out.push(...[min, son]);
  488. return out.join(":");
  489. },
  490. change() {
  491. const roleid = JSON.parse(
  492. window.parent.localStorage.userinfo || "{}"
  493. ).roleid;
  494. const appV = {
  495. length: 0,
  496. };
  497. getRule({
  498. db: "authplat",
  499. exportMark: "0",
  500. menuid: 399,
  501. roleid,
  502. }).then(rule => {
  503. let prvList = rule.output.data.prvRolectrl || [];
  504. for (let i = 0; i < prvList.length; i++) {
  505. const v = prvList[i];
  506. if (v.controlid == "APP_VERSION")
  507. (appV[v.detid] = true), (appV.length = appV.length + 1);
  508. }
  509. getSearchData({
  510. gcode: "APP_VERSION",
  511. source: this.form.app,
  512. }).then(r => {
  513. let version = [
  514. {
  515. label: "不限",
  516. value: -1,
  517. },
  518. ];
  519. r.map(v => {
  520. if ((appV.length && appV[v.mcode]) || appV.length === 0)
  521. version.push({
  522. value: v.mname,
  523. label: v.mname,
  524. });
  525. });
  526. this.version = version;
  527. this.form = {
  528. ...this.form,
  529. version: [-1],
  530. };
  531. });
  532. });
  533. },
  534. changeversion(v) {
  535. if (!v.length) return (this.form.version = [-1]);
  536. const last = v[v.length - 1];
  537. if (last == -1) return (this.form.version = [-1]);
  538. let ver = [];
  539. for (let i = 0; i < v.length; i++) {
  540. const element = v[i];
  541. if (element == -1) continue;
  542. ver.push(element);
  543. }
  544. this.form.version = ver;
  545. },
  546. onExport() {
  547. const S = this.form.date[0]
  548. ? this.FormData(this.form.date[0])
  549. : undefined;
  550. const E = this.form.date[1]
  551. ? this.FormData(this.form.date[1])
  552. : undefined;
  553. const app = this.lastParams.app || this.form.app;
  554. const version =
  555. this.form.version == -1 ? undefined : this.form.version.join(",");
  556. const contentType = this.lastParams.contentType || this.form.contentType;
  557. let url =
  558. config.base.leverAudience + "/cxzx-program/new-media/content/export?start=" + S + "&end=" + E +"&app=" + app;
  559. version && (url += "&version=" + version);
  560. contentType && (url += "&contentType=" + contentType);
  561. window.open(url);
  562. },
  563. },
  564. components: {
  565. countTo,
  566. },
  567. };
  568. </script>
  569. <style>
  570. .Content {
  571. margin: 10px 15px;
  572. }
  573. .libTitle {
  574. color: tomato;
  575. font-weight: 700;
  576. font-size: 14px;
  577. }
  578. .btn-next i,
  579. .btn-prev i {
  580. margin: 0 auto;
  581. }
  582. .caret-wrapper .ascending {
  583. display: none;
  584. }
  585. </style>