index.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. <template>
  2. <div class="RealOnline">
  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-select v-model="form.app" placeholder="请选择时段">
  19. <el-option
  20. v-for="item in cycle"
  21. :key="item.value"
  22. :label="item.label"
  23. :value="item.value"
  24. :disabled="item.disabled"
  25. >
  26. </el-option>
  27. </el-select>
  28. </el-form-item>
  29. <el-form-item label="粒度">
  30. <el-select v-model="form.lidu" placeholder="请选择粒度">
  31. <el-option
  32. v-for="item in lidu"
  33. :key="item.value"
  34. :label="item.label"
  35. :value="item.value"
  36. :disabled="item.disabled"
  37. >
  38. </el-option>
  39. </el-select>
  40. </el-form-item>
  41. <el-form-item style="float: right">
  42. <el-button type="primary" @click="regularData">查询</el-button>
  43. </el-form-item>
  44. </el-form>
  45. </el-card>
  46. <br />
  47. <el-card>
  48. <div class="totalText" style="display: flex; text-align: center">
  49. <div
  50. class="totalItem"
  51. v-for="(item, i) in total"
  52. :key="i + item"
  53. :style="{ color: item.color }"
  54. >
  55. <countTo
  56. :startVal="0"
  57. :endVal="item.value"
  58. :duration="1500"
  59. ></countTo>
  60. <p v-text="item.name" style="font-size: 16px;color: #000;"></p>
  61. </div>
  62. </div>
  63. </el-card>
  64. <br />
  65. <el-card class="box-card">
  66. <div class="realLineChart" ref="realLineChart"></div>
  67. </el-card>
  68. </div>
  69. </template>
  70. <script>
  71. // @ is an alias to /src
  72. import countTo from "@/components/counto/vue-countTo.vue";
  73. import {
  74. getRealAppOnline,
  75. getRule,
  76. getAppList,
  77. getSearchData,
  78. } from "@/api/index";
  79. import * as echarts from "echarts/core";
  80. import { LineChart } from "echarts/charts";
  81. import {
  82. TitleComponent,
  83. TooltipComponent,
  84. GridComponent,
  85. ToolboxComponent,
  86. LegendComponent,
  87. } from "echarts/components";
  88. import { CanvasRenderer } from "echarts/renderers";
  89. echarts.use([
  90. TitleComponent,
  91. TooltipComponent,
  92. GridComponent,
  93. LineChart,
  94. CanvasRenderer,
  95. ToolboxComponent,
  96. LegendComponent,
  97. ]);
  98. // import config from "@/config/index";
  99. let chart = undefined,
  100. time = undefined;
  101. export default {
  102. name: "RealOnline",
  103. data() {
  104. return {
  105. form: {
  106. app: "",
  107. lidu: "",
  108. },
  109. total: {},
  110. tableData: [],
  111. cycle: [],
  112. lidu: [],
  113. realForm: {},
  114. };
  115. },
  116. mounted() {
  117. this.init();
  118. },
  119. computed: {},
  120. methods: {
  121. async init() {
  122. if (chart && chart.dispose) chart.dispose();
  123. const { source, appli, lidu } = await this.getAppListFunc();
  124. appli.map(v => {
  125. if ((length.length && source[v.mcode]) || source.length === 0)
  126. this.cycle.push({
  127. value: v.mname,
  128. label: v.mname,
  129. });
  130. });
  131. this.lidu = lidu.map(v => {
  132. return {
  133. value: v.mcode,
  134. label: v.mname,
  135. };
  136. });
  137. this.form = {
  138. // app: (this.cycle[0] || { value: "" }).value,
  139. app: '起点新闻',
  140. lidu: '60',
  141. };
  142. this.regularData();
  143. },
  144. regularData() {
  145. if (time) clearInterval(time);
  146. this.realForm = {
  147. appName: this.form.app,
  148. minute: this.form.lidu,
  149. };
  150. this.onSubmit();
  151. time = setInterval(this.onSubmit, 5000);
  152. },
  153. onSubmit() {
  154. getRealAppOnline(this.realForm)
  155. .then(r => {
  156. if (!this.$refs.realLineChart) return;
  157. const li = r.list || [],
  158. T = r.total || {},
  159. keys = Object.keys(T),
  160. total = [],
  161. color = ["rgb(244, 127, 146)", "rgb(17, 160, 248)"];
  162. const keyList = [],
  163. valueList_active = [],
  164. valueList_newUser = [];
  165. for (let i = 0; i < keys.length; i++) {
  166. const v = keys[i];
  167. if (typeof T[v].value !== "number") continue;
  168. let p = {
  169. name: T[v].name,
  170. value: T[v].value,
  171. color: color[i % 2],
  172. };
  173. total.push(p);
  174. }
  175. this.total = total;
  176. li.map(v => {
  177. const key = v.recMinute.split(" ")[1] || "";
  178. keyList.push(key);
  179. valueList_active.push(v.active);
  180. valueList_newUser.push(v.newUser);
  181. });
  182. this.createImage(keyList, valueList_active, valueList_newUser);
  183. })
  184. .catch(() => {
  185. if (!chart) return;
  186. chart.clear();
  187. });
  188. },
  189. createImage(keyList, valueList_active, valueList_newUser) {
  190. !chart && (chart = echarts.init(this.$refs.realLineChart));
  191. chart.resize({
  192. height: (this.$refs.realLineChart.offsetWidth * 4) / 16,
  193. });
  194. chart.setOption({
  195. tooltip: {
  196. confine: true,
  197. trigger: "axis",
  198. },
  199. toolbox: {
  200. feature: {
  201. saveAsImage: {
  202. type: "jpg",
  203. name: "实时",
  204. },
  205. },
  206. },
  207. grid: {
  208. left: "3%",
  209. right: "4%",
  210. bottom: "3%",
  211. containLabel: true,
  212. },
  213. legend: {
  214. data: ["活跃用户", "新增用户"],
  215. },
  216. xAxis: {
  217. type: "category",
  218. boundaryGap: true,
  219. data: keyList,
  220. },
  221. yAxis: {
  222. type: "value",
  223. minInterval: 1,
  224. axisLabel: {
  225. formatter(v) {
  226. let val = v;
  227. if (val >= 100000000) {
  228. val = (val / 100000000).toFixed(2) + "亿";
  229. } else if (val >= 10000) {
  230. val = (val / 10000).toFixed(2) + "万";
  231. }
  232. return val;
  233. },
  234. },
  235. },
  236. color: [ "rgb(17, 160, 248)","rgb(244, 127, 146)"],
  237. series: [
  238. {
  239. name: "活跃用户",
  240. data: valueList_active,
  241. symbolSize: 0,
  242. lineStyle: {
  243. width: 1,
  244. },
  245. type: "line",
  246. smooth: true,
  247. areaStyle: {
  248. color: {
  249. type: "linear",
  250. x: 0,
  251. y: 0,
  252. x2: 0,
  253. y2: 1,
  254. colorStops: [
  255. {
  256. offset: 0,
  257. color: "rgba(17, 160, 248, 0.8)", // 0% 处的颜色
  258. },
  259. {
  260. offset: 1,
  261. color: "rgba(17, 160, 248, 0.1)", // 100% 处的颜色
  262. },
  263. ],
  264. global: false, // 缺省为 false
  265. },
  266. },
  267. },
  268. {
  269. name: "新增用户",
  270. data: valueList_newUser,
  271. symbolSize: 0,
  272. lineStyle: {
  273. width: 1,
  274. },
  275. type: "line",
  276. smooth: true,
  277. areaStyle: {
  278. color: {
  279. type: "linear",
  280. x: 0,
  281. y: 0,
  282. x2: 0,
  283. y2: 1,
  284. colorStops: [
  285. {
  286. offset: 0,
  287. color: "rgba(244, 127, 146, 0.8)", // 0% 处的颜色
  288. },
  289. {
  290. offset: 1,
  291. color: "rgba(244, 127, 146, 0.1)", // 100% 处的颜色
  292. },
  293. ],
  294. global: false, // 缺省为 false
  295. },
  296. },
  297. },
  298. ],
  299. });
  300. },
  301. async getAppListFunc() {
  302. const { r, li, lidu } = await this.getAppListOri();
  303. let source = {
  304. length: 0,
  305. },
  306. appli = [];
  307. let prvList = r.output.data.prvRolectrl || [];
  308. for (let i = 0; i < prvList.length; i++) {
  309. const v = prvList[i];
  310. if (v.controlid == "RMT_SOURCE")
  311. (source[v.detid] = true), (source.length = source.length + 1);
  312. }
  313. if (li.status === "0") appli = li.output.data || [];
  314. return {
  315. source,
  316. appli,
  317. lidu,
  318. };
  319. },
  320. async getAppListOri() {
  321. const roleid = JSON.parse(
  322. window.parent.localStorage.userinfo || "{}"
  323. ).roleid;
  324. const r = await getRule({
  325. db: "authplat",
  326. exportMark: "0",
  327. menuid: 400,
  328. roleid,
  329. });
  330. const li = await getAppList({
  331. exportMark: "0",
  332. gcode: "SOURCE",
  333. pageid: 1,
  334. pagesize: 1000,
  335. });
  336. const defaultAppName = li.status == "0" ? li.output.data[0].mname : "";
  337. const lidu = await getSearchData({
  338. gcode: "TIME_MINUTE",
  339. source: defaultAppName,
  340. });
  341. return { r, li, lidu };
  342. },
  343. },
  344. components: { countTo },
  345. };
  346. </script>
  347. <style>
  348. .RealOnline {
  349. margin: 10px 15px;
  350. }
  351. .RealOnline .has-seconds .el-time-spinner__wrapper:last-child {
  352. display: none;
  353. }
  354. .totalText {
  355. display: flex;
  356. }
  357. .totalText .totalItem {
  358. flex: 1;
  359. position: relative;
  360. font-size: 42px;
  361. font-weight: 700;
  362. font-family: sans-serif;
  363. }
  364. .totalText .totalItem:not(:last-child)::after {
  365. position: absolute;
  366. top: 0;
  367. right: 0;
  368. bottom: 0;
  369. content: "";
  370. display: block;
  371. width: 1px;
  372. background: #e7eef0;
  373. }
  374. </style>