You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

333 lines
8.0 KiB

2 months ago
2 months ago
  1. <template>
  2. <div class="cash-management">
  3. <div class="cash-title">
  4. <div class="text1">
  5. 现金管理
  6. <!-- <span class="text1-update-time">-->
  7. <!-- 最后更新时间{{ workDataUpdateTime || '该地区暂无数据' }}-->
  8. <!-- </span>-->
  9. <el-popover
  10. placement="top-start"
  11. title="数据说明"
  12. :width="240"
  13. trigger="hover"
  14. content="此数据实时计算,存在误差,请勿作为最终数据使用。"
  15. >
  16. <template #reference>
  17. <el-icon
  18. class="service-icon"
  19. style="
  20. margin-left: 5px;
  21. cursor: pointer;
  22. font-size: 16px;
  23. transition: all 0.3s ease;">
  24. <Warning/>
  25. </el-icon>
  26. </template>
  27. </el-popover>
  28. </div>
  29. </div>
  30. <div class=" text2
  31. ">
  32. <span class="text2-income">总营收{{ totalIncome.toFixed(2) }} 新币</span>
  33. </div>
  34. <div class="chart-container">
  35. <!-- 左侧数据列表 -->
  36. <div class="market-data">
  37. <div v-if="marksFlag" v-for="market in cashData.markets" :key="market.name" class="market-item">
  38. <span class="market-name">{{ market.name }}</span>
  39. <span class="market-value">{{ market.value.toLocaleString() }} 新币</span>
  40. </div>
  41. <div v-else>
  42. <div v-for="item in cashData.markets" :key="item.name" class="market-item">
  43. <span class="market-name">代收{{ item.name }}</span>
  44. <span class="market-value">{{ item.value.toLocaleString() }} </span>
  45. </div>
  46. </div>
  47. </div>
  48. <!-- 图表 -->
  49. <div ref="chartRef" class="chart"></div>
  50. </div>
  51. </div>
  52. </template>
  53. <script setup>
  54. import * as echarts from 'echarts'
  55. import {onMounted, ref} from 'vue'
  56. import request from "@/util/http.js";
  57. import API from "@/util/http.js";
  58. import {Warning,Service} from "@element-plus/icons-vue";
  59. const chartRef = ref(null)
  60. let chartInstance = null
  61. // 响应式数据
  62. const cashData = ref({
  63. markets: []
  64. })
  65. const markets = ref()
  66. // 定义默认市场
  67. const defaultMarkets = [
  68. {name: '新加坡', value: 0},
  69. {name: '马来西亚', value: 0},
  70. {name: '香港', value: 0},
  71. {name: '泰国', value: 0},
  72. {name: '越南HCM', value: 0},
  73. {name: '加拿大', value: 0},
  74. // {name: '未知', value: 0},
  75. // { name: '其他', value: 0 },
  76. // {name: '市场部', value: 0},
  77. // { name: '深圳运营', value: 0 },
  78. // { name: '研发部', value: 0 },
  79. ]
  80. const workDataUpdateTime = ref('')
  81. const totalIncome = ref(0)
  82. // 获取当前年份
  83. const currentYear = new Date().getFullYear();
  84. // 本年第一天 00:00:00
  85. const startDate = `${currentYear}-01-01 00:00:00`;
  86. // 本年最后一天 23:59:59
  87. const endDate = `${currentYear}-12-31 23:59:59`;
  88. // 获取接口数据
  89. const fetchCashData = async () => {
  90. try {
  91. const res = await request({
  92. url: '/workbench/getTotalRevenue',
  93. method: 'POST',
  94. data: {
  95. startDate: startDate,
  96. endDate: endDate
  97. }
  98. })
  99. console.log("jjjjjjj", res.market)
  100. // const data = res
  101. console.log("jjjjjjj", res)
  102. // 总新币
  103. console.log("totalIncome", res)
  104. totalIncome.value = res.reduce((sum, item) => {
  105. return sum + (item.totalSGD || 0);
  106. }, 0);
  107. // 格式化数据
  108. if (marksFlag.value) {
  109. // 生成接口数据映射表
  110. const resMap = new Map(
  111. res.map(item => [item.market, Number(item.totalSGD) || 0])
  112. )
  113. // 合并:优先用接口数据,否则默认 0
  114. markets.value = defaultMarkets.map(m => ({
  115. name: m.name,
  116. value: resMap.get(m.name) ?? 0
  117. }))
  118. } else {
  119. // 1. 定义币种中英文对照表
  120. const currencyMap = {
  121. usd: '美元',
  122. hkd: '港币',
  123. sgd: '新币',
  124. myr: '马币',
  125. thb: '泰铢',
  126. cad: '加币',
  127. vdn: '越南盾',
  128. krw: '韩元'
  129. }
  130. // 2. 取出所有币种字段(排除 market 与 totalSGD)
  131. const currencyKeys = Object.keys(res[0]).filter(
  132. key => key !== 'market' && key !== 'totalSGD'
  133. )
  134. // 3. 累加每个币种的总额并替换中文名
  135. markets.value = currencyKeys.map(currency => {
  136. const total = res.reduce((sum, item) => sum + (Number(item[currency]) || 0), 0)
  137. return {
  138. name: currencyMap[currency.toLowerCase()] || currency.toUpperCase(),
  139. value: total
  140. }
  141. })
  142. }
  143. // 更新数据
  144. cashData.value.markets = markets.value
  145. console.log("cashData", cashData.value.markets)
  146. // // 使用reduce方法遍历markets数组,将所有市场的value值累加,赋值给totalIncome响应式变量
  147. // totalIncome.value = markets.value.reduce((sum, cur) => sum + cur.value, 0)
  148. // workDataUpdateTime.value = new Date().toLocaleString('zh-CN', { hour12: false })
  149. renderChart()
  150. } catch (err) {
  151. console.error('获取数据失败:', err)
  152. }
  153. }
  154. // 标记地区 为 研发部、总部时值为
  155. const marksFlag = ref();
  156. const loading = ref(true); // 新增加载状态
  157. const getAdminData = async function () {
  158. try {
  159. loading.value = true; // 开始加载
  160. const result = await API({url: '/admin/userinfo', data: {}});
  161. marksFlag.value = result.markets === '总部' || result.markets === '研发部';
  162. console.log("marksFlag", marksFlag.value);
  163. // alert(marksFlag.value)
  164. } catch (error) {
  165. console.log('请求失败', error);
  166. } finally {
  167. loading.value = false; // 无论成功失败都结束加载
  168. }
  169. };
  170. // 渲染饼图
  171. const renderChart = () => {
  172. if (!chartRef.value) return
  173. if (!chartInstance) chartInstance = echarts.init(chartRef.value)
  174. const option = {
  175. tooltip: {trigger: 'item'},
  176. legend: {
  177. bottom: 5,
  178. icon: 'circle',
  179. left: 'center'
  180. },
  181. series: [
  182. {
  183. label: {show: false},
  184. type: 'pie',
  185. radius: ['40%', '70%'],
  186. data: cashData.value.markets,
  187. center: ['60%', '45%']
  188. }
  189. ]
  190. }
  191. chartInstance.setOption(option)
  192. }
  193. onMounted(() => {
  194. fetchCashData()
  195. getAdminData()
  196. })
  197. </script>
  198. <style scoped>
  199. /* 保留你原来的样式 */
  200. .cash-management {
  201. margin: 10px 5px;
  202. width: 100%;
  203. height: 550px;
  204. border-radius: 8px;
  205. background: #E7F4FD;
  206. box-shadow: 0 2px 2px 0 #00000040;
  207. display: flex;
  208. flex-direction: column;
  209. align-items: center;
  210. }
  211. .cash-title {
  212. width: 100%;
  213. height: 5vh;
  214. flex-shrink: 0;
  215. border-radius: 8px;
  216. background: linear-gradient(90deg, #E4F0FC 0%, #C6ADFF 50%, #E4F0FC 100%);
  217. box-shadow: 0 2px 2px 0 #00152940;
  218. display: flex;
  219. align-items: center;
  220. justify-content: center;
  221. }
  222. .text1 {
  223. color: #040a2d;
  224. font-family: "PingFang SC";
  225. font-size: 28px;
  226. font-weight: 900;
  227. }
  228. .text1-update-time {
  229. margin-left: 10px;
  230. color: #040a2d;
  231. font-size: 20px;
  232. font-weight: 700;
  233. }
  234. .text2 {
  235. margin: 13px;
  236. width: 95%;
  237. height: 48px;
  238. border-radius: 8px;
  239. background: linear-gradient(90deg, #E4F0FC 0%, #C1DCF8 50%, #E4F0FC 100%);
  240. box-shadow: 0 2px 2px 0 #00152940;
  241. display: flex;
  242. align-items: center;
  243. justify-content: center;
  244. }
  245. .text2-income {
  246. color: #040a2d;
  247. font-size: 20px;
  248. font-weight: 900;
  249. }
  250. .chart-container {
  251. display: flex;
  252. align-items: center;
  253. width: 100%;
  254. height: 100%;
  255. padding: 10px;
  256. }
  257. .market-data {
  258. display: flex;
  259. width: 245px;
  260. flex-direction: column;
  261. align-items: flex-start;
  262. gap: 20px;
  263. padding: 10px;
  264. margin-left: 40px;
  265. }
  266. .market-item {
  267. display: flex;
  268. justify-content: space-between;
  269. width: 100%;
  270. font-family: "PingFang SC";
  271. font-size: 16px;
  272. color: #040a2d;
  273. white-space: nowrap; /* 禁止换行 */
  274. overflow: hidden; /* 隐藏溢出内容 */
  275. text-overflow: ellipsis; /* 溢出显示省略号 */
  276. margin-bottom: 8px; /* 增加项间距,提升可读性 */
  277. }
  278. .market-name {
  279. flex: 0 0 auto; /* 名称部分自适应宽度 */
  280. margin-right: 16px; /* 与金额保持距离 */
  281. }
  282. .market-value {
  283. flex: 1; /* 金额部分占剩余宽度 */
  284. text-align: right;
  285. }
  286. .chart {
  287. flex: 1;
  288. height: 300px;
  289. width: auto;
  290. margin-top: 10px;
  291. }
  292. </style>