front.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. <template>
  2. <view>
  3. <uni-card>
  4. <uni-row class="flex-row flex-wrap">
  5. <!-- 设备总数 -->
  6. <uni-row class="flex-row flex-wrap" style="flex: 1; padding: 10px">
  7. <uni-col class="count-label" :span="12">{{
  8. $t("statistic.front.deviceCount")
  9. }}</uni-col>
  10. <uni-col class="count-label" :span="12">{{
  11. $t("statistic.front.repairCount")
  12. }}</uni-col>
  13. <uni-col class="count-value" :span="12" style="margin-top: 6px">{{
  14. frontData.device.total || 0
  15. }}</uni-col>
  16. <uni-col class="count-value" :span="12" style="margin-top: 6px">{{
  17. frontData.maintain.total || 0
  18. }}</uni-col>
  19. </uni-row>
  20. <view class="divider-h" style="align-self: end" />
  21. <!-- 运行记录工单数量 -->
  22. <uni-row class="flex-row flex-wrap" style="flex: 1; padding: 10px">
  23. <uni-col class="count-label" :span="24">{{
  24. $t("statistic.front.runCount")
  25. }}</uni-col>
  26. <uni-col class="count-label" :span="12">{{
  27. $t("statistic.front.filled")
  28. }}</uni-col>
  29. <uni-col class="count-label" :span="12">{{
  30. $t("statistic.front.unfilled")
  31. }}</uni-col>
  32. <uni-col class="count-value" :span="12" style="margin-top: 6px">{{
  33. frontData.pending.filledCount || 0
  34. }}</uni-col>
  35. <uni-col class="count-value" :span="12" style="margin-top: 6px">{{
  36. frontData.pending.unfilledCount || 0
  37. }}</uni-col>
  38. </uni-row>
  39. <uni-col :span="24">
  40. <view class="divider-v" />
  41. </uni-col>
  42. <!-- 保养工单数量 -->
  43. <uni-row class="flex-row flex-wrap" style="flex: 1; padding: 10px">
  44. <uni-col class="count-label" :span="24">{{
  45. $t("statistic.front.maintenanceCount")
  46. }}</uni-col>
  47. <uni-col class="count-label" :span="12">{{
  48. $t("statistic.front.execute")
  49. }}</uni-col>
  50. <uni-col class="count-label" :span="12">{{
  51. $t("statistic.front.unexecute")
  52. }}</uni-col>
  53. <uni-col class="count-value" :span="12" style="margin-top: 6px">{{
  54. frontData.maintenance.finished || 0
  55. }}</uni-col>
  56. <uni-col class="count-value" :span="12" style="margin-top: 6px">{{
  57. frontData.maintenance.todo || 0
  58. }}</uni-col>
  59. </uni-row>
  60. <view class="divider-h" style="align-self: start" />
  61. <!-- 巡检工单数量 -->
  62. <uni-row class="flex-row flex-wrap" style="flex: 1; padding: 10px">
  63. <uni-col class="count-label" :span="24">{{
  64. $t("statistic.front.inspectionCount")
  65. }}</uni-col>
  66. <uni-col class="count-label" :span="12">{{
  67. $t("statistic.front.filled")
  68. }}</uni-col>
  69. <uni-col class="count-label" :span="12">{{
  70. $t("statistic.front.unfilled")
  71. }}</uni-col>
  72. <uni-col class="count-value" :span="12" style="margin-top: 6px">{{
  73. frontData.inspect.finished || 0
  74. }}</uni-col>
  75. <uni-col class="count-value" :span="12" style="margin-top: 6px">{{
  76. frontData.inspect.todo || 0
  77. }}</uni-col>
  78. </uni-row>
  79. </uni-row>
  80. </uni-card>
  81. <uni-card>
  82. <uni-row class="flex-row flex-wrap">
  83. <!-- MTTR(平均解决时间) -->
  84. <uni-row class="flex-row flex-wrap" style="flex: 1; padding: 10px">
  85. <uni-col class="count-label" :span="24">{{
  86. $t("statistic.front.mttr")
  87. }}</uni-col>
  88. <uni-col
  89. class="count-value mttr"
  90. :span="24"
  91. style="margin-top: 6px"
  92. >{{ frontData.mttr ? frontData.mttr +'h' : '' }}</uni-col
  93. >
  94. </uni-row>
  95. <view class="divider-h" style="align-self: end" />
  96. <!-- 库存预警物料数量 -->
  97. <uni-row class="flex-row flex-wrap" style="flex: 1; padding: 10px">
  98. <uni-col class="count-label" :span="24">{{
  99. $t("statistic.front.stockWarningCount")
  100. }}</uni-col>
  101. <uni-col
  102. class="count-value warning"
  103. :span="24"
  104. style="margin-top: 6px"
  105. >{{ frontData.stockWarning }}</uni-col
  106. >
  107. </uni-row>
  108. </uni-row>
  109. </uni-card>
  110. <!-- 设备状态统计 -->
  111. <uni-card>
  112. <uni-section :title="$t('statistic.front.deviceStatus')" />
  113. <view class="flex-row align-center">
  114. <view class="charts-box">
  115. <qiun-data-charts
  116. type="ring"
  117. :opts="deviceStatusOpts"
  118. :chartData="frontData.deviceStatusChart"
  119. />
  120. </view>
  121. </view>
  122. </uni-card>
  123. <!-- 设备类别TOP5数量 -->
  124. <uni-card>
  125. <uni-section :title="$t('statistic.front.deviceTypeCount')" />
  126. <view class="flex-row align-center">
  127. <view class="charts-box">
  128. <qiun-data-charts
  129. type="bar"
  130. :opts="deviceTypeOpts"
  131. :chartData="frontData.deviceTypeChart"
  132. />
  133. </view>
  134. </view>
  135. </uni-card>
  136. <!-- 近一周活跃用户数 -->
  137. <uni-card>
  138. <uni-section :title="$t('statistic.front.weekUserActive')" />
  139. <view class="flex-row align-center">
  140. <view class="charts-box">
  141. <qiun-data-charts
  142. type="column"
  143. :opts="weeklyUserActivityOpts"
  144. :chartData="frontData.weeklyUserActivityChart"
  145. />
  146. </view>
  147. </view>
  148. </uni-card>
  149. <!-- 工单数量统计 -->
  150. <uni-card>
  151. <uni-section :title="$t('statistic.front.workOrderCount')" />
  152. <view class="flex-row align-center">
  153. <view class="charts-box">
  154. <qiun-data-charts
  155. type="line"
  156. :opts="workOrderCountOpts"
  157. :chartData="frontData.workOrderCountChart"
  158. />
  159. </view>
  160. </view>
  161. </uni-card>
  162. </view>
  163. </template>
  164. <script setup>
  165. import { getCurrentInstance, reactive, ref } from "vue";
  166. import dayjs from "dayjs";
  167. import { getDeptId } from "@/utils/auth";
  168. import {
  169. getDeviceCount,
  170. getMaintainCount,
  171. getPendingCount,
  172. getMaintenanceCount,
  173. getInspectCount,
  174. getMTTR,
  175. getStockWarningCount,
  176. getDeviceStatusStatistic,
  177. getDeviceTypeCount,
  178. getWeeklyUserActivity,
  179. getWorkOrderCount,
  180. } from "@/api/statistic";
  181. const { appContext } = getCurrentInstance();
  182. const t = appContext.config.globalProperties.$t;
  183. // 初始化环形图 - 设备状态统计
  184. const deviceStatusOpts = {
  185. color: ["#5470c6", "#91cc75", "#fac858", "#ee6666"],
  186. padding: [0, 10, 0, 10],
  187. title: { name: "" },
  188. subtitle: { name: "" },
  189. legend: {
  190. position: "bottom",
  191. },
  192. extra: {
  193. ring: {
  194. ringWidth: 30, // 圆环的宽度
  195. activeOpacity: 0.5, // 启用Tooltip点击时,突出部分的透明度
  196. activeRadius: 10, // 启用Tooltip点击时,突出部分的宽度(最大值不得超过labelWidth)
  197. offsetAngle: -90, // 起始角度偏移度数
  198. labelWidth: 10, // 数据标签到饼图外圆连线的长度
  199. customRadius: 80, // 自定义半径
  200. borderWidth: 3, // 分割线的宽度
  201. borderColor: "#FFFFFF", // 分割线的颜色
  202. },
  203. },
  204. };
  205. // 初始化条状图 - 设备类别TOP5数量
  206. const deviceTypeOpts = {
  207. padding: [10, 50, 10, 10],
  208. title: { name: "" },
  209. subtitle: { name: "" },
  210. legend: {
  211. show: false,
  212. },
  213. xAxis: {
  214. boundaryGap: "justify",
  215. disableGrid: false,
  216. axisLine: false,
  217. },
  218. yAxis: {},
  219. extra: {
  220. bar: {
  221. type: "group",
  222. width: 30,
  223. meterBorde: 1,
  224. meterFillColor: "#FFFFFF",
  225. activeBgColor: "#000000",
  226. activeBgOpacity: 0.08,
  227. linearType: "custom",
  228. barBorderCircle: true,
  229. seriesGap: 2,
  230. categoryGap: 2,
  231. customColor: ["#7fbdf6", "#188df0"],
  232. },
  233. },
  234. };
  235. // 初始化柱状图 - 近一周活跃用户数
  236. const weeklyUserActivityOpts = {
  237. color: [],
  238. padding: [20, 10, 10, 10],
  239. title: { name: "" },
  240. subtitle: { name: "" },
  241. legend: {
  242. position: "bottom",
  243. },
  244. extra: {
  245. column: {
  246. type: "group",
  247. width: 30,
  248. activeBgColor: "#000000",
  249. activeBgOpacity: 0.08,
  250. linearType: "custom",
  251. seriesGap: 5,
  252. linearOpacity: 0.8,
  253. barBorderCircle: false,
  254. customColor: ["#5978cb", "#e2f2ce", ,], //"#e2f2ce","#91cc75"
  255. },
  256. },
  257. };
  258. // 初始化折线图 - 工单数量统计
  259. const workOrderCountOpts = {
  260. color: ["#5470c6", "#f1d209", "#e14f0f", "#91cc75"],
  261. padding: [20, 20, 20, 20],
  262. title: { name: "" },
  263. subtitle: { name: "" },
  264. legend: {
  265. position: "bottom",
  266. },
  267. xAxis: {
  268. rotateLabel: true, // 旋转标签
  269. rotateAngle: 45, // 旋转角度
  270. marginTop: 10, // 标签与轴线的距离
  271. fontSize: 12, // 字体大小
  272. },
  273. yAxis: {
  274. data: [
  275. { type: "value", position: "left" },
  276. { type: "value", position: "right" },
  277. ],
  278. },
  279. };
  280. const frontData = reactive({
  281. device: { total: 0 },
  282. maintain: { total: 0 },
  283. pending: { filledCount: 0, unfilledCount: 0 },
  284. maintenance: { finished: 0, todo: 0 },
  285. inspect: { finished: 0, todo: 0 },
  286. mttr: "4.8",
  287. stockWarning: "0",
  288. deviceStatusChart: {
  289. series: [],
  290. },
  291. deviceTypeChart: {},
  292. });
  293. const isLoadData = ref(false);
  294. // 加载维修相关统计数据
  295. const loadData = async () => {
  296. console.log("🚀 ~ loadData ~ isLoadData.value:", isLoadData.value);
  297. if (isLoadData.value) return; // 已加载数据后不再加载数据
  298. isLoadData.value = true;
  299. const startTime = dayjs().subtract(7, "day").valueOf();
  300. console.log("🚀 ~ loadData ~ startTime:", startTime);
  301. const endTime = dayjs().valueOf();
  302. console.log("🚀 ~ loadData ~ endTime:", endTime);
  303. // 设备数
  304. const getDeviceCountAsync = getDeviceCount();
  305. // 维修工单数
  306. const getMaintainCountAsync = getMaintainCount();
  307. // 运行记录工单数
  308. const getPendingCountAsync = getPendingCount({
  309. startTime,
  310. endTime,
  311. deptId: getDeptId(),
  312. });
  313. // 保养工单数
  314. const getMaintenanceCountAsync = getMaintenanceCount();
  315. // 巡检工单数
  316. const getInspectCountAsync = getInspectCount({
  317. startTime,
  318. endTime,
  319. });
  320. // MTTR(平均解决时间)
  321. const getMTTRAsync = getMTTR();
  322. // 库存预警物料数量
  323. const getStockWarningCountAsync = getStockWarningCount();
  324. // 设备状态统计
  325. const getDeviceStatusStatisticAsync = getDeviceStatusStatistic();
  326. // 设备类别TOP5
  327. const getDeviceTypeCountAsync = getDeviceTypeCount();
  328. // 近一周活跃用户数
  329. const getWeeklyUserActivityAsync = getWeeklyUserActivity();
  330. // 工单数量统计
  331. const getWorkOrderCountAsync = getWorkOrderCount();
  332. // 设备数
  333. frontData.device = (await getDeviceCountAsync).data;
  334. // 维修工单数
  335. frontData.maintain = (await getMaintainCountAsync).data;
  336. // 运行记录工单数
  337. frontData.pending = (await getPendingCountAsync).data?.totalList[0];
  338. // 保养工单数
  339. frontData.maintenance = (await getMaintenanceCountAsync).data;
  340. // 巡检工单数
  341. frontData.inspect = (await getInspectCountAsync).data;
  342. // MTTR(平均解决时间)
  343. frontData.mttr = (await getMTTRAsync).data;
  344. // 库存预警物料数量
  345. frontData.stockWarning = (await getStockWarningCountAsync).data;
  346. // 设备状态统计
  347. const deviceStatusChartSeries = (await getDeviceStatusStatisticAsync).data;
  348. //设备状态统计数据填充
  349. frontData.deviceStatusChart.series = [
  350. {
  351. data: deviceStatusChartSeries.map((item) => ({
  352. name: item.name,
  353. value: item.value,
  354. labelText: item.name + ": " + item.value,
  355. textColor: "#333", // 设置文本颜色
  356. })),
  357. },
  358. ];
  359. // 设备类别TOP5
  360. const deviceTypeChartData = (await getDeviceTypeCountAsync).data;
  361. frontData.deviceTypeChart = {
  362. categories: deviceTypeChartData.map((item) => item.category),
  363. series: [
  364. {
  365. name: "",
  366. data: deviceTypeChartData.map((item) => item.value),
  367. textColor: "#333", // 设置文本颜色
  368. },
  369. ],
  370. };
  371. console.log(
  372. "🚀 ~ loadData ~ frontData.deviceTypeChart:",
  373. frontData.deviceTypeChart
  374. );
  375. // 近一周活跃用户数
  376. const weeklyUserActivity = (await getWeeklyUserActivityAsync).data;
  377. console.log("🚀 ~ loadData ~ weeklyUserActivity:", weeklyUserActivity);
  378. frontData.weeklyUserActivityChart = {
  379. categories: weeklyUserActivity.map((item) => item.department),
  380. series: [
  381. {
  382. name: t("statistic.front.totalUserCount"),
  383. data: weeklyUserActivity.map((item) => item.total),
  384. textColor: "#333", // 设置文本颜色
  385. },
  386. {
  387. name: t("statistic.front.activeUserCount"),
  388. data: weeklyUserActivity.map((item) => item.active),
  389. textColor: "#333", // 设置文本颜色
  390. },
  391. ],
  392. };
  393. console.log(
  394. "🚀 ~ loadData ~ frontData.weeklyUserActivityChart :",
  395. frontData.weeklyUserActivityChart
  396. );
  397. // 工单数量统计 维修与保养一个轴,运行与巡检一个轴
  398. const workOrderCount = (await getWorkOrderCountAsync).data;
  399. frontData.workOrderCountChart = {
  400. categories: workOrderCount.xAxis,
  401. series: workOrderCount.series.map((item, itemIndex) => ({
  402. ...item,
  403. index: itemIndex > 1 ? 1 : 0, // 维修与保养一个轴(0),运行与巡检一个轴(1)
  404. textColor: "#000", // 设置文本颜色
  405. })),
  406. };
  407. console.log(
  408. "🚀 ~ loadData ~ frontData.workOrderCountChart:",
  409. frontData.workOrderCountChart
  410. );
  411. };
  412. defineExpose({ loadData });
  413. </script>
  414. <style scoped lang="scss">
  415. .charts-box {
  416. width: 100%;
  417. height: 300px;
  418. }
  419. :deep(.uni-card) {
  420. padding: 0 !important;
  421. .uni-card__content {
  422. padding: 0 !important;
  423. }
  424. .uni-section {
  425. margin-top: 8px;
  426. padding: 0 15px 0 20px;
  427. .uni-section-header {
  428. padding: 10px 0;
  429. }
  430. .uni-section__content-title {
  431. font-size: 16px !important;
  432. font-weight: 600;
  433. }
  434. }
  435. }
  436. :deep(.uni-section) {
  437. background-color: transparent;
  438. .uni-section-header {
  439. padding: 5px 10px;
  440. }
  441. .line {
  442. width: 3px;
  443. height: 14px;
  444. background-color: #004098;
  445. }
  446. }
  447. .divider {
  448. width: 1px;
  449. height: 114px;
  450. background-color: #cacccf;
  451. margin: 0 24px;
  452. }
  453. .divider-v {
  454. width: auto;
  455. height: 1.5px;
  456. border-bottom: 1.5px #cacccf dashed;
  457. margin: 0 20px;
  458. }
  459. .divider-h {
  460. width: 1.5px;
  461. height: 50px;
  462. border-left: 1.5px #cacccf dashed;
  463. margin: 0 4px;
  464. }
  465. .count-label {
  466. color: #666666;
  467. font-size: 14px;
  468. }
  469. .count-value {
  470. color: #333333;
  471. font-size: 18px;
  472. font-weight: 500;
  473. &.mttr {
  474. color: #20b2aa;
  475. font-weight: 700;
  476. font-size: 28px;
  477. text-align: center;
  478. }
  479. &.warning {
  480. color: #cd5c5c;
  481. font-weight: 700;
  482. font-size: 28px;
  483. text-align: center;
  484. }
  485. }
  486. .mt-5 {
  487. margin-top: 5px;
  488. }
  489. .mt-8 {
  490. margin-top: 8px;
  491. }
  492. .pl-10 {
  493. padding-left: 10px;
  494. }
  495. </style>