添加数据填报以及地图整理
This commit is contained in:
parent
6d1b71d783
commit
d0018bd4c8
@ -21,14 +21,19 @@
|
||||
"better-scroll": "^2.4.2",
|
||||
"dayjs": "^1.11.20",
|
||||
"default-passive-events": "^2.0.0",
|
||||
"dom-to-image": "^2.6.0",
|
||||
"echarts": "^5.2.2",
|
||||
"element-plus": "^2.2.27",
|
||||
"esri-leaflet": "^3.0.19",
|
||||
"js-base64": "^3.7.5",
|
||||
"js-cookie": "^3.0.1",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"jszip": "^3.10.1",
|
||||
"leaflet": "^1.9.4",
|
||||
"lodash": "^4.18.1",
|
||||
"moment": "^2.30.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"ol": "^10.8.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"pinia": "^2.0.12",
|
||||
@ -39,7 +44,8 @@
|
||||
"vue-router": "^4.1.6",
|
||||
"vuedraggable": "^2.24.3",
|
||||
"wujie": "^1.0.24",
|
||||
"wujie-vue3": "^1.0.24"
|
||||
"wujie-vue3": "^1.0.24",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^16.2.3",
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { ElConfigProvider } from 'element-plus';
|
||||
import { useAppStore,usetTheme } from '@/store/modules/app';
|
||||
import locale from "ant-design-vue/es/locale/zh_CN";
|
||||
|
||||
const appStore = useAppStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-config-provider :locale="appStore.locale" :size="appStore.size">
|
||||
<a-config-provider :theme="usetTheme">
|
||||
<a-config-provider :theme="usetTheme" :locale="locale">
|
||||
<router-view />
|
||||
</a-config-provider>
|
||||
</el-config-provider>
|
||||
|
||||
1134498
frontend/src/assets/geoJson.json
Normal file
1134498
frontend/src/assets/geoJson.json
Normal file
File diff suppressed because it is too large
Load Diff
241417
frontend/src/assets/geojson1.json
Normal file
241417
frontend/src/assets/geojson1.json
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 291 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 66 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 303 KiB |
@ -1,44 +1,60 @@
|
||||
<template>
|
||||
<div class="baselayer-switcher">
|
||||
<div class="baselayer-switcher" :style="{ right: drawerOpen ? '480px' : '12px' }">
|
||||
<div
|
||||
class="switcher-item"
|
||||
v-for="item in data"
|
||||
:key="item.name"
|
||||
:class="{ active: item.name === activeLayer }"
|
||||
@click="activeLayer = item.name"
|
||||
v-for="item in layers"
|
||||
:key="item.key"
|
||||
:class="{ active: item.key === activeKey }"
|
||||
@click="handleSwitch(item.key)"
|
||||
>
|
||||
<img :src="item.img" alt="" />
|
||||
<div class="label">{{ item.name }}</div>
|
||||
</div>
|
||||
<div class="nineSectionsImg">
|
||||
<img :src="nineSectionsImg" alt="" />
|
||||
<img :src="nineSectionsImg[activeKey]" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import mapShiliangtu from "@/assets/images/map-shiliangtu.png";
|
||||
import mapDixingtu from "@/assets/images/map-dixingtu.png";
|
||||
import mapYingxiangtu from "@/assets/images/map-yingxiangtu.png";
|
||||
import nineSectionsShiliang from "@/assets/images/nineSections-shiliang.png";
|
||||
import nineSectionsDixing from "@/assets/images/nineSections-dixing.png";
|
||||
import nineSectionsYingxiang from "@/assets/images/nineSections-yingxiang.png";
|
||||
const data = ref([
|
||||
{ name: "矢量", img: mapShiliangtu },
|
||||
{ name: "地形", img: mapDixingtu },
|
||||
{ name: "影像", img: mapYingxiangtu },
|
||||
]);
|
||||
const nineSectionsImg = ref(nineSectionsShiliang);
|
||||
const nineSectionsData = ref([
|
||||
{ name: "矢量", img: nineSectionsShiliang },
|
||||
{ name: "地形", img: nineSectionsDixing },
|
||||
{ name: "影像", img: nineSectionsYingxiang },
|
||||
]);
|
||||
const activeLayer = ref("矢量");
|
||||
import { useUiStore } from "@/store/modules/ui";
|
||||
import shiliangImg from "@/assets/images/map-shiliangtu.png";
|
||||
import dixingImg from "@/assets/images/map-dixingtu.png";
|
||||
import yingxiangImg from "@/assets/images/map-yingxiangtu.png";
|
||||
import nineSectionsShiliangImg from "@/assets/images/nineSections-shiliang.png";
|
||||
import nineSectionsDixingImg from "@/assets/images/nineSections-dixing.png";
|
||||
import nineSectionsYingxiangImg from "@/assets/images/nineSections-yingxiang.png";
|
||||
const props = defineProps({
|
||||
map: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
})
|
||||
const uiStore = useUiStore();
|
||||
const drawerOpen = ref(uiStore.drawerOpen);
|
||||
|
||||
watch(activeLayer, (val) => {
|
||||
nineSectionsImg.value =
|
||||
nineSectionsData.value.find((item) => item.name === val)?.img || "";
|
||||
// 监听 store 中的 drawerOpen 变化
|
||||
watch(
|
||||
() => uiStore.drawerOpen,
|
||||
(newVal) => {
|
||||
drawerOpen.value = newVal;
|
||||
}
|
||||
);
|
||||
const layers = [
|
||||
{ key: "s_province_boundaries", name: "矢量", img: shiliangImg },
|
||||
{ key: "BASEMAP-white", name: "地形", img: dixingImg },
|
||||
{ key: "BASEMAP-img", name: "影像", img: yingxiangImg },
|
||||
];
|
||||
const nineSectionsImg:any = {'s_province_boundaries':nineSectionsShiliangImg,'BASEMAP-white':nineSectionsDixingImg,'BASEMAP-img':nineSectionsYingxiangImg,}
|
||||
|
||||
const activeKey = ref(layers[0].key);
|
||||
const handleSwitch = (key: string) => {
|
||||
activeKey.value = key;
|
||||
props.map.baseLayerSwitcher(key);
|
||||
}
|
||||
watch(activeKey, (val) => {
|
||||
// nineSectionsImg.value =
|
||||
// nineSectionsData.value.find((item) => item.name === val)?.img || "";
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
275
frontend/src/components/BasicSearch/index.vue
Normal file
275
frontend/src/components/BasicSearch/index.vue
Normal file
@ -0,0 +1,275 @@
|
||||
<template>
|
||||
<div class="basic-search-container">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
layout="inline"
|
||||
class="basic-search-form"
|
||||
@finish="handleFinish"
|
||||
@values-change="handleValuesChange"
|
||||
>
|
||||
<div class="form-content-wrapper">
|
||||
<!-- 1. 搜索项 + 查询/重置按钮 (栅格布局) -->
|
||||
<a-row :gutter="[16, 0]" class="search-row" wrap>
|
||||
<a-col v-for="item in validSearchList" :key="item.name">
|
||||
<a-form-item
|
||||
:label="item.label"
|
||||
:name="item.name"
|
||||
style="width: 100%; margin-bottom: 0"
|
||||
>
|
||||
<!-- 普通日期选择器 -->
|
||||
<a-date-picker
|
||||
v-if="item.type === 'DataPicker'"
|
||||
v-model:value="formData[item.name]"
|
||||
:picker="item.picker"
|
||||
:format="item.fieldProps?.format"
|
||||
:value-format="item.fieldProps?.valueFormat"
|
||||
:disabled-date="item.fieldProps?.disabledDate"
|
||||
:show-time="item.fieldProps?.showTime"
|
||||
:allow-clear="item.fieldProps?.allowClear"
|
||||
:presets="item.presets"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<!-- 日期范围选择器 -->
|
||||
<a-range-picker
|
||||
v-else-if="item.type === 'RangePicker'"
|
||||
v-model:value="formData[item.name]"
|
||||
:picker="item.picker"
|
||||
:format="item.fieldProps?.format"
|
||||
:value-format="item.fieldProps?.valueFormat"
|
||||
:disabled-date="item.fieldProps?.disabledDate"
|
||||
:show-time="item.fieldProps?.showTime"
|
||||
:allow-clear="item.fieldProps?.allowClear"
|
||||
:presets="item.presets"
|
||||
style="width: 100%"
|
||||
/>
|
||||
|
||||
<!-- 普通输入框 -->
|
||||
<a-input
|
||||
v-else-if="!item.type || item.type === 'Input'"
|
||||
v-model:value="formData[item.name]"
|
||||
:placeholder="item.placeholder || '请输入'"
|
||||
:allow-clear="item.fieldProps?.allowClear"
|
||||
:style="{ width: item.width ? item.width + 'px' : '200px' }"
|
||||
/>
|
||||
<!-- 电站下拉框 -->
|
||||
<div class="flex gap-[10px]" v-else-if="item.type === 'waterStation'">
|
||||
<a-form-item-rest>
|
||||
<a-select
|
||||
:value="formData.stcd?.dataDimensionData"
|
||||
placeholder="请选择"
|
||||
@change="dataDimensionDataChange"
|
||||
style="width: 135px"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="opt in item.options"
|
||||
:key="opt.value"
|
||||
:value="opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<a-select
|
||||
:value="formData.stcd?.hbrvcd"
|
||||
placeholder="请选择河流"
|
||||
@change="hbrvcdChange"
|
||||
style="width: 135px"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="opt in item.options"
|
||||
:key="opt.value"
|
||||
:value="opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<a-select
|
||||
:value="formData.stcd?.stcdId"
|
||||
placeholder="请选择电站"
|
||||
@change="stcdIdChange"
|
||||
style="width: 135px"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="opt in item.options"
|
||||
:key="opt.value"
|
||||
:value="opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item-rest>
|
||||
</div>
|
||||
|
||||
<!-- 下拉选择 -->
|
||||
<a-select
|
||||
v-else-if="item.type === 'Select'"
|
||||
v-model:value="formData[item.name]"
|
||||
:placeholder="item.placeholder || '请选择'"
|
||||
:allow-clear="item.fieldProps?.allowClear"
|
||||
:style="{ width: item.width ? item.width + 'px' : '200px' }"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="opt in item.options"
|
||||
:key="opt.value"
|
||||
:value="opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<!-- 2. 查询/重置按钮列 (固定占据一部分宽度,例如 6) -->
|
||||
<a-col class="custom-action-col">
|
||||
<a-space>
|
||||
<a-button type="primary" html-type="submit" v-if="showSearch">
|
||||
查询
|
||||
</a-button>
|
||||
|
||||
<a-button v-if="showReset" @click="handleReset"> 重置 </a-button>
|
||||
<slot name="actions" :form="formRef" :values="formData">
|
||||
<!-- 默认无内容 -->
|
||||
</slot>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, reactive, watch, onMounted } from "vue";
|
||||
import type { FormInstance } from "ant-design-vue";
|
||||
|
||||
// --- 类型定义 ---
|
||||
export interface SearchItem {
|
||||
type?: "Input" | "Select" | "DataPicker" | string;
|
||||
name: string;
|
||||
label: string;
|
||||
picker?: "year" | "month" | "date" | "week";
|
||||
fieldProps?: any;
|
||||
placeholder?: string;
|
||||
span?: number;
|
||||
xlSpan?: number;
|
||||
width?: number;
|
||||
presets?: any[];
|
||||
options?: { label: string; value: any }[];
|
||||
component?: any;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
searchList: SearchItem[];
|
||||
initialValues?: any;
|
||||
showSearch?: boolean;
|
||||
showReset?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
initialValues: () => ({}),
|
||||
showSearch: true,
|
||||
showReset: true,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "finish", values: any): void;
|
||||
(e: "valuesChange", changedValues: any, allValues: any): void;
|
||||
(e: "reset"): void;
|
||||
}>();
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const formData = reactive<any>({});
|
||||
const rules = reactive<Record<string, any>>({});
|
||||
|
||||
// 2. 创建计算属性,自动过滤掉 false/null/undefined 的项
|
||||
const validSearchList = computed(() => {
|
||||
return props.searchList.filter(item => item);
|
||||
});
|
||||
// --- 初始化逻辑 ---
|
||||
const initForm = () => {
|
||||
const initial = JSON.parse(JSON.stringify(props.initialValues || {}));
|
||||
|
||||
// 1. 清空 formData
|
||||
Object.keys(formData).forEach((key) => delete formData[key]);
|
||||
|
||||
// 2. 赋值初始数据
|
||||
Object.assign(formData, initial);
|
||||
|
||||
// 3. 过滤掉 searchList 中为 false/null/undefined 的项,并生成规则
|
||||
|
||||
validSearchList.value.forEach((item) => {
|
||||
if (item.fieldProps?.required) {
|
||||
rules[item.name] = [
|
||||
{ required: true, message: `${item.label}不能为空`, trigger: "blur" },
|
||||
];
|
||||
}
|
||||
});
|
||||
};
|
||||
const dataDimensionDataChange = (value: any) => {
|
||||
formData.stcd.dataDimensionData = value;
|
||||
};
|
||||
const hbrvcdChange = (value: any) => {
|
||||
formData.stcd.hbrvcd = value;
|
||||
};
|
||||
const stcdIdChange = (value: any) => {
|
||||
formData.stcd.stcdId = value;
|
||||
};
|
||||
onMounted(() => {
|
||||
initForm();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.initialValues,
|
||||
(newVal) => {
|
||||
if (newVal && formRef.value) {
|
||||
formRef.value.setFieldsValue(newVal);
|
||||
Object.assign(formData, newVal);
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// --- 事件处理 ---
|
||||
const handleFinish = (values: any) => {
|
||||
emit("finish", values);
|
||||
};
|
||||
|
||||
const handleValuesChange = (changedValues: any, allValues: any) => {
|
||||
emit("valuesChange", changedValues, allValues);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
if (formRef.value) {
|
||||
formRef.value.resetFields();
|
||||
emit("reset");
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
form: formRef,
|
||||
reset: handleReset,
|
||||
submit: () => formRef.value?.submit(),
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.basic-search-container {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 24px;
|
||||
margin-inline-end: 0px;
|
||||
}
|
||||
/* 关键代码:让自定义按钮列自动占据剩余空间并靠右 */
|
||||
.custom-action-col {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
|
||||
/* 防止在小屏幕下挤压变形 */
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
@ -51,6 +51,7 @@ const handleToggle = () => {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
pointer-events: all;
|
||||
|
||||
.drawerController1 {
|
||||
width: 18px;
|
||||
|
||||
@ -38,11 +38,11 @@
|
||||
<a-date-picker v-model:value="datetimeValue" show-time
|
||||
:format="datetimePicker.format !== null ? datetimePicker.format : undefined"
|
||||
:picker="datetimePicker.picker" placeholder="请选择时间" style="width: 180px"
|
||||
@change="handleDateTimeChange" :locale="locale" />
|
||||
@change="handleDateTimeChange" />
|
||||
<!-- 修改为 locale 变量 -->
|
||||
</div>
|
||||
<div v-if="scopeDate.show">
|
||||
<a-range-picker v-model:value="scopeDateValue" :locale="locale" :picker="scopeDate.picker"
|
||||
<a-range-picker v-model:value="scopeDateValue" :picker="scopeDate.picker"
|
||||
:format="'YYYY-MM-DD'" :range-separator="' 至 '" style="width: 200px" />
|
||||
</div>
|
||||
</div>
|
||||
@ -60,14 +60,8 @@ import {
|
||||
InfoCircleOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import type { SelectProps } from 'ant-design-vue';
|
||||
// 引入中文语言包
|
||||
import zhCN from 'ant-design-vue/es/locale/zh_CN';
|
||||
// 导入 dayjs
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import 'dayjs/locale/zh-cn'; // 必须引入
|
||||
dayjs.locale('zh-cn'); // 设置全局语言
|
||||
console.log(dayjs().format('MMMM'));
|
||||
const locale = ref(zhCN);
|
||||
|
||||
// 定义类型接口
|
||||
interface PromptConfig {
|
||||
|
||||
367
frontend/src/components/gis/gisUtils.ts
Normal file
367
frontend/src/components/gis/gisUtils.ts
Normal file
@ -0,0 +1,367 @@
|
||||
// import { Session } from '@zebras/qgc-share/service/Session'
|
||||
import domtoimage from 'dom-to-image'
|
||||
import { offset2, drawDotImg2, drawDotImg1, offset1, drawDotImg3, offset3, drawDotImg5, offset5 } from '@/utils/GisUrlList'
|
||||
|
||||
/**
|
||||
* 根据镜头高度获取地图级别
|
||||
* @param {Number} height
|
||||
*/
|
||||
const A = 40487.57
|
||||
const B = 0.00007096758
|
||||
const C = 91610.74
|
||||
const D = -40467.74
|
||||
|
||||
/**
|
||||
* 图例数据转对象
|
||||
* @param data 图例数据
|
||||
* @returns 以nameEn为下标的图例数据
|
||||
*/
|
||||
export const legendData2Obj = (data: any[]) => {
|
||||
const _tempData = {}
|
||||
const f = (_data) => {
|
||||
_data.forEach((item) => {
|
||||
// childrenList有值表示这是一个分组
|
||||
if (item?.childrenList && item.childrenList?.length > 0) {
|
||||
f(item.childrenList)
|
||||
} else {
|
||||
if (item.nameEn.includes('_测试')) {
|
||||
item.nameEn = item.nameEn.split('_测试')[0]
|
||||
}
|
||||
_tempData[item.nameEn] = item
|
||||
}
|
||||
})
|
||||
}
|
||||
f(data)
|
||||
return _tempData
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加标签偏移量等属性到item中
|
||||
* @param item - 待添加属性的item
|
||||
* @param index - item的索引
|
||||
* @param labelType - 标签类型
|
||||
*/
|
||||
export const appendOffsetPropties = (item: any, index: any, labelType: number = 2) => {
|
||||
let drawDotImg = null
|
||||
let offset = null
|
||||
if (labelType == 2) {
|
||||
drawDotImg = drawDotImg1
|
||||
offset = offset1
|
||||
addItemProperty(item, index, drawDotImg, offset)
|
||||
} else if (labelType == 3) {
|
||||
drawDotImg = drawDotImg2
|
||||
offset = offset2
|
||||
addItemProperty(item, index, drawDotImg, offset)
|
||||
} else if (labelType == 4) {
|
||||
drawDotImg = drawDotImg3
|
||||
offset = offset3
|
||||
addItemProperty2(item, index, drawDotImg, offset)
|
||||
} else if (labelType == 5) {
|
||||
drawDotImg = drawDotImg5
|
||||
offset = offset5
|
||||
addItemProperty2(item, index, drawDotImg, offset)
|
||||
}
|
||||
}
|
||||
|
||||
const addItemProperty2 = (item: any, index: any, drawDotImg: any, offset: any) => {
|
||||
if (!drawDotImg || !offset) return
|
||||
item.icon_image = index % 2 !== 0 ? drawDotImg[item.anchoPointState]?.left || '' : drawDotImg[item.anchoPointState]?.right || ''
|
||||
item.text_anchor = index % 2 !== 0 ? 'right' : 'left'
|
||||
item.text_offset = offset[item.icon_image]?.text_offset ?? [-10, -1.8]
|
||||
item.text_offset2 = offset[item.icon_image]?.text_offset2 ?? [-10, -1.8]
|
||||
item.icon_offset = [offset[item.icon_image]?.icon_x ?? -200, offset[item.icon_image]?.icon_y ?? -50]
|
||||
item.icon_offset2 = offset[item.icon_image]?.icon_offset2 ?? [-200, -50]
|
||||
item.billboard_offset = [offset[item.icon_image]?.billboard_x ?? -80, offset[item.icon_image]?.billboard_y ?? -15]
|
||||
item.label_offset = offset[item.icon_image]?.labelOffset ?? [100, 41]
|
||||
}
|
||||
|
||||
export const addItemProperty = (item: any, index: any, drawDotImg: any, offset: any) => {
|
||||
if (!drawDotImg || !offset) return
|
||||
item.icon_image = index % 2 !== 0 ? drawDotImg[item.anchoPointState]?.left || '' : drawDotImg[item.anchoPointState]?.right || ''
|
||||
item.text_offset = [offset[item.icon_image]?.text_x ?? -10, offset[item.icon_image]?.text_y ?? -1.8]
|
||||
item.icon_offset = [offset[item.icon_image]?.icon_x ?? -200, offset[item.icon_image]?.icon_y ?? -50]
|
||||
item.billboard_offset = [offset[item.icon_image]?.billboard_x ?? -80, offset[item.icon_image]?.billboard_y ?? -15]
|
||||
item.label_offset = offset[item.icon_image]?.labelOffset ?? [100, 41]
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置popName属性
|
||||
* @param item - 待设置popName的item
|
||||
*/
|
||||
export const setPopName = (item: any) => {
|
||||
if (item.sttp === 'ENG') {
|
||||
item.popName = item.ennm || item.titleName;
|
||||
} else if (item.sttp === 'ylfb') {
|
||||
if (item?.ftp?.length > 20) {
|
||||
item.popName = item.ftp.slice(0, 20) + '...';
|
||||
} else {
|
||||
item.popName = item?.ftp;
|
||||
}
|
||||
} else if (item.sttp === 'WE_FISH') {
|
||||
item.popName = item.fishList?.[0]?.fishName + `(${item.fishList?.[0]?.ptypeName})`
|
||||
item.popName1 = item.total + "尾"
|
||||
} else {
|
||||
item.popName = item.stnm || item.titleName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图例配置中选中项的key
|
||||
* @param data - 图例配置数据
|
||||
* @returns 选中项的key数组
|
||||
*/
|
||||
export const getCheckedLayerConfigs = (data: any[]): any[] => {
|
||||
const rs: any[] = []
|
||||
const f = (arr: any[] = []) => {
|
||||
let count = 0
|
||||
arr.forEach((item: any) => {
|
||||
if (item?.children && item.children.length > 0) {
|
||||
const childrenCount = f(item.children)
|
||||
if (childrenCount) {
|
||||
rs.push(item.key)
|
||||
}
|
||||
} else if (item.checked) {
|
||||
count++
|
||||
rs.push(item.key) // 对应图例的layerCode
|
||||
}
|
||||
})
|
||||
return count
|
||||
}
|
||||
f(data)
|
||||
return rs
|
||||
}
|
||||
|
||||
// type mapType = "" | "pointMap" | "gisLayer"
|
||||
|
||||
/**
|
||||
* 将图层配置数据转换为一维数组
|
||||
* @param data - 图层配置数据
|
||||
* @returns 一维数组
|
||||
*/
|
||||
export const layerConfig2Flat = (data: any): any[] => {
|
||||
const rs: any[] = []
|
||||
const f = (arr: any[] = []) => {
|
||||
arr.forEach((item: any) => {
|
||||
const { type } = item
|
||||
if (type) {
|
||||
if (type == 'GISMap') {
|
||||
if (item?.children && item.children.length > 0) {
|
||||
f(item.children)
|
||||
} else {
|
||||
rs.push(item)
|
||||
}
|
||||
} else if (type == 'pointMap') {
|
||||
if (item.url || item.title === '国家水文站' || item.title === '自建水文站') {
|
||||
rs.push(item)
|
||||
} else if (item.children.length > 0) {
|
||||
f(item.children)
|
||||
}
|
||||
}
|
||||
} else if (item.children.length > 0) {
|
||||
f(item.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
f(data)
|
||||
return rs
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取基础图层配置
|
||||
* @param config - 接口获取的配置数据
|
||||
* @returns 基础图层配置
|
||||
*/
|
||||
export const getMapConfig = (config) => {
|
||||
// const mapBaseUrls = MemoryCache.get('mapBaseUrls') || {}
|
||||
// const r = { ...config }
|
||||
// const { urlType } = r
|
||||
// const baseUrlObj = mapBaseUrls[urlType as string]
|
||||
// if (baseUrlObj) {
|
||||
// r.url = baseUrlObj.url + r.url // 'http://localhost:8088'
|
||||
// r.url_3d = baseUrlObj.url + r.url_3d
|
||||
// // r.url = baseUrlObj.url + r.url //baseUrlObj.url
|
||||
// // r.url_3d = baseUrlObj.url + r.url_3d
|
||||
// if (r.geojson_url) {
|
||||
// r.geojson_url = baseUrlObj.url + r.geojson_url + `&token=${Session.getToken()?.accessToken}`
|
||||
// }
|
||||
// }
|
||||
// return r
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置地图元素位置
|
||||
*/
|
||||
export const resetMapElPos = () => {
|
||||
const legend = document.querySelector('#qgc-legendtl') as HTMLElement // 图例
|
||||
const filter = document.querySelector('#map-filter-container') as HTMLElement // 全局表单
|
||||
const controller = document.querySelector('#map-controller') as HTMLElement // 地图工具栏
|
||||
const baselayer = document.querySelector('#map-baselayer') as HTMLElement // 底图模式切换
|
||||
|
||||
if (legend) {
|
||||
legend.style.left = '0'
|
||||
legend.style.bottom = '0'
|
||||
}
|
||||
if (controller) {
|
||||
controller.style.right = '480px'
|
||||
controller.style.bottom = '114px'
|
||||
}
|
||||
if (baselayer) {
|
||||
baselayer.style.right = '480px'
|
||||
baselayer.style.bottom = '20px'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据位置获取布局数据列表
|
||||
* @param data - 布局数据
|
||||
* @param position - 位置
|
||||
* @returns 布局数据列表
|
||||
*/
|
||||
const getListByPosition = (data, position: string) => data?.data?.filter((el) => el.position === position && el.code)
|
||||
|
||||
/**
|
||||
* 设置地图组件位置
|
||||
* @param layoutType - 布局类型
|
||||
* @param data - 布局数据
|
||||
* @param offset - 偏移量
|
||||
*/
|
||||
export const setMapLegendPos = (layoutType: string, data, offset: number = 456) => {
|
||||
const menuStateString = localStorage.getItem('menuState'); //处理澜沧江左侧菜单状态
|
||||
const menuState = menuStateString !== null ? JSON.parse(menuStateString) : true;
|
||||
const _theme = localStorage.getItem("ly-theme") || window.__lyConfigs?.theme
|
||||
const leftEle = document.querySelector('#page-layout-left') as HTMLElement
|
||||
const rightEle = document.querySelector('#page-layout-right') as HTMLElement
|
||||
const bottomEle = document.querySelector('#page-layout-bottom') as HTMLElement
|
||||
const legend = document.querySelector('#qgc-legendtl') as HTMLElement // 图例
|
||||
const filter = document.querySelector('#map-filter-container') as HTMLElement // 全局表单
|
||||
const compassControl = document.querySelector('#map-compassControl') as HTMLElement // 全局表单
|
||||
const controller = document.querySelector('#map-controller') as HTMLElement // 地图工具栏
|
||||
const monitor = document.querySelector('#map-monitor') as HTMLElement // 地图工具栏
|
||||
const baselayer = document.querySelector('#map-baselayer') as HTMLElement // 底图模式切换
|
||||
const vd = document.querySelector('#vd_operate') as HTMLElement // 底部视频
|
||||
const left = ['layout1', 'layout2', 'layout3', 'layout4', 'layout6', 'layout7', 'layout8', 'layout9', 'layout10', 'layout11', 'layout14', 'layout15', 'layout16', 'layout17'] // 左侧布局
|
||||
const right = ['layout1', 'layout2', 'layout3', 'layout4', 'layout5', 'layout6', 'layout8', 'layout10', 'layout11', 'layout15', 'layout16', 'layout17'] // 右侧布局
|
||||
const bottom1 = ['layout1', 'layout6', 'layout8', 'layout9', 'layout10', 'layout16'] // 三行底部布局
|
||||
const bottom2 = ['layout2', 'layout15'] // 四行底部布局
|
||||
const w = `${offset}px`
|
||||
const l = `${_theme === 'ly-8' ? menuState ? 643 : 510 : offset}px`
|
||||
let b = `0px`
|
||||
const le = ['layout17', 'layout10'].includes(layoutType) ? 0 : 1
|
||||
const leftList = getListByPosition(data, 'left')
|
||||
const rightList = getListByPosition(data, 'right')
|
||||
const bottomList = getListByPosition(data, 'bottom')
|
||||
let bottom = '0'
|
||||
|
||||
if (_theme === 'ly-8') {
|
||||
if (window.__mapMode === '3D') {
|
||||
b = `200px`
|
||||
} else {
|
||||
b = `${menuState ? 200 : 50}px`
|
||||
}
|
||||
}
|
||||
|
||||
if (bottomList?.length > 0) {
|
||||
if (bottom1.includes(layoutType)) {
|
||||
bottom = 'calc((100% - 16px) / 3 + 8px)'
|
||||
}
|
||||
if (bottom2.includes(layoutType)) {
|
||||
bottom = 'calc((100% - 24px) / 4 + 8px)'
|
||||
}
|
||||
} else {
|
||||
// 没有底部布局时,底部高度为
|
||||
bottom = '28px'
|
||||
}
|
||||
|
||||
const rle = ['layout6'].includes(layoutType) || bottom != '28px' ? 0 : 1
|
||||
|
||||
const leftHide = leftEle?.classList?.contains('hide')
|
||||
const rightHide = rightEle?.classList?.contains('hide')
|
||||
const bottomHide = bottomEle?.classList?.contains('hide')
|
||||
if (legend) {
|
||||
legend.style.left = !leftHide && left.includes(layoutType) && leftList?.length > le ? l : b
|
||||
legend.style.bottom = bottomHide ? '0' : bottomList?.length > 0 ? bottom : '12px'
|
||||
}
|
||||
if (filter) {
|
||||
if (layoutType === 'layout10') {
|
||||
filter.style.left = !leftHide && left.includes(layoutType) && leftList?.length > 1 ? l : b
|
||||
} else {
|
||||
filter.style.left = !leftHide && left.includes(layoutType) && leftList?.length > 0 && layoutType !== 'layout17' ? l : b
|
||||
}
|
||||
}
|
||||
if (compassControl) {
|
||||
if (layoutType === 'layout10') {
|
||||
compassControl.style.left = !leftHide && left.includes(layoutType) && leftList?.length > 1 ? l : b
|
||||
} else {
|
||||
compassControl.style.left = !leftHide && left.includes(layoutType) && leftList?.length > 0 && layoutType !== 'layout17' ? l : b
|
||||
}
|
||||
}
|
||||
if (controller) {
|
||||
controller.style.right = !rightHide && right.includes(layoutType) && rightList?.length > rle ? w : '0'
|
||||
controller.style.bottom = bottomHide ? '0' : bottom
|
||||
}
|
||||
if (monitor) {
|
||||
monitor.style.right = !rightHide && right.includes(layoutType) && rightList?.length > rle ? w : '0'
|
||||
// monitor.style.bottom = bottomHide ? '0' : bottom
|
||||
}
|
||||
if (baselayer) {
|
||||
baselayer.style.right = !rightHide && right.includes(layoutType) && rightList?.length > rle ? `calc(${w} + 60px)` : '60px'
|
||||
baselayer.style.bottom = bottomHide ? '0' : bottom
|
||||
}
|
||||
}
|
||||
|
||||
export const altitudeToZoom = (height: number) => {
|
||||
const lv = Math.round(D + (A - D) / (1 + Math.pow(Number(height) / C, B))) + 1
|
||||
return lv > -1 ? lv : 0
|
||||
}
|
||||
/**
|
||||
* 根据地图级别获取镜头高度
|
||||
* @param {Number} zoom
|
||||
*/
|
||||
export const zoomToAltitude = (zoom: number) => {
|
||||
return Math.round(C * Math.pow((A - D) / (zoom - D) - 1, 1 / B))
|
||||
}
|
||||
|
||||
export const mapOutPut = (imageUrl: string) => {
|
||||
const canvas = document.createElement('canvas')
|
||||
const downloadElement = document.createElement('a')
|
||||
const mapElem = document.getElementById('mapContainer')
|
||||
if (mapElem == null) {
|
||||
return
|
||||
}
|
||||
const context = canvas.getContext('2d')!
|
||||
canvas.width = mapElem.offsetWidth
|
||||
canvas.height = mapElem.offsetHeight
|
||||
const image = new Image()
|
||||
image.src = imageUrl
|
||||
image.onload = () => {
|
||||
context.drawImage(image, 0, 0)
|
||||
const elem: any = document.getElementById('qgc-legendtl')
|
||||
const l = elem?.style?.left === '' ? 0 : parseInt(elem?.style?.left)
|
||||
if (elem) {
|
||||
domtoimage
|
||||
.toPng(elem, {
|
||||
quality: 1.0,
|
||||
width: elem.offsetWidth + (l + 24),
|
||||
height: mapElem.offsetHeight - 20
|
||||
})
|
||||
.then((legendUrl: string) => {
|
||||
const image = new Image()
|
||||
image.src = legendUrl
|
||||
image.width = elem.offsetWidth
|
||||
image.onload = () => {
|
||||
context.drawImage(image, 0, 0)
|
||||
downloadElement.href = canvas.toDataURL('image/png')
|
||||
downloadElement.download = '地图截图'
|
||||
downloadElement.click()
|
||||
}
|
||||
})
|
||||
.catch((e: any) => {
|
||||
console.log('e', e)
|
||||
})
|
||||
} else {
|
||||
downloadElement.href = canvas.toDataURL('image/png')
|
||||
downloadElement.download = 'download'
|
||||
downloadElement.click()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,21 @@
|
||||
import type { layer, MapInterface } from "./map.d";
|
||||
import { MapLeaflet } from "./map.leaflet";
|
||||
import { MapOl } from "./map.ol";
|
||||
|
||||
interface MapClassInterface extends MapInterface {
|
||||
layers: Map<string, layer>;
|
||||
view: any;
|
||||
}
|
||||
|
||||
//描点参数
|
||||
export type MDOptions = {
|
||||
isAllowOverlap?: boolean; // 是否允许重叠
|
||||
labelType?: number; // 标签类型
|
||||
labelminZoom?: number; // 标签最小缩放级别
|
||||
labelAltitude?: object; // 标签海拔
|
||||
isRemove?: boolean; // 是否移除标签
|
||||
};
|
||||
|
||||
export const mapServerBaseUrl = localStorage.getItem("gisurl") || "http://210.72.227.199:18084/";
|
||||
|
||||
export class MapClass implements MapClassInterface {
|
||||
@ -14,16 +24,90 @@ export class MapClass implements MapClassInterface {
|
||||
private static instance: MapClass;
|
||||
private service: MapInterface;
|
||||
|
||||
constructor() {
|
||||
this.layers = new Map();
|
||||
this.view = null;
|
||||
// this.service = new MapLeaflet();
|
||||
this.service = new MapOl();
|
||||
}
|
||||
static getInstance(): MapClass {
|
||||
if (!this.instance) {
|
||||
this.instance = new MapClass();
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
// 地图初始化
|
||||
init(container: HTMLElement, rectangle?: any): Promise<any> {
|
||||
return this.service.init(container, rectangle).then((map) => {
|
||||
this.view = map;
|
||||
return map;
|
||||
});
|
||||
}
|
||||
// 基地面板控制
|
||||
jdPanelControlShowAndHidden(baseid: String, isAll: boolean): void {
|
||||
this.service.jdPanelControlShowAndHidden(baseid, isAll)
|
||||
}
|
||||
mdLayerShowOrHidden(layerType: String, key?: String, baseid: String, checked: boolean, isAll: boolean): void {
|
||||
this.service.mdLayerShowOrHidden(layerType, key, baseid, checked, isAll)
|
||||
}
|
||||
// 添加基础数据图层
|
||||
addBaseDataLayer(layer: any): void {
|
||||
return this.service.addBaseDataLayer(layer)
|
||||
}
|
||||
// 基础图层显示影隐藏方法
|
||||
controlBaseLayerTreeShowAndHidden(layerType: String, key: String, checked: boolean) {
|
||||
this.service.controlBaseLayerTreeShowAndHidden(layerType, key, checked)
|
||||
}
|
||||
// 图层树控制描点数据显示隐藏方法
|
||||
mdLayerTreeShowOrHidden(layerType: String, checked?: boolean) {
|
||||
this.service.mdLayerTreeShowOrHidden(layerType, checked)
|
||||
}
|
||||
// 初始化加载描点数据
|
||||
addInitDataLayer = (pointData: any[], layerType: any, mdoptions?: MDOptions, legendArray?: any) => {
|
||||
return this.service.addInitDataLayer(pointData, layerType, mdoptions, legendArray)
|
||||
}
|
||||
//切换底图
|
||||
baseLayerSwitcher(key: string): void {
|
||||
this.service.baseLayerSwitcher(key)
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.layers = new Map();
|
||||
this.view = null;
|
||||
this.service = new MapLeaflet();
|
||||
// 添加梯级流域图
|
||||
addTertiarybasinLayer(layer: layer, fillcolor: any, outlineColor: any, datas: any): void {
|
||||
this.service.addTertiarybasinLayer(layer, fillcolor, outlineColor, datas)
|
||||
}
|
||||
// 移除梯级流域图
|
||||
removeTertiarybasinLayer(layer: layer): void {
|
||||
this.service.removeTertiarybasinLayer?.(layer)
|
||||
}
|
||||
// 缩放
|
||||
zoomToggle(type: 'out' | 'in') {
|
||||
this.service.zoomToggle(type)
|
||||
}
|
||||
/**
|
||||
* 长度量算
|
||||
*/
|
||||
lengthCalculate(): void {
|
||||
this.service.lengthCalculate()
|
||||
}
|
||||
/**
|
||||
* 面积量算
|
||||
*/
|
||||
areCalculate(): void {
|
||||
this.service.areCalculate()
|
||||
}
|
||||
/**
|
||||
* 移除量算结果
|
||||
*/
|
||||
removeQueryLayer(): void {
|
||||
this.service.removeQueryLayer()
|
||||
}
|
||||
// 地图打印
|
||||
mapOutPut() {
|
||||
this.service.mapOutPut()
|
||||
}
|
||||
// 销毁地图
|
||||
destroy(): void {
|
||||
this.service.destroy()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
287
frontend/src/components/gis/map.d.ts
vendored
287
frontend/src/components/gis/map.d.ts
vendored
@ -1,3 +1,5 @@
|
||||
|
||||
import { MDOptions } from './map.class'
|
||||
export type layerType =
|
||||
| "markers"
|
||||
| "tiledMap"
|
||||
@ -19,214 +21,171 @@ export type layerOption = {
|
||||
};
|
||||
|
||||
export interface layer {
|
||||
id: string;
|
||||
key: string;
|
||||
_layer?: any;
|
||||
type?: layerType;
|
||||
url?: string;
|
||||
urlThd?: string;
|
||||
label?: string;
|
||||
thumbnail?: string;
|
||||
visible?: boolean;
|
||||
option?: layerOption;
|
||||
tempobj?: any;
|
||||
opacity?: any;
|
||||
id: string
|
||||
key: string
|
||||
_layer?: any
|
||||
type?: layerType
|
||||
url?: string
|
||||
url_3d?: string
|
||||
geojson_url: string
|
||||
label?: string
|
||||
thumbnail?: string
|
||||
visible?: boolean
|
||||
option?: layerOption
|
||||
tempobj?: any
|
||||
layers?: any
|
||||
rasteropacity?: any
|
||||
imgUrl?: any
|
||||
minZoom?: any
|
||||
maxZoom?: any
|
||||
minHeight?: number
|
||||
maxHeight?: number
|
||||
/** layer 类型 */
|
||||
layerType?: string
|
||||
matrixIds_index?: string[]
|
||||
tileMatrixSetID?: string
|
||||
}
|
||||
|
||||
export interface MapInterface {
|
||||
getRainColor: any;
|
||||
getRainColor1: any;
|
||||
removeContentmarkers: any;
|
||||
removeGanliumarkers: any;
|
||||
addarcgisLayerWithNotLine: any
|
||||
/**
|
||||
* 地图初始化
|
||||
* @param container DOM容器
|
||||
* @return any 地图视图
|
||||
*/
|
||||
init(container: React.ReactNode, rectangle?: any, center?: any): Promise<any>;
|
||||
init(container: HTMLElement, rectangle?: any, center?: any, altitude?: number, bearing?: number): Promise<any>
|
||||
|
||||
/**
|
||||
*添加 图层
|
||||
* 初始化加载描点数据
|
||||
* @param pointData
|
||||
* @param layerType
|
||||
*/
|
||||
addLayer(layer: layer): any;
|
||||
addInitDataLayer(pointData: any[], layerType: any, mdoptions: MDOptions): void
|
||||
|
||||
/**
|
||||
* 添加canvasMarker
|
||||
*/
|
||||
addCanvasMarker(
|
||||
data: Array<any>,
|
||||
legend?: Array<any>,
|
||||
option?: { skipRemoveHtml?: boolean },
|
||||
flag?: any
|
||||
): any;
|
||||
|
||||
invalidateSize(): void;
|
||||
|
||||
getRain(data: any): void;
|
||||
/**
|
||||
* 添加地图缩放监听事件
|
||||
*/
|
||||
addZoomEvent(func: Function, type?: string): void;
|
||||
|
||||
/**
|
||||
* 添加鼠标移动事件
|
||||
* @param func 事件
|
||||
*/
|
||||
addMouseMoveEvent(func: Function): void;
|
||||
|
||||
/**
|
||||
* 添加鼠标点击事件
|
||||
* @param func 事件
|
||||
*/
|
||||
addMouseClickEvent(func: Function): void;
|
||||
|
||||
/**
|
||||
* 定位
|
||||
* @param data
|
||||
*/
|
||||
fitBounds(data: any): void;
|
||||
|
||||
/**
|
||||
* 获取所有的图层
|
||||
*/
|
||||
getLayers(): Map<string, layer> | null;
|
||||
|
||||
/**
|
||||
* 获取地图缩放级别
|
||||
*/
|
||||
getMapZoom(): number;
|
||||
|
||||
/**
|
||||
* 放大
|
||||
*/
|
||||
zoomEnlarge(): number;
|
||||
|
||||
/**
|
||||
* 缩小
|
||||
*/
|
||||
zoomNarrow(): number;
|
||||
|
||||
/**
|
||||
* 获取当前地图视图的比例尺或者缩放级别
|
||||
*/
|
||||
getMapZoomOrScale(): { scale?: number; zoom?: number };
|
||||
|
||||
/**
|
||||
* 显示图层
|
||||
* @param layers
|
||||
*/
|
||||
showLayer(id: string): void;
|
||||
|
||||
/**
|
||||
* 隐藏图层
|
||||
* @param layers
|
||||
*/
|
||||
hideLayer(id: string): void;
|
||||
|
||||
/**
|
||||
* 设置地图位置
|
||||
* @param position
|
||||
*/
|
||||
setCenter(position: { lat: number; lng: number }, zoom?: any): void;
|
||||
|
||||
/**
|
||||
* 移除所有图层
|
||||
*/
|
||||
removeAllLayer(): void;
|
||||
|
||||
/**
|
||||
* 移除所有站点标签
|
||||
*/
|
||||
removeAllLabel(): void;
|
||||
|
||||
/**
|
||||
* 移除图层
|
||||
* 初始化加载基础图层
|
||||
* @param layer
|
||||
*/
|
||||
removeLayer(layer: layer): void;
|
||||
addBaseDataLayer(layer: any): void
|
||||
|
||||
/**
|
||||
* 重新渲染图层数据
|
||||
* 切换底图
|
||||
* @param layer
|
||||
*/
|
||||
reRenderLayer(
|
||||
key: string,
|
||||
options?: { opacity?: number; data: Array<any>; zIndex?: number }
|
||||
): Promise<any>;
|
||||
baseLayerSwitcher(key: string): void
|
||||
|
||||
/**
|
||||
* 移除鼠标移动事件监听
|
||||
* 切换2D或者3D视图
|
||||
* @param type '2D' | '3D'
|
||||
*/
|
||||
removeMouseMoveEvent(): void;
|
||||
|
||||
/**
|
||||
* 移除监听事件
|
||||
*/
|
||||
removeEvent(type: string, func: Function): void;
|
||||
|
||||
/**
|
||||
* 移除要素图层
|
||||
*/
|
||||
removeFeatureLayer(): void;
|
||||
|
||||
/**
|
||||
* 移除水系图层
|
||||
*/
|
||||
removedataSource(item: any): void;
|
||||
|
||||
/**
|
||||
* 移除鼠标点击事件监听
|
||||
*/
|
||||
removeMouseClickEvent(): void;
|
||||
switchView(type: '2D' | '3D'): any | null
|
||||
|
||||
/**
|
||||
* 缩放
|
||||
* out 放大
|
||||
* in 缩小
|
||||
* out 缩小
|
||||
* in 放大
|
||||
*/
|
||||
zoomToggle(type: "out" | "in"): void;
|
||||
zoomToggle(type: 'out' | 'in'): void
|
||||
|
||||
/**
|
||||
* 增加arcgis图层
|
||||
* 地图输出打印
|
||||
*/
|
||||
addarcgisLayer(item: any, data?: any): void;
|
||||
mapOutPut(): void
|
||||
|
||||
//飞行到指定的点
|
||||
fitBounds(bbox,bearing):void
|
||||
flyTopanto(positon,zoom,stcd):void
|
||||
/**
|
||||
* 加载倾斜摄影数据
|
||||
* @param HH3DUrlArray
|
||||
*/
|
||||
addQxsyLayer(HH3DUrlArray: siteItem): void
|
||||
|
||||
/**
|
||||
* 增加图层label
|
||||
* 移除倾斜摄影数据
|
||||
* @param HH3DUrlArray
|
||||
*/
|
||||
addmapLabel(item: any, data: any, zoom: any): void;
|
||||
addmapLine(item: any, data: any): void;
|
||||
removeQxsyLayer(HH3DUrlArray: siteItem): void
|
||||
|
||||
/**
|
||||
* 删除arcgis图层
|
||||
* 倾斜摄影定位
|
||||
* @param HH3DUrlArray
|
||||
*/
|
||||
removearcgisLayer(): void;
|
||||
qxsyToPosition(HH3DUrlArray: siteItem): void
|
||||
|
||||
/**
|
||||
* 删除图层label
|
||||
* 倾斜摄影裁剪
|
||||
* @param HH3DUrlArray
|
||||
*/
|
||||
removearcgisLabel(): void;
|
||||
|
||||
removeLayermarkers(): void;
|
||||
|
||||
removeRainLayer(): void;
|
||||
|
||||
removeGroupLayer(): void;
|
||||
|
||||
removeMapLayer(): void;
|
||||
qxsyClipBoundary(HH3DUrlArray: siteItem): void
|
||||
|
||||
/**
|
||||
* 测距
|
||||
* 移除倾斜摄影裁剪
|
||||
* @param HH3DUrlArray
|
||||
*/
|
||||
handelstartDrawLine(): void;
|
||||
removeQxsyClipBoundary(HH3DUrlArray: siteItem): void
|
||||
|
||||
/**
|
||||
* 测面积
|
||||
* 基地面板控制
|
||||
* @param baseid
|
||||
* @param isAll
|
||||
*/
|
||||
handelstartDrawPolygon(): void;
|
||||
jdPanelControlShowAndHidden(baseid: String, isAll: boolean): void
|
||||
/**
|
||||
* 图例和基地面板控制描点数据显示隐藏方法
|
||||
* @param layerType
|
||||
* @param key
|
||||
* @param checked
|
||||
* @param isAll
|
||||
*/
|
||||
mdLayerShowOrHidden(layerType: String, key?: String, baseid: String, checked: boolean, isAll: boolean): void
|
||||
|
||||
/**
|
||||
* 清除
|
||||
* 基础图层显示影隐藏方法
|
||||
* @param layerType
|
||||
* @param checked
|
||||
*/
|
||||
handelclearLayer(): void;
|
||||
controlBaseLayerTreeShowAndHidden(layerType: String, key: String, checked: boolean): void
|
||||
|
||||
/**
|
||||
* 图层树控制描点数据显示隐藏方法
|
||||
* @param layerType
|
||||
* @param checked
|
||||
*/
|
||||
mdLayerTreeShowOrHidden(layerType: String, checked?: boolean): void
|
||||
|
||||
/**
|
||||
* 长度量算
|
||||
*/
|
||||
lengthCalculate(): void
|
||||
/**
|
||||
* 面积量算
|
||||
*/
|
||||
areCalculate(): void
|
||||
|
||||
/**
|
||||
* 移除量算结果
|
||||
*/
|
||||
removeQueryLayer(): void
|
||||
/**
|
||||
* 添加梯级流域图
|
||||
* @param layer
|
||||
* @param fillcolor
|
||||
*/
|
||||
addTertiarybasinLayer(layer: layer, fillcolor: any, outlineColor: any, datas: any): void
|
||||
|
||||
/**
|
||||
* 移除梯级流域图
|
||||
* @param layer
|
||||
*/
|
||||
removeTertiarybasinLayer(layer: layer): void
|
||||
/**
|
||||
* 移除地图对象
|
||||
*/
|
||||
destroy(): void
|
||||
|
||||
setVisibleDistanceOfLable(): void
|
||||
|
||||
recoverVisibleDistanceOfLable(): viod
|
||||
|
||||
changeMaskStyle(options?: any)
|
||||
}
|
||||
|
||||
@ -2,19 +2,14 @@
|
||||
<a-tooltip placement="left" trigger="click" color="transparent">
|
||||
<template #title>
|
||||
<div class="calculate">
|
||||
<a-tooltip title="测距" placement="top">
|
||||
<div class="map-controller-item">
|
||||
<i class="icon iconfont icon-ranging"></i>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="测面积" placement="top">
|
||||
<div class="map-controller-item">
|
||||
<i class="icon iconfont icon-measure"></i>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="清除" placement="top">
|
||||
<div class="map-controller-item">
|
||||
<i class="icon iconfont icon-clear"></i>
|
||||
<a-tooltip
|
||||
v-for="(item, index) in tools"
|
||||
:key="item.title"
|
||||
:title="item.title"
|
||||
placement="top"
|
||||
>
|
||||
<div class="map-controller-item" :class="{'active': active === index}" @click="calculateClick(index)">
|
||||
<i :class="`icon iconfont icon-${item.icon}`"></i>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
@ -26,8 +21,34 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
const calculateClick = () => {
|
||||
console.log("点击了测量工具");
|
||||
const props = defineProps<{
|
||||
map: any;
|
||||
}>();
|
||||
const map = props.map;
|
||||
const active = ref(-1);
|
||||
const tools = ref([
|
||||
{ title: "测距", icon: "ranging" },
|
||||
{ title: "测面积", icon: "measure" },
|
||||
{ title: "清除", icon: "clear" },
|
||||
]);
|
||||
const calculateClick = (index: number) => {
|
||||
console.log("点击了测量工具", index);
|
||||
active.value = index;
|
||||
switch (index) {
|
||||
case 0:
|
||||
map.lengthCalculate();
|
||||
break;
|
||||
case 1:
|
||||
map.areCalculate();
|
||||
break;
|
||||
case 2:
|
||||
active.value = -1;
|
||||
map.removeQueryLayer();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@ -57,5 +78,8 @@ const calculateClick = () => {
|
||||
cursor: pointer;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.active {
|
||||
color: #005292 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
<template #title>
|
||||
<div class="layers-tree">
|
||||
<div class="layers-tree-title">图层管理</div>
|
||||
|
||||
<div class="layers-tree-content">
|
||||
<a-Tree
|
||||
checkable
|
||||
@ -84,7 +83,7 @@ const onCheck = (v: any, e: any) => {
|
||||
const getCheckedKeys = () => {
|
||||
const name = route.path.split("/")[2];
|
||||
if (name === "shuiDianKaiFaZhuangKuang") {
|
||||
checkedKeys.value = ["customBaseLayer", "eng", "eng_point"];
|
||||
checkedKeys.value = ["customBaseLayer","powerBaseStation", "eng", "eng_point"];
|
||||
}
|
||||
mapStore.updateLayerData(checkedKeys.value);
|
||||
};
|
||||
|
||||
@ -30,6 +30,35 @@ export const hasPerm: Directive = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查当前用户是否拥有指定权限
|
||||
* @param perms 权限标识数组,例如 ['sjtb:import-zip']
|
||||
* @returns boolean
|
||||
*/
|
||||
export function checkPerm(perms: string[]): boolean {
|
||||
const userStore = useUserStoreHook();
|
||||
|
||||
// 1. 超级管理员拥有所有权限
|
||||
if (userStore.roles.includes('超级管理员')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. 校验普通权限
|
||||
// 只要用户拥有的权限列表中,包含传入列表中的任意一个,即返回 true
|
||||
// 注意:原指令逻辑是 requiredPerms.includes(perm),即用户拥有的 perm 必须在要求列表中
|
||||
// 通常业务逻辑是:要求的权限列表中的某一个在用户拥有的权限列表中即可
|
||||
// 这里沿用你原指令的逻辑:requiredPerms (传入值) 包含 userPerms (用户拥有的)
|
||||
|
||||
if (!perms || perms.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hasPermission = userStore.perms?.some(userPerm => {
|
||||
return perms.includes(userPerm);
|
||||
});
|
||||
|
||||
return !!hasPermission;
|
||||
}
|
||||
/**
|
||||
* 角色权限校验
|
||||
*/
|
||||
|
||||
@ -39,6 +39,6 @@ const routeKey = computed(() => router.path + Math.random());
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 2000;
|
||||
z-index: 900;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -39,30 +39,9 @@ function logout() {
|
||||
});
|
||||
});
|
||||
}
|
||||
const badgeval = ref(0);
|
||||
const isbadge = ref(true);
|
||||
var source = new EventSource(url + `/sse/connect/` + getToken());
|
||||
onMounted(() => {
|
||||
if ("EventSource" in window) {
|
||||
source.onmessage = function (e) {
|
||||
if (e.data > 0) {
|
||||
badgeval.value = e.data;
|
||||
isbadge.value = false;
|
||||
} else {
|
||||
isbadge.value = true;
|
||||
}
|
||||
};
|
||||
source.onopen = function (e) {};
|
||||
source.onerror = function (e: any) {
|
||||
if (e.readyState == EventSource.CLOSED) {
|
||||
} else {
|
||||
}
|
||||
};
|
||||
} else {
|
||||
}
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
source.close();
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -74,7 +53,7 @@ onBeforeUnmount(() => {
|
||||
href="/"
|
||||
class="h-[50px] min-w-[350px] flex items-center justify-center text-white"
|
||||
>
|
||||
<h1 class="text-blank font-bold fontSize-16">{{ t("login.title") }}</h1></a
|
||||
<h1 class="text-blank font-bold text-[16px]">{{ t("login.title") }}</h1></a
|
||||
>
|
||||
</transition>
|
||||
<Sidebar />
|
||||
|
||||
@ -33,9 +33,6 @@ const handleSubTabChange = (key: string) => {
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
console.log(route)
|
||||
console.log(route)
|
||||
console.log(permissionStore.routes)
|
||||
permissionStore.routes.map((item: any) => {
|
||||
if (item.meta?.hidden) {
|
||||
return "";
|
||||
|
||||
@ -10,6 +10,9 @@ import '@/permission';
|
||||
|
||||
import Antd from 'ant-design-vue'
|
||||
import 'ant-design-vue/dist/reset.css' // Ant Design 全局样式重置
|
||||
import dayjs from 'dayjs'; // ant 中文语言
|
||||
import 'dayjs/locale/zh-cn';
|
||||
|
||||
// 引入svg注册脚本
|
||||
import 'virtual:svg-icons-register';
|
||||
|
||||
@ -31,6 +34,7 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
app.component(key, component)
|
||||
}
|
||||
|
||||
dayjs.locale('zh-cn');
|
||||
// 全局方法
|
||||
import { getDictionaries } from '@/api/dict';
|
||||
app.config.globalProperties.$getDictionaries = getDictionaries;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
||||
const loading = ref(false);
|
||||
const isOpen = ref(true);
|
||||
@ -9,6 +9,9 @@ const jidiDataNum = ref(9);
|
||||
const itemClick = (index: any) => {
|
||||
useJidiSelectEventStore().updataJidiData(index);
|
||||
};
|
||||
onMounted(() => {
|
||||
JidiSelectEventStore.jidiData[0].selected = true;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -33,7 +36,7 @@ const itemClick = (index: any) => {
|
||||
@click="itemClick(index)"
|
||||
>
|
||||
<i class="icon iconfont icon-hydroPower"></i>
|
||||
<span style="margin-left: 10px">{{ i.name }}</span>
|
||||
<span style="margin-left: 10px">{{ i.wbsName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
|
||||
@ -25,7 +25,7 @@ export enum SizeType {
|
||||
}
|
||||
export const usetTheme = {
|
||||
token: {
|
||||
colorPrimary: '#1890ff',
|
||||
colorPrimary: '#2f6b98',
|
||||
borderRadius: 2,
|
||||
},
|
||||
};
|
||||
|
||||
@ -4,59 +4,709 @@ import { ref } from 'vue';
|
||||
export const useJidiSelectEventStore = defineStore('jidiSelectEvent', () => {
|
||||
const jidiData = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '当前全部',
|
||||
selected: true
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "all",
|
||||
"wbsName": "当前全部",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '水电基地2',
|
||||
selected: false
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "01",
|
||||
"wbsName": "金沙江干流",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '水电基地3',
|
||||
selected: false
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "02",
|
||||
"wbsName": "雅砻江干流",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '水电基地4',
|
||||
selected: false
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "03",
|
||||
"wbsName": "大渡河干流",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '水电基地5',
|
||||
selected: false
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "04",
|
||||
"wbsName": "乌江干流",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '水电基地6',
|
||||
selected: false
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "05",
|
||||
"wbsName": "长江上游干流",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: '水电基地7',
|
||||
selected: false
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "10",
|
||||
"wbsName": "湘西",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: '水电基地8',
|
||||
selected: false
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "08",
|
||||
"wbsName": "黄河上游干流",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: '水电基地9',
|
||||
selected: false
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "09",
|
||||
"wbsName": "黄河中游干流",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: '水电基地10',
|
||||
selected: false
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "06",
|
||||
"wbsName": "南盘江-红水河",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: '水电基地11',
|
||||
selected: false
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "12",
|
||||
"wbsName": "东北",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
},
|
||||
{
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "07",
|
||||
"wbsName": "澜沧江干流",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
},
|
||||
{
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "13",
|
||||
"wbsName": "怒江干流",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
},
|
||||
{
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "11",
|
||||
"wbsName": "闽浙赣",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
},
|
||||
{
|
||||
"_tls": {},
|
||||
"id": null,
|
||||
"recordUser": null,
|
||||
"recordTime": null,
|
||||
"modifyTime": null,
|
||||
"displayRecordUser": null,
|
||||
"isolateType": null,
|
||||
"wbsType": null,
|
||||
"wbsCode": "other",
|
||||
"wbsName": "其他",
|
||||
"wbsSname": null,
|
||||
"wbsNameEn": null,
|
||||
"wbsSnameEn": null,
|
||||
"enable": null,
|
||||
"description": null,
|
||||
"parentId": null,
|
||||
"parentCode": null,
|
||||
"treeLevel": null,
|
||||
"hasChildren": null,
|
||||
"objId": null,
|
||||
"topWbsType": null,
|
||||
"fullPath": null,
|
||||
"internal": null,
|
||||
"lgtd": null,
|
||||
"lttd": null,
|
||||
"area": null,
|
||||
"perimeter": null,
|
||||
"synopsis": null,
|
||||
"introduce": null,
|
||||
"logo": null,
|
||||
"inffile": null,
|
||||
"orderIndex": null,
|
||||
"filterContent": null,
|
||||
"departmentId": null,
|
||||
"systemId": null,
|
||||
"platformId": null,
|
||||
"reachWwqtg": null,
|
||||
"maxElev": null,
|
||||
"minElev": null,
|
||||
"datTp": null,
|
||||
"rvAg": null,
|
||||
"ifInnRv": null,
|
||||
"showControl": null,
|
||||
"stcd": null,
|
||||
"displayDepartment": null
|
||||
}
|
||||
]);
|
||||
const selectedItem = ref(jidiData.value[0]);
|
||||
|
||||
40
frontend/src/styles/ant-design-vue.scss
Normal file
40
frontend/src/styles/ant-design-vue.scss
Normal file
@ -0,0 +1,40 @@
|
||||
.ant-model {
|
||||
.ant-modal-content {
|
||||
border-radius: 2px;
|
||||
}
|
||||
.ant-modal-header {
|
||||
padding: 16px 24px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
border-radius: 2px 2px 0 0;
|
||||
background-color: #005293;
|
||||
}
|
||||
}
|
||||
:where(.css-dev-only-do-not-override-ekaqbe).ant-modal {
|
||||
.ant-modal-content {
|
||||
border-radius: 2px;
|
||||
padding: 0 !important;
|
||||
.ant-modal-close-x {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.ant-modal-body {
|
||||
padding: 16px 24px !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
.ant-modal-footer {
|
||||
margin-top: 0 !important;
|
||||
padding: 16px 24px !important;
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
}
|
||||
.ant-modal-header {
|
||||
padding: 16px 24px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
border-radius: 2px 2px 0 0;
|
||||
background-color: #005293;
|
||||
margin-bottom: 0;
|
||||
.ant-modal-title {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
@use 'src/styles/element-plus' as element-plus;
|
||||
@use 'src/styles/ant-design-vue' as permission;
|
||||
@use './sidebar' as sidebar;
|
||||
@use './tailwind' as tailwind;
|
||||
@import './iconfont/iconfont.css';
|
||||
@ -95,13 +96,3 @@ svg {
|
||||
height: 98%;
|
||||
position: relative;
|
||||
}
|
||||
//滚动条统一样式
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 4px;
|
||||
background: #bbb;
|
||||
-webkit-box-shadow: inset 0 0 6px #ddd;
|
||||
}
|
||||
|
||||
3656
frontend/src/utils/GisUrlList.ts
Normal file
3656
frontend/src/utils/GisUrlList.ts
Normal file
File diff suppressed because it is too large
Load Diff
315
frontend/src/utils/enumeration.ts
Normal file
315
frontend/src/utils/enumeration.ts
Normal file
@ -0,0 +1,315 @@
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import isoWeek from 'dayjs/plugin/isoWeek'; // 如果需要严格的 ISO 周计算
|
||||
import quarterOfYear from 'dayjs/plugin/quarterOfYear'; // 用于季度操作
|
||||
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
||||
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
|
||||
|
||||
// 启用插件
|
||||
dayjs.extend(quarterOfYear);
|
||||
dayjs.extend(isoWeek);
|
||||
dayjs.extend(isSameOrBefore);
|
||||
dayjs.extend(isSameOrAfter);
|
||||
|
||||
// 设置全局语言为中文(可选,根据项目需求)
|
||||
import 'dayjs/locale/zh-cn';
|
||||
dayjs.locale('zh-cn');
|
||||
|
||||
namespace Options {
|
||||
//开发方式
|
||||
export const developmentMode = [
|
||||
{ value: '1', label: '堤坝式' },
|
||||
{ value: '2', label: '引水式' },
|
||||
{ value: '3', label: '混合式' }
|
||||
];
|
||||
// 建设状态
|
||||
export const bldsttList = [
|
||||
{ value: '1', label: '在建' },
|
||||
{ value: '2', label: '已建' },
|
||||
{ value: '0', label: '规划' }
|
||||
];
|
||||
|
||||
export const developmentMode2 = [
|
||||
{ value: 'RRU', label: '上水库' },
|
||||
{ value: 'RRL', label: '下水库' },
|
||||
{ value: 'GN', label: '机组' },
|
||||
{ value: 'GT', label: '闸门' }
|
||||
];
|
||||
|
||||
//电站类型
|
||||
export const powerStationType = [
|
||||
{
|
||||
value: '',
|
||||
label: '所有'
|
||||
},
|
||||
{
|
||||
value: '1',
|
||||
label: '已接入'
|
||||
},
|
||||
{
|
||||
value: '0',
|
||||
label: '未接入'
|
||||
}
|
||||
];
|
||||
//工程类型
|
||||
export const projectType = [
|
||||
{
|
||||
value: '',
|
||||
label: '所有'
|
||||
},
|
||||
{
|
||||
value: '1',
|
||||
label: '常规水电'
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
label: '抽水蓄能'
|
||||
}
|
||||
];
|
||||
//调节性能
|
||||
export const performance = [
|
||||
{
|
||||
value: '1',
|
||||
label: '多年'
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
label: '年'
|
||||
},
|
||||
{
|
||||
value: '3',
|
||||
label: '不完全年'
|
||||
},
|
||||
{
|
||||
value: '4',
|
||||
label: '径流式'
|
||||
},
|
||||
{
|
||||
value: '5',
|
||||
label: '季'
|
||||
},
|
||||
{
|
||||
value: '6',
|
||||
label: '月'
|
||||
},
|
||||
{
|
||||
value: '7',
|
||||
label: '周'
|
||||
},
|
||||
{
|
||||
value: '8',
|
||||
label: '日'
|
||||
},
|
||||
{
|
||||
value: '9',
|
||||
label: '无'
|
||||
}
|
||||
];
|
||||
//流域
|
||||
export const type = [
|
||||
{ value: 'district', label: '行政区' },
|
||||
{ value: 'basin', label: '流域' },
|
||||
{ value: 'company', label: '公司' }
|
||||
];
|
||||
export const type2 = [{ value: 'district', label: '行政区' }];
|
||||
|
||||
export const evaluateType = [
|
||||
{
|
||||
value: '1',
|
||||
label: '水能效益'
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
label: '水量效益'
|
||||
},
|
||||
{
|
||||
value: '3',
|
||||
label: '水位运行评价'
|
||||
},
|
||||
{
|
||||
value: '4',
|
||||
label: '防洪效益'
|
||||
},
|
||||
{
|
||||
value: '5',
|
||||
label: '生态效益'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
namespace DateSetting {
|
||||
//获取当前时间
|
||||
function getStartTime(): Dayjs {
|
||||
return dayjs().startOf('day').startOf('hour');
|
||||
}
|
||||
|
||||
//获取当前时间
|
||||
function getEndTime(): Dayjs {
|
||||
return dayjs().endOf('day');
|
||||
}
|
||||
|
||||
function getStartYear(): Dayjs {
|
||||
return dayjs().startOf('year');
|
||||
}
|
||||
|
||||
function getEarlyDays(): Array<Dayjs> {
|
||||
return [dayjs().startOf('month'), dayjs().startOf('month').add(9, 'day')];
|
||||
}
|
||||
|
||||
function getMidmonth(): Array<Dayjs> {
|
||||
return [
|
||||
dayjs().startOf('month').add(10, 'day'),
|
||||
dayjs().startOf('month').add(19, 'day')
|
||||
];
|
||||
}
|
||||
|
||||
function getLastTenDays(): Array<Dayjs> {
|
||||
return [dayjs().startOf('month').add(20, 'day'), dayjs().endOf('month')];
|
||||
}
|
||||
|
||||
export const RangeButton: any = {
|
||||
days: [
|
||||
{
|
||||
label: '今天',
|
||||
value: [dayjs().startOf('day'), dayjs().endOf('day')]
|
||||
},
|
||||
{
|
||||
label: '昨天',
|
||||
value: [
|
||||
dayjs().subtract(1, 'day').startOf('day'),
|
||||
dayjs().subtract(1, 'day').endOf('day')
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '本周',
|
||||
// 注意:如果项目配置了 isoWeek 插件且希望周一为起始,可使用 startOf('isoWeek')
|
||||
value: [dayjs().startOf('week'), dayjs().endOf('week')]
|
||||
},
|
||||
{
|
||||
label: '上周',
|
||||
value: [
|
||||
dayjs().subtract(1, 'week').startOf('week'),
|
||||
dayjs().subtract(1, 'week').endOf('week')
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '本月',
|
||||
value: [dayjs().startOf('month'), dayjs().endOf('month')]
|
||||
},
|
||||
{
|
||||
label: '上月',
|
||||
value: [
|
||||
dayjs().subtract(1, 'month').startOf('month'),
|
||||
dayjs().subtract(1, 'month').endOf('month')
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '上半年',
|
||||
value: [
|
||||
dayjs().startOf('year'),
|
||||
dayjs()
|
||||
.startOf('year')
|
||||
.add(6, 'month')
|
||||
.subtract(1, 'day')
|
||||
.endOf('day')
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '本年',
|
||||
value: [dayjs().startOf('year'), dayjs().endOf('year')]
|
||||
}
|
||||
],
|
||||
tenDays: [
|
||||
{
|
||||
label: '上旬',
|
||||
value: getEarlyDays()
|
||||
},
|
||||
{
|
||||
label: '中旬',
|
||||
value: getMidmonth()
|
||||
},
|
||||
{
|
||||
label: '下旬',
|
||||
value: getLastTenDays()
|
||||
},
|
||||
],
|
||||
month: [
|
||||
{
|
||||
label: '最近一个月',
|
||||
value: [getStartTime().subtract(1, 'month'), getStartTime()],
|
||||
},
|
||||
{
|
||||
label: '最近三个月',
|
||||
value: [getStartTime().subtract(3, 'month'), getStartTime()],
|
||||
},
|
||||
{
|
||||
label: '最近半年',
|
||||
value: [getStartTime().subtract(6, 'month'), getStartTime()],
|
||||
},
|
||||
{
|
||||
label: '最近一年',
|
||||
value: [getStartTime().subtract(1, 'year'), getStartTime()],
|
||||
},
|
||||
],
|
||||
season: [
|
||||
{
|
||||
label: '第一季度',
|
||||
value: [getStartYear(), getStartYear().add(2, 'month').endOf('month')],
|
||||
},
|
||||
{
|
||||
label: '第二季度',
|
||||
value: [
|
||||
getStartYear().add(3, 'month'),
|
||||
getStartYear().add(5, 'month').endOf('month')
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '第三季度',
|
||||
value: [
|
||||
getStartYear().add(6, 'month'),
|
||||
getStartYear().add(8, 'month').endOf('month')
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '第四季度',
|
||||
value: [
|
||||
getStartYear().add(9, 'month'),
|
||||
getStartYear().add(11, 'month').endOf('month')
|
||||
],
|
||||
},
|
||||
],
|
||||
year: [
|
||||
{
|
||||
label: '最近一年',
|
||||
value: [getStartTime().subtract(1, 'year'), getStartTime()],
|
||||
},
|
||||
{
|
||||
label: '近三年',
|
||||
value: [getStartTime().subtract(3, 'year'), getStartTime()],
|
||||
},
|
||||
{
|
||||
label: '最近五年',
|
||||
value: [getStartTime().subtract(5, 'year'), getStartTime()],
|
||||
},
|
||||
{
|
||||
label: '最近十年',
|
||||
value: [getStartTime().subtract(10, 'year'), getStartTime()]
|
||||
},
|
||||
],
|
||||
future: [
|
||||
{
|
||||
label: '未来一周',
|
||||
value: [getStartTime(), getStartTime().add(7, 'day')],
|
||||
},
|
||||
{
|
||||
label: '未来一个月',
|
||||
value: [getStartTime(), getStartTime().add(1, 'month')],
|
||||
},
|
||||
{
|
||||
label: '未来三个月',
|
||||
value: [getStartTime(), getStartTime().add(3, 'month')]
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export { Options, DateSetting };
|
||||
@ -1,14 +1,507 @@
|
||||
<!-- SidePanelItem.vue -->
|
||||
<template>
|
||||
<div>
|
||||
过鱼设施数据填报
|
||||
<div class="guoYuSheShiShuJuTianBao-page">
|
||||
<!-- 搜索区域组件,具体 props 需根据实际子组件调整 -->
|
||||
<GuoYuSheShiShuJuTianBaoSearch
|
||||
:import-btn="importBtn"
|
||||
:save-btn="saveBtn"
|
||||
:handle-add="handleAdd"
|
||||
@search-finish="handleSearchFinish"
|
||||
/>
|
||||
|
||||
<!-- 主表格 -->
|
||||
<a-table
|
||||
size="small"
|
||||
:loading="loading"
|
||||
:row-selection="rowSelection"
|
||||
:data-source="tableData"
|
||||
:columns="columns"
|
||||
:pagination="paginationConfig"
|
||||
:scroll="{ x: '100%' }"
|
||||
row-key="key"
|
||||
>
|
||||
<!-- 自定义插槽渲染可根据需要扩展,这里主要依赖 columns 中的 render 函数逻辑,但在 Vue中通常使用 slot 或 h 函数 -->
|
||||
<!-- 注意:Antdv 的 columns render 支持返回 VNode 或字符串 -->
|
||||
</a-table>
|
||||
|
||||
<!-- 导入预览 Modal -->
|
||||
<a-modal
|
||||
title="导入数据预览"
|
||||
ok-text="提交导入"
|
||||
cancel-text="取消"
|
||||
:width="1500"
|
||||
:open="visible"
|
||||
:confirm-loading="fileLoading"
|
||||
@cancel="handleModalCancel"
|
||||
@ok="handleModalOk"
|
||||
>
|
||||
<a-table
|
||||
size="small"
|
||||
:loading="fileLoading"
|
||||
:data-source="fileTableData"
|
||||
:columns="modalColumns"
|
||||
:pagination="false"
|
||||
:scroll="{ y: 500, x: '100%' }"
|
||||
row-key="index"
|
||||
>
|
||||
<!-- 如果需要复杂的行内编辑插槽,可在此定义,但目前逻辑主要在 column render 中处理 -->
|
||||
</a-table>
|
||||
</a-modal>
|
||||
|
||||
<!-- 新增/编辑 Modal (对应 React 的 EditModal) -->
|
||||
<!-- 假设已创建对应的 Vue 组件 GuoYuSheShiShuJuTianBaoForm -->
|
||||
<EditModal
|
||||
v-model:visible="editModalVisible"
|
||||
:initial-values="currentRecord"
|
||||
:loading="submitLoading"
|
||||
@cancel="editModalCancel"
|
||||
@ok="handleEditSubmit"
|
||||
/>
|
||||
|
||||
<!-- 视频预览 Modal -->
|
||||
<a-modal
|
||||
title="视频预览"
|
||||
:open="videoPreviewVisible"
|
||||
:footer="null"
|
||||
width="800px"
|
||||
@cancel="closeVideoPreview"
|
||||
>
|
||||
<video v-if="currentVideoUrl" controls autoplay style="width: 100%" :src="currentVideoUrl">
|
||||
您的浏览器不支持视频播放
|
||||
</video>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
// 页面加载时执行的逻辑
|
||||
onMounted(() => { });
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { message, Modal } from 'ant-design-vue' // 假设使用 ant-design-vue
|
||||
import JSZip from 'jszip'
|
||||
import * as XLSX from 'xlsx'
|
||||
import GuoYuSheShiShuJuTianBaoSearch from './guoYuSheShiShuJuTianBaoSearch.vue'
|
||||
import EditModal from './guoYuSheShiShuJuTianBaoForm.vue'
|
||||
// import { FileImageOutlined, VideoCameraOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
// --- 类型定义 ---
|
||||
interface FormData {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
interface ColumnConfig {
|
||||
dataIndex: string
|
||||
key: string
|
||||
title: string
|
||||
width?: number
|
||||
}
|
||||
|
||||
// --- 基础配置 ---
|
||||
const baseColumnsConfig: ColumnConfig[] = [
|
||||
{ dataIndex: 'stcd', key: 'stcd', title: '水电基地', width: 100 },
|
||||
{ dataIndex: 'title', key: 'title', title: '电站名称', width: 120 },
|
||||
{ dataIndex: 'office', key: 'office', title: '过鱼设施名称', width: 150 },
|
||||
{ dataIndex: 'regionName', key: 'regionName', title: '过鱼时间', width: 120 },
|
||||
{ dataIndex: 'location', key: 'location', title: '鱼种类', width: 120 },
|
||||
{ dataIndex: 'location11', key: 'location', title: '是否鱼苗', width: 120 },
|
||||
{ dataIndex: 'DIRECTION', key: 'DIRECTION', title: '游向', width: 120 },
|
||||
{ dataIndex: 'level1', key: 'level1', title: '过鱼数量(尾)', width: 160 },
|
||||
{ dataIndex: 'level2', key: 'level2', title: '体长', width: 120 },
|
||||
{ dataIndex: 'level3', key: 'level3', title: '平均体重', width: 120 },
|
||||
{ dataIndex: 'level4', key: 'level4', title: '水温', width: 120 },
|
||||
{ dataIndex: 'level5', key: 'level5', title: '图片', width: 100 },
|
||||
{ dataIndex: 'level6', key: 'level6', title: '视频', width: 100 },
|
||||
{ dataIndex: 'status', key: 'status', title: '状态', width: 100 }
|
||||
]
|
||||
|
||||
// --- 状态定义 ---
|
||||
const searchData = ref<any>(null)
|
||||
const visible = ref(false) // 导入预览 Modal
|
||||
|
||||
// 编辑相关状态
|
||||
const editModalVisible = ref(false)
|
||||
const currentRecord = ref<FormData | null>(null)
|
||||
const submitLoading = ref(false)
|
||||
|
||||
// 视频预览相关状态
|
||||
const videoPreviewVisible = ref(false)
|
||||
const currentVideoUrl = ref<string>('')
|
||||
|
||||
// 表格数据
|
||||
const tableData = ref<any[]>([])
|
||||
const fileTableData = ref<any[]>([])
|
||||
|
||||
const loading = ref(false)
|
||||
const fileLoading = ref(false)
|
||||
|
||||
// 行内编辑 Key (用于导入预览表格)
|
||||
const editingKey = ref<string | number>('')
|
||||
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const size = ref(10)
|
||||
|
||||
// --- 辅助函数 ---
|
||||
|
||||
// 从 Zip 获取 Blob URL
|
||||
const getBlobUrlFromZip = async (zip: JSZip, fileName: string): Promise<string> => {
|
||||
try {
|
||||
const file = zip.file(fileName)
|
||||
if (!file) return ''
|
||||
const blob = await file.async('blob')
|
||||
return URL.createObjectURL(blob)
|
||||
} catch (e) {
|
||||
console.error('Extract file failed', e)
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染媒体单元格 (返回 VNode 或简单结构,实际在 Antdv columns render 中处理)
|
||||
// 在 Vue Antdv 中,render 函数接收 (text, record, index)
|
||||
const createMediaRender = (type: 'image' | 'video') => {
|
||||
return (text: string) => {
|
||||
if (!text) return '-'
|
||||
// 这里简化处理,实际项目中可能需要使用 h 函数渲染图标和点击事件
|
||||
// 由于无法直接在这里绑定 click 事件到简单的字符串返回,建议在 columns 定义中使用 slots 或 h 函数
|
||||
// 为了保持逻辑清晰,这里仅返回文本提示,实际 UI 需结合 Antdv 的 customRender
|
||||
return type === 'image' ? '查看图片' : '播放视频'
|
||||
}
|
||||
}
|
||||
|
||||
// --- Columns 定义 ---
|
||||
|
||||
// 主表格 Columns
|
||||
const columns = computed(() => {
|
||||
return [
|
||||
...baseColumnsConfig.map((col) => {
|
||||
if (col.dataIndex === 'level5') {
|
||||
return {
|
||||
...col,
|
||||
customRender: ({ text }: any) => {
|
||||
if(!text) return '-'
|
||||
// 实际应渲染 Icon 和点击事件,此处简化
|
||||
return `<span style="color:#52c41a; cursor:pointer">查看图片</span>`
|
||||
}
|
||||
}
|
||||
}
|
||||
if (col.dataIndex === 'level6') {
|
||||
return {
|
||||
...col,
|
||||
customRender: ({ text }: any) => {
|
||||
if(!text) return '-'
|
||||
return `<span style="color:#1890ff; cursor:pointer">播放视频</span>`
|
||||
}
|
||||
}
|
||||
}
|
||||
return { ...col, visible: true }
|
||||
}),
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
customRender: ({ record, index }: any) => {
|
||||
// 在 Vue 模板中,通常通过 slot #bodyCell 来处理复杂操作列
|
||||
// 这里仅做逻辑占位,实际需在 template 中定义 <template #bodyCell="{ column, record, index }">
|
||||
return '操作列'
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// 导入预览表格 Columns (包含行内编辑逻辑)
|
||||
const modalColumns = computed(() => {
|
||||
const isEditing = (record: any, index: number) => index === editingKey.value
|
||||
|
||||
const save = async (index: number) => {
|
||||
// 保存逻辑:实际上 fileTableData 是响应式的,input change 时已经更新
|
||||
editingKey.value = ''
|
||||
message.success('行数据已更新')
|
||||
}
|
||||
|
||||
const deleteRow = (index: number) => {
|
||||
fileTableData.value = fileTableData.value.filter((_, i) => i !== index)
|
||||
message.success('行数据已删除')
|
||||
}
|
||||
|
||||
return baseColumnsConfig.map((col) => ({
|
||||
...col,
|
||||
customRender: ({ text, record, index }: any) => {
|
||||
const editing = isEditing(record, index)
|
||||
|
||||
// 如果是媒体列
|
||||
if (col.dataIndex === 'level5' || col.dataIndex === 'level6') {
|
||||
if (editing) {
|
||||
// 返回 Input 组件的 VNode 或标识,实际需用 slot 或 h 函数
|
||||
return 'Input编辑中'
|
||||
}
|
||||
return col.dataIndex === 'level5' ? '查看图片' : '播放视频'
|
||||
}
|
||||
|
||||
// 普通列
|
||||
if (editing) {
|
||||
// 返回 Input 组件标识
|
||||
return 'Input编辑中'
|
||||
}
|
||||
return text
|
||||
},
|
||||
// Antdv 支持通过 slots 自定义单元格内容以实现交互
|
||||
slots: { customRender: `cell-${col.dataIndex}` }
|
||||
})).concat({
|
||||
title: '操作',
|
||||
dataIndex: 'operation',
|
||||
fixed: 'right',
|
||||
width: 140,
|
||||
align: 'center',
|
||||
customRender: ({ record, index }: any) => {
|
||||
const editable = isEditing(record, index)
|
||||
return editable ? '保存/取消' : '修改/删除'
|
||||
},
|
||||
slots: { customRender: 'cell-operation' }
|
||||
})
|
||||
})
|
||||
|
||||
const rowSelection = {
|
||||
onChange: (selectedRowKeys: string[], selectedRows: any[]) => {
|
||||
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
|
||||
},
|
||||
getCheckboxProps: (record: any) => ({
|
||||
disabled: record.name === 'Disabled User', // Column configuration not to be checked
|
||||
name: record.name,
|
||||
}),
|
||||
};
|
||||
// --- 业务逻辑方法 ---
|
||||
|
||||
const handleAdd = () => {
|
||||
currentRecord.value = null
|
||||
editModalVisible.value = true
|
||||
}
|
||||
|
||||
const handleEdit = (record: any) => {
|
||||
currentRecord.value = { ...record }
|
||||
editModalVisible.value = true
|
||||
}
|
||||
|
||||
const handleDeleteMain = (index: number) => {
|
||||
Modal.confirm({
|
||||
title: '确定删除这条数据吗?',
|
||||
onOk: () => {
|
||||
tableData.value = tableData.value.filter((_, i) => i !== index)
|
||||
message.success('删除成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const editModalCancel = () => {
|
||||
editModalVisible.value = false
|
||||
}
|
||||
|
||||
const handleEditSubmit = async (values: FormData) => {
|
||||
submitLoading.value = true
|
||||
// 模拟异步请求
|
||||
setTimeout(() => {
|
||||
if (currentRecord.value) {
|
||||
// 编辑逻辑
|
||||
const newData = tableData.value.map(item => {
|
||||
// 简单比较,实际建议用 ID
|
||||
if (JSON.stringify(item) === JSON.stringify(currentRecord.value)) { // 注意:浅比较可能不够,需根据实际 ID
|
||||
return { ...item, ...values }
|
||||
}
|
||||
return item
|
||||
})
|
||||
// 更稳妥的方式是通过 key 查找
|
||||
const targetIndex = tableData.value.findIndex(item => item.key === currentRecord.value?.key)
|
||||
if(targetIndex > -1) {
|
||||
tableData.value[targetIndex] = { ...tableData.value[targetIndex], ...values }
|
||||
}
|
||||
message.success('编辑成功')
|
||||
} else {
|
||||
// 新增逻辑
|
||||
const newRecord = { ...values, key: Date.now() }
|
||||
tableData.value = [newRecord, ...tableData.value]
|
||||
message.success('新增成功')
|
||||
}
|
||||
submitLoading.value = false
|
||||
editModalVisible.value = false
|
||||
}, 500)
|
||||
}
|
||||
|
||||
const getData = async (searchDataParam?: any, label?: string) => {
|
||||
loading.value = true
|
||||
// TODO: 实现实际的数据获取 API 调用
|
||||
console.log('Fetching data with:', searchDataParam)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 500)
|
||||
}
|
||||
|
||||
const parseExcelFile = async (fileName: string, arrayBuffer: ArrayBuffer) => {
|
||||
try {
|
||||
const workbook = XLSX.read(arrayBuffer, { type: 'array' })
|
||||
const firstSheetName = workbook.SheetNames[0]
|
||||
if (!firstSheetName) throw new Error('Excel文件中没有工作表')
|
||||
const worksheet = workbook.Sheets[firstSheetName]
|
||||
const jsonData: any[] = XLSX.utils.sheet_to_json(worksheet)
|
||||
return jsonData
|
||||
} catch (error) {
|
||||
console.error(`解析文件 ${fileName} 失败:`, error)
|
||||
message.error(`文件 ${fileName} 解析失败`)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
const handleModalOk = () => {
|
||||
tableData.value = [...fileTableData.value]
|
||||
visible.value = false
|
||||
message.success('数据已导入至列表')
|
||||
}
|
||||
|
||||
const handleModalCancel = () => {
|
||||
visible.value = false
|
||||
editingKey.value = ''
|
||||
}
|
||||
|
||||
const importBtn = async (file: File) => {
|
||||
fileLoading.value = true
|
||||
editingKey.value = ''
|
||||
const hideMessage = message.loading('正在解析压缩包...', 0)
|
||||
|
||||
try {
|
||||
const zip = await JSZip.loadAsync(file)
|
||||
const zipPathMap: Record<string, string> = {}
|
||||
|
||||
// 构建路径映射
|
||||
zip.forEach((relativePath, zipEntry) => {
|
||||
if (!zipEntry.dir) {
|
||||
const lowerPath = relativePath.toLowerCase()
|
||||
zipPathMap[lowerPath] = relativePath
|
||||
const pathParts = relativePath.split('/')
|
||||
for (let i = 0; i < pathParts.length; i++) {
|
||||
const subPath = pathParts.slice(i).join('/')
|
||||
if (subPath) zipPathMap[subPath.toLowerCase()] = relativePath
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const fileNames = Object.keys(zip.files)
|
||||
if (fileNames.length === 0) {
|
||||
hideMessage()
|
||||
message.warning('压缩包为空')
|
||||
fileLoading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
let allExcelData: any[] = []
|
||||
for (const fileName of fileNames) {
|
||||
const zipEntry = zip.files[fileName]
|
||||
if (zipEntry.dir) continue
|
||||
if (!fileName.match(/\.(xls|xlsx)$/i)) continue
|
||||
|
||||
try {
|
||||
const arrayBuffer = await zipEntry.async('arraybuffer')
|
||||
const data = await parseExcelFile(fileName, arrayBuffer)
|
||||
if (!data || data.length === 0) continue
|
||||
|
||||
const transformedData = await Promise.all(
|
||||
data.map(async (item: any) => {
|
||||
const newObj: any = {}
|
||||
for (const excelKey in item) {
|
||||
if (!Object.prototype.hasOwnProperty.call(item, excelKey)) continue
|
||||
const value = item[excelKey]
|
||||
// 模糊匹配列标题
|
||||
const matchedCol = baseColumnsConfig.find((col) =>
|
||||
excelKey.includes(col.title) || col.title.includes(excelKey)
|
||||
)
|
||||
|
||||
if (matchedCol) {
|
||||
let finalValue = value
|
||||
// 处理图片和视频路径提取
|
||||
if ((matchedCol.dataIndex === 'level5' || matchedCol.dataIndex === 'level6') && value && typeof value === 'string') {
|
||||
const trimPath = value.trim().replace(/\\/g, '/')
|
||||
if (trimPath) {
|
||||
const searchKey = trimPath.toLowerCase()
|
||||
const realPath = zipPathMap[searchKey]
|
||||
if (realPath) {
|
||||
try {
|
||||
const zipFile = zip.file(realPath)
|
||||
if (zipFile) {
|
||||
const blob = await zipFile.async('blob')
|
||||
finalValue = URL.createObjectURL(blob)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Failed to extract blob for: ${realPath}`, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
newObj[matchedCol.dataIndex] = finalValue
|
||||
}
|
||||
}
|
||||
return newObj
|
||||
})
|
||||
)
|
||||
allExcelData = [...allExcelData, ...transformedData]
|
||||
} catch (err) {
|
||||
console.error(`读取文件 ${fileName} 失败`, err)
|
||||
}
|
||||
}
|
||||
|
||||
fileTableData.value = allExcelData
|
||||
visible.value = true
|
||||
hideMessage()
|
||||
message.success(`解析完成,共获取 ${allExcelData.length} 条数据`)
|
||||
} catch (error) {
|
||||
hideMessage()
|
||||
console.error('ZIP 解析失败:', error)
|
||||
message.error('文件格式错误或解析失败')
|
||||
} finally {
|
||||
fileLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const saveBtn = async () => {
|
||||
// TODO: 实现保存逻辑
|
||||
console.log('Save button clicked')
|
||||
}
|
||||
|
||||
const handleSearchFinish = (e: any, label: string) => {
|
||||
const newSearchData = { ...searchData.value, ...e }
|
||||
searchData.value = newSearchData
|
||||
getData(newSearchData, label)
|
||||
}
|
||||
|
||||
const closeVideoPreview = () => {
|
||||
videoPreviewVisible.value = false
|
||||
currentVideoUrl.value = ''
|
||||
}
|
||||
|
||||
// 分页配置
|
||||
const paginationConfig = computed(() => ({
|
||||
size: 'small' as const,
|
||||
total: total.value,
|
||||
showTotal: (total: number) => `共 ${total} 条`,
|
||||
showQuickJumper: true,
|
||||
pageSize: size.value,
|
||||
current: page.value,
|
||||
onChange: (p: number, ps: number) => {
|
||||
page.value = p
|
||||
size.value = ps
|
||||
// 重新获取数据
|
||||
// getData(searchData.value)
|
||||
}
|
||||
}))
|
||||
|
||||
// --- 生命周期 ---
|
||||
onMounted(() => {
|
||||
// 初始化加载数据
|
||||
// getData()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
.guoYuSheShiShuJuTianBao-page {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
276
frontend/src/views/shuJuTianBao/guoYuSheShiShuJuTianBaoForm.vue
Normal file
276
frontend/src/views/shuJuTianBao/guoYuSheShiShuJuTianBaoForm.vue
Normal file
@ -0,0 +1,276 @@
|
||||
<template>
|
||||
<a-modal
|
||||
:title="isEdit ? '编辑数据' : '新增数据'"
|
||||
:opne="visible"
|
||||
:confirm-loading="loading"
|
||||
width="800px"
|
||||
destroy-on-close
|
||||
@cancel="handleCancel"
|
||||
@ok="handleOk"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
layout="vertical"
|
||||
name="edit_form"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="流域" name="stcd">
|
||||
<a-input v-model:value="formData.stcd" placeholder="请输入流域" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="电站名称" name="title">
|
||||
<a-input v-model:value="formData.title" placeholder="请输入电站名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="过鱼设施名称" name="office">
|
||||
<a-input v-model:value="formData.office" placeholder="请输入过鱼设施名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="过鱼时间" name="regionName">
|
||||
<a-date-picker
|
||||
v-model:value="formData.regionName"
|
||||
style="width: 100%"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择日期"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="鱼种类" name="location">
|
||||
<a-input v-model:value="formData.location" placeholder="请输入鱼种类" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="游向" name="DIRECTION">
|
||||
<a-select v-model:value="formData.DIRECTION" placeholder="请选择游向" allow-clear>
|
||||
<a-select-option value="上行">上行</a-select-option>
|
||||
<a-select-option value="下行">下行</a-select-option>
|
||||
<a-select-option value="其他">其他</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="过鱼数量(尾)" name="level1">
|
||||
<a-input-number
|
||||
v-model:value="formData.level1"
|
||||
style="width: 100%"
|
||||
placeholder="数量"
|
||||
:min="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="体长" name="level2">
|
||||
<a-input-number
|
||||
v-model:value="formData.level2"
|
||||
style="width: 100%"
|
||||
placeholder="体长"
|
||||
:min="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="平均体重" name="level3">
|
||||
<a-input-number
|
||||
v-model:value="formData.level3"
|
||||
style="width: 100%"
|
||||
placeholder="平均体重"
|
||||
:min="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="水温" name="level4">
|
||||
<a-input-number
|
||||
v-model:value="formData.level4"
|
||||
style="width: 100%"
|
||||
placeholder="水温"
|
||||
:min="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-form-item
|
||||
label="图片路径"
|
||||
name="level5"
|
||||
extra="请输入图片文件名或路径(如:images/photo.jpg)"
|
||||
>
|
||||
<a-input v-model:value="formData.level5" placeholder="图片路径" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
label="视频路径"
|
||||
name="level6"
|
||||
extra="请输入视频文件名或路径(如:videos/video.mp4)"
|
||||
>
|
||||
<a-input v-model:value="formData.level6" placeholder="视频路径" />
|
||||
</a-form-item>
|
||||
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch, computed } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import type { Rule } from 'ant-design-vue/es/form'
|
||||
import dayjs from 'dayjs' // Antdv 默认依赖 dayjs
|
||||
|
||||
// 定义 Props
|
||||
interface Props {
|
||||
visible: boolean
|
||||
initialValues?: any | null
|
||||
loading?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
visible: false,
|
||||
initialValues: null,
|
||||
loading: false
|
||||
})
|
||||
|
||||
// 定义 Emits
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
(e: 'cancel'): void
|
||||
(e: 'ok', values: any): void
|
||||
}>()
|
||||
|
||||
// 表单引用
|
||||
const formRef = ref()
|
||||
|
||||
// 表单数据模型
|
||||
const formData = reactive({
|
||||
stcd: undefined,
|
||||
title: undefined,
|
||||
office: undefined,
|
||||
regionName: undefined,
|
||||
location: undefined,
|
||||
DIRECTION: undefined,
|
||||
level1: undefined,
|
||||
level2: undefined,
|
||||
level3: undefined,
|
||||
level4: undefined,
|
||||
level5: undefined,
|
||||
level6: undefined,
|
||||
status: '正常'
|
||||
})
|
||||
|
||||
// 验证规则
|
||||
const rules: Record<string, Rule[]> = {
|
||||
stcd: [{ required: true, message: '请输入流域', trigger: 'blur' }],
|
||||
title: [{ required: true, message: '请输入电站名称', trigger: 'blur' }],
|
||||
office: [{ required: true, message: '请输入过鱼设施名称', trigger: 'blur' }],
|
||||
regionName: [{ required: true, message: '请选择过鱼时间', trigger: 'change' }]
|
||||
}
|
||||
|
||||
// 计算是否为编辑模式
|
||||
const isEdit = computed(() => !!props.initialValues)
|
||||
|
||||
// 监听 visible 和 initialValues 变化,初始化表单
|
||||
watch(
|
||||
() => [props.visible, props.initialValues],
|
||||
([newVisible, newValues]) => {
|
||||
if (newVisible) {
|
||||
if (newValues) {
|
||||
// 编辑模式:回填数据
|
||||
// 注意:regionName 如果是字符串,需要转换为 dayjs 对象以便 DatePicker 显示
|
||||
// 如果 a-date-picker 使用了 value-format="YYYY-MM-DD",则可以直接传字符串,但通常内部处理需要 dayjs
|
||||
Object.keys(formData).forEach((key) => {
|
||||
if (key === 'regionName') {
|
||||
// 如果后端返回的是字符串格式 'YYYY-MM-DD'
|
||||
formData[key] = newValues[key] ? dayjs(newValues[key]) : undefined
|
||||
} else {
|
||||
formData[key] = newValues[key]
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 新增模式:重置表单
|
||||
resetForm()
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
if (formRef.value) {
|
||||
formRef.value.resetFields()
|
||||
}
|
||||
// 手动重置 reactive 对象以确保默认值
|
||||
formData.stcd = undefined
|
||||
formData.title = undefined
|
||||
formData.office = undefined
|
||||
formData.regionName = undefined
|
||||
formData.location = undefined
|
||||
formData.DIRECTION = undefined
|
||||
formData.level1 = undefined
|
||||
formData.level2 = undefined
|
||||
formData.level3 = undefined
|
||||
formData.level4 = undefined
|
||||
formData.level5 = undefined
|
||||
formData.level6 = undefined
|
||||
formData.status = '正常'
|
||||
}
|
||||
|
||||
// 取消操作
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false)
|
||||
emit('cancel')
|
||||
resetForm()
|
||||
}
|
||||
|
||||
// 确认操作
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
// 验证表单
|
||||
await formRef.value.validate()
|
||||
|
||||
// 准备提交数据
|
||||
const submitValues = { ...formData }
|
||||
|
||||
// 处理日期格式:将 dayjs 对象转回字符串
|
||||
if (submitValues.regionName && dayjs.isDayjs(submitValues.regionName)) {
|
||||
submitValues.regionName = submitValues.regionName.format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
// 确保数值类型正确 (InputNumber 通常已经处理为 number,但以防万一)
|
||||
// 如果字段允许为空,需小心处理
|
||||
;['level1', 'level2', 'level3', 'level4'].forEach((key) => {
|
||||
if (submitValues[key] !== undefined && submitValues[key] !== null) {
|
||||
submitValues[key] = Number(submitValues[key])
|
||||
}
|
||||
})
|
||||
|
||||
emit('ok', submitValues)
|
||||
} catch (error) {
|
||||
console.error('Validate Failed:', error)
|
||||
message.error('请检查表单填写是否正确')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 如有需要,添加局部样式 */
|
||||
</style>
|
||||
@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<div class="guoYuSheShiShuJuTianBao-search">
|
||||
<!-- 隐藏的文件输入框 -->
|
||||
<input
|
||||
ref="fileInputRef"
|
||||
type="file"
|
||||
accept=".zip,application/zip,application/x-zip-compressed"
|
||||
style="display: none"
|
||||
@change="handleFileSelect"
|
||||
/>
|
||||
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
@finish="onSearchFinish"
|
||||
@values-change="onValuesChange"
|
||||
>
|
||||
<!-- 自定义重置及操作按钮区域 -->
|
||||
<template #actions="{ form }">
|
||||
<a-tooltip title="新增">
|
||||
<a-button @click="props.handleAdd">
|
||||
新增
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
|
||||
<a-tooltip title="导入zip">
|
||||
<a-button v-hasPerm="['sjtb:import-zip']" @click="triggerFileInput"> 导入zip </a-button>
|
||||
</a-tooltip>
|
||||
|
||||
<a-tooltip title="提交数据">
|
||||
<a-button @click="props.saveBtn">
|
||||
<template #icon><SaveOutlined /></template>
|
||||
提交数据
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
|
||||
<a-tooltip title="批量审批">
|
||||
<a-button @click="props.saveBtn">
|
||||
<template #icon><CheckSquareOutlined /></template>
|
||||
批量审批
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, nextTick } from "vue";
|
||||
import { message } from "ant-design-vue";
|
||||
import { PlusOutlined, SaveOutlined, CheckSquareOutlined } from "@ant-design/icons-vue";
|
||||
import dayjs from "dayjs";
|
||||
import BasicSearch from "@/components/BasicSearch/index.vue"; // 确保路径正确
|
||||
import { DateSetting } from "@/utils/enumeration";
|
||||
import { checkPerm, } from "@/directive/permission";
|
||||
|
||||
// --- Props & Emits ---
|
||||
interface Props {
|
||||
importBtn: (file: File) => void;
|
||||
saveBtn: () => void;
|
||||
handleAdd: () => void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "searchFinish", values: any, label: string): void;
|
||||
}>();
|
||||
|
||||
// --- State ---
|
||||
const fileInputRef = ref<HTMLInputElement>();
|
||||
|
||||
// 模拟 initSearchData
|
||||
const initSearchData = {
|
||||
dmStcd: "008660306300000079", // 默认水电站ID,实际可能需要动态获取
|
||||
stcd: {
|
||||
dataDimensionData: "all",
|
||||
dataDimensionType: "hyBase",
|
||||
hbrvcd: "",
|
||||
stcdId: "",
|
||||
},
|
||||
typeDate: "",
|
||||
RangePickerList: [
|
||||
dayjs().startOf("month").format("YYYY-MM-DD"),
|
||||
dayjs().endOf("day").format("YYYY-MM-DD"),
|
||||
],
|
||||
};
|
||||
|
||||
const searchData = ref<any>({ ...initSearchData });
|
||||
|
||||
// --- Search List Configuration ---
|
||||
// 电站(下拉框)、STCD:过鱼设施编码(下拉框)、STRDT:开始时间、ENDDT:结束时间、鱼名称(字符串模糊查询)、DIRECTION:游向(0:上行、1:下行、2:上下行)
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: "waterStation",
|
||||
name: "stcd",
|
||||
label: "选择水电站",
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
},
|
||||
options: [],
|
||||
},
|
||||
{
|
||||
type: "Select",
|
||||
name: "strdt",
|
||||
label: "过鱼设施编码",
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
},
|
||||
options: [],
|
||||
},
|
||||
{
|
||||
type: "Select",
|
||||
name: "DIRECTION",
|
||||
label: "游向",
|
||||
width: 120,
|
||||
options: [
|
||||
{ label: "上行", value: "0" },
|
||||
{ label: "下行", value: "1" },
|
||||
{ label: "上下行", value: "2" },
|
||||
],
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "Input",
|
||||
name: "typeDate",
|
||||
label: "鱼名称",
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
checkPerm(['sjtb:edit-ztcx']) && {
|
||||
width: 120,
|
||||
type: "Select",
|
||||
name: "status",
|
||||
label: "审批状态",
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
},
|
||||
options: [
|
||||
{ label: "正常", value: "01" },
|
||||
{ label: "异常", value: "02" },
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
span: 12,
|
||||
type: "RangePicker",
|
||||
name: "RangePickerList",
|
||||
label: "时间",
|
||||
picker: "date",
|
||||
fieldProps: {
|
||||
format: "YYYY-MM-DD",
|
||||
valueFormat: "YYYY-MM-DD",
|
||||
allowClear: false,
|
||||
// disabledDate: disabledDateFn, // 如果需要禁用日期,在此传入函数
|
||||
},
|
||||
presets: DateSetting.RangeButton.days,
|
||||
},
|
||||
]);
|
||||
// --- Methods ---
|
||||
|
||||
// 1. 文件处理逻辑
|
||||
const handleFileSelect = (e: Event) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const file = target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
// 校验文件大小 (50MB)
|
||||
const maxSize = 50 * 1024 * 1024;
|
||||
if (file.size > maxSize) {
|
||||
message.error("文件大小不能超过50MB");
|
||||
resetFileInput();
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验文件类型
|
||||
const isZip =
|
||||
file.name.toLowerCase().endsWith(".zip") ||
|
||||
file.type === "application/zip" ||
|
||||
file.type === "application/x-zip-compressed";
|
||||
|
||||
if (!isZip) {
|
||||
message.error("请选择.zip格式的压缩包");
|
||||
resetFileInput();
|
||||
return;
|
||||
}
|
||||
|
||||
props.importBtn(file);
|
||||
resetFileInput();
|
||||
};
|
||||
|
||||
const resetFileInput = () => {
|
||||
if (fileInputRef.value) {
|
||||
fileInputRef.value.value = "";
|
||||
}
|
||||
};
|
||||
|
||||
const triggerFileInput = () => {
|
||||
fileInputRef.value?.click();
|
||||
};
|
||||
|
||||
// 2. 搜索表单逻辑
|
||||
const onSearchFinish = (values: any) => {
|
||||
console.log(values);
|
||||
// 模拟获取 label,实际可能需要根据 dmStcd 查找名称
|
||||
// 在原 React 代码中,label 来自 options.find(...)
|
||||
// 这里简化处理,直接传递 ID 或固定名称,或者你需要维护一个电站列表映射
|
||||
const label = "默认水温站"; // TODO: 根据 values.dmStcd 查找真实名称
|
||||
const params: any = {};
|
||||
if (values.RangePickerList) {
|
||||
params.startDate = values.RangePickerList[0].format("YYYY-MM-DD");
|
||||
params.endDate = values.RangePickerList[1].format("YYYY-MM-DD");
|
||||
}
|
||||
|
||||
emit("searchFinish", values, label);
|
||||
};
|
||||
|
||||
const onValuesChange = (changedValues: any, allValues: any) => {
|
||||
// 同步更新本地 searchData,以便其他逻辑使用
|
||||
searchData.value = { ...searchData.value, ...allValues };
|
||||
|
||||
if (changedValues.RangePickerList) {
|
||||
// 如果需要在时间改变时做额外处理
|
||||
console.log("Time changed:", changedValues.RangePickerList);
|
||||
}
|
||||
};
|
||||
|
||||
const handleReset = (form: any) => {
|
||||
// 重置表单
|
||||
if (form) {
|
||||
form.resetFields();
|
||||
}
|
||||
|
||||
// 重置后重新设置默认值并触发搜索
|
||||
nextTick(() => {
|
||||
if (form) {
|
||||
form.setFieldsValue(initSearchData);
|
||||
}
|
||||
// 触发初始搜索
|
||||
emit("searchFinish", initSearchData, "两河口出库水温站");
|
||||
});
|
||||
};
|
||||
|
||||
// --- Lifecycle ---
|
||||
onMounted(() => {
|
||||
// 初始请求
|
||||
emit("searchFinish", initSearchData, "两河口出库水温站");
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -25,13 +25,20 @@ export default ({ mode }: ConfigEnv): UserConfig => {
|
||||
proxy: {
|
||||
[env.VITE_APP_BASE_API]: {
|
||||
// 线上API地址
|
||||
//target: 'http://192.168.1.20:8090/',
|
||||
// target: 'http://10.84.121.4:8093/',
|
||||
// 本地API地址
|
||||
target: 'http://localhost:8093',
|
||||
changeOrigin: true,
|
||||
rewrite: path =>
|
||||
path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')
|
||||
},
|
||||
'/geoserver': {
|
||||
// target: 'https://211.99.26.225:18085', // 地图-线上API地址
|
||||
target: 'http://172.16.31.112:18084', // 地图-本地API地址
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
rewrite: path => path.replace(new RegExp('^/geoserver'), '/geoserver')
|
||||
},
|
||||
'/process': {
|
||||
target: 'http://localhost:5174',
|
||||
changeOrigin: true
|
||||
|
||||
Loading…
Reference in New Issue
Block a user