添加栖息地弹框,修改批复文件显示多个

This commit is contained in:
扈兆增 2026-06-10 15:45:16 +08:00
parent cc8c1e0647
commit 34502b7ed2
18 changed files with 7774 additions and 4450 deletions

View File

@ -1,6 +1,12 @@
import request from '@/utils/request'; import request from '@/utils/request';
import { AxiosPromise } from 'axios'; import { AxiosPromise } from 'axios';
import { LoginData, TokenResult, VerifyCode } from './types'; import { LoginData, TokenResult, VerifyCode } from './types';
export function getAccessToken() {
return request({
url: '/register/accessToken',
method: 'get'
});
}
/** /**
* *
@ -52,8 +58,11 @@ export function registerUser(data: any): AxiosPromise<any> {
*/ */
export function getBasinList(): AxiosPromise<any[]> { export function getBasinList(): AxiosPromise<any[]> {
return request({ return request({
url: '/env/rvcd/regDropdown', url: '/register/dropdown/rvcd',
method: 'get', method: 'get',
headers: {
'register-token': localStorage.getItem('register-token') || ''
}
// params: baseid ? { baseid } : {} // params: baseid ? { baseid } : {}
}); });
} }
@ -63,9 +72,12 @@ export function getBasinList(): AxiosPromise<any[]> {
*/ */
export function getGroupList(): AxiosPromise<any[]> { export function getGroupList(): AxiosPromise<any[]> {
return request({ return request({
url: '/env/hycd/regDropdown', url: '/register/dropdown/hycd',
method: 'get', method: 'get',
params:{lx:1} params: { lx: 1 },
headers: {
'register-token': localStorage.getItem('register-token') || ''
}
}); });
} }
@ -74,9 +86,12 @@ export function getGroupList(): AxiosPromise<any[]> {
*/ */
export function getCompanyList(): AxiosPromise<any[]> { export function getCompanyList(): AxiosPromise<any[]> {
return request({ return request({
url: '/env/hycd/regDropdown', url: '/register/dropdown/hycd',
method: 'get', method: 'get',
params: { lx:2 } params: { lx: 2 },
headers: {
'register-token': localStorage.getItem('register-token') || ''
}
}); });
} }
@ -85,9 +100,12 @@ export function getCompanyList(): AxiosPromise<any[]> {
*/ */
export function getStationList(params: any): AxiosPromise<any[]> { export function getStationList(params: any): AxiosPromise<any[]> {
return request({ return request({
url: '/env/engInfo/regDropdown', url: '/register/dropdown/engInfo',
method: 'post', method: 'post',
data: params data: params,
headers: {
'register-token': localStorage.getItem('register-token') || ''
}
}); });
} }
@ -110,7 +128,11 @@ export function sendSmsCode(phone: string, type = 1): AxiosPromise<any> {
* @param code * @param code
* @param type 1- 2- * @param type 1- 2-
*/ */
export function verifySmsCode(phone: string, code: string, type = 1): AxiosPromise<any> { export function verifySmsCode(
phone: string,
code: string,
type = 1
): AxiosPromise<any> {
return request({ return request({
url: '/sms/verifyCode', url: '/sms/verifyCode',
method: 'post', method: 'post',
@ -123,7 +145,10 @@ export function verifySmsCode(phone: string, code: string, type = 1): AxiosPromi
* @param phone * @param phone
* @param code * @param code
*/ */
export function smsLoginApi(phone: string, code: string): AxiosPromise<TokenResult> { export function smsLoginApi(
phone: string,
code: string
): AxiosPromise<TokenResult> {
return request({ return request({
url: '/sms/login', url: '/sms/login',
method: 'post', method: 'post',
@ -137,7 +162,11 @@ export function smsLoginApi(phone: string, code: string): AxiosPromise<TokenResu
* @param code * @param code
* @param password * @param password
*/ */
export function resetPassword(phone: string, code: string, password: string): AxiosPromise<any> { export function resetPassword(
phone: string,
code: string,
password: string
): AxiosPromise<any> {
return request({ return request({
url: '/sms/resetPassword', url: '/sms/resetPassword',
method: 'post', method: 'post',

View File

@ -62,10 +62,33 @@ export function getMonitorData(data: any) {
data data
}); });
} }
// 监测数据 水温查询 栖息地
export function getMonitorDataWt(data: any) {
return request({
url: '/wt/msstbprpt/GetKendoList',
method: 'post',
data
});
}
export function getMonitorDataWq(data: any) {
return request({
url: '/wq/msstbprpt/GetKendoList',
method: 'post',
data
});
}
export function getMonitorDataZq(data: any) {
return request({
url: '/zq/msstbprpt/GetKendoList',
method: 'post',
data
});
}
// 监测数据 - 水温 // 监测数据 - 水温
export function getMonitorDataWaterTemp(data: any) { export function getMonitorDataWaterTemp(data: any) {
return request({ return request({
url: '/api/wmp-env-server/sw/alongDetail/qgc/GetKendoListCust', url: '/wt/alongDetail/qgc/GetKendoListCust',
method: 'post', method: 'post',
data data
}); });
@ -73,7 +96,7 @@ export function getMonitorDataWaterTemp(data: any) {
// 监测数据 - 水温 - 查询显示 综合/等温 // 监测数据 - 水温 - 查询显示 综合/等温
export function getMonitorDataWaterTempPowerStation(params: any) { export function getMonitorDataWaterTempPowerStation(params: any) {
return request({ return request({
url: '/api/wmp-env-server/sw/wtrv/getIoWtrvFlag', url: '/wt/wtrv/getIoWtrvFlag',
method: 'get', method: 'get',
params params
}); });
@ -81,7 +104,7 @@ export function getMonitorDataWaterTempPowerStation(params: any) {
// 监测数据 - 水温 - 、综合分析导出数据 // 监测数据 - 水温 - 、综合分析导出数据
export function exportMonitorDataWaterTempPowerStation(params: any) { export function exportMonitorDataWaterTempPowerStation(params: any) {
return request({ return request({
url: '/api/wmp-env-server/sw/alongDetail/qgc/GetKendoListCust', url: '/wt/alongDetail/qgc/GetKendoListCust',
method: 'post', method: 'post',
params, params,
responseType: 'blob' responseType: 'blob'
@ -91,32 +114,55 @@ export function exportMonitorDataWaterTempPowerStation(params: any) {
// 监测数据 - 水温 - 查询是否显示 等温水深 // 监测数据 - 水温 - 查询是否显示 等温水深
export function getMonitorDataWaterTempPowerStation2(params: any) { export function getMonitorDataWaterTempPowerStation2(params: any) {
return request({ return request({
url: '/api/wmp-env-server/sw/alongDetail/qgc/stcdCheck2', url: '/wt/alongDetail/qgc/stcdCheck2',
method: 'get', method: 'get',
params params
}); });
} }
// 监测数据 - 垂向水温 // 监测数据 - 垂向水温
export function getMonitorDataWaterTempVertical(data: any) { export function getMonitorDataWaterTempVertical(data: any) {
return request({ return request({
url: '/api/wmp-env-server/sw/cxDetail/GetKendoListCust', url: '/wt/cxDetail/GetKendoListCust',
method: 'post',
data
});
}
// 监测数据 - 水质 判断是否显示综合分析
export function getMonitorDataWaterQuality(data: any) {
return request({
url: '/wq/vmsstbprpt/GetKendoList',
method: 'post',
data
});
}
// 监测数据 - 水质 - 获取水质参数
export function getMonitorDataWaterQualityDetail(data: any) {
return request({
url: '/wq/stbprp/GetStbprpYsByStcd',
method: 'get',
params: data
});
}
// 监测数据 - 水质 - 获取水质数据
export function getMonitorDataWaterQualityList(data: any) {
return request({
url: '/wq/data/noAuth/GetKendoListCust',
method: 'post', method: 'post',
data data
}); });
} }
// 生态流量 - 限制范围查询 - 日 // 生态流量 - 限制范围查询 - 日
export function getEngLimitDay(data: any) { export function getMonitorDataWaterTempVerticalDay(data: any) {
return request({ return request({
url: '/eq/interval/qgc/day/GetKendoListCust', url: '/eq/data/day/GetKendoListCust',
method: 'post', method: 'post',
data data
}); });
} }
// 生态流量 - 限制范围查询 - 月 // 生态流量 - 限制范围查询 - 小时
export function getEngLimit(data: any) { export function getMonitorDataWaterTempVerticalHour(data: any) {
return request({ return request({
url: '/eq/interval/qgc/month/GetKendoListCust', url: '/eq/data/GetKendoListCust',
method: 'post', method: 'post',
data data
}); });

File diff suppressed because it is too large Load Diff

View File

@ -143,6 +143,7 @@ const loadApprovalData = () => {
loading.value = true; loading.value = true;
queryPostUrlList(props.url, params).then(res => { queryPostUrlList(props.url, params).then(res => {
const data = res.data?.data || []; const data = res.data?.data || [];
if (data[0].flid == null) {
const fileArray = data.map((item: any) => ({ const fileArray = data.map((item: any) => ({
id: item.flpath.split('?')[1] || '', id: item.flpath.split('?')[1] || '',
name: item.approvalFileNo || item.approvalFileTitle, name: item.approvalFileNo || item.approvalFileTitle,
@ -150,6 +151,21 @@ const loadApprovalData = () => {
})); }));
hasLoaded.value = true; hasLoaded.value = true;
updateFileList(fileArray); updateFileList(fileArray);
} else {
getIdUrl({
id: data[0].flid
}).then(res1 => {
const data = res1?.data || {};
const fileArray = Object.keys(data).map(key => ({
id: key,
name: data[key].name,
url: data[key].fullpath
}));
loading.value = false;
hasLoaded.value = true;
updateFileList(fileArray);
});
}
}); });
}; };

View File

@ -39,7 +39,15 @@
</a-tooltip> </a-tooltip>
</div> </div>
<div class="col-right" v-if="item.visible"> <div class="col-right" v-if="item.visible">
<a-tooltip :title="!isEdit ? item.vlsr : ''"> <a-tooltip
:title="
!isEdit
? item.vlsr || String(item.value).length > 10
? item.value
: ''
: ''
"
>
<span v-if="!isEdit && item.filed != ''">{{ <span v-if="!isEdit && item.filed != ''">{{
item.value || '-' item.value || '-'
}}</span> }}</span>
@ -250,6 +258,10 @@ const columnsConfig = ref([
{ {
type: 'DW_1', type: 'DW_1',
columns: DwOnePointColumns columns: DwOnePointColumns
},
{
type: 'ZQ',
columns: FhZQPointColumns
} }
//DW_1 //DW_1

View File

@ -45,9 +45,15 @@
</div> </div>
<!-- 底部图表区 --> <!-- 底部图表区 -->
<div class="bottom-section"> <div class="bottom-section">
<a-spin :spinning="isChartLoading" tip="加载中..."> <a-spin v-show="hasData" :spinning="isChartLoading" tip="加载中...">
<div class="chart-wrapper" ref="chartRef"></div> <div class="chart-wrapper" ref="chartRef"></div>
</a-spin> </a-spin>
<a-empty
v-show="!hasData && !isChartLoading"
description="暂无数据"
class="empty-wrapper h-full flex items-center justify-center"
style="flex-direction: column"
/>
</div> </div>
</div> </div>
</template> </template>
@ -55,7 +61,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, computed, watch, onUnmounted, nextTick } from 'vue'; import { ref, onMounted, computed, watch, onUnmounted, nextTick } from 'vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import { getEngLimit, getEngLimitDay } from '@/api/mapModal'; import {
getMonitorDataWaterTempVerticalDay,
getMonitorDataWaterTempVerticalHour
} from '@/api/mapModal';
import { useModelStore } from '@/store/modules/model'; import { useModelStore } from '@/store/modules/model';
import { DateSetting } from '@/utils/enumeration'; import { DateSetting } from '@/utils/enumeration';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
@ -74,6 +83,7 @@ const props = defineProps({
}); });
const hasLoaded = ref<boolean>(false); // const hasLoaded = ref<boolean>(false); //
const isChartLoading = ref<boolean>(false); const isChartLoading = ref<boolean>(false);
const hasData = ref<boolean>(true); //
const unqualifiedCount = ref<number>(0); const unqualifiedCount = ref<number>(0);
const complianceRate = ref<string>(''); const complianceRate = ref<string>('');
const limitInfo = ref<string>(''); const limitInfo = ref<string>('');
@ -131,7 +141,7 @@ const fetchLimitInfo = async () => {
const stcd = modelStore.params.stcd; const stcd = modelStore.params.stcd;
const { startTime, endTime } = getDateRange(); const { startTime, endTime } = getDateRange();
const res = await getEngLimit({ const res = await getMonitorDataWaterTempVerticalHour({
filter: { filter: {
logic: 'and', logic: 'and',
filters: [ filters: [
@ -165,7 +175,7 @@ const fetchDayData = async () => {
const stcd = modelStore.params.stcd; const stcd = modelStore.params.stcd;
const { startTime, endTime } = getDateRange(); const { startTime, endTime } = getDateRange();
const res = await getEngLimitDay({ const res = await getMonitorDataWaterTempVerticalDay({
filter: { filter: {
logic: 'and', logic: 'and',
filters: [ filters: [
@ -206,27 +216,6 @@ const fetchDayData = async () => {
const chartRef = ref<HTMLDivElement | null>(null); const chartRef = ref<HTMLDivElement | null>(null);
let chartInstance: echarts.ECharts | null = null; let chartInstance: echarts.ECharts | null = null;
//
const getMockData = () => {
const now = dayjs();
const data: any[] = [];
const count = timeType.value === 'hour' ? 48 : 30; // 4830
for (let i = count - 1; i >= 0; i--) {
const tm =
timeType.value === 'hour'
? now.subtract(i, 'hour').format('YYYY-MM-DD HH:mm:ss')
: now.subtract(i, 'day').format('YYYY-MM-DD HH:mm:ss');
data.push({
tm,
qi: Math.round(Math.random() * 100 + 150), // 150-250
qec: Math.round(Math.random() * 50 + 100), // 100-150
qecLimit: 145, //
qecSameLastYear: Math.round(Math.random() * 60 + 90) // 90-150
});
}
return data;
};
// //
const updateChart = (data: any[]) => { const updateChart = (data: any[]) => {
if (!chartInstance) return; if (!chartInstance) return;
@ -241,18 +230,22 @@ const updateChart = (data: any[]) => {
(a, b) => new Date(a.tm).getTime() - new Date(b.tm).getTime() (a, b) => new Date(a.tm).getTime() - new Date(b.tm).getTime()
); );
const xAxisData = sorted.map(item => dayjs(item.tm).format('MM-DD HH:mm')); const xAxisData = sorted.map(item =>
dayjs(timeType.value === 'hour' ? item.tm : item.dt).format(
timeType.value === 'hour' ? 'YYYY-MM-DD HH:mm' : 'YYYY-MM-DD'
)
);
const qiData = sorted.map(item => item.qi); const qiData = sorted.map(item => item.qi);
const qecData = sorted.map(item => item.qec); const qecData = sorted.map(item => item.qec);
const qecLimitData = sorted.map(item => item.qecLimit); const qecLimitData = sorted.map(item => item.qecLimit);
const qecSameLastYearData = sorted.map(item => item.qecSameLastYear); const beforeQecData = sorted.map(item => item.beforeQec);
// Y // Y
const flowValues = [ const flowValues = [
...qiData, ...qiData,
...qecData, ...qecData,
...qecLimitData, ...qecLimitData,
...qecSameLastYearData ...beforeQecData
].filter(v => v !== null && v !== undefined); ].filter(v => v !== null && v !== undefined);
const flowMax = flowValues.length > 0 ? Math.max(...flowValues) : 200; const flowMax = flowValues.length > 0 ? Math.max(...flowValues) : 200;
const flowYMax = Math.ceil(flowMax * 1.1); const flowYMax = Math.ceil(flowMax * 1.1);
@ -267,8 +260,18 @@ const updateChart = (data: any[]) => {
formatter: (params: any) => { formatter: (params: any) => {
if (!params || params.length === 0) return ''; if (!params || params.length === 0) return '';
const dataIndex = params[0].dataIndex; const dataIndex = params[0].dataIndex;
const fullTime = sorted[dataIndex]?.tm console.log(sorted[dataIndex]);
? dayjs(sorted[dataIndex].tm).format('YYYY-MM-DD HH:mm:ss') const fullTime =
timeType.value === 'hour'
? sorted[dataIndex]?.tm
: sorted[dataIndex]?.dt
? dayjs(
timeType.value === 'hour'
? sorted[dataIndex].tm
: sorted[dataIndex].dt
).format(
timeType.value === 'hour' ? 'YYYY-MM-DD HH:mm' : 'YYYY-MM-DD'
)
: ''; : '';
let html = `<div style="font-size:16px;margin-bottom:8px;">${fullTime}</div>`; let html = `<div style="font-size:16px;margin-bottom:8px;">${fullTime}</div>`;
// //
@ -369,7 +372,7 @@ const updateChart = (data: any[]) => {
{ {
name: '去年同期生态流量', name: '去年同期生态流量',
type: 'line', type: 'line',
data: qecSameLastYearData, data: beforeQecData,
smooth: true, smooth: true,
symbol: 'circle', symbol: 'circle',
symbolSize: 4, symbolSize: 4,
@ -405,7 +408,9 @@ const initChart = () => {
if (chartInstance) { if (chartInstance) {
chartInstance.dispose(); chartInstance.dispose();
} }
nextTick(() => {
chartInstance = echarts.init(chartRef.value); chartInstance = echarts.init(chartRef.value);
});
}; };
const handleResize = () => { const handleResize = () => {
@ -422,22 +427,45 @@ const destroyChart = () => {
window.removeEventListener('resize', handleResize); window.removeEventListener('resize', handleResize);
}; };
// //
const fetchChartData = async () => { const fetchChartData = async () => {
isChartLoading.value = true; isChartLoading.value = true;
try { try {
// TODO: const stcd = modelStore.params.stcd;
// const stcd = modelStore.params.stcd; const { startTime, endTime } = getDateRange();
// const { startTime, endTime } = getDateRange();
// const res = await getEngChartList({ ... });
// updateChart(res?.data?.data || []);
// 2 loading const apiFn =
await new Promise(resolve => setTimeout(resolve, 2000)); timeType.value === 'day'
? getMonitorDataWaterTempVerticalDay
: getMonitorDataWaterTempVerticalHour;
// 使 const res = await apiFn({
const mockData = getMockData(); filter: {
updateChart(mockData); logic: 'and',
filters: [
{ field: 'stcd', operator: 'eq', dataType: 'string', value: stcd },
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: startTime
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: endTime
}
]
},
sort: [{ field: 'tm', dir: 'asc' }]
});
const rawData = res?.data?.data || [];
hasData.value = rawData.length > 0;
if (hasData.value) {
updateChart(rawData);
}
hasLoaded.value = true; hasLoaded.value = true;
} catch (error) { } catch (error) {
console.error('获取图表数据失败:', error); console.error('获取图表数据失败:', error);
@ -448,6 +476,7 @@ const fetchChartData = async () => {
// //
const handleQuery = () => { const handleQuery = () => {
hasData.value = true;
fetchLimitInfo(); fetchLimitInfo();
fetchDayData(); fetchDayData();
fetchChartData(); fetchChartData();
@ -456,6 +485,8 @@ const handleQuery = () => {
// timeType // timeType
watch(timeType, () => { watch(timeType, () => {
dateRange.value = initDateRange(); dateRange.value = initDateRange();
hasData.value = true;
fetchChartData();
}); });
watch( watch(
@ -466,10 +497,8 @@ watch(
fetchDayData(); fetchDayData();
// //
nextTick(() => { nextTick(() => {
setTimeout(() => {
initChart(); initChart();
fetchChartData(); fetchChartData();
}, 2000);
}); });
window.addEventListener('resize', handleResize); window.addEventListener('resize', handleResize);
} }
@ -477,7 +506,9 @@ watch(
{ immediate: true } { immediate: true }
); );
onMounted(() => {}); onMounted(() => {
window.addEventListener('resize', handleResize);
});
onUnmounted(() => { onUnmounted(() => {
destroyChart(); destroyChart();

View File

@ -0,0 +1,626 @@
<template>
<div class="flow-measure">
<a-tabs v-model:activeKey="activeTabKey">
<a-tab-pane v-for="tab in tabsList" :key="tab.key" :tab="tab.name" />
<template #rightExtra>
<div class="search-bar">
<a-range-picker
v-model:value="dateRange"
format="YYYY-MM-DD HH:mm"
:show-time="showTimeConfig"
:allowClear="false"
:presets="DateSetting.RangeButton.month1"
:disabled-date="disabledDate"
/>
<a-button type="primary" class="search-btn" @click="handleSearch">
查询
</a-button>
</div>
</template>
</a-tabs>
<a-spin :spinning="isLoading" tip="加载中...">
<div class="tab-content">
<div class="content-body">
<div class="chart-wrapper">
<div ref="chartRef" class="chart-container"></div>
<a-empty
v-if="!chartData || chartData.length === 0"
description="暂无数据"
class="chart-empty"
/>
</div>
<div class="table-wrapper">
<BasicTable
:scrollY="480"
:scrollX="tableScrollX"
:columns="tableColumns"
:data="tableData"
/>
</div>
</div>
</div>
</a-spin>
<!-- 视频播放弹窗 -->
<a-modal
v-model:open="videoModalVisible"
:title="videoModalTitle"
:width="800"
:footer="null"
>
<div class="video-container">
<video
v-if="currentVideoUrl"
:src="currentVideoUrl"
controls
autoplay
class="video-player"
/>
<a-empty v-else description="暂无视频" />
</div>
</a-modal>
</div>
</template>
<script lang="ts" setup>
import {
ref,
watch,
onBeforeUnmount,
onMounted,
nextTick,
h,
computed
} from 'vue';
import dayjs, { Dayjs } from 'dayjs';
import * as echarts from 'echarts';
import { queryPostUrlList } from '@/api/mapModal';
import { useModelStore } from '@/store/modules/model';
import BasicTable from '@/components/BasicTable/index.vue';
import { DateSetting } from '@/utils/enumeration';
const modelStore = useModelStore();
const props = defineProps({
isActive: { type: Boolean, default: false },
url: { type: String, default: '' },
code: { type: String, default: '' },
stcd: { type: String, default: '' }
});
const isQxdMode = computed(() => props.code === 'qxd.tabs.lcjs');
const hasLoaded = ref(false);
const isLoading = ref(false);
const tableData = ref<any[]>([]);
const chartData = ref<any[]>([]);
const chartRef = ref<HTMLElement>();
let chartInstance: echarts.ECharts | null = null;
//
const videoModalVisible = ref(false);
const videoModalTitle = ref('');
const currentVideoUrl = ref('');
const tabsList = [{ name: '流量监测', key: 'flowMeasure' }];
const activeTabKey = ref('flowMeasure');
//
const showTimeConfig = {
format: 'HH:mm',
hourStep: 1,
minuteStep: 5,
secondStep: 60
};
const disabledDate = (current: Dayjs) =>
current && current.isAfter(dayjs(), 'day');
const initDateRange = (): [Dayjs, Dayjs] => {
return [dayjs().subtract(7, 'day'), dayjs()];
};
const dateRange = ref<[Dayjs, Dayjs] | undefined>(initDateRange());
//
const tableColumns = [
{
title: '时间',
dataIndex: 'tm',
width: 180,
fixed: 'left',
customRender: ({ text }: any) =>
text ? dayjs(text).format('YYYY-MM-DD HH:mm:ss') : '-'
},
{
title: '水位(m)',
dataIndex: 'z',
width: 120,
customRender: ({ text }: any) =>
text !== undefined && text !== null ? Number(text).toFixed(2) : '-'
},
{
title: '流量(m³/s)',
dataIndex: 'q',
width: 130,
customRender: ({ text }: any) =>
text !== undefined && text !== null ? Number(text).toFixed(1) : '-'
},
{
title: '流速(m³/s)',
dataIndex: 'v',
width: 130,
customRender: ({ text }: any) =>
text !== undefined && text !== null ? Number(text).toFixed(2) : '-'
},
{
title: '水位视频',
dataIndex: 'rzVideoPath',
width: 120,
customRender: ({ record }: any) => {
if (!record.rzVideoPath) return '-';
return h(
'a-button',
{
type: 'link',
size: 'small',
onClick: () => playVideo(record.rzVideoPath, '水位视频')
},
'-'
);
}
},
{
title: '流速视频',
dataIndex: 'speedVideoPath',
width: 120,
customRender: ({ record }: any) => {
if (!record.speedVideoPath) return '-';
return h(
'a-button',
{
type: 'link',
size: 'small',
onClick: () => playVideo(record.speedVideoPath, '流速视频')
},
'-'
);
}
}
];
const tableScrollX = tableColumns.reduce((s, c) => s + (c.width || 100), 0);
//
const playVideo = (videoUrl: string, title: string) => {
currentVideoUrl.value = videoUrl;
videoModalTitle.value = title;
videoModalVisible.value = true;
};
//
const initChart = () => {
if (!chartRef.value) return;
if (chartInstance) chartInstance.dispose();
chartInstance = echarts.init(chartRef.value);
updateChart(chartData.value);
};
//
const updateChart = (data: any[]) => {
if (!chartInstance) return;
if (!data || data.length === 0) {
chartInstance.clear();
return;
}
const sorted = [...data].sort(
(a, b) => new Date(a.tm).getTime() - new Date(b.tm).getTime()
);
const xAxisData = sorted.map(item => {
const hm = dayjs(item.tm).format('HH:mm');
if (hm === '00:00') {
const date = dayjs(item.tm).format('MM-DD');
return `${hm}\n${date}`;
}
return hm;
});
const zData = sorted.map(item => item.z);
const qData = sorted.map(item => item.q);
const vData = sorted.map(item => item.v);
const waterValues = zData.filter(v => v !== null && v !== undefined);
const waterMin = waterValues.length > 0 ? Math.min(...waterValues) : 0;
const waterMax = waterValues.length > 0 ? Math.max(...waterValues) : 10;
const flowValues = [...qData, ...vData].filter(
v => v !== null && v !== undefined
);
const flowMin = flowValues.length > 0 ? Math.min(...flowValues) : 0;
const flowMax = flowValues.length > 0 ? Math.max(...flowValues) : 10;
const formatRules: Record<
string,
{ format: (v: number) => string; unit: string }
> = {
水位: { format: v => v.toFixed(2), unit: '(m)' },
流量: { format: v => String(Math.round(v)), unit: '(m³/s)' },
流速: { format: v => v.toFixed(2), unit: '(m³/s)' }
};
const option = {
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(50, 50, 50, 0.9)',
textStyle: { color: '#fff', fontSize: 12 },
axisPointer: { type: 'cross' },
formatter: (params: any) => {
if (!params || params.length === 0) return '';
const dataIndex = params[0].dataIndex;
const fullTime = sorted[dataIndex]?.tm
? dayjs(sorted[dataIndex].tm).format('YYYY-MM-DD HH:mm:ss')
: '';
let html = `<div style="font-size:16px;margin-bottom:8px;">${fullTime}</div>`;
params.forEach((param: any) => {
if (param.value == null) return;
const rule = Object.entries(formatRules).find(([key]) =>
param.seriesName.includes(key)
);
const displayValue = rule
? rule[1].format(Number(param.value))
: param.value;
const unit = rule ? rule[1].unit : '';
html += `
<div style="display:flex;align-items:center;justify-content:space-between;margin:4px 0;">
<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${param.color};margin-right:8px;"></span>
<span style="flex:1;font-size:14px;text-align:left;margin-right:6px;">${param.seriesName}: </span>
<span style="font-size:14px;min-width:60px;text-align:right;"><strong>${displayValue}</strong> ${unit}</span>
</div>
`;
});
return html;
}
},
legend: {
type: 'scroll',
top: 10,
data: ['水位', '流量', '流速'],
textStyle: { fontSize: 12 },
selected: {
水位: true,
流量: true,
流速: true
}
},
grid: {
left: 60,
right: 100,
top: 80,
bottom: 60
},
xAxis: {
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#000000' } },
axisTick: { show: false },
axisLabel: { fontSize: 12 },
splitLine: {
show: true,
lineStyle: { color: '#bfbfbf', type: 'solid' }
}
},
yAxis: [
{
name: '水位(m)',
type: 'value',
position: 'left',
axisLine: { show: true, lineStyle: { color: '#56C2E3' } },
axisLabel: { color: '#56C2E3' },
nameTextStyle: { color: '#56C2E3' },
alignTicks: true,
scale: true,
splitNumber: 9
},
{
name: '流量(m³/s)',
type: 'value',
position: 'right',
axisLine: { show: true, lineStyle: { color: '#9556A4' } },
axisLabel: { color: '#9556A4' },
nameTextStyle: { color: '#9556A4' },
alignTicks: true,
scale: true,
splitNumber: 9
},
{
name: '流速(m³/s)',
type: 'value',
position: 'right',
axisLine: { show: true, lineStyle: { color: '#78C300' } },
axisLabel: { color: '#78C300' },
nameTextStyle: { color: '#78C300' },
alignTicks: true,
scale: true,
splitNumber: 9,
offset: 60
}
],
series: [
{
name: '水位',
type: 'line',
yAxisIndex: 0,
data: zData,
smooth: true,
symbol: 'circle',
symbolSize: 4,
lineStyle: { color: '#56C2E3', width: 2 },
itemStyle: { color: '#56C2E3' }
},
{
name: '流量',
type: 'line',
yAxisIndex: 1,
data: qData,
smooth: true,
symbol: 'circle',
symbolSize: 4,
lineStyle: { color: '#9556A4', width: 2 },
itemStyle: { color: '#9556A4' }
},
{
name: '流速',
type: 'line',
yAxisIndex: 2,
data: vData,
smooth: true,
symbol: 'circle',
symbolSize: 4,
lineStyle: { color: '#78C300', width: 2 },
itemStyle: { color: '#78C300' }
}
],
dataZoom: [
{
type: 'inside',
xAxisIndex: [0],
throttle: 50,
start: 0,
end: 100
}
],
toolbox: {
show: true,
feature: {
saveAsImage: { title: '保存为图片', type: 'png', pixelRatio: 2 }
},
right: 20,
top: 10
}
};
chartInstance.setOption(option, true);
};
//
const fetchData = async () => {
if (!dateRange.value) return;
// qxdstcd
if (isQxdMode.value && !props.stcd) {
return;
}
isLoading.value = true;
try {
// 使stcdqxd使props.stcd使modelStore.params.stcd
const currentStcd = isQxdMode.value ? props.stcd : modelStore.params.stcd;
const filterParams = {
filter: {
logic: 'and',
filters: [
{
field: 'stcd',
operator: 'eq',
dataType: 'string',
value: currentStcd
},
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: dateRange.value[0].format('YYYY-MM-DD HH:mm:ss')
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: dateRange.value[1].format('YYYY-MM-DD HH:mm:ss')
}
]
},
sort: [{ field: 'tm', dir: 'asc' }]
};
// qxd使getMonitorDataZqqxd使queryPostUrlList
let rawData;
if (isQxdMode.value) {
const { getMonitorDataZq } = await import('@/api/mapModal');
const res = await getMonitorDataZq(filterParams);
rawData = res?.data?.data || res?.data?.records || [];
} else {
const res = await queryPostUrlList(props.url, filterParams);
rawData = res?.data?.data || res?.data?.records || [];
}
tableData.value = [...rawData].reverse();
chartData.value = rawData;
nextTick(() => updateChart(rawData));
} catch (error) {
console.error('获取数据失败:', error);
tableData.value = [];
chartData.value = [];
if (chartInstance) chartInstance.clear();
} finally {
isLoading.value = false;
}
};
const handleSearch = () => fetchData();
const initData = () => {
if (hasLoaded.value) return;
nextTick(() => initChart());
fetchData();
hasLoaded.value = true;
};
watch(
() => props.isActive,
active => {
if (active && !hasLoaded.value) initData();
},
{ immediate: true }
);
watch(
() => modelStore.params.stcd,
(newStcd, oldStcd) => {
// qxdstorestcd
if (!isQxdMode.value && newStcd && newStcd !== oldStcd) fetchData();
}
);
// qxdprops.stcd
watch(
() => props.stcd,
(newStcd, oldStcd) => {
if (isQxdMode.value && newStcd && newStcd !== oldStcd) {
hasLoaded.value = false;
initData();
}
}
);
let resizeObserver: ResizeObserver | null = null;
watch(
() => props.isActive,
active => {
if (active && chartRef.value) {
setTimeout(() => {
if (chartInstance) chartInstance.resize();
}, 200);
if (!resizeObserver) {
resizeObserver = new ResizeObserver(() => {
if (chartInstance) chartInstance.resize();
});
resizeObserver.observe(chartRef.value);
}
}
}
);
const handleResize = () => {
if (chartInstance) chartInstance.resize();
};
onMounted(() => {
window.addEventListener('resize', handleResize);
});
onBeforeUnmount(() => {
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
window.removeEventListener('resize', handleResize);
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
});
</script>
<style lang="scss" scoped>
.flow-measure {
width: 100%;
min-height: 600px;
position: relative;
:deep(.ant-tabs-nav) {
margin-bottom: 4px;
}
.search-bar {
width: 100%;
margin-bottom: 12px;
display: flex;
align-items: center;
justify-content: flex-end;
.search-btn {
margin-left: 8px;
}
}
.tab-content {
width: 100%;
.content-body {
width: 100%;
display: flex;
height: 600px;
position: relative;
.chart-wrapper {
flex: 1;
height: 100%;
position: relative;
box-sizing: border-box;
padding-top: 10px;
.chart-container {
width: 100%;
height: 100%;
}
}
.chart-empty {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.table-wrapper {
flex: 1;
min-width: 500px;
height: 100%;
box-sizing: border-box;
padding: 10px;
overflow: hidden;
overflow-x: auto;
border-left: 1px solid #dcdfe6;
margin-left: 10px;
}
}
}
}
.video-container {
width: 100%;
min-height: 400px;
display: flex;
align-items: center;
justify-content: center;
.video-player {
width: 100%;
max-height: 600px;
border-radius: 4px;
}
}
</style>

View File

@ -321,7 +321,7 @@ const updateChart = (selectedRows: any[]) => {
fontSize: 12, fontSize: 12,
color: '#000000', color: '#000000',
formatter: function (value) { formatter: function (value) {
return value + '℃'; return value.toFixed(1) + '℃';
} }
}, },
splitLine: { show: true } splitLine: { show: true }
@ -349,6 +349,13 @@ const updateChart = (selectedRows: any[]) => {
}, },
right: 20, right: 20,
top: 10 top: 10
},
dataZoom: {
type: 'inside',
xAxisIndex: [0],
throttle: 50,
start: 0,
end: 100
} }
}; };

View File

@ -0,0 +1,778 @@
<template>
<div class="water-quality">
<a-tabs v-model:activeKey="activeTabKey">
<a-tab-pane v-for="tab in tabsList" :key="tab.key" :tab="tab.name" />
<template #rightExtra>
<div class="search-bar">
<a-range-picker
v-model:value="dateRange"
format="YYYY-MM-DD HH"
:show-time="showTimeConfig"
:allowClear="false"
:presets="DateSetting.RangeButton.hour"
:disabled-date="disabledDate"
/>
<a-button type="primary" class="search-btn" @click="handleSearch">
查询
</a-button>
<a-button
v-if="isSummaryMode"
class="search-btn"
@click="handleExport"
>
导出
</a-button>
</div>
</template>
</a-tabs>
<div class="tab-checkbox">
<a-checkbox-group
v-model:value="selectedColumns"
name="checkboxgroup"
:options="filteredCheckBoxOptions"
class="checkbox-group"
@change="handleCheckboxChange"
/>
</div>
<a-spin :spinning="isLoading" tip="加载中...">
<div class="tab-content">
<div class="content-body">
<div class="chart-wrapper">
<div ref="chartRef" class="chart-container"></div>
<a-empty
v-if="!chartData || chartData.length === 0"
description="暂无数据"
class="chart-empty"
/>
</div>
<div class="table-wrapper">
<BasicTable
:scrollY="480"
:scrollX="tableScrollX"
:columns="currentColumns"
:data="tableData"
/>
</div>
</div>
</div>
</a-spin>
</div>
</template>
<script lang="ts" setup>
import {
ref,
watch,
computed,
onMounted,
onBeforeUnmount,
nextTick
} from 'vue';
import dayjs, { Dayjs } from 'dayjs';
import * as echarts from 'echarts';
import {
getMonitorDataWaterQuality,
getMonitorDataWaterQualityDetail,
getMonitorDataWaterQualityList
} from '@/api/mapModal';
import { useModelStore } from '@/store/modules/model';
import BasicTable from '@/components/BasicTable/index.vue';
import { DateSetting } from '@/utils/enumeration';
const modelStore = useModelStore();
const props = defineProps({
isActive: { type: Boolean, default: false },
code: { type: String, default: '' },
stcd: { type: String, default: '' }
});
const isQxdMode = computed(() => props.code === 'qxd.tabs.szjc');
const hasLoaded = ref(false);
const isLoading = ref(false);
const tableData = ref<any[]>([]);
const chartData = ref<any[]>([]);
const chartRef = ref<HTMLElement>();
let chartInstance: echarts.ECharts | null = null;
const tabsList = [{ name: '水质', key: 'szjc.tabs.jcsj' }];
const activeTabKey = ref('szjc.tabs.jcsj');
//
const showTimeConfig = {
format: 'HH',
hourStep: 1,
minuteStep: 5,
secondStep: 60
};
const disabledDate = (current: Dayjs) =>
current && current.isAfter(dayjs(), 'day');
const initDateRange = (): [Dayjs, Dayjs] => {
const startDate = dayjs().subtract(7, 'day').startOf('day');
return [startDate, dayjs()];
};
const dateRange = ref<[Dayjs, Dayjs] | undefined>(initDateRange());
// ==================== Checkbox ====================
const selectedColumns = ref<any[]>([]);
const checkBoxOptions = [{ label: '综合分析', value: 'summary' }];
const showSummaryCheckbox = ref(false);
const isDataEmpty = computed(() => {
const data = Array.isArray(chartData.value) ? chartData.value : [];
return data.length === 0;
});
const filteredCheckBoxOptions = computed(() => {
if (isDataEmpty.value) return [];
const available = checkBoxOptions.filter(opt => {
if (opt.value === 'summary' && !showSummaryCheckbox.value) return false;
return true;
});
return available;
});
const isSummaryMode = computed(() => selectedColumns.value.includes('summary'));
const handleCheckboxChange = (values: any[]) => {
selectedColumns.value = values;
if (values.length === 0) {
selectedColumns.value = [];
}
fetchData();
};
// ==================== ====================
const qualityParams = ref<any[]>([]); // enable==1
const allDetailParams = ref<any[]>([]); //
const visibleSeriesNames = ref<string[]>([]); // series2
// showControlunit
const parseUnit = (showControl: string) => {
if (!showControl) return '';
try {
const config = JSON.parse(showControl);
return config.unit || '';
} catch {
return '';
}
};
// ==================== ====================
//
const fixedColumns = [
{
title: '时间',
dataIndex: 'tm',
fixed: 'left',
width: 160,
customRender: ({ text }: any) =>
text ? dayjs(text).format('YYYY-MM-DD HH:mm') : '-'
},
{
title: '水质要求',
dataIndex: 'wwqtgName',
fixed: 'left',
width: 100
},
{
title: '水质等级',
dataIndex: 'wqgrdName',
fixed: 'left',
width: 100
},
{
title: '是否达标',
dataIndex: 'sfdb',
fixed: 'left',
width: 100,
customRender: ({ text }: any) => {
const isUnqualified = text == 0; // 01
return {
props: {
style: { color: isUnqualified ? 'red' : 'inherit' }
},
children: text == 0 ? '不达标' : text == 1 ? '达标' : '-'
};
}
}
];
//
const fixedQualityColumns = computed(() => {
const columns = allDetailParams.value.map((p: any) => {
const unit = parseUnit(p.showControl);
const title = unit ? `${p.ysShowName}(${unit})` : p.ysShowName;
const dataIndex = p.ys.toLowerCase();
const isEnabled = p.enable == 1;
// 10051406160
const titleLen = title.length;
let width = 100;
if (titleLen > 6) {
width = 160;
} else if (titleLen > 5) {
width = 140;
}
const fieldUpper = p.ys.toUpperCase(); // max/min
return {
title,
dataIndex,
enabled: isEnabled,
width,
customRender: ({ text, record }: any) => {
if (!isEnabled) return '-'; // enable=0 -
if (text === undefined || text === null || text === '') return '-';
const num = Number(text);
if (isNaN(num)) return '-';
// max/minmax/min record
const maxList = record?.max;
const minList = record?.min;
let isOutOfRange = false;
if (Array.isArray(maxList) && maxList.length > 0) {
const maxItem = maxList.find((m: any) => m[fieldUpper] !== undefined);
if (maxItem && num > maxItem[fieldUpper]) {
isOutOfRange = true;
}
}
if (Array.isArray(minList) && minList.length > 0) {
const minItem = minList.find((m: any) => m[fieldUpper] !== undefined);
if (minItem && num < minItem[fieldUpper]) {
isOutOfRange = true;
}
}
return {
props: {
style: { color: isOutOfRange ? 'red' : 'inherit' }
},
children: num
};
}
};
});
//
if (isSummaryMode.value) {
columns.push({
title: '出库流量(m³/s)',
dataIndex: 'qo',
width: 140,
customRender: ({ text }: any) => {
if (text === undefined || text === null || text === '') return '-';
const num = Number(text);
return isNaN(num) ? '-' : num;
}
});
}
return columns;
});
const swjcColumns = computed(() => {
return [...fixedColumns, ...fixedQualityColumns.value];
});
const currentColumns = swjcColumns;
const tableScrollX = computed(() => {
const columns = currentColumns.value;
const totalWidth = columns.reduce(
(sum: number, col: any) => sum + (col.width || 120),
0
);
return totalWidth > 600 ? totalWidth : undefined;
});
// ==================== ====================
function shouldStagger(width: number, dataLen: number) {
return dataLen > Math.floor(width / 100);
}
function getAxisLabelConfig(chartWidth: number, xAxisData: string[]) {
const needStagger = shouldStagger(chartWidth, xAxisData.length);
return {
fontSize: 12,
interval: 'auto',
formatter: (value: string, index: number) => {
const [date, time] = value.split(' ');
if (needStagger) {
return index % 2 === 0 ? `${date}\n${time}\n ` : ` \n${date}\n${time}`;
}
return `${date}\n${time}`;
}
};
}
const updateChart = (data: any[], enabledParams: any[]) => {
if (!chartInstance) return;
if (!data || data.length === 0) {
chartInstance.clear();
return;
}
const sorted = [...data].sort(
(a, b) => new Date(a.tm).getTime() - new Date(b.tm).getTime()
);
const xAxisData = sorted.map(item =>
dayjs(item.tm).format('YYYY-MM-DD HH:mm')
);
const width = chartRef.value?.clientWidth || 0;
const axisLabelConfig = getAxisLabelConfig(width, xAxisData);
// series
const allSeries = enabledParams.map((param: any, index: number) => {
const showControl = param.showControl || '{}';
let lineColor = '';
let unit = '';
try {
const config = JSON.parse(showControl);
lineColor = config.lineColor || '';
unit = config.unit || '';
} catch {
// 使
}
console.log(param);
const dataIndex = param.ys.toLowerCase();
const legendName = param.ysShowName; // legendunit
const yAxisName = unit ? `${param.ysShowName}\n${unit}` : param.ysShowName; // Y+unit
const color = lineColor;
return {
name: legendName,
ysShowName: param.ysShowName,
unit,
yAxisName,
type: 'line',
data: sorted.map(item => item[dataIndex]),
smooth: true,
symbol: 'none',
lineStyle: { color: color, width: 2 },
itemStyle: { color: color }
};
});
// series
if (isSummaryMode.value) {
allSeries.push({
name: '出库流量',
ysShowName: '出库流量',
unit: 'm³/s',
yAxisName: '出库流量\nm³/s',
type: 'line',
data: sorted.map(item => item.qo),
smooth: true,
symbol: 'none',
lineStyle: { color: '#4B79AB', width: 2 },
itemStyle: { color: '#4B79AB' }
});
}
const legendData = allSeries.map((s: any) => s.name);
// series
if (visibleSeriesNames.value.length === 0) {
const defaultName = '溶解氧';
if (legendData.includes(defaultName)) {
visibleSeriesNames.value = [defaultName];
} else if (legendData.length > 0) {
visibleSeriesNames.value = [legendData[0]];
}
}
// series legend selected
const series = allSeries;
// Y visibleSeriesNames
const yAxisConfig: any[] = [];
const visibleSeriesList = series.filter((s: any) =>
visibleSeriesNames.value.includes(s.name)
);
console.log(visibleSeriesList[0]);
if (visibleSeriesList.length >= 1) {
yAxisConfig.push({
name: visibleSeriesList[0].yAxisName,
// nameLocation: 'middle',
// nameGap: 30,
type: 'value',
position: 'left',
// axisLine: { lineStyle: { color: visibleSeriesList[0].itemStyle.color } },
scale: true,
splitLine: {
show: true,
lineStyle: { color: '#bfbfbf', type: 'solid' }
}
});
// seriesyAxisIndex
series.forEach((s: any) => {
s.yAxisIndex = visibleSeriesNames.value.includes(s.name) ? 0 : 0;
});
}
if (visibleSeriesList.length >= 2) {
yAxisConfig.push({
name: visibleSeriesList[1].yAxisName,
// nameLocation: 'middle',
// nameGap: 35,
type: 'value',
position: 'right',
// axisLine: { lineStyle: { color: visibleSeriesList[1].itemStyle.color } },
scale: true,
splitLine: { show: false }
});
visibleSeriesList[0].yAxisIndex = 0;
visibleSeriesList[1].yAxisIndex = 1;
}
// legend selected
const legendSelected = legendData.reduce((acc: any, name) => {
acc[name] = visibleSeriesNames.value.includes(name);
return acc;
}, {});
const option: any = {
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(50,50,50,0.9)',
textStyle: { color: '#fff', fontSize: 12 },
axisPointer: { type: 'cross' },
formatter: (params: any) => {
if (!params?.length) return '';
const idx = params[0].dataIndex;
const fullTime = sorted[idx]?.tm
? dayjs(sorted[idx].tm).format('YYYY-MM-DD HH:mm')
: '';
let html = `<div style="font-size:16px;margin-bottom:8px;">${fullTime}</div>`;
params.forEach((p: any) => {
const v = p.value != null ? Number(p.value).toFixed(2) : '-';
html += `<div style="display:flex;align-items:center;justify-content:space-between;margin:4px 0;"><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${p.color};margin-right:8px;"></span><span style="flex:1;font-size:14px;">${p.seriesName}: </span><span style="font-size:14px;min-width:60px;text-align:right;"><strong>${v}</strong></span></div>`;
});
return html;
}
},
legend: {
type: 'scroll',
width: '80%',
right: 60,
top: 10,
data: legendData,
textStyle: { fontSize: 12 },
selected: legendSelected
},
grid: {
left: 60,
right: series.length >= 2 ? 60 : 40,
top: 80,
bottom: 80
},
xAxis: {
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#000000' } },
axisTick: { show: false },
axisLabel: axisLabelConfig,
splitLine: {
show: true,
lineStyle: { color: '#bfbfbf', type: 'solid' }
}
},
yAxis: yAxisConfig.length === 1 ? yAxisConfig[0] : yAxisConfig,
series,
dataZoom: [
{ type: 'inside', xAxisIndex: [0], throttle: 50, start: 0, end: 100 }
],
toolbox: {
show: true,
feature: {
saveAsImage: { title: '保存为图片', type: 'png', pixelRatio: 2 }
},
right: 20,
top: 10
}
};
chartInstance.setOption(option, true);
};
// legend
const bindLegendEvent = () => {
if (!chartInstance) return;
chartInstance.off('legendselectchanged');
chartInstance.on('legendselectchanged', (params: any) => {
const selected = params.selected;
const name = params.name;
const wasSelected = selected[name]; // echarts already updated internal state
if (wasSelected) {
//
const prevVisible = [...visibleSeriesNames.value];
if (prevVisible.length >= 2) {
// 2
visibleSeriesNames.value = [prevVisible[1], name];
} else {
visibleSeriesNames.value = [...new Set([...prevVisible, name])];
}
} else {
//
visibleSeriesNames.value = visibleSeriesNames.value.filter(
n => n !== name
);
}
// 使allDetailParams
updateChart(chartData.value, allDetailParams.value);
});
};
// ==================== ====================
const fetchData = async () => {
if (!dateRange.value) return;
// qxdstcd
if (isQxdMode.value && !props.stcd) {
return;
}
isLoading.value = true;
try {
// 使stcdqxd使props.stcd使modelStore.params.stcd
const currentStcd = isQxdMode.value ? props.stcd : modelStore.params.stcd;
const filterParams = {
filter: {
logic: 'and',
filters: [
{
field: 'stcd',
operator: 'eq',
dataType: 'string',
value: currentStcd
},
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: dateRange.value[0].format('YYYY-MM-DD HH:mm:ss')
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: dateRange.value[1].format('YYYY-MM-DD HH:mm:ss')
}
]
},
sort: [{ field: 'tm', dir: 'asc' }]
};
//
const [qualityRes, detailRes, listRes] = await Promise.all([
getMonitorDataWaterQuality(filterParams),
getMonitorDataWaterQualityDetail({
stcd: currentStcd,
tbCode: 'WQ_R',
startTime: dateRange.value[0].format('YYYY-MM-DD HH:mm:ss'),
endTime: dateRange.value[1].format('YYYY-MM-DD HH:mm:ss')
}),
getMonitorDataWaterQualityList(filterParams)
]);
// datanull
const qualityData = qualityRes?.data?.data || qualityRes?.data?.records;
showSummaryCheckbox.value = !!qualityData && qualityData.length > 0;
// detail
const detailData = detailRes?.data?.data || detailRes?.data || [];
allDetailParams.value = detailData; //
qualityParams.value = detailData.filter((item: any) => item.enable == 1); // enable==1
// series
visibleSeriesNames.value = [];
//
const listData = listRes?.data?.data || listRes?.data?.records || [];
tableData.value = [...listData].reverse();
chartData.value = listData;
nextTick(() => updateChart(listData, allDetailParams.value));
} catch (error) {
console.error('获取水质数据失败:', error);
tableData.value = [];
chartData.value = [];
qualityParams.value = [];
if (chartInstance) chartInstance.clear();
} finally {
isLoading.value = false;
}
};
const handleSearch = () => fetchData();
const handleExport = () => {
/* 导出逻辑 */
};
const initData = () => {
if (hasLoaded.value) return;
nextTick(() => {
if (chartRef.value && !chartInstance)
chartInstance = echarts.init(chartRef.value);
// chart
nextTick(() => {
bindLegendEvent();
});
});
fetchData();
hasLoaded.value = true;
};
watch(
() => props.isActive,
active => {
if (active && !hasLoaded.value) initData();
},
{ immediate: true }
);
watch(
() => modelStore.params.stcd,
(newStcd, oldStcd) => {
// qxdstorestcd
if (!isQxdMode.value && newStcd && newStcd !== oldStcd) fetchData();
}
);
// qxdprops.stcd
watch(
() => props.stcd,
(newStcd, oldStcd) => {
if (isQxdMode.value && newStcd && newStcd !== oldStcd) {
hasLoaded.value = false;
initData();
}
}
);
// ResizeObserver
let resizeObserver: ResizeObserver | null = null;
watch(
() => props.isActive,
active => {
if (active && chartRef.value) {
setTimeout(() => {
if (chartInstance) chartInstance.resize();
}, 200);
if (!resizeObserver) {
resizeObserver = new ResizeObserver(() => {
if (chartInstance) chartInstance.resize();
});
resizeObserver.observe(chartRef.value);
}
}
}
);
const handleResize = () => {
if (chartInstance) chartInstance.resize();
};
onMounted(() => {
window.addEventListener('resize', handleResize);
});
onBeforeUnmount(() => {
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
window.removeEventListener('resize', handleResize);
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
});
</script>
<style lang="scss" scoped>
.water-quality {
width: 100%;
min-height: 600px;
position: relative;
:deep(.ant-tabs-nav) {
margin-bottom: 4px;
}
.search-bar {
width: 100%;
margin-bottom: 12px;
display: flex;
align-items: center;
justify-content: flex-end;
.search-btn {
margin-left: 8px;
}
}
.tab-checkbox {
position: absolute;
top: 12px;
left: 30%;
z-index: 100;
}
.tab-content {
width: 100%;
.content-body {
width: 100%;
display: flex;
height: 600px;
position: relative;
.chart-wrapper {
flex: 1;
height: 100%;
position: relative;
box-sizing: border-box;
padding-top: 10px;
.chart-container {
width: 100%;
height: 100%;
}
}
.chart-empty {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.table-wrapper {
flex: 1;
min-width: 500px;
height: 100%;
box-sizing: border-box;
padding: 10px;
overflow: hidden;
overflow-x: auto;
border-left: 1px solid #dcdfe6;
margin-left: 10px;
}
}
}
}
</style>

View File

@ -26,7 +26,7 @@
</template> </template>
</a-tabs> </a-tabs>
<div class="tab-checkbox" v-if="activeTabKey === 'swjc.tabs.jcsj'"> <div class="tab-checkbox">
<a-checkbox-group <a-checkbox-group
v-model:value="selectedColumns" v-model:value="selectedColumns"
name="checkboxgroup" name="checkboxgroup"
@ -105,6 +105,7 @@ import dayjs, { Dayjs } from 'dayjs';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import { Tooltip } from 'ant-design-vue'; import { Tooltip } from 'ant-design-vue';
import { import {
// getMonitorDataWaterTempList,
getMonitorDataWaterTemp, getMonitorDataWaterTemp,
getMonitorDataWaterTempPowerStation, getMonitorDataWaterTempPowerStation,
getMonitorDataWaterTempPowerStation2 getMonitorDataWaterTempPowerStation2
@ -117,7 +118,9 @@ import { InfoCircleOutlined } from '@ant-design/icons-vue';
const modelStore = useModelStore(); const modelStore = useModelStore();
const props = defineProps({ const props = defineProps({
isActive: { type: Boolean, default: false } code: { type: String, default: '' },
isActive: { type: Boolean, default: false },
stcd: { type: String, default: '' }
}); });
const hasLoaded = ref(false); const hasLoaded = ref(false);
@ -503,6 +506,7 @@ const updateChart = (data: any[]) => {
} }
}, },
legend: { legend: {
width: '80%',
type: 'scroll', type: 'scroll',
top: 10, top: 10,
data: ['水温'], data: ['水温'],
@ -685,6 +689,8 @@ const updateChart = (data: any[]) => {
}, },
legend: { legend: {
type: 'scroll', type: 'scroll',
width: '80%',
right: 60,
top: 10, top: 10,
data: [ data: [
'出库水温', '出库水温',
@ -809,10 +815,21 @@ const updateChart = (data: any[]) => {
}; };
// ==================== ==================== // ==================== ====================
const isQxdMode = computed(() => props.code === 'qxd.tabs.jcsj');
const fetchData = async () => { const fetchData = async () => {
if (!dateRange.value) return; if (!dateRange.value) return;
// qxdstcd
if (isQxdMode.value && !props.stcd) {
return;
}
isLoading.value = true; isLoading.value = true;
try { try {
// 使stcdqxd使props.stcd使modelStore.params.stcd
const currentStcd = isQxdMode.value ? props.stcd : modelStore.params.stcd;
const filterParams = { const filterParams = {
filter: { filter: {
logic: 'and', logic: 'and',
@ -821,7 +838,7 @@ const fetchData = async () => {
field: 'stcd', field: 'stcd',
operator: 'eq', operator: 'eq',
dataType: 'string', dataType: 'string',
value: modelStore.params.stcd value: currentStcd
}, },
{ {
field: 'tm', field: 'tm',
@ -840,10 +857,22 @@ const fetchData = async () => {
sort: [{ field: 'tm', dir: 'asc' }] sort: [{ field: 'tm', dir: 'asc' }]
}; };
// qxdgetMonitorDataWaterTemp
if (isQxdMode.value) {
const mainRes = await getMonitorDataWaterTemp(filterParams);
const mainData = mainRes?.data?.data || mainRes?.data?.records || [];
showSummaryCheckbox.value = false;
showDeepCheckbox.value = false;
selectedColumns.value = [];
tableData.value = [...mainData].reverse();
chartData.value = mainData;
nextTick(() => updateChart(mainData));
} else {
//
const [mainRes, powerStationRes, powerStationRes2] = await Promise.all([ const [mainRes, powerStationRes, powerStationRes2] = await Promise.all([
getMonitorDataWaterTemp(filterParams), getMonitorDataWaterTemp(filterParams),
getMonitorDataWaterTempPowerStation({ stcd: modelStore.params.stcd }), getMonitorDataWaterTempPowerStation({ stcd: currentStcd }),
getMonitorDataWaterTempPowerStation2({ stcd: modelStore.params.stcd }) getMonitorDataWaterTempPowerStation2({ stcd: currentStcd })
]); ]);
const mainData = mainRes?.data?.data || mainRes?.data?.records || []; const mainData = mainRes?.data?.data || mainRes?.data?.records || [];
@ -856,6 +885,7 @@ const fetchData = async () => {
tableData.value = [...mainData].reverse(); tableData.value = [...mainData].reverse();
chartData.value = mainData; chartData.value = mainData;
nextTick(() => updateChart(mainData)); nextTick(() => updateChart(mainData));
}
} catch (error) { } catch (error) {
console.error('获取数据失败:', error); console.error('获取数据失败:', error);
tableData.value = []; tableData.value = [];
@ -892,7 +922,19 @@ watch(
watch( watch(
() => modelStore.params.stcd, () => modelStore.params.stcd,
(newStcd, oldStcd) => { (newStcd, oldStcd) => {
if (newStcd && newStcd !== oldStcd) fetchData(); // qxdstorestcd
if (!isQxdMode.value && newStcd && newStcd !== oldStcd) fetchData();
}
);
// qxdprops.stcd
watch(
() => props.stcd,
(newStcd, oldStcd) => {
if (isQxdMode.value && newStcd && newStcd !== oldStcd) {
hasLoaded.value = false;
initData();
}
} }
); );

View File

@ -19,6 +19,7 @@
<a-empty <a-empty
v-if="!loading && videoList.length === 0" v-if="!loading && videoList.length === 0"
description="暂无数据" description="暂无数据"
class="flex align-center justify-center h-[530px] flex-col"
/> />
</a-spin> </a-spin>
</div> </div>
@ -27,7 +28,11 @@
<!-- 中间视频区域 55% --> <!-- 中间视频区域 55% -->
<a-col flex="55%" class="center-panel"> <a-col flex="55%" class="center-panel">
<!-- 视频宫格 --> <!-- 视频宫格 -->
<div class="video-grid" :class="`layout-${viewLayout}`"> <div
class="video-grid"
:class="`layout-${viewLayout}`"
v-show="videoList.length > 0"
>
<div <div
v-for="index in parseInt(viewLayout)" v-for="index in parseInt(viewLayout)"
:key="index" :key="index"
@ -42,6 +47,11 @@
/> />
</div> </div>
</div> </div>
<a-empty
v-show="videoList.length === 0"
description="暂无数据"
class="h-full flex align-center justify-center flex-col"
/>
</a-col> </a-col>
<!-- 右侧录像列表 30% --> <!-- 右侧录像列表 30% -->
@ -80,7 +90,9 @@
<div class="record-list"> <div class="record-list">
<a-spin :spinning="recordLoading"> <a-spin :spinning="recordLoading">
<div v-if="paginatedRecords.length === 0" class="empty-record"> <div v-if="paginatedRecords.length === 0" class="empty-record">
<a-empty /> <a-empty
class="h-[410px] flex align-center justify-center flex-col"
/>
</div> </div>
<div v-else class="record-grid"> <div v-else class="record-grid">
<div <div

View File

@ -12,11 +12,14 @@
<!-- 问题 <!-- 问题
1.基础信息图片展示是什么逻辑 1.基础信息图片展示是什么逻辑
2. 电站专题展示逻辑 2. 电站专题展示逻辑
3. 实时视频回放 少接口 3. 实时视频回放 少接口
4. 预警提示 少接口 4. 预警提示 少接口
5. 生态流量 达标率查询不对 5. 生态流量 达标率查询不对()
6. 鱼类适应性繁殖同期对比NAN () 6. 鱼类适应性繁殖同期对比NAN ()
7. 出库水温 综合分析 导出没做 7. 出库水温 综合分析 导出没做
8. 栖息地-流量监测 没有水位视频和流量视频 字段没数据
9. 栖息地 水温水质流量没有数据没法测
--> -->
<div v-if="modelStore.showStcdSelector" class="stcd-selector-wrapper"> <div v-if="modelStore.showStcdSelector" class="stcd-selector-wrapper">
<a-select <a-select
@ -33,7 +36,7 @@
<a-tabs :active-key="currentActiveKey" @change="onTabChange"> <a-tabs :active-key="currentActiveKey" @change="onTabChange">
<a-tab-pane v-for="tab in tabsConfig" :key="tab.key" :tab="tab.name"> <a-tab-pane v-for="tab in tabsConfig" :key="tab.key" :tab="tab.name">
</a-tab-pane> </a-tab-pane>
<template #rightExtra> <template #rightExtra v-if="anchor.sttp == 'ENG' || anchor.sttp == 'eng'">
<a-tooltip :title="!isEngConfig ? '' : '该电站无专题配置'"> <a-tooltip :title="!isEngConfig ? '' : '该电站无专题配置'">
<a-button type="primary" :disabled="isEngConfig"> <a-button type="primary" :disabled="isEngConfig">
<i class="icon iconfont icon-topic mr-[5px]"></i> <i class="icon iconfont icon-topic mr-[5px]"></i>
@ -45,6 +48,16 @@
<!-- 内容区域 - 使用动态组件 + keep-alive 实现按需加载和缓存 --> <!-- 内容区域 - 使用动态组件 + keep-alive 实现按需加载和缓存 -->
<keep-alive> <keep-alive>
<div class="content"> <div class="content">
<div class="content-wrapper" v-show="isQxdMode(currentActiveKey)">
测站
<a-select
v-model:value="currentQxdSelectedStcd"
:options="qxdStationOptions"
style="width: 200px; margin-bottom: 12px"
placeholder="请选择站点"
@change="handleQxdStationChange"
/>
</div>
<!-- 基本信息 --> <!-- 基本信息 -->
<BasicInfo <BasicInfo
v-show="currentActiveKey === 'basicInfo'" v-show="currentActiveKey === 'basicInfo'"
@ -71,12 +84,41 @@
<WaterTemperature <WaterTemperature
v-show="currentActiveKey === 'WaterTemperature'" v-show="currentActiveKey === 'WaterTemperature'"
:is-active="currentActiveKey === 'WaterTemperature'" :is-active="currentActiveKey === 'WaterTemperature'"
:code="getTabCode('WaterTemperature')"
:stcd="
getTabCode('WaterTemperature') == 'qxd.tabs.jcsj'
? qxdSelectedStcd
: ''
"
/> />
<!-- 监测数据 - 垂向水温 --> <!-- 监测数据 - 垂向水温 -->
<VerticalWaterTemperature <VerticalWaterTemperature
v-show="currentActiveKey === 'VerticalWaterTemperature'" v-show="currentActiveKey === 'VerticalWaterTemperature'"
:is-active="currentActiveKey === 'VerticalWaterTemperature'" :is-active="currentActiveKey === 'VerticalWaterTemperature'"
/> />
<!-- 监测数据 - 水质 -->
<WaterQuality
v-show="currentActiveKey === 'WaterQuality'"
:is-active="currentActiveKey === 'WaterQuality'"
:code="getTabCode('WaterQuality')"
:stcd="
getTabCode('WaterQuality') == 'qxd.tabs.szjc'
? qxdSelectedStcdWq
: ''
"
/>
<!-- 流量监测 -->
<FlowMeasure
v-show="currentActiveKey === 'FlowMeasure'"
:is-active="currentActiveKey === 'FlowMeasure'"
:url="getTabUrl('FlowMeasure')"
:code="getTabCode('FlowMeasure')"
:stcd="
getTabCode('FlowMeasure') == 'qxd.tabs.lcjs'
? qxdSelectedStcdZq
: ''
"
/>
<!-- 预警提示 --> <!-- 预警提示 -->
<EarlyWarningAlert <EarlyWarningAlert
v-show="currentActiveKey === 'tableTabs'" v-show="currentActiveKey === 'tableTabs'"
@ -123,17 +165,160 @@ import PanoramaInfo from './components/PanoramaInfo.vue'; // 全景影像
import MonitorInfo from './components/MonitorInfo.vue'; // 线 import MonitorInfo from './components/MonitorInfo.vue'; // 线
import WaterTemperature from './components/WaterTemperature.vue'; // import WaterTemperature from './components/WaterTemperature.vue'; //
import VerticalWaterTemperature from './components/VerticalWaterTemperature.vue'; // import VerticalWaterTemperature from './components/VerticalWaterTemperature.vue'; //
import WaterQuality from './components/WaterQuality.vue'; //
import EarlyWarningAlert from './components/EarlyWarningAlert.vue'; // import EarlyWarningAlert from './components/EarlyWarningAlert.vue'; //
import FlowMeasure from './components/FlowMeasure.vue'; //
import EcologicalFlow from './components/EcologicalFlow.vue'; // import EcologicalFlow from './components/EcologicalFlow.vue'; //
import Attachment from './components/Attachment.vue'; // / import Attachment from './components/Attachment.vue'; // /
import WaterTemperatureRep from './components/WaterTemperatureRep.vue'; // import WaterTemperatureRep from './components/WaterTemperatureRep.vue'; //
import { useModelStore } from '@/store/modules/model'; import { useModelStore } from '@/store/modules/model';
import { handleTabs } from './setting.config'; import { handleTabs } from './setting.config';
import {
getMonitorDataWt,
getMonitorDataWq,
getMonitorDataZq
} from '@/api/mapModal';
const modelStore = useModelStore(); const modelStore = useModelStore();
const tabsConfig = ref([]); const tabsConfig = ref([]);
// //
const isEngConfig = ref(true); const isEngConfig = ref(true);
// ==================== qxd ====================
// qxd
const qxdConfigMap: Record<
string,
{
sttpCode: string;
apiFn: (data: any) => Promise<any>;
selectProp: string;
}
> = {
'qxd.tabs.jcsj': {
//
sttpCode: 'WTRV',
apiFn: getMonitorDataWt,
selectProp: 'qxdSelectedStcd'
},
'qxd.tabs.szjc': {
//
sttpCode: 'WQ',
apiFn: getMonitorDataWq,
selectProp: 'qxdSelectedStcdWq'
},
'qxd.tabs.lcjs': {
//
sttpCode: 'ZQ',
apiFn: getMonitorDataZq,
selectProp: 'qxdSelectedStcdZq'
}
};
//
const qxdStationOptions = ref<{ label: string; value: string }[]>([]);
const qxdSelectedStcd = ref<string>('');
const qxdSelectedStcdWq = ref<string>('');
const qxdSelectedStcdZq = ref<string>('');
const qxdLoadedCodes = ref<Set<string>>(new Set());
// tabqxd
const getCurrentQxdConfig = (tabKey: string) => {
const code = getTabCode(tabKey);
return code && qxdConfigMap[code] ? qxdConfigMap[code] : null;
};
// stcd
const getQxdSelectedStcd = (tabKey: string) => {
const config = getCurrentQxdConfig(tabKey);
if (!config) return '';
switch (config.selectProp) {
case 'qxdSelectedStcd':
return qxdSelectedStcd.value;
case 'qxdSelectedStcdWq':
return qxdSelectedStcdWq.value;
case 'qxdSelectedStcdZq':
return qxdSelectedStcdZq.value;
default:
return '';
}
};
// stcd
const setQxdSelectedStcd = (tabKey: string, value: string) => {
const config = getCurrentQxdConfig(tabKey);
if (!config) return;
switch (config.selectProp) {
case 'qxdSelectedStcd':
qxdSelectedStcd.value = value;
break;
case 'qxdSelectedStcdWq':
qxdSelectedStcdWq.value = value;
break;
case 'qxdSelectedStcdZq':
qxdSelectedStcdZq.value = value;
break;
}
};
//
const loadQxdStationList = async (tabKey: string) => {
const code = getTabCode(tabKey);
if (!code || !qxdConfigMap[code]) return;
if (qxdLoadedCodes.value.has(code)) return;
const config = qxdConfigMap[code];
try {
const params = {
filter: {
logic: 'and',
filters: [
{
field: 'fhstcd',
operator: 'eq',
dataType: 'string',
value: modelStore.params.stcd
},
{
field: 'sttpCode',
operator: 'eq',
dataType: 'string',
value: config.sttpCode
}
]
},
select: ['stcd', 'stnm']
};
const res = await config.apiFn(params);
const stations = res?.data?.data || res?.data?.records || [];
if (stations.length > 0) {
qxdStationOptions.value = stations.map((item: any) => ({
label: item.stnm,
value: item.stcd
}));
setQxdSelectedStcd(tabKey, stations[0].stcd);
}
qxdLoadedCodes.value.add(code);
} catch (error) {
console.error(`获取站点列表失败(${code}):`, error);
}
};
// tabstcd (v-model)
const currentQxdSelectedStcd = computed({
get: () => getQxdSelectedStcd(currentActiveKey.value),
set: (val: string) => setQxdSelectedStcd(currentActiveKey.value, val)
});
// qxd
const isQxdMode = (tabKey: string) => {
const code = getTabCode(tabKey);
return code && qxdConfigMap[code] ? true : false;
};
//
const handleQxdStationChange = () => {
// watch stcd
};
// import MapView from './components/MapView.vue'; // import MapView from './components/MapView.vue';
// import SurroundingInfo from './components/SurroundingInfo.vue'; // import SurroundingInfo from './components/SurroundingInfo.vue';
@ -176,6 +361,7 @@ const emit = defineEmits<{
// activeKey // activeKey
const currentActiveKey = ref<string>(''); const currentActiveKey = ref<string>('');
const anchor = ref<string>('');
// activeKey // activeKey
watch( watch(
@ -192,12 +378,29 @@ watch(
newVal => { newVal => {
if (!newVal || Object.keys(newVal).length === 0) return; if (!newVal || Object.keys(newVal).length === 0) return;
console.log(newVal); console.log(newVal);
tabsConfig.value = handleTabs(newVal); anchor.value = newVal;
let params = newVal;
if (['VD', 'WTRV', 'ZQ', 'WQ'].includes(params?.sttpCode)) {
params.sttp = params.sttpCode;
}
tabsConfig.value = handleTabs({ ...params });
let value = tabsConfig.value.find((item: any) => item.default); let value = tabsConfig.value.find((item: any) => item.default);
currentActiveKey.value = value?.key; currentActiveKey.value = value?.key;
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
); );
// tab qxd
watch(
() => currentActiveKey.value,
activeKey => {
const code = getTabCode(activeKey);
if (code && qxdConfigMap[code]) {
loadQxdStationList(activeKey);
}
},
{ immediate: true }
);
// tab // tab
const onTabChange = (key: string) => { const onTabChange = (key: string) => {
currentActiveKey.value = key; currentActiveKey.value = key;
@ -213,6 +416,12 @@ const handleClose = () => {
// , // ,
modelStore.showStcdSelector = false; modelStore.showStcdSelector = false;
modelStore.stcdOptions = []; modelStore.stcdOptions = [];
// qxd
qxdStationOptions.value = [];
qxdSelectedStcd.value = '';
qxdSelectedStcdWq.value = '';
qxdSelectedStcdZq.value = '';
qxdLoadedCodes.value = new Set();
}; };
// 便使 props.data // 便使 props.data

View File

@ -320,11 +320,10 @@ const FPTabs: Array<any> =
// code: 'gyss.tabs.sssp' // code: 'gyss.tabs.sssp'
// } // }
{ {
name: '监测数据', name: '在线监测数据',
key: 'FishFacilityMonitorData', //Normal key: 'FishFacilityMonitorData', //Normal
type: 'FishFacilityMonitorData', //Normal type: 'FishFacilityMonitorData', //Normal
code: 'gyss.tabs.jcsj', code: 'gyss.tabs.jcsj'
default: true // 默认显示
// tabs: [ // tabs: [
// { // {
// name: '', // name: '',
@ -340,7 +339,8 @@ const FPTabs: Array<any> =
name: '运行情况', name: '运行情况',
key: 'FishFacilityRunState', //Normal1 key: 'FishFacilityRunState', //Normal1
type: 'FishFacilityRunState', //Normal type: 'FishFacilityRunState', //Normal
code: 'gyss.tabs.yxqk' code: 'gyss.tabs.yxqk',
default: true // 默认显示
// tabs: [ // tabs: [
// { // {
// name: '', // name: '',
@ -504,119 +504,20 @@ const FHTabs: Array<any> = [
name: '水温监测', name: '水温监测',
key: 'WaterTemperature', key: 'WaterTemperature',
type: 'WaterTemperature', type: 'WaterTemperature',
tabs: [ code: 'qxd.tabs.jcsj'
{
url: '/wmp-env-server/sw/alongDetail/GetKendoListCust',
name: '水温',
type: 'WaterTemperature',
tableUrl: '/wmp-env-server/sw/alongDetail/GetKendoListCust',
chartType: 'line',
// filter: NormalStcdFilter,
filterProps: {
params: {
ftype: 'WE',
sttp: 'WT', //WT
dataDimensionType: 'hyBase',
dataDimensionVal: 'all'
}
}
}
]
}, },
{ {
name: '水质监测', name: '水质监测',
key: 'WaterQuality', key: 'WaterQuality',
type: 'WaterQuality', type: 'WaterQuality',
tabs: [ code: 'qxd.tabs.szjc'
{
url: '/wmp-env-server/env/wq/data/GetKendoListCust',
name: '水质',
type: 'monitor',
key: 'WaterQuality',
tableUrl: '/wmp-env-server/env/wq/data/GetKendoListCust',
chartType: 'lines',
// chartEvent: {
// legendselectchanged: (object: any, instance: any) => {
// const selected = object.selected
// const options = instance.getOption()
// if (options.grid instanceof Array) {
// options.grid = options.grid[0]
// }
// if (selected != undefined && options) {
// let yAxis: any = options.yAxis
// yAxis.map((item: any, index: number) => {
// item.show = false
// for (let i in selected) {
// if (selected[i] == true && item.name.indexOf(i) !== -1) {
// item.show = true
// }
// }
// })
// options.legend && (options.legend.selected = selected)
// let offset = 30
// let padding = 60
// if (options.yAxis && options.yAxis instanceof Array && options.grid && !(options.grid instanceof Array)) {
// let showYAxis = yAxis.filter((x: any) => {
// return x.show
// })
// showYAxis.map((yAxis: any, index: number) => {
// yAxis.position = index % 2 === 0 ? "left" : "right"
// yAxis.offset = padding * Math.floor(index / 2)
// if (index >= 4) {
// yAxis.offset = padding * Math.floor(2 / 2)
// }
// })
// if (showYAxis.length % 2 === 0) {
// options.grid.left = (showYAxis.length >= 4 ? 4 : showYAxis.length) * offset
// options.grid.right = (showYAxis.length >= 4 ? 4 : showYAxis.length) * offset
// } else {
// const left = showYAxis.filter((x: any) => x.position === "left")
// const right = showYAxis.filter((x: any) => x.position === "right")
// const left_start = left.length ? 0 : 60
// const right_start = left.length ? 0 : 60
// options.grid.left = left_start + padding * (left.length >= 2 ? 2 : left.length)
// options.grid.right = right_start + padding * (right.length >= 2 ? 2 : right.length)
// }
// }
// instance.setOption(options, false, true)
// }
// }
// },
// filter: NormalStcdFilter,
filterProps: {
params: {
ftype: 'WE',
sttp: 'WQ',
dataDimensionType: 'hyBase',
dataDimensionVal: 'all'
}
}
}
]
}, },
{ {
name: '流量监测', name: '流量监测',
key: 'FlowMeasure', key: 'FlowMeasure',
type: 'FlowMeasure' type: 'FlowMeasure',
// tabs: [ code: 'qxd.tabs.lcjs',
// { url: '/zq/river/GetKendoListCust'
// url: '/wmp-eng-server/eng/river/GetKendoListCust',
// name: '流量',
// type: 'monitor',
// tableUrl: '/wmp-eng-server/eng/river/GetKendoListCust',
// chartType: 'line',
// // filter: NormalStcdFilter,
// filterProps: {
// params: {
// ftype: 'WE',
// sttp: 'ZQ',
// dataDimensionType: 'hyBase',
// dataDimensionVal: 'all',
// maptype: '2'
// }
// }
// }
// ]
}, },
{ {
name: '实时视频', name: '实时视频',
@ -624,43 +525,22 @@ const FHTabs: Array<any> = [
type: 'video', type: 'video',
url: '' url: ''
} }
// {
// name: "全景影像",
// key: "panoramaInfo",
// type: "panorama"
// }
]; ];
// const ZQTabs: Array<any> = [ const ZQTabs: Array<any> = [
// { {
// name: '基础信息', name: '基础信息',
// key: 'basicInfo', key: 'basicInfo',
// type: 'basic', type: 'basic',
// url: '/bbi/siteBipc/getSiteBasicInfo' url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
// }, default: true // 默认显示
// { },
// name: '流量监测', {
// key: 'FlowMeasure', name: '流量监测',
// type: 'FlowMeasure', key: 'FlowMeasure',
// tabs: [ type: 'FlowMeasure',
// { url: '/zq/river/GetKendoListCust'
// url: '/wmp-swqx-server/zq/river/GetKendoListCust', }
// name: '流量', ];
// type: 'monitor',
// tableUrl: '/wmp-swqx-server/zq/river/GetKendoListCust',
// chartType: 'line',
// filterProps: {
// params: {
// ftype: 'FHT',
// sttp: 'ZQ',
// dataDimensionType: 'hyBase',
// dataDimensionVal: 'all',
// maptype: '2'
// }
// }
// }
// ]
// }
// ]
// //水质监测站 √ // //水质监测站 √
const WQFBTabs: Array<any> = [ const WQFBTabs: Array<any> = [
@ -1212,8 +1092,8 @@ const handleTabs = (modaldata: any) => {
} else { } else {
return ENGTabs; return ENGTabs;
} }
case 'WT': case 'WT': // 水温
case 'WTRV': case 'WTRV': // 垂向水温
if (modaldata.enfc == '1') { if (modaldata.enfc == '1') {
return WTTabs1; return WTTabs1;
} else { } else {
@ -1340,8 +1220,8 @@ const handleTabs = (modaldata: any) => {
} else { } else {
return FPTabs; return FPTabs;
} }
// case 'ZQ': case 'ZQ': // 流量检测站
// return ZQTabs return ZQTabs;
// case 'FB': // case 'FB':
// if (modaldata.bldstt == '1' || modaldata.bldstt == '0') { // if (modaldata.bldstt == '1' || modaldata.bldstt == '0') {
// return FBTabs1 // return FBTabs1

View File

@ -146,9 +146,12 @@ export class MapOl implements MapInterface {
this.map.on('click', evt => { this.map.on('click', evt => {
this.popupManager.showPopup(undefined, undefined); this.popupManager.showPopup(undefined, undefined);
this.popupManager.handleMapClick(evt.pixel, detectedFeature => { this.popupManager.handleMapClick(evt.pixel, detectedFeature => {
console.log(detectedFeature);
modelStore.modalVisible = true; modelStore.modalVisible = true;
modelStore.params = detectedFeature.values_; modelStore.params = detectedFeature.values_;
modelStore.title = detectedFeature.values_.titleName + ' 详情信息'; modelStore.title =
detectedFeature.values_.titleName ||
detectedFeature.values_.stnm + ' 详情信息';
}); });
}); });

View File

@ -187,10 +187,11 @@ const handleControllerClick = (item: any) => {
switch (item.key) { switch (item.key) {
case 'fullScreen': case 'fullScreen':
isFullScreen.value = !isFullScreen.value; isFullScreen.value = !isFullScreen.value;
map.flyTopanto([92.39728, 29.24587], 14);
// console.log(mapClass.hasLayer('eng_point')); // console.log(mapClass.hasLayer('eng_point'));
// map.jdPanelControlShowAndHidden("01", false); // map.jdPanelControlShowAndHidden("01", false);
map.mdLayerTreeShowOrHidden('eng_point', false); // map.mdLayerTreeShowOrHidden('eng_point', false);
map.mdLayerTreeShowOrHidden('wq_countryWq_point', true); // map.mdLayerTreeShowOrHidden('wq_countryWq_point', true);
// map.controlBaseLayerTreeShowAndHidden( // map.controlBaseLayerTreeShowAndHidden(
// 'eng_point', // 'eng_point',
// 'eng_point', // 'eng_point',

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,6 @@ service.interceptors.request.use(
config.url.includes('/wmp-eng-server') || config.url.includes('/wmp-eng-server') ||
config.url.includes('/dec-modules-usm-springcloud-starter') || config.url.includes('/dec-modules-usm-springcloud-starter') ||
config.url.includes('/wmp-swqx-server') config.url.includes('/wmp-swqx-server')
) { ) {
config.headers._appid = '974975A6-47FD-4C04-9ACD-68938D2992BD'; config.headers._appid = '974975A6-47FD-4C04-9ACD-68938D2992BD';
config.headers._isolateid = '5b34aecb-adfb-4dfc-ad95-21505a9eb388'; config.headers._isolateid = '5b34aecb-adfb-4dfc-ad95-21505a9eb388';
@ -34,7 +33,7 @@ service.interceptors.request.use(
config.headers._sysid = '10EC2E0B-AEA9-4757-83A2-201BA1BC54E9'; config.headers._sysid = '10EC2E0B-AEA9-4757-83A2-201BA1BC54E9';
config.headers.authorization = config.headers.authorization =
'bearer 7894f05d-7204-4117-bda5-8d2f850ae463'; 'bearer f6ce5036-cefe-487b-b731-b6d7b8922e0f';
config.baseURL = '/'; config.baseURL = '/';
} else { } else {
const user = useUserStoreHook(); const user = useUserStoreHook();

View File

@ -4,7 +4,7 @@
<!-- 左侧背景图区域 --> <!-- 左侧背景图区域 -->
<div class="left-section"> <div class="left-section">
<div class="slogan"> <div class="slogan">
<p>{{ $t("login.titleSjtb") }}</p> <p>{{ $t('login.titleSjtb') }}</p>
</div> </div>
</div> </div>
@ -12,28 +12,45 @@
<div class="right-section"> <div class="right-section">
<a-tabs v-model:activeKey="activeTab" class="register-tabs"> <a-tabs v-model:activeKey="activeTab" class="register-tabs">
<a-tab-pane key="register" tab="用户注册"> <a-tab-pane key="register" tab="用户注册">
<a-form :model="registerData" :rules="registerRules" layout="horizontal" <a-form
:label-col="{ span: 6 }" :wrapper-col="{ span: 18 }" class="form-container" :model="registerData"
@finish="handleFormFinish"> :rules="registerRules"
layout="horizontal"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
class="form-container"
@finish="handleFormFinish"
>
<!-- 账号信息 --> <!-- 账号信息 -->
<div class="form-section"> <div class="form-section">
<div class="section-title">账号信息</div> <div class="section-title">账号信息</div>
<!-- 账号 --> <!-- 账号 -->
<a-form-item name="username" label="账&nbsp;&nbsp;号"> <a-form-item name="username" label="账&nbsp;&nbsp;号">
<a-input v-model:value="registerData.username" placeholder="请输入登录账号4-20个字符" /> <a-input
v-model:value="registerData.username"
placeholder="请输入登录账号4-20个字符"
/>
</a-form-item> </a-form-item>
<!-- 密码 --> <!-- 密码 -->
<a-form-item name="password" label="密&nbsp;&nbsp;码" :dependencies="['username']"> <a-form-item
<a-input-password v-model:value="registerData.password" name="password"
placeholder="请设置密码6-20个字符" /> label="密&nbsp;&nbsp;码"
:dependencies="['username']"
>
<a-input-password
v-model:value="registerData.password"
placeholder="请设置密码6-20个字符"
/>
</a-form-item> </a-form-item>
<!-- 确认密码 --> <!-- 确认密码 -->
<a-form-item name="confirmPassword" label="确认密码"> <a-form-item name="confirmPassword" label="确认密码">
<a-input-password v-model:value="registerData.confirmPassword" <a-input-password
placeholder="请再次输入密码" /> v-model:value="registerData.confirmPassword"
placeholder="请再次输入密码"
/>
</a-form-item> </a-form-item>
</div> </div>
@ -43,24 +60,38 @@
<!-- 真实姓名 --> <!-- 真实姓名 -->
<a-form-item name="realName" label="真实姓名"> <a-form-item name="realName" label="真实姓名">
<a-input v-model:value="registerData.realName" placeholder="请输入真实姓名" /> <a-input
v-model:value="registerData.realName"
placeholder="请输入真实姓名"
/>
</a-form-item> </a-form-item>
<!-- 所属单位 --> <!-- 所属单位 -->
<a-form-item name="belongingUnit" label="所属单位"> <a-form-item name="belongingUnit" label="所属单位">
<a-input v-model:value="registerData.belongingUnit" placeholder="请输入所属单位(选填)" /> <a-input
v-model:value="registerData.belongingUnit"
placeholder="请输入所属单位(选填)"
/>
</a-form-item> </a-form-item>
<!-- 手机号 --> <!-- 手机号 -->
<a-form-item name="phone" label="手 机 号"> <a-form-item name="phone" label="手 机 号">
<a-row class="w-full" :gutter="8"> <a-row class="w-full" :gutter="8">
<a-col :span="16"> <a-col :span="16">
<a-input v-model:value="registerData.phone" placeholder="请输入11位手机号" /> <a-input
v-model:value="registerData.phone"
placeholder="请输入11位手机号"
/>
</a-col> </a-col>
<a-col :span="8"> <a-col :span="8">
<a-button type="primary" :disabled="smsCountdown > 0" @click="handleSendSms" <a-button
type="primary"
:disabled="smsCountdown > 0"
@click="handleSendSms"
> >
{{ smsCountdown > 0 ? `${smsCountdown}s` : '获取验证码' }} {{
smsCountdown > 0 ? `${smsCountdown}s` : '获取验证码'
}}
</a-button> </a-button>
</a-col> </a-col>
</a-row> </a-row>
@ -68,29 +99,52 @@
<!-- 短信验证码 --> <!-- 短信验证码 -->
<a-form-item name="smsCode" label="验证码"> <a-form-item name="smsCode" label="验证码">
<a-input v-model:value="registerData.smsCode" placeholder="请输入短信验证码" /> <a-input
v-model:value="registerData.smsCode"
placeholder="请输入短信验证码"
/>
</a-form-item> </a-form-item>
</div> </div>
<!-- 注册按钮 --> <!-- 注册按钮 -->
<a-form-item style="display: flex;align-items: center;justify-content: center;width: 100%;"> <a-form-item
<a-button type="primary" size="large" block htmlType="submit" :loading="loading" style="
style="margin-left: 20px;"> display: flex;
align-items: center;
justify-content: center;
width: 100%;
"
>
<a-button
type="primary"
size="large"
block
htmlType="submit"
:loading="loading"
style="margin-left: 20px"
>
<span>立即注册</span> <span>立即注册</span>
</a-button> </a-button>
</a-form-item> </a-form-item>
<!-- 返回登录 --> <!-- 返回登录 -->
<a-form-item style="display: flex;align-items: center;justify-content: center;width: 100%;"> <a-form-item
style="
display: flex;
align-items: center;
justify-content: center;
width: 100%;
"
>
<a-button type="link" size="small" block @click="backToLogin"> <a-button type="link" size="small" block @click="backToLogin">
已有账号返回登录 已有账号返回登录
</a-button> </a-button>
<a-button type="link" size="small" block > <a-button type="link" size="small" block> | </a-button>
|
</a-button>
<div></div> <div></div>
<a href="/file/注册操作手册.docx" download="注册用户操作手册.docx"> <a
href="/file/注册操作手册.docx"
download="注册用户操作手册.docx"
>
<a-button type="link" size="small" block> <a-button type="link" size="small" block>
注册用户操作手册 注册用户操作手册
</a-button> </a-button>
@ -101,17 +155,39 @@
</a-tabs> </a-tabs>
<!-- 注册确认弹框 --> <!-- 注册确认弹框 -->
<a-modal v-model:open="modalVisible" title="选择所属组织" width="600px" :confirm-loading="loading" <a-modal
@ok="onRegister" @cancel="handleModalCancel" :maskClosable="false"> v-model:open="modalVisible"
<a-form :model="organizationData" :rules="organizationRules" layout="horizontal" title="选择所属组织"
ref="organizationFormRef" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }"> width="600px"
:confirm-loading="loading"
@ok="onRegister"
@cancel="handleModalCancel"
:maskClosable="false"
>
<a-form
:model="organizationData"
:rules="organizationRules"
layout="horizontal"
ref="organizationFormRef"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 20 }"
>
<!-- 集团单选字符串 --> <!-- 集团单选字符串 -->
<a-form-item name="groupCode" label="集 团"> <a-form-item name="groupCode" label="集 团">
<a-select v-model:value="organizationData.groupCode" placeholder="请选择集团" style="width: 100%" <a-select
show-search :filter-option="filterOption" @change="onGroupChange"> v-model:value="organizationData.groupCode"
<a-select-option v-for="item in groupList" :key="item.hycd" :value="item.hycd" placeholder="请选择集团"
:label="item.hynm"> style="width: 100%"
show-search
:filter-option="filterOption"
@change="onGroupChange"
>
<a-select-option
v-for="item in groupList"
:key="item.hycd"
:value="item.hycd"
:label="item.hynm"
>
{{ item.hynm }} {{ item.hynm }}
</a-select-option> </a-select-option>
</a-select> </a-select>
@ -119,10 +195,20 @@
<!-- 公司单选字符串 --> <!-- 公司单选字符串 -->
<a-form-item name="companyCode" label="公 司"> <a-form-item name="companyCode" label="公 司">
<a-select v-model:value="organizationData.companyCode" placeholder="请选择公司" <a-select
style="width: 100%" show-search :filter-option="filterOption" allow-clear> v-model:value="organizationData.companyCode"
<a-select-option v-for="item in companyList" :key="item.hycd" :value="item.hycd" placeholder="请选择公司"
:label="item.hynm"> style="width: 100%"
show-search
:filter-option="filterOption"
allow-clear
>
<a-select-option
v-for="item in companyList"
:key="item.hycd"
:value="item.hycd"
:label="item.hynm"
>
{{ item.hynm }} {{ item.hynm }}
</a-select-option> </a-select-option>
</a-select> </a-select>
@ -130,10 +216,21 @@
<!-- 流域多选数组必填 --> <!-- 流域多选数组必填 -->
<a-form-item name="rvcdCode" label="流 域" required> <a-form-item name="rvcdCode" label="流 域" required>
<a-select v-model:value="organizationData.rvcdCode" mode="multiple" placeholder="请选择流域" <a-select
style="width: 100%" show-search :filter-option="filterOption" @change="onRvcdChange"> v-model:value="organizationData.rvcdCode"
<a-select-option v-for="item in basinList" :key="item.rvcd" :value="item.rvcd" mode="multiple"
:label="item.rvnm"> placeholder="请选择流域"
style="width: 100%"
show-search
:filter-option="filterOption"
@change="onRvcdChange"
>
<a-select-option
v-for="item in basinList"
:key="item.rvcd"
:value="item.rvcd"
:label="item.rvnm"
>
{{ item.rvnm }} {{ item.rvnm }}
</a-select-option> </a-select-option>
</a-select> </a-select>
@ -141,11 +238,21 @@
<!-- 电站多选数组必填依赖流域 --> <!-- 电站多选数组必填依赖流域 -->
<a-form-item name="stationCode" label="电 站" required> <a-form-item name="stationCode" label="电 站" required>
<a-select v-model:value="organizationData.stationCode" mode="multiple" placeholder="请先选择流域" <a-select
:disabled="organizationData.rvcdCode.length === 0" style="width: 100%" show-search v-model:value="organizationData.stationCode"
:filter-option="filterOption"> mode="multiple"
<a-select-option v-for="item in stationList" :key="item.stcd" :value="item.stcd" placeholder="请先选择流域"
:label="item.ennm"> :disabled="organizationData.rvcdCode.length === 0"
style="width: 100%"
show-search
:filter-option="filterOption"
>
<a-select-option
v-for="item in stationList"
:key="item.stcd"
:value="item.stcd"
:label="item.ennm"
>
{{ item.ennm }} {{ item.ennm }}
</a-select-option> </a-select-option>
</a-select> </a-select>
@ -158,7 +265,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref, onMounted, onUnmounted } from "vue"; import { reactive, ref, onMounted, onUnmounted } from 'vue';
import { import {
getBasinList, getBasinList,
getGroupList, getGroupList,
@ -166,25 +273,26 @@ import {
getStationList, getStationList,
sendSmsCode, sendSmsCode,
verifySmsCode, verifySmsCode,
registerUser registerUser,
} from "@/api/auth"; getAccessToken
import { message } from "ant-design-vue"; } from '@/api/auth';
import router from "@/router"; import { message } from 'ant-design-vue';
import { encrypt } from "@/utils/rsaEncrypt"; import router from '@/router';
import { encrypt } from '@/utils/rsaEncrypt';
// //
const registerData = reactive({ const registerData = reactive({
// //
username: "", username: '',
password: "", password: '',
confirmPassword: "", confirmPassword: '',
realName: "", realName: '',
belongingUnit: "", belongingUnit: '',
phone: "", phone: '',
smsCode: "", smsCode: ''
}); });
const accessToken = ref(''); // access_token
// //
const basinList = ref<any[]>([]); const basinList = ref<any[]>([]);
const groupList = ref<any[]>([]); const groupList = ref<any[]>([]);
@ -212,22 +320,22 @@ const organizationRules = {
{ {
validator: (rule: any, value: any[]) => { validator: (rule: any, value: any[]) => {
if (!value || value.length === 0) { if (!value || value.length === 0) {
return Promise.reject("请至少选择一个流域"); return Promise.reject('请至少选择一个流域');
} }
return Promise.resolve(); return Promise.resolve();
}, },
trigger: "change" trigger: 'change'
} }
], ],
stationCode: [ stationCode: [
{ {
validator: (rule: any, value: any[]) => { validator: (rule: any, value: any[]) => {
if (!value || value.length === 0) { if (!value || value.length === 0) {
return Promise.reject("请至少选择一个电站"); return Promise.reject('请至少选择一个电站');
} }
return Promise.resolve(); return Promise.resolve();
}, },
trigger: "change" trigger: 'change'
} }
] ]
}; };
@ -236,22 +344,27 @@ const organizationRules = {
const registerRules = { const registerRules = {
// //
username: [ username: [
{ required: true, message: "请输入登录账号", trigger: "blur" }, { required: true, message: '请输入登录账号', trigger: 'blur' },
{ min: 4, max: 20, message: "账号长度4-20个字符", trigger: "blur" }, { min: 4, max: 20, message: '账号长度4-20个字符', trigger: 'blur' },
{ pattern: /^[a-zA-Z0-9_]+$/, message: "只能包含字母、数字和下划线", trigger: "blur" } {
pattern: /^[a-zA-Z0-9_]+$/,
message: '只能包含字母、数字和下划线',
trigger: 'blur'
}
], ],
// //
password: [ password: [
{ {
required: true,
validator: (rule: any, value: string) => { validator: (rule: any, value: string) => {
if (!value) { if (!value) {
return Promise.reject("请输入密码"); return Promise.reject('请输入密码');
} }
// 1. 10 // 1. 10
if (value.length < 10) { if (value.length < 10) {
return Promise.reject("密码长度不能少于10位"); return Promise.reject('密码长度不能少于10位');
} }
// 2. // 2.
@ -259,17 +372,26 @@ const registerRules = {
const hasLowerCase = /[a-z]/.test(value); const hasLowerCase = /[a-z]/.test(value);
const hasNumber = /[0-9]/.test(value); const hasNumber = /[0-9]/.test(value);
const hasSpecial = /[^a-zA-Z0-9]/.test(value); const hasSpecial = /[^a-zA-Z0-9]/.test(value);
const typeCount = [hasUpperCase, hasLowerCase, hasNumber, hasSpecial].filter(Boolean).length; const typeCount = [
hasUpperCase,
hasLowerCase,
hasNumber,
hasSpecial
].filter(Boolean).length;
if (typeCount < 3) { if (typeCount < 3) {
return Promise.reject("密码必须包含大写字母、小写字母、数字、特殊字符中的至少三类"); return Promise.reject(
'密码必须包含大写字母、小写字母、数字、特殊字符中的至少三类'
);
} }
// 3. // 3.
// 3.1 2 // 3.1 2
for (let i = 0; i <= value.length - 2; i++) { for (let i = 0; i <= value.length - 2; i++) {
if (value[i] === value[i + 1]) { if (value[i] === value[i + 1]) {
return Promise.reject("密码不能包含2位及以上相同字符的连续重复如11、aa"); return Promise.reject(
'密码不能包含2位及以上相同字符的连续重复如11、aa'
);
} }
} }
@ -282,7 +404,9 @@ const registerRules = {
if (/\d/.test(value[i]) && /\d/.test(value[i + 1])) { if (/\d/.test(value[i]) && /\d/.test(value[i + 1])) {
// //
if (Math.abs(char2 - char1) === 1) { if (Math.abs(char2 - char1) === 1) {
return Promise.reject("密码不能包含2位及以上连续递增或递减的数字如12、21"); return Promise.reject(
'密码不能包含2位及以上连续递增或递减的数字如12、21'
);
} }
} }
} }
@ -296,7 +420,9 @@ const registerRules = {
if (/[a-zA-Z]/.test(value[i]) && /[a-zA-Z]/.test(value[i + 1])) { if (/[a-zA-Z]/.test(value[i]) && /[a-zA-Z]/.test(value[i + 1])) {
// //
if (Math.abs(char2 - char1) === 1) { if (Math.abs(char2 - char1) === 1) {
return Promise.reject("密码不能包含2位及以上连续递增或递减的字母如ab、ba"); return Promise.reject(
'密码不能包含2位及以上连续递增或递减的字母如ab、ba'
);
} }
} }
} }
@ -314,68 +440,75 @@ const registerRules = {
// 4.1 // 4.1
if (passwordLower.includes(usernameLower)) { if (passwordLower.includes(usernameLower)) {
return Promise.reject("密码不能包含用户名"); return Promise.reject('密码不能包含用户名');
} }
// 4.2 // 4.2
const passwordLetters = passwordLower.replace(/[^a-z]/g, ''); const passwordLetters = passwordLower.replace(/[^a-z]/g, '');
// 4.3 >= 3 // 4.3 >= 3
if (passwordLetters.length >= 3 && usernameLower.includes(passwordLetters)) { if (
return Promise.reject("密码的字母部分不能是用户名的子串"); passwordLetters.length >= 3 &&
usernameLower.includes(passwordLetters)
) {
return Promise.reject('密码的字母部分不能是用户名的子串');
} }
// 4.4 3 // 4.4 3
for (let i = 0; i <= passwordLetters.length - 3; i++) { for (let i = 0; i <= passwordLetters.length - 3; i++) {
const substring = passwordLetters.substring(i, i + 3); const substring = passwordLetters.substring(i, i + 3);
if (usernameLower.includes(substring)) { if (usernameLower.includes(substring)) {
return Promise.reject("密码不能与用户名存在明显关联"); return Promise.reject('密码不能与用户名存在明显关联');
} }
} }
return Promise.resolve(); return Promise.resolve();
}, },
trigger: "blur" trigger: 'blur'
} }
], ],
// //
confirmPassword: [ confirmPassword: [
{ required: true, message: "请再次输入密码", trigger: "blur" }, { required: true, message: '请再次输入密码', trigger: 'blur' },
{ {
validator: (rule: any, value: string) => { validator: (rule: any, value: string) => {
if (value && value !== registerData.password) { if (value && value !== registerData.password) {
return Promise.reject("两次输入的密码不一致"); return Promise.reject('两次输入的密码不一致');
} }
return Promise.resolve(); return Promise.resolve();
}, },
trigger: "blur" trigger: 'blur'
} }
], ],
// //
realName: [ realName: [
{ required: true, message: "请输入真实姓名", trigger: "blur" }, { required: true, message: '请输入真实姓名', trigger: 'blur' },
{ min: 2, max: 20, message: "姓名长度2-20个字符", trigger: "blur" } { min: 2, max: 20, message: '姓名长度2-20个字符', trigger: 'blur' }
], ],
// //
// //
phone: [ phone: [
{ required: true, message: "请输入手机号", trigger: "blur" }, { required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: "请输入正确的11位手机号", trigger: "blur" } {
pattern: /^1[3-9]\d{9}$/,
message: '请输入正确的11位手机号',
trigger: 'blur'
}
], ],
// //
smsCode: [ smsCode: [
{ required: true, message: "请输入短信验证码", trigger: "blur" }, { required: true, message: '请输入短信验证码', trigger: 'blur' },
{ len: 6, message: "验证码为6位数字", trigger: "blur" } { len: 6, message: '验证码为6位数字', trigger: 'blur' }
] ]
}; };
const loading = ref(false); const loading = ref(false);
const activeTab = ref("register"); const activeTab = ref('register');
const modalVisible = ref(false); const modalVisible = ref(false);
// //
@ -396,7 +529,6 @@ const onGroupChange = async () => {
// const res = await getCompanyList(organizationData.groupCode ? organizationData.groupCode : ''); // const res = await getCompanyList(organizationData.groupCode ? organizationData.groupCode : '');
const res = await getCompanyList(); const res = await getCompanyList();
companyList.value = res.data || []; companyList.value = res.data || [];
} catch (error) { } catch (error) {
// message.error(""); // message.error("");
} }
@ -446,7 +578,7 @@ const onRvcdChange = () => {
const handleSendSms = async () => { const handleSendSms = async () => {
// //
if (!/^1[3-9]\d{9}$/.test(registerData.phone)) { if (!/^1[3-9]\d{9}$/.test(registerData.phone)) {
message.error("请输入正确的手机号"); message.error('请输入正确的手机号');
return; return;
} }
@ -455,7 +587,7 @@ const handleSendSms = async () => {
if (res.code == 1) { if (res.code == 1) {
return; return;
} }
message.success("验证码已发送"); message.success('验证码已发送');
// 60 // 60
smsCountdown.value = 60; smsCountdown.value = 60;
@ -509,7 +641,6 @@ const onRegister = async () => {
loading.value = true; loading.value = true;
try { try {
// //
const encryptedPassword = encrypt(registerData.password); const encryptedPassword = encrypt(registerData.password);
@ -520,7 +651,7 @@ const onRegister = async () => {
code: registerData.smsCode, // code: registerData.smsCode, //
username: registerData.username, // username: registerData.username, //
password: encryptedPassword, // password: encryptedPassword, //
realName: registerData.realName, // realName: registerData.realName //
}; };
// //
@ -544,27 +675,29 @@ const onRegister = async () => {
} }
// //
if (organizationData.stationCode && organizationData.stationCode.length > 0) { if (
organizationData.stationCode &&
organizationData.stationCode.length > 0
) {
registerParams.stationCode = organizationData.stationCode.join(','); registerParams.stationCode = organizationData.stationCode.join(',');
} }
// //
const res1: any = await registerUser(registerParams); const res1: any = await registerUser(registerParams);
if (res1.code == 1) { if (res1.code == 1) {
message.error('注册失败,请重试') message.error('注册失败,请重试');
return return;
} }
message.success("注册成功,等待管理员审核"); message.success('注册成功,等待管理员审核');
// //
modalVisible.value = false; modalVisible.value = false;
// //
setTimeout(() => { setTimeout(() => {
router.push({ path: "/login" }); router.push({ path: '/login' });
}, 1500); }, 1500);
} catch (error: any) { } catch (error: any) {
// //
// if (error.message && error.message.includes('')) { // if (error.message && error.message.includes('')) {
@ -579,16 +712,30 @@ const onRegister = async () => {
// //
const backToLogin = () => { const backToLogin = () => {
router.push({ path: "/login" }); router.push({ path: '/login' });
};
const init = async () => {
try {
const res: any = await getAccessToken();
if (res.code == 1) {
message.error('获取令牌失败,请重试');
return;
}
localStorage.setItem('register-token', res.data.registerToken);
accessToken.value = res.data.accessToken;
loadGroupList();
onGroupChange();
onCompanyChange();
} catch (error) {
// message.error("");
}
}; };
// //
onMounted(() => { onMounted(() => {
loadGroupList() init();
onGroupChange()
onCompanyChange()
// modalVisible.value = true
// modalVisible.value = true
}); });
// //
@ -618,7 +765,7 @@ const filterOption = (inputValue: string, option: any) => {
width: 100%; width: 100%;
height: 100%; height: 100%;
min-height: 600px; min-height: 600px;
background: url("@/assets/images/bg_sjtb.png"); background: url('@/assets/images/bg_sjtb.png');
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; // cover background-size: cover; // cover
background-position: center; // background-position: center; //
@ -808,8 +955,7 @@ const filterOption = (inputValue: string, option: any) => {
} }
// 125%150% // 125%150%
@media (-webkit-min-device-pixel-ratio: 1.25), @media (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
(min-resolution: 120dpi) {
.register-container { .register-container {
.right-section { .right-section {
padding: clamp(12px, 1.5vw, 20px); padding: clamp(12px, 1.5vw, 20px);