天气预报模块

This commit is contained in:
冯少康 2026-07-29 17:47:52 +08:00
parent 33859185ff
commit c31bb50108
5 changed files with 501 additions and 453 deletions

View File

@ -274,6 +274,7 @@
"monitoringData": "监测数据", "monitoringData": "监测数据",
"ImportMonitoringData": "导入监测数据", "ImportMonitoringData": "导入监测数据",
"pinToTop": "置顶", "pinToTop": "置顶",
"unpin": "取消置顶" "unpin": "取消置顶",
"executeAssociation":"执行关联"
} }

View File

@ -5,7 +5,7 @@ import { gisImgOpt, tqycAnimationType, weathFlag } from '@/gis/ol/index';
import { playbackSearchData, weatherDataLine, weatherPreview } from '@/utils/axios/tqyb/data/index'; import { playbackSearchData, weatherDataLine, weatherPreview } from '@/utils/axios/tqyb/data/index';
import tab1 from './tab-1/index.vue'; import tab1 from './tab-1/index.vue';
import tab2 from './tab-2/index.vue'; import tab2 from './tab-2/index.vue';
// import tab3 from './tab-3/index.vue'; import tab3 from './tab-3/index.vue';
import WindRose from './WindRose.vue'; import WindRose from './WindRose.vue';
const { t } = useI18n(); const { t } = useI18n();
@ -50,6 +50,7 @@ function handleSendData(params: any) {
console.log('选中的回放数据,需要传至gis --->', params); console.log('选中的回放数据,需要传至gis --->', params);
delete params.key delete params.key
delete params.endTime delete params.endTime
params.hour+=6;
playbackSearchData(params, false, (res) => { playbackSearchData(params, false, (res) => {
console.log(res); console.log(res);
// //
@ -239,7 +240,7 @@ onMounted(() => {
<tab2 v-if="tabActive === '公开气象数据'" type="gkqx" @playback="handleSendData" @preview="weatherPreviewHandle" /> <tab2 v-if="tabActive === '公开气象数据'" type="gkqx" @playback="handleSendData" @preview="weatherPreviewHandle" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="t('associateMeteorologicalData')" name="关联气象数据"> <el-tab-pane :label="t('associateMeteorologicalData')" name="关联气象数据">
<!-- //<tab3 /> --> <tab3 />
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</Dialog> </Dialog>

View File

@ -1,16 +1,33 @@
<script setup lang="ts"> <script setup lang="ts">
import type { TableInstance, UploadInstance, UploadProps, UploadRawFile } from 'element-plus'; import type {
import dayjs from 'dayjs'; TableInstance,
import { ElLoading, ElMessage, ElMessageBox, ElNotification, genFileId } from 'element-plus'; UploadInstance,
import SparkMD5 from 'spark-md5'; UploadProps,
import { useI18n } from 'vue-i18n'; UploadRawFile,
import { gisImgOpt, weathFlag } from '@/gis/ol/index'; } from "element-plus";
import { deleteData, getData, getTimeBatch, importFile } from '@/utils/axios/tqyb/data'; import dayjs from "dayjs";
import {
ElLoading,
ElMessage,
ElMessageBox,
ElNotification,
genFileId,
} from "element-plus";
import SparkMD5 from "spark-md5";
import { useI18n } from "vue-i18n";
import { gisImgOpt, weathFlag } from "@/gis/ol/index";
import {
deleteData,
getData,
getTimeBatch,
importFile,
getPcValueBySjlx,
} from "@/utils/axios/tqyb/data";
const propsWithDefaults = withDefaults(defineProps<Props>(), { const propsWithDefaults = withDefaults(defineProps<Props>(), {
type: '', type: "",
}); });
const emit = defineEmits(['playback', 'preview']); const emit = defineEmits(["playback", "preview"]);
const { t } = useI18n(); const { t } = useI18n();
const chunks = ref([]); const chunks = ref([]);
@ -37,7 +54,7 @@ const searchForm = reactive({});
const fileInput: Ref<HTMLInputElement | null> = ref(null); const fileInput: Ref<HTMLInputElement | null> = ref(null);
function search() { function search() {
console.log('搜索条件:', searchForm); console.log("搜索条件:", searchForm);
getList(); getList();
} }
@ -45,13 +62,17 @@ function search() {
const timeBatchList = ref<any>([]); const timeBatchList = ref<any>([]);
const timeBatch = ref<any>(null); const timeBatch = ref<any>(null);
const dataSourceList = ref([ const dataSourceList = ref([
{ label: 'T1H', value: 6 }, { label: "T1H", value: 6 },
{ label: 'GFS', value: 7 }, { label: "GFS", value: 7 },
]); ]);
const xxxxxxxxxxxxx = ref(''); const xxxxxxxxxxxxx = ref("");
// //
const searchSelectValue = ref<any>(null); const searchSelectValue = ref<any>(null);
//
const searchPcSelectValue = ref<any>(null);
// //
const fileDatasource = ref<any>(null); const fileDatasource = ref<any>(null);
@ -59,13 +80,17 @@ const fileDatasource = ref<any>(null);
const selectValue = ref<any>(null); const selectValue = ref<any>(null);
// //
const selectOption = ref<any>([]); const selectOption = ref<any>([]);
const pcSelectOption = ref<any>([]);
// //
const pickerValue = ref([]); const pickerValue = ref([]);
const hfSelectSjy = ref<any>(null);
getTimeBatchList(); getTimeBatchList();
function getTimeBatchList() { function getTimeBatchList() {
getTimeBatch({ getTimeBatch({}, (res) => {
}, (res) => {
timeBatchList.value = res; timeBatchList.value = res;
}); });
} }
@ -75,49 +100,65 @@ function getList() {
startTime = searchForm.createTime[0]; startTime = searchForm.createTime[0];
endTime = searchForm.createTime[1]; endTime = searchForm.createTime[1];
} }
if (propsWithDefaults.type === 'sjyc') { if (propsWithDefaults.type === "sjyc") {
dataS = '1,2'; dataS = "1,2";
selectOption.value = [{ selectOption.value = [
value: '1', {
label: '盘古模型', value: "1",
}, { label: "盘古模型",
value: '2', },
label: 'graphcast', {
}]; value: "2",
label: "graphcast",
},
];
} }
if (propsWithDefaults.type === 'zfx') { if (propsWithDefaults.type === "zfx") {
dataS = '3,4,5'; dataS = "3,4,5";
selectOption.value = [{ selectOption.value = [
value: '3', {
label: 'cra40', value: "3",
}, { label: "cra40",
value: '4', },
label: 'ncep', {
}, { value: "4",
value: '5', label: "ncep",
label: 'fnl', },
}]; {
value: "5",
label: "fnl",
},
];
} }
if (propsWithDefaults.type === 'gkqx') { if (propsWithDefaults.type === "gkqx") {
dataS = ''; dataS = "6,7";
selectOption.value = [{ selectOption.value = [
value: '6', {
label: 't1h', value: "6",
}]; label: "T1H",
selectValue.value = '6'; },
{
value: "7",
label: "GFS",
},
];
selectValue.value = "6";
} }
dataSource.value = dataS; dataSource.value = dataS;
getData({ getData(
{
...searchForm, ...searchForm,
startTime, startTime,
endTime, endTime,
dataSource: searchSelectValue.value || dataS, dataSource: searchSelectValue.value || dataS,
pageSize: pageSize.value, pageSize: pageSize.value,
pageNum: currentPage.value, pageNum: currentPage.value,
}, (res) => { },
(res) => {
taskList.value = res.rows; taskList.value = res.rows;
total.value = res.total; total.value = res.total;
}); }
);
} }
function handleSizeChange(val: number) { function handleSizeChange(val: number) {
@ -132,16 +173,12 @@ function handleCurrentChange(val: number) {
} }
function deleteTask(task: any) { function deleteTask(task: any) {
console.log('删除任务', task); console.log("删除任务", task);
ElMessageBox.confirm( ElMessageBox.confirm(t("isSureDelete"), "Warning", {
t('isSureDelete'), confirmButtonText: t("sure"),
'Warning', cancelButtonText: t("cancel"),
{ type: "warning",
confirmButtonText: t('sure'), })
cancelButtonText: t('cancel'),
type: 'warning',
},
)
.then(() => { .then(() => {
let arr = []; let arr = [];
arr.push(task.id); arr.push(task.id);
@ -165,29 +202,28 @@ function handleSelectionChange(selection: any[]) {
multipleTableRef.value?.clearSelection(); // multipleTableRef.value?.clearSelection(); //
multipleTableRef.value?.toggleRowSelection(latestSelection, true); // multipleTableRef.value?.toggleRowSelection(latestSelection, true); //
currentSelection.value = [latestSelection]; currentSelection.value = [latestSelection];
} } else {
else {
// //
currentSelection.value = selection; currentSelection.value = selection;
} }
console.log('当前选中项:', currentSelection.value); console.log("当前选中项:", currentSelection.value);
} }
function playback() { function playback() {
if (!selectValue.value) { if (!selectValue.value) {
ElMessage({ ElMessage({
type: 'warning', type: "warning",
message: t(`selectTqybTableDataType`), message: t(`selectTqybTableDataType`),
}); });
} }
if (!pickerValue.value.length) { if (!pickerValue.value.length) {
ElMessage({ ElMessage({
type: 'warning', type: "warning",
message: t(`selectStartAndEndTime`), message: t(`selectStartAndEndTime`),
}); });
} }
const startTime = dayjs(pickerValue.value[0]).format('YYYY-MM-DD HH:mm:ss'); const startTime = dayjs(pickerValue.value[0]).format("YYYY-MM-DD HH:mm:ss");
const endTime = dayjs(pickerValue.value[1]).format('YYYY-MM-DD HH:mm:ss'); const endTime = dayjs(pickerValue.value[1]).format("YYYY-MM-DD HH:mm:ss");
const params = { const params = {
dataType: selectValue.value, dataType: selectValue.value,
@ -197,8 +233,8 @@ function playback() {
timeBatch: timeBatch.value, timeBatch: timeBatch.value,
}; };
EventBusTool.getEventBus().publish('playData', { EventBusTool.getEventBus().publish("playData", {
key: 'setTaishiParam', key: "setTaishiParam",
dataType: selectValue.value, dataType: selectValue.value,
weatherType: 3, weatherType: 3,
startTime, startTime,
@ -208,74 +244,76 @@ function playback() {
}); });
// //
weathFlag[gisImgOpt.weatherType] = true; weathFlag[gisImgOpt.weatherType] = true;
emit('playback', params); emit("playback", params);
} }
function disabledHour() { function disabledHour() {
return [1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 19, 20, 21, 22, 23]; return [
1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 19, 20, 21, 22, 23,
];
} }
function handleClick(): void { function handleClick(): void {
if (!fileDatasource.value) { if (!fileDatasource.value) {
return ElMessage({ return ElMessage({
type: 'warning', type: "warning",
message: '请先选择数据来源', message: "请先选择数据来源",
}); });
} }
if (fileInput.value) { if (fileInput.value) {
fileInput.value.value = ''; fileInput.value.value = "";
fileInput.value.click(); fileInput.value.click();
} }
} }
// //
const uploadData = ref({ const uploadData = ref({
md5Value: '', md5Value: "",
fileName: '', fileName: "",
fileExt: 'grib2', fileExt: "grib2",
dataSource: '', dataSource: "",
shareTotal: '1', shareTotal: "1",
shareIndex: '', // shareIndex: "", //
fileShare: 'false', fileShare: "false",
}); });
// //
function handleFileChange(event: Event): void { function handleFileChange(event: Event): void {
const target = event.target as HTMLInputElement; const target = event.target as HTMLInputElement;
const file = target.files?.[0]; const file = target.files?.[0];
if (!file) { if (!file) {
console.warn('未选择文件'); console.warn("未选择文件");
return; return;
} }
console.log('file', file, dataSource.value, dataSource); console.log("file", file, dataSource.value, dataSource);
let md5; let md5;
// MD5 // MD5
const fileReader = new FileReader(); const fileReader = new FileReader();
const spark = new SparkMD5.ArrayBuffer(); const spark = new SparkMD5.ArrayBuffer();
// //
const getFileExtension = (filename: any) => { const getFileExtension = (filename: any) => {
const lastDotIndex = filename.lastIndexOf('.'); const lastDotIndex = filename.lastIndexOf(".");
return lastDotIndex !== -1 && lastDotIndex < filename.length - 1 return lastDotIndex !== -1 && lastDotIndex < filename.length - 1
? filename.slice(lastDotIndex + 1) ? filename.slice(lastDotIndex + 1)
: ''; : "";
}; };
const formData = new FormData(); const formData = new FormData();
formData.append('fileExt', getFileExtension(file.name)); formData.append("fileExt", getFileExtension(file.name));
fileReader.onload = (e) => { fileReader.onload = (e) => {
spark.append(e.target.result); spark.append(e.target.result);
md5 = spark.end(); md5 = spark.end();
// FormData // FormData
formData.append('md5Value', md5); formData.append("md5Value", md5);
formData.append('fileName', file.name); formData.append("fileName", file.name);
formData.append('file', file); formData.append("file", file);
formData.append('shareTotal', '1'); formData.append("shareTotal", "1");
formData.append('fileShare', 'false'); formData.append("fileShare", "false");
formData.append('dataSource', fileDatasource.value); formData.append("dataSource", fileDatasource.value);
// importTaskformDatacallback // importTaskformDatacallback
importFile(formData, (res: any) => { importFile(formData, (res: any) => {
console.log('接口响应:', res); console.log("接口响应:", res);
getList(); getList();
}); });
}; };
@ -286,12 +324,12 @@ function handleFileChange(event: Event): void {
function getDataSource(code: any) { function getDataSource(code: any) {
// //
const dataSourceMap: any = { const dataSourceMap: any = {
1: '盘古模型', 1: "盘古模型",
2: 'graphcast', 2: "graphcast",
3: 'cra40', 3: "cra40",
4: 'ncep', 4: "ncep",
5: 'fnl', 5: "fnl",
6: 't1h', 6: "t1h",
}; };
// //
return dataSourceMap[code] || code; return dataSourceMap[code] || code;
@ -306,16 +344,53 @@ function previewTask(row: any) {
weatherType: 3, weatherType: 3,
timeBatch: row.timeBatch, timeBatch: row.timeBatch,
}; };
EventBusTool.getEventBus().publish('playData', { EventBusTool.getEventBus().publish("playData", {
key: 'setTaishiParam', key: "setTaishiParam",
dataType: row.dataSource, dataType: row.dataSource,
weatherType: 3, weatherType: 3,
startTime: row.dataStartTime, startTime: row.dataStartTime,
endTime: row.dataStartTime, endTime: row.dataStartTime,
hour: 0, hour: 0,
}); });
emit('preview', params); emit("preview", params);
} }
const changeSjyValue = (val) => {
pcSelectOption.value = [];
//
if (val) {
//
getPcValueBySjlx(
{
dataType: val,
},
(res) => {
pcSelectOption.value = res.result.map((element) => {
return {
value: element,
label: element,
};
});
}
);
}
};
const changeSjyValue1 = (val) => {
timeBatchList.value = [];
//
if (val) {
//
getPcValueBySjlx(
{
dataType: val,
},
(res) => {
timeBatchList.value = res.result;
}
);
}
};
getList(); getList();
</script> </script>
@ -323,20 +398,50 @@ getList();
<div class="tab2"> <div class="tab2">
<ElForm :inline="true" :model="searchForm" class="custom-form"> <ElForm :inline="true" :model="searchForm" class="custom-form">
<ElFormItem label=""> <ElFormItem label="">
<ElInput v-model="searchForm.fileName" :placeholder="`${t('keyword')}...`" class="custom-input" /> <ElInput
v-model="searchForm.fileName"
:placeholder="`${t('keyword')}...`"
class="custom-input"
/>
</ElFormItem> </ElFormItem>
<ElFormItem v-if="type !== 'gkqx'" :label="t('tqybTableDataType')"> <ElFormItem :label="t('tqybTableDataType')">
<el-select-v2 v-model="searchSelectValue" clearable :options="selectOption" placeholder="选择数据源" <el-select-v2
style="width: 240px;margin-right: 10px;" /> v-model="searchSelectValue"
clearable
:options="selectOption"
placeholder="选择数据源"
style="width: 240px; margin-right: 10px"
@change="changeSjyValue"
/>
</ElFormItem>
<ElFormItem :label="t('timeBatch')">
<el-select-v2
v-model="searchPcSelectValue"
clearable
:options="pcSelectOption"
placeholder="选择批次"
style="width: 240px; margin-right: 10px"
/>
</ElFormItem> </ElFormItem>
<ElFormItem> <ElFormItem>
<ElButton type="primary" class="custom-btn" icon="Search" @click="search"> <ElButton
{{ t('search') }} type="primary"
class="custom-btn"
icon="Search"
@click="search"
>
{{ t("search") }}
</ElButton> </ElButton>
</ElFormItem> </ElFormItem>
</ElForm> </ElForm>
<ElTable ref="multipleTableRef" :data="taskList" width="100%" max-height="60vh" class="custom-table" <ElTable
@selection-change="handleSelectionChange"> ref="multipleTableRef"
:data="taskList"
width="100%"
max-height="60vh"
class="custom-table"
@selection-change="handleSelectionChange"
>
<ElTableColumn type="index" :label="t('serialNumber')" width="80" /> <ElTableColumn type="index" :label="t('serialNumber')" width="80" />
<ElTableColumn prop="fileName" :label="t('fileName')" width="250" /> <ElTableColumn prop="fileName" :label="t('fileName')" width="250" />
<ElTableColumn prop="dataSource" :label="t('tqybTableDataType')"> <ElTableColumn prop="dataSource" :label="t('tqybTableDataType')">
@ -361,7 +466,12 @@ getList();
</div> </div>
</template> </template>
</ElTableColumn> </ElTableColumn>
<ElTableColumn v-if="type === 'gkqx'" prop="timeBatch" :label="t('timeBatch')" width="180"> <ElTableColumn
v-if="type === 'gkqx'"
prop="timeBatch"
:label="t('timeBatch')"
width="180"
>
<template #default="scope"> <template #default="scope">
<div> <div>
{{ scope.row?.timeBatch }} {{ scope.row?.timeBatch }}
@ -377,46 +487,90 @@ getList();
</ElTableColumn> </ElTableColumn>
<ElTableColumn :label="t('operation')" width="120"> <ElTableColumn :label="t('operation')" width="120">
<template #default="scope"> <template #default="scope">
<ElButton type="text" class="custom-text-btn" @click="previewTask(scope.row)"> <ElButton
{{ t('preview') }} type="text"
class="custom-text-btn"
@click="previewTask(scope.row)"
>
{{ t("preview") }}
</ElButton> </ElButton>
<ElButton type="text" class="custom-text-btn" style="color:red" @click="deleteTask(scope.row)"> <ElButton
{{ t('delete') }} type="text"
class="custom-text-btn"
style="color: red"
@click="deleteTask(scope.row)"
>
{{ t("delete") }}
</ElButton> </ElButton>
</template> </template>
</ElTableColumn> </ElTableColumn>
</ElTable> </ElTable>
<div class="pagination"> <div class="pagination">
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 20, 50, 100, 200]" <el-pagination
size="small" :background="true" layout="total, sizes, prev, pager, next, jumper" :total="total" v-model:current-page="currentPage"
@size-change="handleSizeChange" @current-change="handleCurrentChange" /> v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100, 200]"
size="small"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div> </div>
<div class="dialog-footer"> <div class="dialog-footer">
<!-- 下拉菜单 --> <!-- 下拉菜单 -->
<el-select-v2 v-if="type !== 'gkqx'" v-model="selectValue" :options="selectOption" placeholder="选择数据源" <el-select-v2
style="width: 240px;margin-right: 10px;" /> v-if="type !== 'gkqx'"
v-model="selectValue"
:options="selectOption"
placeholder="选择数据源"
style="width: 240px; margin-right: 10px"
/>
<!-- 时间选择 --> <!-- 时间选择 -->
<div style="width: 760px;display: flex;margin-right: 10px"> <div style="display: flex; margin-right: 10px" class="bottomBox">
<ElSelect v-if="type === 'gkqx'" v-model="xxxxxxxxxxxxx" placeholder="选择数据源" filterable <ElSelect
style="width: 160px;margin-right: 10px"> v-if="type === 'gkqx'"
<ElOption v-for="v in dataSourceList" :label="v.label" :value="v.value" /> v-model="hfSelectSjy"
placeholder="选择数据源"
filterable
style="width: 160px; margin-right: 10px"
@change="changeSjyValue1"
>
<ElOption
v-for="v in dataSourceList"
:label="v.label"
:value="v.value"
/>
</ElSelect> </ElSelect>
<ElSelect v-if="type === 'gkqx'" v-model="timeBatch" placeholder="请选择批次" filterable <ElSelect
style="width: 200px;margin-right: 10px"> v-if="type === 'gkqx'"
v-model="timeBatch"
placeholder="请选择批次"
filterable
style="width: 200px; margin-right: 10px"
>
<ElOption v-for="v in timeBatchList" :label="v" :value="v" /> <ElOption v-for="v in timeBatchList" :label="v" :value="v" />
</ElSelect> </ElSelect>
<el-date-picker v-model="pickerValue" unlink-panels type="datetimerange" :default-time="new Date()" <el-date-picker
range-separator="至" :start-placeholder="t('startDate')" :end-placeholder="t('endDate')" v-model="pickerValue"
unlink-panels
type="datetimerange"
:default-time="new Date()"
range-separator="至"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
:disabled-minutes="() => Array.from({ length: 60 }, (_, i) => i)" :disabled-minutes="() => Array.from({ length: 60 }, (_, i) => i)"
:disabled-seconds="() => Array.from({ length: 60 }, (_, i) => i)" :disabled-seconds="() => Array.from({ length: 60 }, (_, i) => i)"
value-format="YYYY-MM-DD HH:00:00" value-format="YYYY-MM-DD HH:00:00"
:disabled-hours="disabledHour" /> :disabled-hours="disabledHour"
/>
</div> </div>
<!-- 回放 --> <!-- 回放 -->
<el-button class="custom-btn" icon="VideoPlay" @click="playback"> <el-button class="custom-btn" icon="VideoPlay" @click="playback">
{{ t('playback') }} {{ t("playback") }}
</el-button> </el-button>
<!-- <el-upload v-if="type !== 'sjyc'" ref="upload" class="upload-file" --> <!-- <el-upload v-if="type !== 'sjyc'" ref="upload" class="upload-file" -->
@ -436,8 +590,12 @@ getList();
</div> </div>
</template> </template>
<style lang="less"> <style scoped lang="less">
.tab2 { .tab2 {
:deep(.el-date-editor .el-range-separator) {
color: #fff !important;
}
.config-dialog { .config-dialog {
margin-top: 35vh; margin-top: 35vh;
@ -485,7 +643,6 @@ getList();
} }
.config-custom-date { .config-custom-date {
.el-date-editor.el-input, .el-date-editor.el-input,
.el-date-editor.el-input__wrapper { .el-date-editor.el-input__wrapper {
width: calc(20vw - 80px) !important; width: calc(20vw - 80px) !important;
@ -503,4 +660,10 @@ getList();
margin-top: 10px; margin-top: 10px;
} }
} }
.bottomBox {
:deep(.el-date-range-picker__separator) {
color: #fff !important;
}
}
</style> </style>

View File

@ -1,343 +1,173 @@
<script setup lang="ts"> <script setup lang="ts">
import { ElButton, ElOption, TableInstance, UploadInstance } from 'element-plus'; import { useI18n } from "vue-i18n";
import dayjs from 'dayjs'; import dayjs from "dayjs";
import { ElMessage } from 'element-plus'; import { getZxglData, getGlqxsjRzData } from "@/utils/axios/tqyb/data";
import { ref, reactive } from 'vue'; // Vue set
import { useI18n } from 'vue-i18n';
import { getData, getTaskLog } from '@/utils/axios/tqyb/task';
import log from '../log/index.vue';
const emit = defineEmits(['playback']);
const { t } = useI18n(); const { t } = useI18n();
const configVisible = ref(false); const searchForm = ref({});
const configType = ref(''); //
/* 新增、编辑表单 */ const searchSelectValue = ref<any>(null);
const configForm = ref<any>({}); //
const selectOption = ref<any>([]);
// ref //
const fileInput: Ref<HTMLInputElement | null> = ref(null); const pickerValue = ref([]);
function disabledHour() {
const taskList = ref<any[]>([]); return [
const logData = ref<any>([]); 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 19, 20, 21, 22, 23,
];
const currentPage = ref(1);
const pageSize = ref(20);
const total = ref(0);
//
const currentSelection = ref<any>([]);
const multipleTableRef = ref<TableInstance>();
const logVisible = ref(false);
/* drawer侧边栏表单 */
const form = ref<any>({});
/* 任务列表搜索表单 */
const searchForm = reactive({});
interface OptionItem {
label: string;
value: number;
} }
const taskStatusList = ref<OptionItem[]>([
// -101234 //
{ label: '执行失败', value: -1 }, let pollTimer = null;
{ label: '未开始', value: 0 }, //
{ label: '等待中', value: 1 }, const isPolling = ref(false);
{ label: '运行中', value: 2 }, // loading
{ label: '已完成', value: 3 }, const pollLoading = ref(false);
{ label: '检查未通过', value: 4 }, //
const pollResult = ref(null);
//
const emptyCount = ref(0);
/**
* 轮询请求接口方法
*/
const fetchData = () => {
getGlqxsjRzData(
{
lastId: "",
},
(res) => {
console.log(222222, res);
//
const isEmpty = !res.result || res.result.length === 0;
if (isEmpty) {
emptyCount.value += 1;
// 2
if (emptyCount.value >= 2) {
stopPoll();
console.log("连续2次无数据终止轮询");
}
} else {
//
emptyCount.value = 0;
console.log("获取到数据,继续轮询");
}
}
);
};
//
const startPoll = () => {
if (isPolling.value) return;
//
emptyCount.value = 0;
isPolling.value = true;
pollLoading.value = true;
//
fetchData();
// 3
pollTimer = setInterval(fetchData, 3000);
};
//
const stopPoll = () => {
if (pollTimer) {
clearInterval(pollTimer);
pollTimer = null;
}
isPolling.value = false;
pollLoading.value = false;
};
const dataSourceList = ref([
{ label: "cra40", value: 3 },
{ label: "ncep", value: 4 },
{ label: "fnl", value: 5 },
]); ]);
function search() { const zxglClick = () => {
console.log('搜索条件:', searchForm); startPoll();
getList(); // const startTime = dayjs(pickerValue.value[0]).format("YYYY-MM-DD");
} // const endTime = dayjs(pickerValue.value[1]).format("YYYY-MM-DD");
// getZxglData(
function getList() { // {
let startDate, endDate; // dataType: searchSelectValue.value,
if (searchForm.createTime && searchForm.createTime.length) { // startDate: startTime,
startDate = searchForm.createTime[0]; // endDate: endTime,
endDate = searchForm.createTime[1]; // },
} // (res) => {
getData({ // if(res.code==200){
...searchForm, // startPoll();
startDate, // }
endDate, // }
pageSize: pageSize.value, // );
pageNum: currentPage.value, };
}, (res) => { //
taskList.value = res.rows;
total.value = res.total;
});
}
function refresh() {
console.log('刷新任务列表');
getList();
}
function showLog(task: any) {
console.log('显示运行日志');
getTaskLog({ taskId: task.id }, (res) => {
console.log('res', res);
logData.value = res;
logVisible.value = true;
});
}
function handleSelectionChange(selection: any[]) {
// multipleTableRef.value!.clearSelection()
// multipleSelection.value = val
// 1
if (selection.length > 1) {
//
const latestSelection = selection[selection.length - 1];
multipleTableRef.value?.clearSelection(); //
multipleTableRef.value?.toggleRowSelection(latestSelection, true); //
currentSelection.value = [latestSelection];
}
else {
//
currentSelection.value = selection;
}
console.log('当前选中项:', currentSelection.value);
}
function playback() {
console.log('当前选中项:', currentSelection.value);
if (Object.keys(currentSelection.value).length === 0) {
ElMessage({
type: 'warning',
message: t(`isSelect`),
});
}
else {
const startTime = dayjs(currentSelection.value[0].startDate).add(0, 'hour').format('YYYY-MM-DD HH:mm:ss');
const params = {
dataType: currentSelection.value[0].predictionModel,
weatherType: 3,
startTime,
endTime: dayjs(startTime).add(currentSelection.value[0].leadTime, 'hour').format('YYYY-MM-DD HH:mm:ss'),
// hour: currentSelection.value[0].leadTime,
hour: 0,
};
emit('playback', params);
EventBusTool.getEventBus().publish('playData', {
key: 'setTaishiParam',
dataType: currentSelection.value[0].predictionModel,
weatherType: 3,
startTime,
// hour: currentSelection.value[0].leadTime,
hour: 0,
endTime: dayjs(startTime).add(currentSelection.value[0].leadTime, 'hour').format('YYYY-MM-DD HH:mm:ss'),
});
}
}
function handleClick(): void {
if (fileInput.value) {
fileInput.value.value = '';
fileInput.value.click();
}
}
//
function handleFileChange(event: Event): void {
const target = event.target as HTMLInputElement;
const file = target.files?.[0];
if (!file) {
console.warn('未选择文件');
return;
}
configForm.value.file = file;
configForm.value.inputFile = file.name;
// const formData = new FormData();
// // FormData
// formData.append('inputFile', file.name);
// formData.append('file', file);
// importTaskformDatacallback
// importFile(formData, (res:any) => {
// console.log(':', res);
// getList();
// });
}
function handleSizeChange(val: number) {
console.log(`${val} items per page`);
pageSize.value = val;
getList();
}
function handleCurrentChange(val: number) {
console.log(`current page: ${val}`);
currentPage.value = val;
getList();
}
getList();
</script> </script>
<template> <template>
<div class="ycrw"> <div class="ycrw">
<!-- 主表 --> <!-- 主表 -->
<ElForm :inline="true" :model="searchForm" class="custom-form"> <ElForm :inline="true" :model="searchForm" class="custom-form">
<ElFormItem label=""> <ElFormItem :label="t('tqybTableDataType')">
<ElInput v-model="searchForm.taskName" :placeholder="`${t('keyword')}...`" class="custom-input" /> <ElSelect
</ElFormItem> v-model="searchSelectValue"
<ElFormItem :label="t('taskStatus')"> placeholder="选择数据源"
<ElSelect v-model="searchForm.taskStatus" :placeholder="t('pleaseSelect')" clearable class="custom-select"> filterable
<ElOption v-for="item in taskStatusList" :key="item.value" :label="item.label" :value="item.value" /> style="width: 160px; margin-right: 10px"
>
<ElOption
v-for="v in dataSourceList"
:label="v.label"
:value="v.value"
/>
</ElSelect> </ElSelect>
</ElFormItem> </ElFormItem>
<ElFormItem :label="t('predictionDate')"> <ElFormItem :label="t('timeRange')">
<ElDatePicker v-model="searchForm.createTime" value-format="YYYY-MM-DD" type="daterange" range-separator="--" <el-date-picker
:start-placeholder="t('startDate')" :end-placeholder="t('endDate')" class="custom-date-picker" /> v-model="pickerValue"
unlink-panels
type="datetimerange"
:default-time="new Date()"
range-separator="至"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
:disabled-minutes="() => Array.from({ length: 60 }, (_, i) => i)"
:disabled-seconds="() => Array.from({ length: 60 }, (_, i) => i)"
value-format="YYYY-MM-DD HH:00:00"
:disabled-hours="disabledHour"
/>
</ElFormItem> </ElFormItem>
<ElFormItem> <ElFormItem>
<ElButton type="primary" class="custom-btn" icon="Search" @click="search"> <ElButton type="primary" class="custom-btn" @click="zxglClick">
{{ t('search') }} {{ t("executeAssociation") }}
</ElButton> </ElButton>
</ElFormItem> </ElFormItem>
</ElForm> </ElForm>
<ElTable ref="multipleTableRef" :data="taskList" width="100%" max-height="60vh" class="custom-table" <div class="gltext-content">
@selection-change="handleSelectionChange"> {{ pollResult }}
<ElTableColumn type="selection" width="55" />
<ElTableColumn type="index" :label="t('serialNumber')" width="80" />
<ElTableColumn prop="taskName" :label="t('taskName')" />
<ElTableColumn :label="t('taskStatus')">
<template #default="scope">
<div v-if="scope.row.taskStatus === -1" style="color:#f42a2a;">执行失败</div>
<div v-if="scope.row.taskStatus === 0" style="color:#f42a2a;">未开始</div>
<div v-if="scope.row.taskStatus === 1" style="color:#ffcb40;">等待中</div>
<div v-if="scope.row.taskStatus === 2" style="color:#ffcb40;">运行中</div>
<div v-if="scope.row.taskStatus === 3" style="color:#2af442;">已完成</div>
<div v-if="scope.row.taskStatus === 4" style="color:#f42a2a;">检查未通过</div>
</template>
</ElTableColumn>
<ElTableColumn prop="model" :label="t('predictionModel')">
<template #default="scope">
<div v-if="scope.row.predictionModel === 1">
{{ '盘古' }}
</div>
<div v-if="scope.row.predictionModel === 2">
{{ 'Graphcast' }}
</div>
</template>
</ElTableColumn>
<ElTableColumn prop="time" :label="t('predictionDate')">
<!-- 预测日期 -->
<template #default="scope">
<div>
{{ scope.row.startDate }}
</div>
</template>
</ElTableColumn>
<ElTableColumn prop="startTime" :label="t('predictionStartTime')">
<!-- 预测时间 -->
<template #default="scope">
<div>
{{ scope.row.startTime }}
</div>
</template>
</ElTableColumn>
<ElTableColumn prop="leadTime" :label="t('predictionDuration')">
<!-- 预测时长 -->
<template #default="scope">
<div>
{{ scope.row.leadTime }}小时
</div>
</template>
</ElTableColumn>
<ElTableColumn prop="hours" :label="t('timeConsuming')">
<!-- 耗时 -->
<template #default="scope">
<div>
{{ (scope.row.timeConsuming) }}分钟
</div>
</template>
</ElTableColumn>
<!--<ElTableColumn prop="creator" :label="t('creator')">
<template #default="scope">
{{ scope.row.createBy || '暂无信息' }}
</template>
</ElTableColumn>-->
<ElTableColumn prop="createTime" :label="t('creationTime')" width="220" />
</ElTable>
<div class="pagination">
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 20, 50, 100, 200]"
size="small" :background="true" layout="total, sizes, prev, pager, next, jumper" :total="total"
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
<div class="dialog-footer">
<el-button type="primary" class="custom-btn" icon="Refresh" @click="refresh">
{{ t('refresh') }}
</el-button>
</div> </div>
</div> </div>
</template> </template>
<style lang="less"> <style scoped lang="less">
.ycrw { .ycrw {
.config-dialog { :deep(.el-date-editor) {
margin-top: 35vh; .el-date-editor {
color: #fff !important;
.config-custom-form {
padding: 20px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.config-custom-select,
.config-custom-input {
width: calc(20vw - 80px) !important;
}
.custom-select-dropdown {
max-width: 200px;
/* 根据实际需求调整 */
} }
} }
.config-custom-btn { .gltext-content {
.el-button {
width: 92px;
height: 32px;
background-color: #08a7cf;
border-radius: 0;
border: 0;
color: #fff;
&:hover {
filter: brightness(1.3);
}
&:last-child {
background-color: #b98326;
}
}
}
}
.el-table__header-wrapper {
.el-checkbox {
display: none;
}
}
.config-custom-date {
.el-date-editor.el-input,
.el-date-editor.el-input__wrapper {
width: calc(20vw - 80px) !important;
}
}
.pagination {
width: 100%; width: 100%;
display: flex; height: 400px;
justify-content: left; border: solid 1px #064048;
margin-top: 10px; border-radius: 5px;
color: #fff;
}
:deep(.el-date-editor .el-range-separator) {
color: #fff !important;
} }
} }
</style> </style>

View File

@ -235,3 +235,56 @@ export async function importFile(
} }
// 127.0.0.1:9999/weatherData/getDataLine?startTime=2025-07-31 00:00:00&endTime=2025-07-31 18:00:00&longitude=1&latitude=1&dataType=1 // 127.0.0.1:9999/weatherData/getDataLine?startTime=2025-07-31 00:00:00&endTime=2025-07-31 18:00:00&longitude=1&latitude=1&dataType=1
/**
*
*/
export async function getPcValueBySjlx(
params: any,
callback?: (data: any) => void,
): Promise<any> {
const response = await request({
url: `${serverIp}${baseURL}/weatherData/getTimeBatch`,
method: 'GET',
params,
});
if (response.success && response.code === 200) {
callback?.(response);
}
}
/**
*
*/
export async function getZxglData(
params: any,
callback?: (data: any) => void,
): Promise<any> {
const response = await request({
url: `${serverIp}${baseURL}/weatherData/linkedData`,
method: 'PUT',
params,
});
if (response.success && response.code === 200) {
callback?.(response);
}
}
/**
*
*/
export async function getGlqxsjRzData(
params: any,
callback?: (data: any) => void,
): Promise<any> {
const response = await request({
url: `${serverIp}${baseURL}/weatherData/getLinkedDataLog`,
method: 'GET',
params,
});
if (response.success && response.code === 200) {
callback?.(response);
}
}