irradianceFrequencyDistribution.vue 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <template>
  2. <div class="irradiance-frequency-distribution-page">
  3. <a-card :bordered="false">
  4. <a-row :gutter="[12, 12]" align="middle">
  5. <a-col :xs="24" :md="6">
  6. <StationSelector v-model:value="query.stationId" :options="stationOptions" :multiple="false" />
  7. </a-col>
  8. <a-col :xs="24" :md="5">
  9. <a-date-picker
  10. v-model:value="query.startDay"
  11. format="YYYY-MM-DD"
  12. value-format="YYYY-MM-DD"
  13. placeholder="起始时间"
  14. class="full-control"
  15. />
  16. </a-col>
  17. <a-col :xs="24" :md="5">
  18. <a-date-picker
  19. v-model:value="query.endDay"
  20. format="YYYY-MM-DD"
  21. value-format="YYYY-MM-DD"
  22. placeholder="结束时间(不含当天)"
  23. class="full-control"
  24. />
  25. </a-col>
  26. <a-col :xs="24" :md="4">
  27. <a-space>
  28. <a-button type="primary" :loading="loading" :disabled="!queryValid" @click="handleQuery">确认</a-button>
  29. <a-button :loading="exporting" :disabled="!queryValid" @click="handleExport">导出</a-button>
  30. </a-space>
  31. </a-col>
  32. </a-row>
  33. </a-card>
  34. <a-card :bordered="false" class="content-card">
  35. <BusinessDataTable
  36. :columns="columns"
  37. :data-source="rows"
  38. :pagination="{ pageSize: 10 }"
  39. :row-key="'day'"
  40. :scroll-x="'max-content'"
  41. />
  42. </a-card>
  43. </div>
  44. </template>
  45. <script setup name="IrradianceFrequencyDistribution">
  46. import {computed, onMounted, reactive, ref} from 'vue';
  47. import {message} from 'ant-design-vue';
  48. import dayjs from 'dayjs';
  49. import {saveAs} from 'file-saver';
  50. import {blobValidate} from '@/utils/bearjia';
  51. import {listStation} from '@/api/config/station';
  52. import {
  53. exportIrradianceFrequencyDistribution,
  54. listIrradianceFrequencyDistribution
  55. } from '@/api/data/irradianceFrequencyDistribution';
  56. import BusinessDataTable from '../components/BusinessDataTable.vue';
  57. import StationSelector from '../components/StationSelector.vue';
  58. import {normalizeRows} from '../components/dataAdapter';
  59. import {toStationOptions} from '../components/stationAdapter';
  60. const loading = ref(false);
  61. const exporting = ref(false);
  62. const stationOptions = ref([]);
  63. const rows = ref([]);
  64. const query = reactive({
  65. stationId: undefined,
  66. startDay: dayjs().subtract(7, 'day').format('YYYY-MM-DD'),
  67. endDay: dayjs().format('YYYY-MM-DD')
  68. });
  69. const queryValid = computed(() => {
  70. if (!query.stationId || !query.startDay || !query.endDay) return false;
  71. const start = dayjs(query.startDay);
  72. const end = dayjs(query.endDay);
  73. if (!start.isValid() || !end.isValid()) return false;
  74. return !start.isAfter(end);
  75. });
  76. const renderBucket = (key) => ({record}) => {
  77. const num = record[`${key}Num`] ?? 0;
  78. const percent = record[`${key}Percent`] ?? '0.00';
  79. return `${num}(${percent}%)`;
  80. };
  81. const bucketColumn = (key, title) => ({
  82. title,
  83. key: `${key}Text`,
  84. width: 110,
  85. align: 'right',
  86. customRender: renderBucket(key)
  87. });
  88. const columns = [
  89. {title: '时间', dataIndex: 'day', key: 'day', width: 120},
  90. {title: '数据总条数', dataIndex: 'totalRecord', key: 'totalRecord', width: 110, align: 'right'},
  91. bucketColumn('lt600', '[0-600)w/㎡'),
  92. bucketColumn('lt800', '[600-800)w/㎡'),
  93. bucketColumn('lt1000', '[800-1000)w/㎡'),
  94. bucketColumn('lt1200', '[1000-1200)w/㎡'),
  95. bucketColumn('lt1400', '[1200-1400)w/㎡'),
  96. bucketColumn('ge1400', '≥1400w/㎡')
  97. ];
  98. const loadStationOptions = async () => {
  99. const res = await listStation({pageNum: 1, pageSize: 500});
  100. stationOptions.value = toStationOptions(normalizeRows(res));
  101. if (!query.stationId && stationOptions.value.length) {
  102. query.stationId = stationOptions.value[0].value;
  103. }
  104. };
  105. const buildParams = () => ({
  106. stationId: query.stationId,
  107. startDay: query.startDay,
  108. endDay: query.endDay
  109. });
  110. const fetchList = async () => {
  111. const res = await listIrradianceFrequencyDistribution(buildParams());
  112. rows.value = normalizeRows(res);
  113. };
  114. const handleQuery = async () => {
  115. if (!query.stationId) {
  116. message.warning('请先选择场站');
  117. return;
  118. }
  119. if (!queryValid.value) return;
  120. loading.value = true;
  121. try {
  122. await fetchList();
  123. } finally {
  124. loading.value = false;
  125. }
  126. };
  127. const handleExport = async () => {
  128. if (!query.stationId) {
  129. message.warning('请先选择场站');
  130. return;
  131. }
  132. if (!queryValid.value) return;
  133. exporting.value = true;
  134. try {
  135. const res = await exportIrradianceFrequencyDistribution(buildParams());
  136. const isFile = await blobValidate(res);
  137. if (isFile) {
  138. const name = `辐照度频率分布统计-${query.startDay}-${query.endDay}.xlsx`;
  139. saveAs(new Blob([res]), name);
  140. message.success('导出成功');
  141. } else {
  142. const text = await res.text();
  143. let msg = '导出失败';
  144. try {
  145. msg = JSON.parse(text)?.msg || msg;
  146. } catch (e) {
  147. /* ignore */
  148. }
  149. message.error(msg);
  150. }
  151. } finally {
  152. exporting.value = false;
  153. }
  154. };
  155. onMounted(async () => {
  156. await loadStationOptions();
  157. await handleQuery();
  158. });
  159. </script>
  160. <style lang="less" scoped>
  161. .irradiance-frequency-distribution-page {
  162. padding: 16px;
  163. }
  164. .full-control {
  165. width: 100%;
  166. }
  167. .content-card {
  168. margin-top: 16px;
  169. }
  170. </style>