index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. <template>
  2. <div class="table-box">
  3. <div class="card table-search" style="overflow: hidden;">
  4. <el-form ref="formRef" :model="pageable" :inline="true" label-width="auto">
  5. <el-form-item label="自动站:" prop="base">
  6. <el-select v-model="pageable.as_code" filterable placeholder="请搜索自动站" remote reserve-keyword
  7. clearable style="width: 200px" @change="changeStation">
  8. <el-option v-for="item in platformList" :key="item.as_code"
  9. :label="item.as_code + ' ' + item.as_name" :value="item.as_code" />
  10. <template #prefix>
  11. <el-icon class="el-input__icon">
  12. <search />
  13. </el-icon>
  14. </template>
  15. </el-select>
  16. </el-form-item>
  17. <el-form-item label="观测时间:" prop="base">
  18. <div>
  19. <el-switch v-model="value1" @change="changetype" class="ml-2" inline-prompt active-text="正点"
  20. inactive-text="分钟" style="margin-right: 10px;" />
  21. <el-date-picker v-model="dateRange" value-format="YYYY-MM-DD HH:mm"
  22. :format="value1 ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm'"
  23. :type="value1 ? 'daterange' : 'datetimerange'" range-separator="-" start-placeholder="开始时间"
  24. end-placeholder="结束时间" :disabled-date="disabledDate"
  25. :disabled-minutes="disabledMinutes" @focus="handleFocus" @calendar-change="handleChange"
  26. @change="handleDateChange"
  27. :default-time="[new Date(2000, 1, 1, 0, 0), new Date(2000, 1, 1, new Date().getHours(), 0)]"
  28. style="width: 300px;"></el-date-picker>
  29. </div>
  30. </el-form-item>
  31. <el-form-item>
  32. <div>
  33. <el-button type="primary" @click="getHistoryList">查询</el-button>
  34. <el-button plain @click="resetForm(formRef)">重置</el-button>
  35. </div>
  36. </el-form-item>
  37. </el-form>
  38. </div>
  39. <div class="main_list">
  40. <el-row :gutter="15">
  41. <el-col :span="24">
  42. <div class="chart_item zhuti">
  43. <div style="font-weight: bold;" class="item_title">
  44. <span>气温 ( ℃ ) / 相对湿度 ( % ) / 本站气压 ( hPa )</span>
  45. </div>
  46. <div class="mt5">
  47. <div ref="data" id="data" class="data_box"></div>
  48. </div>
  49. </div>
  50. </el-col>
  51. </el-row>
  52. </div>
  53. </div>
  54. </template>
  55. <script setup lang="ts" name="dataSynthesis">
  56. import * as echarts from 'echarts';
  57. import { useRouter } from "vue-router";
  58. import { ElMessage } from "element-plus";
  59. import { useGlobalStore } from "@/stores/modules/global";
  60. import { ref, computed, onMounted, reactive, watch, onDeactivated, onActivated } from "vue";
  61. import { getDataItemList, getPlatformList, getRgDataList, getTacRecordList } from "@/api/modules/allData";
  62. import { Platform } from "@/api/interface";
  63. import { parseTime } from '@/utils/index';
  64. import { isToday, isWithinThirtyMinutes, getTimeStamp, getStamp, isDateFuture, isDateRangeWithinDays } from "@/utils/dateTime";
  65. import { ElLoading } from "element-plus";
  66. /* 全局请求 loading */
  67. const loading = ref()
  68. /**
  69. * @description 开启 Loading
  70. * */
  71. const startFullLoading = () => {
  72. loading.value = ElLoading.service({
  73. fullscreen: true,
  74. lock: true,
  75. text: "Loading",
  76. background: "rgba(0, 0, 0, 0.7)"
  77. });
  78. };
  79. /**
  80. * @description 结束 Loading
  81. * */
  82. const endFullLoading = () => {
  83. loading.value.close();
  84. };
  85. const value1 = ref(true)
  86. const dateRange = ref<[any, any]>([parseTime(new Date(), '{y}-{m}-{d}') + ' 00:00', parseTime(new Date(), '{y}-{m}-{d} {h}:{i}')]);
  87. const changetype = (e) => {
  88. if (!e) {
  89. dateRange.value = [parseTime(getTimeStamp(1), '{y}-{m}-{d} {h}:{i}'), parseTime(new Date(), '{y}-{m}-{d} {h}:{i}')]
  90. }
  91. getHistoryList()
  92. }
  93. const handleDateRangeChange = (newVal) => {
  94. const [startDate, endDate] = newVal;
  95. if (endDate && isToday(endDate)) {
  96. // 获取当前时间的整点
  97. const currentHour = new Date().getHours();
  98. const roundedEndDate = new Date(endDate);
  99. roundedEndDate.setMinutes(0, 0, 0); // 设置分钟、秒、毫秒为0
  100. const startZeroed = new Date(startDate);
  101. startZeroed.setHours(0, 0, 0, 0);
  102. dateRange.value = [parseTime(new Date(startZeroed)), parseTime(new Date(roundedEndDate))];
  103. console.log(dateRange.value, '今天');
  104. } else {
  105. const noTodayStart = new Date(startDate).setMinutes(0, 0, 0);
  106. const noTodayEnd = new Date(endDate).setHours(23, 0, 0, 0);
  107. dateRange.value = [parseTime(new Date(noTodayStart)), parseTime(new Date(noTodayEnd))];
  108. console.log(dateRange.value, '不是今天');
  109. }
  110. };
  111. const handleDateChange = async (e) => {
  112. if (isDateFuture(e[0]) ||isDateFuture(e[1])) {
  113. ElMessage.error("不得选择未来的时间");
  114. } else {
  115. if (!value1.value) {
  116. if (isWithinThirtyMinutes(e[0], e[1])) {
  117. // 获取参数
  118. getHistoryList()
  119. } else {
  120. ElMessage.error("分钟模式下时间间隔不能大于3小时");
  121. }
  122. } else {
  123. if (isDateRangeWithinDays(e[0], e[1], 7)) {
  124. await handleDateRangeChange(e)
  125. getHistoryList()
  126. } else {
  127. ElMessage.error("正点模式下时间间隔不能大于7天");
  128. }
  129. }
  130. }
  131. }
  132. const pickDay = ref()
  133. const handleFocus = () => {
  134. pickDay.value = null
  135. }
  136. const disabledDate = (time: Date) => {
  137. return time.getTime() > Date.now() - 8.64e6
  138. }
  139. const handleChange = (val: Date[]) => {
  140. const [pointDay] = val
  141. pickDay.value = pointDay
  142. }
  143. const disabledHours = () => {
  144. const a: number[] = [];
  145. for (let i = 0; i < 24; i++) {
  146. if (new Date().getHours() >= i) continue;
  147. a.push(i);
  148. }
  149. return a;
  150. };
  151. const disabledMinutes = () => {
  152. const a: number[] = [];
  153. // for (let i = 0; i < 60; i++) {
  154. // if (new Date().getMinutes() >= i) continue;
  155. // a.push(i);
  156. // }
  157. return a;
  158. };
  159. const pageable = ref({
  160. data_type: true,
  161. time_order: 1,
  162. as_code: undefined,
  163. data_items: [],
  164. pageNum: 1,
  165. pageSize: 20,
  166. total: 0
  167. });
  168. let historyColumns = [
  169. {
  170. data_id: 202,
  171. data_r_table: "SHI_SHI_LIU_YAO_SU_SHU_JU",
  172. data_type: "气压",
  173. data_name: "本站气压",
  174. data_item: "BEN_ZHAN_QI_YA",
  175. data_unit: "hPa",
  176. data_h_table: "LI_SHI_LIU_YAO_SU_SHU_JU",
  177. data_value: "",
  178. data_order: 0,
  179. data_condition: 0
  180. },
  181. {
  182. data_r_table: "SHI_SHI_LIU_YAO_SU_SHU_JU",
  183. data_id: 101,
  184. data_type: "温湿度",
  185. data_item: "QI_WEN",
  186. data_name: "气温",
  187. data_unit: "℃",
  188. data_h_table: "LI_SHI_LIU_YAO_SU_SHU_JU",
  189. data_value: "",
  190. data_order: 0,
  191. data_condition: 0
  192. },
  193. {
  194. data_r_table: "SHI_SHI_LIU_YAO_SU_SHU_JU",
  195. data_id: 106,
  196. data_type: "温湿度",
  197. data_item: "XIANG_DUI_SHI_DU",
  198. data_name: "相对湿度",
  199. data_unit: "%",
  200. data_h_table: "LI_SHI_LIU_YAO_SU_SHU_JU",
  201. data_value: "",
  202. data_order: 0,
  203. data_condition: 0,
  204. isSelected: true
  205. },
  206. ]
  207. // 查询自动站列表
  208. const queryas = ref<Platform>({
  209. pageSize: 5000,
  210. pageNum: 1,
  211. currentpage: 1
  212. })
  213. const platformList = ref<any>([])
  214. const selectPlatform = ref()
  215. const getPlatforms = async () => {
  216. startFullLoading()
  217. const { data } = await getPlatformList(queryas.value);
  218. platformList.value = data.list
  219. pageable.value.as_code = data.list[0].as_code
  220. selectPlatform.value = data.list[0]
  221. getHistoryList()
  222. endFullLoading()
  223. };
  224. const changeStation = () => {
  225. getHistoryList()
  226. }
  227. //时间戳转换为指定格式的日期字符串
  228. function formatDate(timestamp, type) {
  229. const date = new Date(timestamp * 1000); // 将时间戳转换为毫秒
  230. const year = date.getFullYear();
  231. const month = String(date.getMonth() + 1).padStart(2, '0');
  232. const day = String(date.getDate()).padStart(2, '0');
  233. const hours = String(date.getHours()).padStart(2, '0');
  234. const minutes = String(date.getMinutes()).padStart(2, '0');
  235. if (type === 1) {
  236. return `${month}/${day} ${hours}:${minutes}`;
  237. } else {
  238. return `${year}-${month}-${day} ${hours}:${minutes}`;
  239. }
  240. }
  241. const timeArr = ref()
  242. const tempLineArr = ref()
  243. const humidityLineArr = ref()
  244. const pressureLineArr = ref()
  245. const getHistoryList = async () => {
  246. if (isDateFuture(dateRange.value[0]) ||isDateFuture(dateRange.value[1])) {
  247. ElMessage.error("不得选择未来的时间");
  248. return false;
  249. }
  250. const { data } = await getDataItemList({ ...pageable.value, pageSize: 99, as_code_list: [pageable.value.as_code], data_items: historyColumns, data_type: false, time_space: value1.value ? 60 : 1, end_time: getStamp(dateRange.value[1]), begin_time: getStamp(dateRange.value[0]) });
  251. timeArr.value = data.list?.map(item => {
  252. return formatDate(item.data_time_i, 1)
  253. })
  254. tempLineArr.value = data.list?.map(item => {
  255. return item[101]
  256. })
  257. humidityLineArr.value = data.list?.map(item => {
  258. return item[106]
  259. })
  260. pressureLineArr.value = data.list?.map(item => {
  261. return item[202]
  262. })
  263. console.log(pressureLineArr.value, 555555);
  264. showData()
  265. };
  266. const formRef = ref()
  267. const data = ref()
  268. const globalStore = useGlobalStore();
  269. const isDark = computed(() => globalStore.isDark);
  270. // resetForm
  271. const resetForm = (formEl) => {
  272. if (!formEl) return;
  273. formEl.resetFields();
  274. };
  275. const showData = () => {
  276. function getMaxAndAdd50(arr) {
  277. console.log(arr);
  278. if (!arr||arr.length === 0) {
  279. return
  280. } else {
  281. const maxValue = Math.max(...arr);
  282. // 将最大值加 50
  283. const result = maxValue + 50;
  284. return result;
  285. }
  286. }
  287. function getMaxAndMinus50(arr) {
  288. if (!arr||arr.length === 0) {
  289. return
  290. } else {
  291. const maxValue = Math.min(...arr);
  292. // 将最大值加 50
  293. const result = maxValue - 50;
  294. return result>=0 ? result : 0;
  295. }
  296. }
  297. const colors = ['#5470C6', '#59A073', '#FFB652'];
  298. var dom = document.getElementById("data");
  299. // var myChart = echarts.init(dom);
  300. let mychart = echarts.init(dom);
  301. let option = {
  302. color: colors,
  303. title: {
  304. text: '暂无数据',
  305. show: !timeArr.value ? true : false,
  306. x: 'center',
  307. y: 'center',
  308. textStyle: {
  309. fontSize: 14,
  310. fontWeight: 'normal',
  311. }
  312. },
  313. tooltip: {
  314. trigger: 'axis',
  315. axisPointer: {
  316. type: 'cross'
  317. }
  318. },
  319. grid: {
  320. left: '80',
  321. right: '130',
  322. bottom: '40',
  323. },
  324. legend: {
  325. data: ['气温', '相对湿度', '本站气压'],
  326. textStyle: {//图例文字的样式
  327. color: isDark.value ? '#fff' : "#000",
  328. }
  329. },
  330. xAxis: [
  331. {
  332. type: 'category',
  333. axisTick: {
  334. alignWithLabel: true
  335. },
  336. // prettier-ignore
  337. data: timeArr.value
  338. }
  339. ],
  340. yAxis: [
  341. {
  342. type: 'value',
  343. name: '气温',
  344. position: 'right',
  345. alignTicks: true,
  346. axisLine: {
  347. show: true,
  348. lineStyle: {
  349. color: colors[0]
  350. }
  351. },
  352. axisLabel: {
  353. formatter: '{value} °C'
  354. }
  355. },
  356. {
  357. type: 'value',
  358. name: '相对湿度',
  359. position: 'right',
  360. max: 100,
  361. offset: 80,
  362. axisLine: {
  363. show: true,
  364. lineStyle: {
  365. color: colors[1]
  366. }
  367. },
  368. axisLabel: {
  369. formatter: '{value} %'
  370. }
  371. },
  372. {
  373. type: 'value',
  374. name: '本站气压',
  375. min: getMaxAndMinus50(pressureLineArr.value),
  376. max: getMaxAndAdd50(pressureLineArr.value),
  377. position: 'left',
  378. axisLine: {
  379. show: true,
  380. lineStyle: {
  381. color: colors[2
  382. ]
  383. }
  384. },
  385. axisLabel: {
  386. formatter: '{value} hPa'
  387. }
  388. }
  389. ],
  390. series: [
  391. {
  392. name: '气温',
  393. smooth: true, // 添加此行
  394. type: 'line',
  395. data: tempLineArr.value
  396. },
  397. {
  398. name: '相对湿度',
  399. type: 'line',
  400. smooth: true, // 添加此行
  401. yAxisIndex: 1,
  402. data: humidityLineArr.value
  403. },
  404. {
  405. name: '本站气压',
  406. type: 'line',
  407. smooth: true, // 添加此行
  408. yAxisIndex: 2,
  409. data: pressureLineArr.value
  410. }
  411. ]
  412. };
  413. //单击后执行操作
  414. mychart.on('click',(param) => {
  415. console.log(param)
  416. });
  417. mychart.setOption(option)
  418. window.addEventListener("resize", function () {
  419. mychart.resize();
  420. });
  421. }
  422. watch(isDark, () => {
  423. showData()
  424. });
  425. onMounted(() => {
  426. getPlatforms()
  427. // showData()
  428. })
  429. </script>
  430. <style scoped lang="scss">
  431. .main_list {
  432. background: transparent !important;
  433. box-shadow: none !important;
  434. padding: 0 !important;
  435. height: calc(100vh - 150px);
  436. margin-bottom: 10px;
  437. overflow-y: scroll;
  438. overflow-x: hidden;
  439. .chart_item {
  440. border-radius: 10px;
  441. padding: 10px;
  442. background-color: #fff;
  443. // height: 100%;
  444. // margin-bottom: 15px;
  445. .item_title {
  446. display: flex;
  447. justify-content: space-between;
  448. align-items: center;
  449. }
  450. .FTP_box {
  451. padding: 0 50px;
  452. display: flex;
  453. align-items: center;
  454. justify-content: space-between;
  455. .FTP_item {
  456. flex: 1;
  457. flex-shrink: 0;
  458. display: flex;
  459. flex-direction: column;
  460. justify-content: center;
  461. align-items: center;
  462. img {
  463. width: 56px;
  464. margin-bottom: 20px;
  465. }
  466. }
  467. }
  468. }
  469. .info_box {
  470. .info_title {
  471. color: #999999;
  472. font-size: 16px;
  473. }
  474. .info_content {
  475. height: 40px;
  476. font-weight: bold;
  477. color: #000;
  478. font-size: 26px;
  479. }
  480. }
  481. }
  482. .data_box {
  483. height: calc(100vh - 215px);
  484. // height: 100%;
  485. }
  486. </style>