manageOrganAll.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. <template>
  2. <div>
  3. <div
  4. class="wall"
  5. style="height: 68px"
  6. v-if="JSON.stringify(items) == '{}'"
  7. ></div>
  8. <statistic :col="3" class="statistic" :cols="12" v-else>
  9. <statistic-item @click="active = 0">
  10. <span>
  11. {{ items["TOTAL_AMOUNT"].title + "(元)" }}
  12. <el-tooltip
  13. v-if="items['TOTAL_AMOUNT'].desc"
  14. :content="items['TOTAL_AMOUNT'].desc"
  15. :open-delay="0.3"
  16. placement="top"
  17. >
  18. <i
  19. style="margin-left: 5px; cursor: pointer"
  20. class="el-icon-warning-outline"
  21. />
  22. </el-tooltip>
  23. </span>
  24. <!-- 12345678901.23 -->
  25. <span>
  26. <count-to
  27. :endVal="items['TOTAL_AMOUNT'].percent"
  28. :decimals="2"
  29. :class="'blod'"
  30. /></span>
  31. </statistic-item>
  32. <template v-for="(item, key) in items">
  33. <statistic-item
  34. v-if="item.dataType != 'TOTAL_AMOUNT'"
  35. :key="key"
  36. :class="{ active: active === key }"
  37. @click="active = key"
  38. >
  39. <span>
  40. {{ item.title + "(元)" }}
  41. <el-tooltip
  42. v-if="item.desc"
  43. :content="item.desc"
  44. :open-delay="0.3"
  45. placement="top"
  46. >
  47. <i
  48. style="margin-left: 5px; cursor: pointer"
  49. class="el-icon-warning-outline"
  50. />
  51. </el-tooltip>
  52. </span>
  53. <!-- 12345678901.23 -->
  54. <span>
  55. <count-to
  56. :endVal="item.percent"
  57. :decimals="2"
  58. :class="item.dataType == 'TOTAL_AMOUNT' ? 'blod' : ''"
  59. /></span>
  60. </statistic-item>
  61. </template>
  62. <statistic-item></statistic-item>
  63. </statistic>
  64. <div class="statisticWrap" v-if="JSON.stringify(items2) !== '{}'">
  65. <statistic :col="5" class="statistic" :cols="0" :isNoLine="true">
  66. <statistic-item
  67. v-for="(item, key) in items2"
  68. :key="key"
  69. :class="{ active: active === key }"
  70. @click="active = key"
  71. >
  72. <template v-if="item.dataType != 'OTHER_AMOUNT'">
  73. <span>
  74. {{ item.title + "(元)" }}
  75. <!-- {{ item.dataType == 'OTHER_AMOUNT' ? 'other' : null }} -->
  76. <el-tooltip
  77. v-if="item.desc"
  78. :content="item.desc"
  79. :open-delay="0.3"
  80. placement="top"
  81. >
  82. <i
  83. style="margin-left: 5px; cursor: pointer"
  84. class="el-icon-warning-outline"
  85. />
  86. </el-tooltip>
  87. </span>
  88. <!-- 12345678901.23 -->
  89. <span> <count-to :endVal="item.percent" :decimals="2" /></span>
  90. </template>
  91. <el-popover placement="top" width="400" v-else trigger="hover">
  92. <descriptions
  93. :column="1"
  94. v-if="(item.indexMonthDataDetail || []).length > 0"
  95. >
  96. <template
  97. v-for="(item, index) in item.indexMonthDataDetail || []"
  98. >
  99. <descriptions-item
  100. :key="index"
  101. :label="orderType[item.orderType]"
  102. >{{ item.totalNum | moneyFormat }}元
  103. </descriptions-item>
  104. </template>
  105. </descriptions>
  106. <div slot="reference">
  107. <span>
  108. {{ item.title + "(元)" }}
  109. <el-tooltip
  110. v-if="item.desc"
  111. :content="item.desc"
  112. :open-delay="0.3"
  113. placement="top"
  114. >
  115. <i
  116. style="margin-left: 5px; cursor: pointer"
  117. class="el-icon-warning-outline"
  118. />
  119. </el-tooltip>
  120. </span>
  121. <span
  122. style="
  123. font-size: 18px;
  124. display: block;
  125. color: rgba(0, 0, 0, 0.85);
  126. "
  127. >
  128. <count-to :endVal="item.percent" :decimals="2"
  129. /></span>
  130. </div>
  131. </el-popover>
  132. </statistic-item>
  133. </statistic>
  134. </div>
  135. <!-- 按月/按天 -->
  136. <div class="wrap">
  137. <div class="chioseBox">
  138. <el-radio-group v-model="timer">
  139. <el-radio-button label="day">按天</el-radio-button>
  140. <el-radio-button label="month">按月</el-radio-button>
  141. </el-radio-group>
  142. </div>
  143. <ve-line
  144. :loading="dataLoading"
  145. :data="timer == 'day' ? chartData : chartDataForMoth"
  146. height="350px"
  147. :data-empty="!dataLoading && dataEmpty"
  148. :extend="chartExtend"
  149. :legend="legend"
  150. />
  151. <!-- <ve-line
  152. v-else
  153. :data-zoom="dataZoom"
  154. :settings="{
  155. area: true,
  156. }"
  157. :data="chartDataForMoth"
  158. height="350px"
  159. :data-empty="dataEmpty"
  160. :data-zoom="dataZoom"
  161. :extend="chartExtend"
  162. :legend="legend"
  163. /> -->
  164. </div>
  165. </div>
  166. </template>
  167. <script>
  168. import "echarts/lib/component/dataZoom";
  169. import countTo from "vue-count-to";
  170. import veLine from "v-charts/lib/line.common";
  171. import histogram from "v-charts/lib/histogram.common";
  172. import searchHeader from "./searchHeader";
  173. import { getIndex } from "../../api";
  174. import { getTimes } from "@/utils";
  175. import { descs, chioseNum } from "../../constant";
  176. import { orderType } from "@/constant";
  177. export default {
  178. props: ["data", "search"],
  179. components: {
  180. "ve-line": veLine,
  181. "count-to": countTo,
  182. "ve-histogram": histogram,
  183. searchHeader,
  184. },
  185. computed: {
  186. legend() {
  187. return {
  188. left: "10px",
  189. };
  190. },
  191. items() {
  192. let obj = {};
  193. let arr = [
  194. "TOTAL_AMOUNT",
  195. "FINANCE_AMOUNT",
  196. "FINANCE_BALANCE_AMOUNT",
  197. // "FINANCE_PAY",
  198. ];
  199. arr.forEach((str) => {
  200. if (this.data[str]) {
  201. obj[str] = this.data[str];
  202. }
  203. });
  204. // console.log(obj);
  205. return obj;
  206. },
  207. items2() {
  208. let obj = {};
  209. let arr = [
  210. "APPLY_AMOUNT",
  211. "RENEW_AMOUNT",
  212. "VIP_AMOUNT",
  213. "PRACTICE_AMOUNT",
  214. "OTHER_AMOUNT",
  215. ];
  216. arr.forEach((str) => {
  217. if (this.data[str]) {
  218. obj[str] = this.data[str];
  219. }
  220. });
  221. return obj;
  222. },
  223. chartExtend() {
  224. return {
  225. series: {
  226. smooth: false,
  227. },
  228. yAxis: {
  229. //纵轴标尺固定
  230. minInterval: 1,
  231. type: "value",
  232. scale: true,
  233. min: 0,
  234. axisLabel: {
  235. formatter: "{value}元",
  236. },
  237. },
  238. tooltip: {
  239. axisPointer: {
  240. type: "shadow",
  241. shadowStyle: {
  242. color: "rgba(150,150,150,0.2)",
  243. },
  244. },
  245. formatter: (item) => {
  246. return [
  247. item[0].axisValueLabel,
  248. ...item.map(
  249. (d) => `<br/>${d.marker}${d.seriesName}: ${d.value[1]} 元`
  250. ),
  251. ].join("");
  252. },
  253. },
  254. };
  255. },
  256. dataZoom() {
  257. return [
  258. {
  259. type: "slider",
  260. start: 50,
  261. end: 100,
  262. filterMode: "empty",
  263. },
  264. ];
  265. },
  266. chartData() {
  267. const values = Object.values({ ...this.items, ...this.items2 });
  268. const months = {};
  269. for (const item of values) {
  270. for (const row of item.indexMonthData || []) {
  271. const key = this.$helpers.dayjs(row.month).format("YYYY-MM-DD");
  272. if (!months[key]) {
  273. months[key] = {
  274. 日期: key,
  275. };
  276. months[key][item.title] = row.percent;
  277. } else {
  278. if (months[key][item.title]) {
  279. months[key][item.title] = (
  280. parseFloat(months[key][item.title]) + parseFloat(row.percent)
  281. ).toFixed(2);
  282. } else {
  283. months[key][item.title] = parseFloat(row.percent).toFixed(2);
  284. }
  285. }
  286. }
  287. }
  288. // console.log(values);
  289. this.dataLoading = false;
  290. return {
  291. columns: [
  292. "日期",
  293. "总收入",
  294. "现金收入",
  295. "余额收入",
  296. // "财务支出",
  297. "报名缴费收入",
  298. "网管课收入",
  299. "其他收入",
  300. "乐团续费收入",
  301. "VIP课收入",
  302. ],
  303. rows: Object.values(months),
  304. loading: true,
  305. };
  306. },
  307. chartDataForMoth() {
  308. const values = Object.values({ ...this.items, ...this.items2 });
  309. const months = {};
  310. for (const item of values) {
  311. for (const row of item.indexMonthData || []) {
  312. const key = this.$helpers.dayjs(row.month).format("YYYY-MM");
  313. if (!months[key]) {
  314. months[key] = {
  315. 月份: key,
  316. };
  317. months[key][item.title] = row.percent;
  318. } else {
  319. if (months[key][item.title]) {
  320. months[key][item.title] = (
  321. parseFloat(months[key][item.title]) + parseFloat(row.percent)
  322. ).toFixed(2);
  323. } else {
  324. months[key][item.title] = parseFloat(row.percent).toFixed(2);
  325. }
  326. }
  327. }
  328. }
  329. this.dataLoading = false;
  330. return {
  331. columns: [
  332. "月份",
  333. "总收入",
  334. "现金收入",
  335. "余额收入",
  336. "财务支出",
  337. "报名缴费收入",
  338. "网管课收入",
  339. "其他收入",
  340. "乐团续费收入",
  341. "VIP课收入",
  342. ],
  343. rows: Object.values(months),
  344. loading: true,
  345. };
  346. },
  347. dataEmpty() {
  348. return !this.chartData.rows.length;
  349. },
  350. },
  351. data() {
  352. // this.chartSettings = {
  353. // stack: { 总收入: [`现金收入`, `余额收入`] },
  354. // };
  355. return {
  356. orderType,
  357. active: "SHOULD_INCOME_MONEY",
  358. mdate: [],
  359. loading: false,
  360. endDate: "",
  361. show: true,
  362. activeName: "first",
  363. timer: "day",
  364. dataLoading: true
  365. };
  366. },
  367. mounted() {
  368. this.init();
  369. },
  370. methods: {
  371. init() {
  372. // this.$refs.searchHeader.initStatue("month");
  373. this.mdate = this.getInitDate();
  374. this.endDate = this.$helpers.dayjs(new Date()).format("YYYY-MM-DD");
  375. this.isDayOrMoth(this.mdate);
  376. // // this.$refs.searchHeader.initStatue()
  377. // this.changeValue(this.mdate);
  378. },
  379. // changeValue(date) {
  380. // // 请求更改数据
  381. // this.mdate = date;
  382. // this.isDayOrMoth(date);
  383. // this.FetchDetail();
  384. // },
  385. // async FetchDetail() {
  386. // this.loading = true;
  387. // let data = [];
  388. // try {
  389. // const { dates, ...rest } = this.search;
  390. // const res = await getIndex({
  391. // ...rest,
  392. // ...getTimes(this.mdate, ["startDate", "endDate"]),
  393. // //
  394. // dataTypes:
  395. // "FINANCE_AMOUNT,FINANCE_BALANCE_AMOUNT,FINANCE_PAY,TOTAL_AMOUNT",
  396. // });
  397. // for (const item of res.data) {
  398. // // 再循环一遍
  399. // for (const key in { ...this.items, ...this.items2 }) {
  400. // // console.log(key);
  401. // if (item.dataType == key) {
  402. // data[item.dataType] = {
  403. // ...item,
  404. // desc: descs[item.dataType],
  405. // };
  406. // }
  407. // }
  408. // }
  409. // } catch (error) {
  410. // console.log(error);
  411. // }
  412. // this.loading = false;
  413. // this.$emit("resetDate", data);
  414. // },
  415. isDayOrMoth(arr) {
  416. if (!arr || arr.length < 1) {
  417. this.timer = "day";
  418. } else {
  419. const count = this.$helpers
  420. .dayjs(arr[0])
  421. .diff(this.$helpers.dayjs(arr[1]), "day");
  422. Math.abs(count) > chioseNum
  423. ? (this.timer = "month")
  424. : (this.timer = "day");
  425. }
  426. },
  427. getInitDate() {
  428. const end = this.$helpers.dayjs(new Date()).format("YYYY-MM-DD");
  429. const start = this.$helpers
  430. .dayjs(new Date())
  431. .set("date", 1)
  432. .format("YYYY-MM-DD");
  433. return [start, end];
  434. },
  435. },
  436. };
  437. </script>
  438. <style lang="less" scoped>
  439. ::v-deep .description-title {
  440. margin-bottom: 0;
  441. }
  442. #management .statistic .statistic-content > span {
  443. &:first-child {
  444. font-size: 14px !important;
  445. }
  446. font-size: 18px !important;
  447. }
  448. .chioseBox {
  449. position: absolute;
  450. right: 20px;
  451. z-index: 1000;
  452. }
  453. .wrap {
  454. position: relative;
  455. }
  456. .blod {
  457. font-weight: bold;
  458. color: var(--color-primary);
  459. font-size: 22px !important;
  460. }
  461. // .chioseBox {
  462. // position: absolute;
  463. // right: 20px;
  464. // z-index: 1000;
  465. // }
  466. // .wrap {
  467. // position: relative;
  468. // }
  469. .statisticWrap {
  470. // box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
  471. border-radius: 4px;
  472. border: 1px solid #ebeef5;
  473. padding-top: 10px;
  474. margin-bottom: 10px;
  475. position: relative;
  476. &:after {
  477. top: -16px;
  478. margin-left: calc(17% - 14px);
  479. border-top-width: 0;
  480. border-color: transparent;
  481. border-bottom-color: #fff;
  482. content: " ";
  483. border-width: 8px;
  484. position: absolute;
  485. display: block;
  486. width: 0;
  487. height: 0;
  488. border-style: solid;
  489. z-index: 101;
  490. }
  491. &:before {
  492. top: -20px;
  493. margin-left: calc(17% - 16px);
  494. border-top-width: 0;
  495. border-color: transparent;
  496. border-bottom-color: #ebeef5;
  497. content: " ";
  498. border-width: 10px;
  499. position: absolute;
  500. display: block;
  501. width: 0;
  502. height: 0;
  503. border-style: solid;
  504. z-index: 100;
  505. }
  506. }
  507. </style>