index.vue 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. <template>
  2. <div>
  3. <div
  4. class="mt-[30px] mb-[20px] bg-[#fff] rounded-[10px] p-[15px] flex justify-between"
  5. >
  6. <div
  7. v-for="item in tabList"
  8. :key="item.value"
  9. @click="
  10. () => {
  11. searchForm.type = item.value;
  12. activeTab = item.value;
  13. refresh();
  14. }
  15. "
  16. class="px-[20px] py-[10px] rounded-[5px] cursor-pointer transition-all duration-300"
  17. :class="{ 'bg-[#4466ee] text-white': searchForm.type == item.value }"
  18. >
  19. <span class="text-[16px] flex items-center"
  20. ><sa-icon :icon="item.icon" class="text-[16px] mr-[5px]" />{{
  21. item.label
  22. }}</span
  23. >
  24. </div>
  25. </div>
  26. <div>
  27. <div class="mb-[15px]">
  28. <sa-icon
  29. :icon="tabList.find((item) => item.value === activeTab).icon"
  30. class="text-[25px] mr-[5px]"
  31. />
  32. <span class="text-[25px] font-bold"
  33. >{{
  34. tabList.find((item) => item.value === activeTab).label
  35. }}对比</span
  36. >
  37. </div>
  38. </div>
  39. <div class="grid grid-cols-3 gap-[15px]">
  40. <a-card
  41. class="text-center rounded-[10px] hover:translate-y-[-5px] transition-all duration-300 cursor-pointer p-[10px] border-none"
  42. >
  43. <div class="text-[#6c757d] text-[18px] font-bold">
  44. {{ dictOptions[searchForm.type] }}下单量
  45. </div>
  46. <a-statistic
  47. class="my-[5px]"
  48. :value="orderNum || 0"
  49. :value-from="0"
  50. animation
  51. show-group-separator
  52. value-style="color: #4361ee; font-size: 44px; font-weight: bold; line-height: 1.2;"
  53. />
  54. <div class="text-[15px] font-bold">
  55. <a-statistic
  56. :value="comparison.changes.orderChange.percentageText"
  57. :precision="2"
  58. :value-from="0"
  59. :start="start"
  60. animation
  61. value-style="color: #09d6a1; font-size: 15px; font-weight: bold; line-height: 1.2;"
  62. >
  63. <template #prefix>
  64. <icon-arrow-rise
  65. v-if="comparison.changes.orderChange.trend === 'up'"
  66. />
  67. <icon-arrow-fall v-else />
  68. </template>
  69. <template #suffix
  70. >% {{ comparison.changes.orderChange.label }}</template
  71. >
  72. </a-statistic>
  73. </div>
  74. </a-card>
  75. <a-card
  76. class="text-center rounded-[10px] hover:translate-y-[-5px] transition-all duration-300 cursor-pointer p-[10px] border-none"
  77. >
  78. <div class="text-[#6c757d] text-[18px] font-bold">
  79. {{ dictOptions[searchForm.type] }}支付量
  80. </div>
  81. <a-statistic
  82. class="my-[5px]"
  83. :value="successNum || 0"
  84. :value-from="0"
  85. animation
  86. show-group-separator
  87. value-style="color: #4361ee; font-size: 44px; font-weight: bold; line-height: 1.2;"
  88. />
  89. <div class="text-[15px] font-bold">
  90. <a-statistic
  91. :value="comparison.changes.payChange.percentageText"
  92. :precision="2"
  93. :value-from="0"
  94. :start="start"
  95. animation
  96. value-style="color: #09d6a1; font-size: 15px; font-weight: bold; line-height: 1.2;"
  97. >
  98. <template #prefix>
  99. <icon-arrow-rise
  100. v-if="comparison.changes.orderChange.trend === 'up'"
  101. />
  102. <icon-arrow-fall v-else />
  103. </template>
  104. <template #suffix
  105. >% {{ comparison.changes.payChange.label }}</template
  106. >
  107. </a-statistic>
  108. </div>
  109. </a-card>
  110. <a-card
  111. class="text-center rounded-[10px] hover:translate-y-[-5px] transition-all duration-300 cursor-pointer p-[10px] border-none"
  112. >
  113. <div class="text-[#6c757d] text-[18px] font-bold">
  114. {{ dictOptions[searchForm.type] }}支付成功率
  115. </div>
  116. <a-statistic
  117. class="my-[5px]"
  118. :value="paySuccessRate * 100 || 0"
  119. :value-from="0"
  120. animation
  121. show-group-separator
  122. value-style="color: #4361ee; font-size: 44px; font-weight: bold; line-height: 1.2;"
  123. >
  124. <template #suffix>%</template>
  125. </a-statistic>
  126. <div class="text-[15px] font-bold">
  127. <a-statistic
  128. :value="comparison.changes.rateChange.percentageText"
  129. :precision="2"
  130. :value-from="0"
  131. :start="start"
  132. animation
  133. value-style="color: #f53f3f; font-size: 15px; font-weight: bold; line-height: 1.2;"
  134. >
  135. <template #prefix>
  136. <icon-arrow-rise
  137. v-if="comparison.changes.orderChange.trend === 'up'"
  138. />
  139. <icon-arrow-fall v-else />
  140. </template>
  141. <template #suffix
  142. >% {{ comparison.changes.orderChange.label }}</template
  143. >
  144. </a-statistic>
  145. </div>
  146. </a-card>
  147. </div>
  148. <div class="mt-[24px] gap-[15px] flex">
  149. <a-card class="w-[70%]" title="支付成功率对比">
  150. <sa-chart :option="lineChartOption" height="300px" />
  151. </a-card>
  152. <a-card class="w-[30%]" title="支付渠道下单比例">
  153. <div class="text-center text-[16px] text-[#6c757d]">
  154. 待确认开发中...
  155. </div>
  156. <!-- <sa-chart :option="lineChartOption" height="300px" /> -->
  157. </a-card>
  158. </div>
  159. <a-card title="详细数据" class="mt-[24px]">
  160. <sa-table
  161. ref="crudRef"
  162. :searchForm="searchForm"
  163. :columns="columns"
  164. :options="options"
  165. />
  166. </a-card>
  167. </div>
  168. </template>
  169. <script setup>
  170. import { onMounted, reactive, ref, watch, nextTick } from "vue";
  171. import SaIcon from "@/components/sa-icon/index.vue";
  172. import SaChart from "@/components/sa-chart/index.vue";
  173. import api from "../../api/gameLog/analyse";
  174. const crudRef = ref();
  175. const orderNum = ref(0);
  176. const successNum = ref(0);
  177. const paySuccessRate = ref(0);
  178. const dictOptions = ref({
  179. day: "今日",
  180. week: "本周",
  181. month: "本月",
  182. });
  183. const searchForm = ref({
  184. type: "day",
  185. });
  186. const lineChartOption = ref({});
  187. const chartOption = ref({});
  188. const comparison = ref({
  189. changes: {
  190. orderChange: {
  191. percentage: 0,
  192. trend: "up",
  193. value: 0,
  194. percentageText: "",
  195. icon: "icon-arrow-rise",
  196. label: "",
  197. },
  198. payChange: {
  199. percentage: 0,
  200. trend: "up",
  201. value: 0,
  202. percentageText: "",
  203. icon: "icon-arrow-rise",
  204. label: "",
  205. },
  206. rateChange: {
  207. percentage: 0,
  208. trend: "up",
  209. percentageText: "",
  210. value: 0,
  211. icon: "icon-arrow-rise",
  212. label: "",
  213. },
  214. },
  215. });
  216. const options = reactive({
  217. api: api.getPaySuccessRate,
  218. pk: "time",
  219. showSort: false,
  220. operationColumn: false,
  221. showSearch: false,
  222. pageSimple: true,
  223. pageLayout: "normal", // 改为普通布局,避免固定高度问题
  224. height: "auto", // 自动高度
  225. });
  226. const columns = reactive([
  227. {
  228. title: "时段",
  229. dataIndex: "time",
  230. width: 100,
  231. },
  232. {
  233. title: "今日下单量",
  234. dataIndex: "todayOrderNum",
  235. },
  236. {
  237. title: "今日支付量",
  238. dataIndex: "todayPayNum",
  239. },
  240. {
  241. title: "今日成功率",
  242. dataIndex: "todayPaySuccessRate",
  243. },
  244. {
  245. title: "昨日下单量",
  246. dataIndex: "yesterdayOrderNum",
  247. },
  248. {
  249. title: "昨日支付量",
  250. dataIndex: "yesterdayPayNum",
  251. },
  252. {
  253. title: "昨日成功率",
  254. dataIndex: "yesterdayPaySuccessRate",
  255. },
  256. {
  257. title: "成功率变化",
  258. dataIndex: "successRateChange",
  259. },
  260. ]);
  261. const tabList = ref([
  262. {
  263. label: "今日 vs 昨日",
  264. value: "day",
  265. icon: "IconSun",
  266. },
  267. {
  268. label: "本周 vs 上周",
  269. value: "week",
  270. icon: "IconCalendar",
  271. },
  272. // {
  273. // label: "本月 vs 上月",
  274. // value: "month",
  275. // icon: "IconComputer",
  276. // },
  277. ]);
  278. const activeTab = ref("day");
  279. // 请求支付成功率
  280. const getPaySuccessRate = async () => {
  281. const res = await api.getPaySuccessRate({
  282. type: searchForm.value.type,
  283. });
  284. let data = res.data;
  285. orderNum.value = data.orderNum;
  286. successNum.value = data.successNum;
  287. paySuccessRate.value = data.paySuccessRate;
  288. chartOption.value = {
  289. legend: {
  290. data: ["今日支付成功率", "昨日支付成功率"],
  291. itemGap: 5,
  292. },
  293. grid: {
  294. left: "1%",
  295. right: "10%",
  296. containLabel: true,
  297. },
  298. xAxis: {
  299. type: "category",
  300. data: data.xAxisData,
  301. },
  302. yAxis: {
  303. type: "value",
  304. },
  305. series: [
  306. {
  307. name: "今日支付成功率",
  308. type: "bar",
  309. data: data.PaySuccessRateArr,
  310. },
  311. {
  312. name: "昨日支付成功率",
  313. type: "bar",
  314. data: data.OldPaySuccessRate,
  315. },
  316. ],
  317. tooltip: {
  318. trigger: "axis",
  319. },
  320. };
  321. lineChartOption.value = {
  322. tooltip: {
  323. trigger: "axis",
  324. axisPointer: {
  325. type: "cross",
  326. label: {
  327. backgroundColor: "#6a7985",
  328. },
  329. },
  330. },
  331. legend: {
  332. data: ["今日", "昨日"],
  333. itemGap: 5,
  334. },
  335. toolbox: {
  336. feature: {
  337. saveAsImage: {},
  338. },
  339. },
  340. xAxis: [
  341. {
  342. type: "category",
  343. boundaryGap: false,
  344. data: data.xAxisData,
  345. },
  346. ],
  347. yAxis: [
  348. {
  349. type: "value",
  350. },
  351. ],
  352. series: [
  353. {
  354. name: "今日",
  355. type: "line",
  356. areaStyle: {},
  357. emphasis: {
  358. focus: "series",
  359. },
  360. data: data.PaySuccessRateArr,
  361. },
  362. {
  363. name: "昨日",
  364. type: "line",
  365. areaStyle: {},
  366. emphasis: {
  367. focus: "series",
  368. },
  369. data: data.OldPaySuccessRateArr,
  370. },
  371. ],
  372. };
  373. };
  374. // SaTable 数据请求
  375. const refresh = async () => {
  376. await getPaySuccessRate();
  377. // 延迟刷新表格,确保数据已经更新
  378. await nextTick();
  379. crudRef.value?.refresh();
  380. };
  381. onMounted(() => {
  382. refresh();
  383. });
  384. </script>
  385. <style scoped></style>