orderStatisics.uvue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <template>
  2. <!-- #ifdef APP -->
  3. <scroll-view class="page-scroll">
  4. <!-- #endif -->
  5. <view class="page">
  6. <view class="summary-card">
  7. <text class="summary-title">数据总览</text>
  8. <text class="summary-subtitle">按当前统计口径展示技师经营数据</text>
  9. </view>
  10. <view class="summary-strip">
  11. <view class="summary-strip-item">
  12. <text class="summary-strip-value">{{ stats.registerDays.count }}</text>
  13. <text class="summary-strip-label">入驻天数</text>
  14. </view>
  15. <view class="summary-strip-item">
  16. <text class="summary-strip-value">{{ stats.additional.count }}</text>
  17. <text class="summary-strip-label">7日在线</text>
  18. </view>
  19. </view>
  20. <view v-for="item in statCards" :key="item.key" class="stat-card">
  21. <view class="stat-row">
  22. <text class="stat-label">{{ item.title }}</text>
  23. <text class="stat-rate">{{ item.rateText }}</text>
  24. </view>
  25. <view class="stat-row stat-row-gap">
  26. <view class="stat-column">
  27. <text class="stat-value">{{ item.countText }}</text>
  28. <text class="stat-caption">数量</text>
  29. </view>
  30. <view class="stat-column">
  31. <text class="stat-value">{{ item.amountText }}</text>
  32. <text class="stat-caption">金额</text>
  33. </view>
  34. </view>
  35. </view>
  36. </view>
  37. <!-- #ifdef APP -->
  38. </scroll-view>
  39. <!-- #endif -->
  40. </template>
  41. <script setup lang="uts">
  42. import { computed, reactive } from 'vue'
  43. import { getAllData } from '@/utils/api/workbenches.uts'
  44. type StatInfo = {
  45. count: number
  46. amount: number
  47. rate: number
  48. }
  49. type StatsState = {
  50. additional: StatInfo
  51. registerDays: StatInfo
  52. deal: StatInfo
  53. receiving: StatInfo
  54. refund: StatInfo
  55. renewal: StatInfo
  56. comment: StatInfo
  57. }
  58. type StatCard = {
  59. key: string
  60. title: string
  61. rateText: string
  62. countText: string
  63. amountText: string
  64. }
  65. const createStat = () : StatInfo => {
  66. return { count: 0, amount: 0, rate: 0 }
  67. }
  68. const stats = reactive<StatsState>({
  69. additional: createStat(),
  70. registerDays: createStat(),
  71. deal: createStat(),
  72. receiving: createStat(),
  73. refund: createStat(),
  74. renewal: createStat(),
  75. comment: createStat(),
  76. })
  77. const applyStat = (target : StatInfo, source : UTSJSONObject | null) : void => {
  78. if (source == null) {
  79. target.count = 0
  80. target.amount = 0
  81. target.rate = 0
  82. return
  83. }
  84. target.count = (source['count'] as number | null) ?? 0
  85. target.amount = (source['amount'] as number | null) ?? 0
  86. target.rate = (source['rate'] as number | null) ?? 0
  87. }
  88. const statCards = computed(() : StatCard[] => {
  89. return [
  90. { key: 'receiving', title: '接单数据', rateText: `${stats.receiving.rate}%`, countText: `${stats.receiving.count}`, amountText: `${stats.receiving.amount}` },
  91. { key: 'deal', title: '成交数据', rateText: `${stats.deal.rate}%`, countText: `${stats.deal.count}`, amountText: `${stats.deal.amount}` },
  92. { key: 'renewal', title: '加钟数据', rateText: `${stats.renewal.rate}%`, countText: `${stats.renewal.count}`, amountText: `${stats.renewal.amount}` },
  93. { key: 'refund', title: '退单数据', rateText: `${stats.refund.rate}%`, countText: `${stats.refund.count}`, amountText: `${stats.refund.amount}` },
  94. { key: 'comment', title: '评价数据', rateText: `${stats.comment.rate}%`, countText: `${stats.comment.count}`, amountText: `${stats.comment.amount}` },
  95. ]
  96. })
  97. const loadStats = async () : Promise<void> => {
  98. try {
  99. const response = await getAllData({} as UTSJSONObject) as UTSJSONObject
  100. const code = (response['code'] as number | null) ?? -1
  101. if (code != 0 && code != 200) {
  102. return
  103. }
  104. const data = response['data'] as UTSJSONObject | null
  105. applyStat(stats.additional, data?.['additional'] as UTSJSONObject | null)
  106. applyStat(stats.registerDays, data?.['registerDays'] as UTSJSONObject | null)
  107. applyStat(stats.deal, data?.['deal'] as UTSJSONObject | null)
  108. applyStat(stats.receiving, data?.['receiving'] as UTSJSONObject | null)
  109. applyStat(stats.refund, data?.['refund'] as UTSJSONObject | null)
  110. applyStat(stats.renewal, data?.['renewal'] as UTSJSONObject | null)
  111. applyStat(stats.comment, data?.['comment'] as UTSJSONObject | null)
  112. } catch (error) {
  113. uni.showToast({ title: '数据加载失败', icon: 'none' })
  114. }
  115. }
  116. onLoad(() => {
  117. loadStats()
  118. })
  119. </script>
  120. <style>
  121. .page-scroll {
  122. flex: 1;
  123. }
  124. .page {
  125. min-height: 1000rpx;
  126. padding: 24rpx;
  127. box-sizing: border-box;
  128. background-color: #f7f2e7;
  129. flex-direction: column;
  130. }
  131. .summary-card {
  132. padding: 28rpx;
  133. border-radius: 24rpx;
  134. background-color: #ffe48a;
  135. flex-direction: column;
  136. }
  137. .summary-title {
  138. font-size: 36rpx;
  139. font-weight: 700;
  140. color: #2e281f;
  141. }
  142. .summary-subtitle {
  143. margin-top: 12rpx;
  144. font-size: 24rpx;
  145. color: #5f533c;
  146. }
  147. .summary-strip {
  148. margin-top: 18rpx;
  149. flex-direction: row;
  150. justify-content: space-between;
  151. }
  152. .summary-strip-item {
  153. width: 48%;
  154. padding: 24rpx;
  155. border-radius: 22rpx;
  156. background-color: #ffffff;
  157. flex-direction: column;
  158. }
  159. .summary-strip-value {
  160. font-size: 36rpx;
  161. font-weight: 700;
  162. color: #2e281f;
  163. }
  164. .summary-strip-label {
  165. margin-top: 8rpx;
  166. font-size: 22rpx;
  167. color: #8b8376;
  168. }
  169. .stat-card {
  170. margin-top: 20rpx;
  171. padding: 28rpx;
  172. border-radius: 24rpx;
  173. background-color: #ffffff;
  174. flex-direction: column;
  175. }
  176. .stat-row {
  177. flex-direction: row;
  178. justify-content: space-between;
  179. align-items: center;
  180. }
  181. .stat-row-gap {
  182. margin-top: 18rpx;
  183. }
  184. .stat-label {
  185. font-size: 30rpx;
  186. font-weight: 700;
  187. color: #2e281f;
  188. }
  189. .stat-rate {
  190. font-size: 26rpx;
  191. color: #d76b31;
  192. }
  193. .stat-column {
  194. width: 46%;
  195. padding: 20rpx;
  196. border-radius: 18rpx;
  197. background-color: #faf7f1;
  198. flex-direction: column;
  199. }
  200. .stat-value {
  201. font-size: 34rpx;
  202. font-weight: 700;
  203. color: #2e281f;
  204. }
  205. .stat-caption {
  206. margin-top: 8rpx;
  207. font-size: 22rpx;
  208. color: #8b8376;
  209. }
  210. </style>