index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. <template>
  2. <div class="w-full h-full relative flex justify-between items-center">
  3. <!-- 左侧面板 -->
  4. <Panel>
  5. <CardTitle>项目倒计时</CardTitle>
  6. <div class="w-full flex flex-row gap-10px pt-10px mb-10px">
  7. <div class="flex flex-col">
  8. <div class="p-5px flex flex-row justify-center items-center bg-[#348bc74c] w-200px">
  9. <div class="backgroundImages w-70px h-70px flex justify-center items-center">
  10. <img src="@/assets/images/u1407.svg" />
  11. </div>
  12. <div class="ml-10px">
  13. <div class="text-15px color-[#ffffffb2]">合同开工日期</div>
  14. <div class="text-20px mt-10px">{{ dataSource?.xmdjs?.start_time }}</div>
  15. </div>
  16. </div>
  17. <div
  18. class="p-5px flex flex-row justify-center items-center bg-[#348bc74c] mt-10px w-200px"
  19. >
  20. <div class="backgroundImages w-70px h-70px flex justify-center items-center">
  21. <img src="@/assets/images/u1420.svg" />
  22. </div>
  23. <div class="ml-10px">
  24. <div class="text-15px color-[#ffffffb2]">合同结束日期</div>
  25. <div class="text-20px mt-10px">{{ dataSource?.xmdjs?.end_time }}</div>
  26. </div>
  27. </div>
  28. </div>
  29. <div class="bg-[#348bc74c] w-211px flex flex-col justify-center items-center">
  30. <DigitalFlop :config="config5" />
  31. <div class="text-17px color-[#ffffffb2]">剩余天数</div>
  32. </div>
  33. </div>
  34. <CardTitle>WBS分解</CardTitle>
  35. <div class="text-white h-350px w-full pt-10px left-bottom-board">
  36. <ScrollBoard :config="config1" @mouseover="mouseoverHandler" @click="clickHandler" />
  37. </div>
  38. <CardTitle>CWS分解</CardTitle>
  39. <div class="w-full h-260px flex gap-10px p-10px box-border justify-between items-center left-bottom-board">
  40. <ScrollBoard :config="config2" @mouseover="mouseoverHandler" @click="clickHandler" />
  41. </div>
  42. </Panel>
  43. <!-- 中间区域 -->
  44. <!-- <div
  45. class="h-full box-border pr-10px pl-10px middle-content flex-1 flex flex-col gap-10px justify-center"
  46. >
  47. <div class="text-white h-500px w-full center-board">
  48. <ProjectMilestone />
  49. </div>
  50. </div> -->
  51. <!-- 右侧面板 -->
  52. <Panel>
  53. <div class="bg-[#348bc74c] w-full h-150px flex flex-col justify-center items-center">
  54. <DigitalFlop :config="config6" style="height: 80px; width: 100%" />
  55. <div class="text-32px color-[#ffffffb2]">实际AC</div>
  56. </div>
  57. <CardTitle>单体完成比率统计</CardTitle>
  58. <div class="text-white h-350px w-full center-board right-top-board">
  59. <Chart :options="chartOptionsRightTop" />
  60. </div>
  61. <CardTitle>单体-分部完成比率统计</CardTitle>
  62. <div class="w-full box-border p-10px h-350px">
  63. <Chart :options="chartOptionsRightBottom" />
  64. </div>
  65. </Panel>
  66. </div>
  67. </template>
  68. <script setup lang="ts">
  69. import { ref, computed, inject } from 'vue';
  70. import { ScrollBoard, DigitalFlop } from '@kjgl77/datav-vue3';
  71. import Panel from '@/components/Panel.vue';
  72. import CardTitle from '@/components/CardTitle.vue';
  73. import Chart from '@/components/Chart/index.vue';
  74. import type { EChartsOption } from 'echarts';
  75. import { invoke } from '@/api';
  76. import { useRequest } from 'vue-hooks-plus';
  77. import type { Ref } from 'vue';
  78. const projectCode = inject<Ref<string[]>>('projectCode');
  79. const dataSource = ref<{
  80. // ac
  81. ac: string;
  82. // 项目倒计时
  83. xmdjs: {
  84. // 结束时间
  85. end_time: string;
  86. // 开始时间
  87. start_time: string;
  88. // 工期
  89. days: number;
  90. };
  91. // wbs分解
  92. wbsfj: [
  93. {
  94. // 状态
  95. tas_state: string;
  96. // 分类
  97. tas_classify: string;
  98. // 单体
  99. tas_monomer: string;
  100. },
  101. ];
  102. // 单体完成比率
  103. dtwcbl: [
  104. {
  105. // 标题
  106. title: string;
  107. // 完成比例
  108. value: number;
  109. },
  110. ];
  111. // cws分解
  112. cwsfj: [
  113. {
  114. // 状态
  115. tas_state: string;
  116. // 分类
  117. tas_classify: string;
  118. // 单体预算金额(万元)
  119. tas_budget_amount: number;
  120. // 单体
  121. tas_monomer: number;
  122. },
  123. ];
  124. // 单体-分部完成比率统计
  125. dtbfbl: [
  126. {
  127. // 标题
  128. title: string;
  129. // 完成比例
  130. value: number;
  131. },
  132. ];
  133. }>();
  134. useRequest(invoke, {
  135. defaultParams: [
  136. {
  137. // 固定值
  138. interfaceCode: 'BigScreen.progressManagement',
  139. // 编号代码
  140. projectCode: projectCode?.value?.[1],
  141. },
  142. ],
  143. onSuccess(res: any) {
  144. dataSource.value = res;
  145. },
  146. pollingInterval: 10000
  147. });
  148. const config1 = computed(() => {
  149. const list = dataSource.value?.wbsfj || [];
  150. return {
  151. header: ['单体', '当前状态', '所属分类'],
  152. data: list.map((item) => [item.tas_monomer, item.tas_state, item.tas_classify]),
  153. headerBGC: '#348bc763',
  154. evenRowBGC: 'rgba(29, 66, 110, 0.6)',
  155. oddRowBGC: 'rgba(52, 139, 199, 0.0980392156862745)',
  156. align: ['center'],
  157. index: true,
  158. columnWidth: [50],
  159. rowNum: 6,
  160. headerHeight: 35,
  161. waitTime: 50005000,
  162. };
  163. });
  164. const config5 = computed(() => {
  165. return {
  166. number: [dataSource.value?.xmdjs?.days || 0],
  167. content: '{nt}%',
  168. };
  169. });
  170. const config6 = computed(() => {
  171. return {
  172. number: [dataSource.value?.ac || 0],
  173. content: '{nt}',
  174. };
  175. });
  176. let currentZoom = { start: 0, end: 50 };
  177. const config2 = computed(() => {
  178. const list = dataSource.value?.cwsfj || [];
  179. return {
  180. header: ['单体', '当前状态', '所属分类', '预算金额(万元)'],
  181. data: list.map((item) => [
  182. item.tas_monomer,
  183. item.tas_state,
  184. item.tas_classify,
  185. item.tas_budget_amount,
  186. ]),
  187. headerBGC: '#348bc763',
  188. evenRowBGC: 'rgba(29, 66, 110, 0.6)',
  189. oddRowBGC: 'rgba(52, 139, 199, 0.0980392156862745)',
  190. align: ['center'],
  191. index: true,
  192. columnWidth: [50],
  193. rowNum: 6,
  194. headerHeight: 35,
  195. waitTime: 50005000,
  196. };
  197. });
  198. // 右侧顶部柱状图
  199. // let scrollIntervalRightTop: ReturnType<typeof setInterval>;
  200. // let currentZoomeRightTop = { start: 0, end: 50 };
  201. const chartOptionsRightTop = computed((): EChartsOption => {
  202. const list = dataSource.value?.dtbfbl || [];
  203. return {
  204. tooltip: {
  205. trigger: 'item',
  206. textStyle: { color: '#000' },
  207. },
  208. xAxis: {
  209. type: 'value',
  210. axisLabel: { color: '#fff' },
  211. },
  212. yAxis: {
  213. type: 'category',
  214. data: list.map((item) => item.title),
  215. axisLabel: { color: '#fff' },
  216. },
  217. series: [
  218. {
  219. name: '进度',
  220. type: 'bar',
  221. data: list.map((item) => item.value),
  222. barWidth: 16,
  223. itemStyle: {
  224. color: 'rgba(42, 254, 255, 1)',
  225. borderRadius: [4, 4, 4, 4],
  226. },
  227. label: {
  228. show: true,
  229. position: 'right',
  230. color: '#fff',
  231. fontSize: 10,
  232. },
  233. },
  234. ],
  235. grid: {
  236. top: '5%',
  237. left: '3%',
  238. right: '4%',
  239. bottom: '3%',
  240. containLabel: true,
  241. },
  242. dataZoom: [
  243. {
  244. show: false,
  245. type: 'slider',
  246. yAxisIndex: 0,
  247. start: currentZoom.start,
  248. end: currentZoom.end,
  249. },
  250. ],
  251. };
  252. });
  253. // const startAutoScrollRightTop = () => {
  254. // clearInterval(scrollIntervalRightTop);
  255. // scrollIntervalRightTop = setInterval(() => {
  256. // if (currentZoomeRightTop.end >= 100) {
  257. // currentZoomeRightTop.start = 0;
  258. // currentZoomeRightTop.end = 50;
  259. // } else {
  260. // currentZoomeRightTop.start += 2;
  261. // currentZoomeRightTop.end += 2;
  262. // }
  263. // chartOptionsRightTop.value = {
  264. // ...chartOptionsRightTop.value,
  265. // dataZoom: [
  266. // {
  267. // show: false,
  268. // type: 'slider',
  269. // yAxisIndex: 0,
  270. // start: currentZoomeRightTop.start,
  271. // end: currentZoomeRightTop.end,
  272. // },
  273. // ],
  274. // };
  275. // }, 2000);
  276. // };
  277. // 右侧底部
  278. // let scrollIntervalRightBottom: ReturnType<typeof setInterval>;
  279. // let currentZoomeRightBottom = { start: 0, end: 50 };
  280. const chartOptionsRightBottom = computed((): EChartsOption => {
  281. const list = dataSource.value?.dtbfbl || [];
  282. return {
  283. tooltip: {
  284. trigger: 'item',
  285. textStyle: { color: '#000' },
  286. },
  287. xAxis: {
  288. type: 'value',
  289. axisLabel: { color: '#fff' },
  290. },
  291. yAxis: {
  292. type: 'category',
  293. data: list.map((item) => item.title),
  294. axisLabel: { color: '#fff' },
  295. },
  296. series: [
  297. {
  298. name: '进度',
  299. type: 'bar',
  300. data: list.map((item) => item.value),
  301. barWidth: 16,
  302. itemStyle: {
  303. color: 'rgba(42, 254, 255, 1)',
  304. borderRadius: [4, 4, 4, 4],
  305. },
  306. label: {
  307. show: true,
  308. position: 'right',
  309. color: '#fff',
  310. fontSize: 10,
  311. },
  312. },
  313. ],
  314. grid: {
  315. top: '5%',
  316. left: '3%',
  317. right: '4%',
  318. bottom: '3%',
  319. containLabel: true,
  320. },
  321. dataZoom: [
  322. {
  323. show: false,
  324. type: 'slider',
  325. yAxisIndex: 0,
  326. start: currentZoom.start,
  327. end: currentZoom.end,
  328. },
  329. ],
  330. }
  331. });
  332. // const startAutoScrollRightBottom = () => {
  333. // clearInterval(scrollIntervalRightBottom);
  334. // scrollIntervalRightBottom = setInterval(() => {
  335. // if (currentZoomeRightBottom.end >= 100) {
  336. // currentZoomeRightBottom.start = 0;
  337. // currentZoomeRightBottom.end = 50;
  338. // } else {
  339. // currentZoomeRightBottom.start += 2;
  340. // currentZoomeRightBottom.end += 2;
  341. // }
  342. // chartOptionsRightBottom.value = {
  343. // ...chartOptionsRightBottom.value,
  344. // dataZoom: [
  345. // {
  346. // show: false,
  347. // type: 'slider',
  348. // yAxisIndex: 0,
  349. // start: currentZoomeRightBottom.start,
  350. // end: currentZoomeRightBottom.end,
  351. // },
  352. // ],
  353. // };
  354. // }, 2000);
  355. // };
  356. // onMounted(() => {
  357. // startAutoScrollRightTop();
  358. // startAutoScrollRightBottom();
  359. // });
  360. const mouseoverHandler = (e: any) => {
  361. console.log(e);
  362. };
  363. const clickHandler = (e: any) => {
  364. console.log(e);
  365. };
  366. // onUnmounted(() => {
  367. // clearInterval(scrollIntervalRightBottom);
  368. // clearInterval(scrollIntervalRightTop);
  369. // });
  370. </script>
  371. <style lang="less" scoped>
  372. :deep(*) {
  373. .el-descriptions__body {
  374. background-color: transparent;
  375. }
  376. .el-descriptions__label {
  377. color: #fff;
  378. }
  379. .el-descriptions__content {
  380. color: #ccc;
  381. }
  382. }
  383. .backgroundImages {
  384. background: url(@/assets/images/u1404.svg) center center no-repeat;
  385. }
  386. :deep(.left-bottom-board .dv-scroll-board .header) {
  387. height: 35px;
  388. }
  389. :deep(.left-bottom-board .dv-scroll-board .header-item) {
  390. font-size: 17px !important;
  391. color: #31e4cf !important;
  392. }
  393. :deep(.left-board .dv-scroll-board .header) {
  394. height: 30px !important;
  395. color: #31e4cf;
  396. }
  397. :deep(.left-board .row-item) {
  398. font-size: 17px;
  399. font-weight: 400;
  400. }
  401. :deep(.left-board .header-item) {
  402. font-weight: 400;
  403. font-size: 17px;
  404. }
  405. :deep(.center-board .dv-scroll-board .header) {
  406. height: 48px !important;
  407. color: #fff;
  408. font-size: 20px;
  409. font-weight: 700;
  410. }
  411. :deep(.left-bottom-board .dv-scroll-board .rows .ceil) {
  412. font-size: 17px;
  413. font-weight: 400;
  414. }
  415. :deep(.center-board .dv-scroll-board .rows .ceil) {
  416. font-size: 16px;
  417. color: rgba(247, 247, 247, 0.698039215686274);
  418. }
  419. :deep(.dv-scroll-ranking-board .ranking-info .rank) {
  420. width: auto !important;
  421. margin-right: 10px;
  422. }
  423. :deep(.dv-scroll-ranking-board .ranking-column) {
  424. height: 14px;
  425. border: none;
  426. background-color: rgba(232, 232, 232, 0.27843137254902);
  427. }
  428. :deep(.dv-scroll-ranking-board .ranking-column .inside-column) {
  429. height: 100%;
  430. background-color: rgba(115, 235, 233, 1);
  431. }
  432. </style>