鱼类调查装置
This commit is contained in:
parent
fcab0f077e
commit
71802724b1
64
frontend/src/api/yldc/index.ts
Normal file
64
frontend/src/api/yldc/index.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import request from '@/utils/request';
|
||||||
|
//获取有数据的流域
|
||||||
|
export function wbsbGetKendoList(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/fpr/wbsb/GetKendoList',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//获取鱼类调查装置
|
||||||
|
export function msstbprptGetKendoList(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/fpr/msstbprpt/GetKendoList',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//获取鱼类介绍
|
||||||
|
export function fishDicGetKendoList(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/fpr/fishDic/GetKendoList',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//获取鱼类调查统计数据
|
||||||
|
export function getFprdDataInfo(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/fpr/sdFprdR/getFprdDataInfo',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//
|
||||||
|
export function sdFprdRGetKendoListCust(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/fpr/sdFprdR/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//装置名称 /api/dec-lygk-base-server/base/vmsstbprpt/GetKendoList
|
||||||
|
export function vmsstbprptGetKendoList(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/fpr/vmsstbprpt/GetKendoList',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//鱼种类 /api/wmp-env-server/sdFprdR/getFishInfo
|
||||||
|
export function sdFprdRgetFishInfo() {
|
||||||
|
return request({
|
||||||
|
url: '/fpr/sdFprdR/getFishInfo',
|
||||||
|
method: 'post',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//获取鱼类变化信息
|
||||||
|
export function sdFprdRGetFtpChangeInfo(data: { baseId: string; year: string }) {
|
||||||
|
return request({
|
||||||
|
url: '/fpr/sdFprdR/getFtpChangeInfo',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
BIN
frontend/src/assets/images/fish_survey_liang.png
Normal file
BIN
frontend/src/assets/images/fish_survey_liang.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
BIN
frontend/src/assets/images/fish_survey_shu.png
Normal file
BIN
frontend/src/assets/images/fish_survey_shu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
BIN
frontend/src/assets/images/fish_survey_zhong.png
Normal file
BIN
frontend/src/assets/images/fish_survey_zhong.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
@ -27,7 +27,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="selectedFile.fileType === 'video'" class="video-viewer">
|
<div v-else-if="selectedFile.fileType === 'video'" class="video-viewer">
|
||||||
<video :src="selectedFile.url" controls class="video-player"></video>
|
<!-- <video :src="selectedFile.url" controls class="video-player"></video> -->
|
||||||
|
<LiveVideoBox
|
||||||
|
:videoData="{ url: selectedFile.url }"
|
||||||
|
height="100%"
|
||||||
|
:showTitle="false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="selectedFile.fileType === 'pdf'" class="pdf-viewer">
|
<div v-else-if="selectedFile.fileType === 'pdf'" class="pdf-viewer">
|
||||||
@ -52,7 +57,7 @@
|
|||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { getIdUrl } from '@/api/mapModal';
|
import { getIdUrl } from '@/api/mapModal';
|
||||||
import PdfView from '@/components/pdf/pdfView.vue';
|
import PdfView from '@/components/pdf/pdfView.vue';
|
||||||
|
import LiveVideoBox from '@/views/shiPinJianKong/components/LiveVideoBox.vue';
|
||||||
interface AttachmentFileItem {
|
interface AttachmentFileItem {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
v-model:value="dateRange"
|
v-model:value="dateRange"
|
||||||
format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
||||||
value-format="YYYY-MM-DD"
|
value-format="YYYY-MM-DD"
|
||||||
style="width: 100%"
|
style="width: 300px"
|
||||||
@change="handleDateChange"
|
@change="handleDateChange"
|
||||||
/>
|
/>
|
||||||
<a-button type="primary" class="ml-2" @click="handleSearch">
|
<a-button type="primary" class="ml-2" @click="handleSearch">
|
||||||
@ -281,7 +281,9 @@ watch(
|
|||||||
|
|
||||||
.video-mode {
|
.video-mode {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 600px;
|
height: 60vh;
|
||||||
|
min-height: 400px;
|
||||||
|
max-height: 800px;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
|
||||||
.video-main {
|
.video-main {
|
||||||
@ -309,16 +311,24 @@ watch(
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border: 1px solid #e8e8e8;
|
border: 1px solid #e8e8e8;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
.sidebar-search {
|
.sidebar-search {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-bottom: 1px solid #e8e8e8;
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
:deep(.ant-picker) {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-list {
|
.sidebar-list {
|
||||||
height: 600px;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
@ -339,13 +349,13 @@ watch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
border-color: #1890ff;
|
// border-color: #1890ff;
|
||||||
background: #e6f7ff;
|
// background: #e6f7ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-thumb {
|
.item-thumb {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 105px;
|
height: 143px;
|
||||||
background: #1a1a1a;
|
background: #1a1a1a;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -375,8 +385,8 @@ watch(
|
|||||||
|
|
||||||
.item-time {
|
.item-time {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
font-size: 11px;
|
font-size: 14px;
|
||||||
color: #666;
|
color: #000000D9;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -393,4 +403,78 @@ watch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 响应式适配
|
||||||
|
@media screen and (max-width: 1400px) {
|
||||||
|
.no-live-video {
|
||||||
|
.video-mode {
|
||||||
|
height: 55vh;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.video-main {
|
||||||
|
flex: 0 0 65%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-sidebar {
|
||||||
|
flex: 0 0 35%;
|
||||||
|
|
||||||
|
.sidebar-list {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 6px;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1200px) {
|
||||||
|
.no-live-video {
|
||||||
|
.video-mode {
|
||||||
|
height: 50vh;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.video-main {
|
||||||
|
flex: 0 0 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-sidebar {
|
||||||
|
flex: 0 0 30%;
|
||||||
|
|
||||||
|
.sidebar-list {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-search {
|
||||||
|
:deep(.ant-picker-range) {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.no-live-video {
|
||||||
|
.video-mode {
|
||||||
|
flex-direction: column;
|
||||||
|
height: auto;
|
||||||
|
|
||||||
|
.video-main {
|
||||||
|
flex: none;
|
||||||
|
height: 50vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-sidebar {
|
||||||
|
flex: none;
|
||||||
|
height: 40vh;
|
||||||
|
|
||||||
|
.sidebar-list {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -677,7 +677,7 @@ const setQGCTabList = async (_tabs: any, stcd: string) => {
|
|||||||
};
|
};
|
||||||
const res2 = await getVideoList(params);
|
const res2 = await getVideoList(params);
|
||||||
console.log('', modelStore.params.sttp);
|
console.log('', modelStore.params.sttp);
|
||||||
// debugger
|
//
|
||||||
if (
|
if (
|
||||||
res2?.data?.data?.length > 0 ||
|
res2?.data?.data?.length > 0 ||
|
||||||
res2?.data?.records?.length > 0 ||
|
res2?.data?.records?.length > 0 ||
|
||||||
|
|||||||
@ -310,7 +310,9 @@ const FPTabs1: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo'
|
default: true, // 默认显示
|
||||||
|
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '实时视频',
|
name: '实时视频',
|
||||||
@ -396,14 +398,14 @@ const FBTabs: Array<any> = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '实时视频',
|
||||||
|
key: 'videoInfo',
|
||||||
|
type: 'video',
|
||||||
|
url: '/video/dataStcdFrame/getVideoMonitorList'
|
||||||
}
|
}
|
||||||
// {
|
// {
|
||||||
// name: '实时视频',
|
|
||||||
// key: 'videoInfo',
|
|
||||||
// type: 'video',
|
|
||||||
// url: '/video/dataStcdFrame/getVideoMonitorList'
|
|
||||||
// }
|
|
||||||
// {
|
|
||||||
// name: "全景影像",
|
// name: "全景影像",
|
||||||
// key: "panoramaInfo",
|
// key: "panoramaInfo",
|
||||||
// type: "panorama"
|
// type: "panorama"
|
||||||
@ -873,7 +875,7 @@ const noLiveVideoTabs: Array<any> = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '录像视频',
|
name: '录像视频',
|
||||||
key: 'videoInfo',
|
key: 'noLiveVideo',
|
||||||
type: 'noLiveVideo',
|
type: 'noLiveVideo',
|
||||||
url: '/video/dataStcdFrame/getVideoMonitorList'
|
url: '/video/dataStcdFrame/getVideoMonitorList'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,7 +53,12 @@
|
|||||||
show-search
|
show-search
|
||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
:size="'small'"
|
:size="'small'"
|
||||||
style="width: 120px"
|
:style="{ width: select.width || '80px' }"
|
||||||
|
:dropdown-style="{
|
||||||
|
maxHeight: '400px',
|
||||||
|
overflow: 'auto',
|
||||||
|
minWidth: '200px'
|
||||||
|
}"
|
||||||
:options="select.options"
|
:options="select.options"
|
||||||
:filter-option="filterOption"
|
:filter-option="filterOption"
|
||||||
@focus="handleFocus"
|
@focus="handleFocus"
|
||||||
@ -84,7 +89,7 @@
|
|||||||
value: 'value',
|
value: 'value',
|
||||||
children: 'children'
|
children: 'children'
|
||||||
}"
|
}"
|
||||||
tree-node-filter-prop="label"
|
tree-node-filter-prop="title"
|
||||||
popup-class-name="no-wrap-tree-select"
|
popup-class-name="no-wrap-tree-select"
|
||||||
@select="handleTreeSelect"
|
@select="handleTreeSelect"
|
||||||
@expand="handleTreeExpand"
|
@expand="handleTreeExpand"
|
||||||
@ -177,11 +182,12 @@ interface PromptConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface SelectConfig {
|
interface SelectConfig {
|
||||||
picker: any;
|
picker?: any;
|
||||||
format: any;
|
format?: any;
|
||||||
show: boolean;
|
show?: boolean;
|
||||||
value: string | undefined;
|
value?: string | undefined;
|
||||||
options: SelectProps['options'];
|
options?: SelectProps['options'];
|
||||||
|
width?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义组件名(便于调试和递归)
|
// 定义组件名(便于调试和递归)
|
||||||
@ -239,7 +245,8 @@ const props = defineProps({
|
|||||||
default: () => ({
|
default: () => ({
|
||||||
show: false,
|
show: false,
|
||||||
value: undefined,
|
value: undefined,
|
||||||
options: []
|
options: [],
|
||||||
|
width: ''
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
moreSelect: {
|
moreSelect: {
|
||||||
@ -652,7 +659,7 @@ const handleTreeSelect = (selectedKeys: string | string[], info: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
moreSelectValue.value = selectedValue;
|
moreSelectValue.value = selectedValue;
|
||||||
|
//
|
||||||
// 保存选中节点的额外属性(如 lgtd、lttd 等)
|
// 保存选中节点的额外属性(如 lgtd、lttd 等)
|
||||||
if (info && typeof info === 'object') {
|
if (info && typeof info === 'object') {
|
||||||
selectedNodeExtra.value = {
|
selectedNodeExtra.value = {
|
||||||
@ -683,11 +690,16 @@ const handleFocus = () => {
|
|||||||
// Handle focus
|
// Handle focus
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterOption = (input: string, option?: { label: string; value: string }) => {
|
const filterOption = (
|
||||||
|
input: string,
|
||||||
|
option?: { label: string; value: string }
|
||||||
|
) => {
|
||||||
if (!option) return false;
|
if (!option) return false;
|
||||||
const label = option.label?.toLowerCase() || '';
|
const label = option.label?.toLowerCase() || '';
|
||||||
const value = option.value?.toLowerCase() || '';
|
const value = option.value?.toLowerCase() || '';
|
||||||
return label.includes(input.toLowerCase()) || value.includes(input.toLowerCase());
|
return (
|
||||||
|
label.includes(input.toLowerCase()) || value.includes(input.toLowerCase())
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 文字提示容器
|
// 文字提示容器
|
||||||
@ -759,7 +771,6 @@ const emitAllValues = () => {
|
|||||||
} else {
|
} else {
|
||||||
payload.scopeDate = [];
|
payload.scopeDate = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
emit('update-values', payload);
|
emit('update-values', payload);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -596,7 +596,7 @@ const handleSearch = () => {
|
|||||||
// console.log( typeOne.value )
|
// console.log( typeOne.value )
|
||||||
// console.log( formState.dtinType )
|
// console.log( formState.dtinType )
|
||||||
// console.log(typeOne.value == 'all' && formState.dtinType !='')
|
// console.log(typeOne.value == 'all' && formState.dtinType !='')
|
||||||
// debugger
|
//
|
||||||
const filters: any[] = [
|
const filters: any[] = [
|
||||||
formState.dataDimensionVal != 'all' ? {
|
formState.dataDimensionVal != 'all' ? {
|
||||||
field: 'baseId',
|
field: 'baseId',
|
||||||
@ -660,7 +660,7 @@ const handleSearch = () => {
|
|||||||
// 刷新表格
|
// 刷新表格
|
||||||
tableRef.value?.getList(filter)
|
tableRef.value?.getList(filter)
|
||||||
if (kong.value) {
|
if (kong.value) {
|
||||||
// debugger
|
//
|
||||||
fetchPieData()
|
fetchPieData()
|
||||||
}
|
}
|
||||||
kong.value = true
|
kong.value = true
|
||||||
@ -781,7 +781,7 @@ watch(
|
|||||||
() => props.typeOne,
|
() => props.typeOne,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
typeOne.value = newVal
|
typeOne.value = newVal
|
||||||
// debugger
|
//
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -798,7 +798,7 @@ watch(
|
|||||||
() => formState.types,
|
() => formState.types,
|
||||||
(newType, oldType) => {
|
(newType, oldType) => {
|
||||||
if (!formState.dateValues || newType === oldType) return
|
if (!formState.dateValues || newType === oldType) return
|
||||||
// debugger
|
//
|
||||||
const currentValue: any = formState.dateValues
|
const currentValue: any = formState.dateValues
|
||||||
|
|
||||||
if (newType === 'year') {
|
if (newType === 'year') {
|
||||||
|
|||||||
@ -564,7 +564,7 @@ watch(
|
|||||||
if (!stcd) {
|
if (!stcd) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// debugger
|
//
|
||||||
tableRef.value.tableData = [];
|
tableRef.value.tableData = [];
|
||||||
getCol({ stcd, startTime, endTime });
|
getCol({ stcd, startTime, endTime });
|
||||||
},
|
},
|
||||||
|
|||||||
@ -319,7 +319,7 @@ const initChart = (): Promise<void> => {
|
|||||||
const getEchartsData = async () => {
|
const getEchartsData = async () => {
|
||||||
// 1. 格式验证
|
// 1. 格式验证
|
||||||
const currentValue = datetimePicker.value.value;
|
const currentValue = datetimePicker.value.value;
|
||||||
// debugger
|
//
|
||||||
if (select.value.value === 'year') {
|
if (select.value.value === 'year') {
|
||||||
// 年度模式:验证是否为 YYYY 格式(4位数字)
|
// 年度模式:验证是否为 YYYY 格式(4位数字)
|
||||||
if (!/^\d{4}$/.test(currentValue)) {
|
if (!/^\d{4}$/.test(currentValue)) {
|
||||||
@ -361,7 +361,7 @@ const getEchartsData = async () => {
|
|||||||
if( ! baseid.value){
|
if( ! baseid.value){
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// debugger
|
//
|
||||||
let params = {
|
let params = {
|
||||||
"filter": {
|
"filter": {
|
||||||
"logic": "and",
|
"logic": "and",
|
||||||
@ -439,7 +439,7 @@ const getEchartsData = async () => {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
console.log('请求参数:', params);
|
console.log('请求参数:', params);
|
||||||
// debugger
|
//
|
||||||
// 4. 调用API并处理数据
|
// 4. 调用API并处理数据
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
@ -687,7 +687,7 @@ const updateChartData = (currentLabel: string, lastYearLabel: string) => {
|
|||||||
if (barData) {
|
if (barData) {
|
||||||
// 新增:获取对应的 baseid 或 rstcd
|
// 新增:获取对应的 baseid 或 rstcd
|
||||||
const selectedId = chartDataIdMap.value[dataIndex] || '';
|
const selectedId = chartDataIdMap.value[dataIndex] || '';
|
||||||
// debugger
|
//
|
||||||
clickedBarData.value = {
|
clickedBarData.value = {
|
||||||
name: barData.name.replace('\n', ''), // 去除换行符
|
name: barData.name.replace('\n', ''), // 去除换行符
|
||||||
current: barData.current,
|
current: barData.current,
|
||||||
|
|||||||
@ -714,7 +714,7 @@ const getechartsdata = async (yearOne: string) => {
|
|||||||
stopAutoPlay();
|
stopAutoPlay();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// debugger
|
//
|
||||||
// 缓存全量数据
|
// 缓存全量数据
|
||||||
cachedApiData.value = responseData;
|
cachedApiData.value = responseData;
|
||||||
currentYear.value = yearOne;
|
currentYear.value = yearOne;
|
||||||
|
|||||||
@ -37,7 +37,7 @@ import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
|||||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
import { getChuiXiangShuiWenTreeStcd, getCxswList } from '@/api/sw';
|
import { getChuiXiangShuiWenTreeStcd, getCxswList } from '@/api/sw';
|
||||||
import { useModelStore } from '@/store/modules/model';
|
import { useModelStore } from '@/store/modules/model';
|
||||||
import { useMapModalStore } from '@/store/modules/mapModal';
|
// import { useMapModalStore } from '@/store/modules/mapModal';
|
||||||
import { MapClass } from '@/components/gis/map.class';
|
import { MapClass } from '@/components/gis/map.class';
|
||||||
|
|
||||||
const modelStore = useModelStore();
|
const modelStore = useModelStore();
|
||||||
@ -356,7 +356,6 @@ const selectOptions = async () => {
|
|||||||
const data = { filter, sort };
|
const data = { filter, sort };
|
||||||
const res = await getChuiXiangShuiWenTreeStcd(data);
|
const res = await getChuiXiangShuiWenTreeStcd(data);
|
||||||
let dataOne = res?.data?.data || res.data;
|
let dataOne = res?.data?.data || res.data;
|
||||||
|
|
||||||
if (dataOne.length > 0) {
|
if (dataOne.length > 0) {
|
||||||
const treeData = dataOne.map(item => {
|
const treeData = dataOne.map(item => {
|
||||||
return {
|
return {
|
||||||
@ -547,7 +546,7 @@ const initChart = () => {
|
|||||||
)}-${lastDay} 23:59:59`;
|
)}-${lastDay} 23:59:59`;
|
||||||
console.log(endTime);
|
console.log(endTime);
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = 'WT';
|
modelStore.params.sttp = 'WTRV';
|
||||||
modelStore.params.enfc = '1';
|
modelStore.params.enfc = '1';
|
||||||
modelStore.title = stnm;
|
modelStore.title = stnm;
|
||||||
// modelStore.isBasicEdit = true;
|
// modelStore.isBasicEdit = true;
|
||||||
@ -663,53 +662,53 @@ const updateChart = () => {
|
|||||||
chartInstance.setOption(option, { notMerge: true });
|
chartInstance.setOption(option, { notMerge: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChartClick = (params: any) => {
|
// const handleChartClick = (params: any) => {
|
||||||
if (!params || !params.seriesName) return;
|
// if (!params || !params.seriesName) return;
|
||||||
|
|
||||||
const matchMonth = params.seriesName.match(/\d+/);
|
// const matchMonth = params.seriesName.match(/\d+/);
|
||||||
const monthNum = matchMonth ? parseInt(matchMonth[0]) : 1;
|
// const monthNum = matchMonth ? parseInt(matchMonth[0]) : 1;
|
||||||
|
|
||||||
const year =
|
// const year =
|
||||||
typeof datetimePicker.value.value === 'string'
|
// typeof datetimePicker.value.value === 'string'
|
||||||
? parseInt(datetimePicker.value.value)
|
// ? parseInt(datetimePicker.value.value)
|
||||||
: datetimePicker.value.value;
|
// : datetimePicker.value.value;
|
||||||
|
|
||||||
const startTime = `${year}-${String(monthNum).padStart(2, '0')}-01 00:00:00`;
|
// const startTime = `${year}-${String(monthNum).padStart(2, '0')}-01 00:00:00`;
|
||||||
const lastDay = new Date(year, monthNum, 0).getDate();
|
// const lastDay = new Date(year, monthNum, 0).getDate();
|
||||||
const endTime = `${year}-${String(monthNum).padStart(
|
// const endTime = `${year}-${String(monthNum).padStart(
|
||||||
2,
|
// 2,
|
||||||
'0'
|
// '0'
|
||||||
)}-${lastDay} 23:59:59`;
|
// )}-${lastDay} 23:59:59`;
|
||||||
|
|
||||||
const currentStcd = select.value.value;
|
// const currentStcd = select.value.value;
|
||||||
let currentStnm = '';
|
// let currentStnm = '';
|
||||||
|
|
||||||
select.value.options.forEach((station: any) => {
|
// select.value.options.forEach((station: any) => {
|
||||||
station.children?.forEach((child: any) => {
|
// station.children?.forEach((child: any) => {
|
||||||
if (child.value === currentStcd) {
|
// if (child.value === currentStcd) {
|
||||||
currentStnm = child.title;
|
// currentStnm = child.title;
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
import('@/store/modules/model').then(({ useModelStore }) => {
|
// import('@/store/modules/model').then(({ useModelStore }) => {
|
||||||
import('@/components/MapModal/setting.config').then(({ handleTabs }) => {
|
// import('@/components/MapModal/setting.config').then(({ handleTabs }) => {
|
||||||
const modelStore = useModelStore();
|
// const modelStore = useModelStore();
|
||||||
|
|
||||||
modelStore.params = {
|
// modelStore.params = {
|
||||||
sttp: 'WT',
|
// sttp: 'WT',
|
||||||
stcd: currentStcd,
|
// stcd: currentStcd,
|
||||||
titleName: currentStnm,
|
// titleName: currentStnm,
|
||||||
enfc: '1',
|
// enfc: '1',
|
||||||
timeRange: [startTime, endTime]
|
// timeRange: [startTime, endTime]
|
||||||
} as any;
|
// } as any;
|
||||||
|
|
||||||
modelStore.title = currentStnm ? `${currentStnm} ` : '';
|
// modelStore.title = currentStnm ? `${currentStnm} ` : '';
|
||||||
modelStore.tabList = handleTabs(modelStore.params);
|
// modelStore.tabList = handleTabs(modelStore.params);
|
||||||
modelStore.modalVisible = true;
|
// modelStore.modalVisible = true;
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
};
|
// };
|
||||||
|
|
||||||
// ==================== 生命周期和监听 ====================
|
// ==================== 生命周期和监听 ====================
|
||||||
|
|
||||||
@ -750,19 +749,9 @@ watch(
|
|||||||
// },
|
// },
|
||||||
// { deep: true }
|
// { deep: true }
|
||||||
// );
|
// );
|
||||||
const handlePanelChange1 = (data: any, type: string) => {
|
const handlePanelChange1 = (data: any) => {
|
||||||
console.log('当前所有控件状态:', data);
|
console.log('当前所有控件状态:', data);
|
||||||
if (data.moreSelect && data.datetime) {
|
if (data.moreSelect && data.datetime) {
|
||||||
if (type == 'click') {
|
|
||||||
const mapClass = MapClass.getInstance();
|
|
||||||
mapClass.flyTopanto(
|
|
||||||
[
|
|
||||||
Number(data.selectedNodeExtra.lgtd),
|
|
||||||
Number(data.selectedNodeExtra.lttd)
|
|
||||||
],
|
|
||||||
15
|
|
||||||
);
|
|
||||||
}
|
|
||||||
year.value = data.datetime;
|
year.value = data.datetime;
|
||||||
fetchChartData(data.moreSelect, data.datetime);
|
fetchChartData(data.moreSelect, data.datetime);
|
||||||
} else {
|
} else {
|
||||||
@ -772,6 +761,16 @@ const handlePanelChange1 = (data: any, type: string) => {
|
|||||||
selected.value = {};
|
selected.value = {};
|
||||||
showemit.value = false;
|
showemit.value = false;
|
||||||
}
|
}
|
||||||
|
if (data?.selectedNodeExtra?.lttd && data?.selectedNodeExtra?.lgtd) {
|
||||||
|
const mapClass = MapClass.getInstance();
|
||||||
|
mapClass.flyTopanto(
|
||||||
|
[
|
||||||
|
Number(data.selectedNodeExtra.lgtd),
|
||||||
|
Number(data.selectedNodeExtra.lttd)
|
||||||
|
],
|
||||||
|
15
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -52,6 +52,7 @@ import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
|||||||
import { getVmsstbprpt, inOutOneGetKendoListCust } from '@/api/sw';
|
import { getVmsstbprpt, inOutOneGetKendoListCust } from '@/api/sw';
|
||||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
import churukushuiwen from './churukushuiwen.vue';
|
import churukushuiwen from './churukushuiwen.vue';
|
||||||
|
import { MapClass } from '@/components/gis/map.class';
|
||||||
// ==================== 组件基础配置 ====================
|
// ==================== 组件基础配置 ====================
|
||||||
// 定义组件名(便于调试和递归)
|
// 定义组件名(便于调试和递归)
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@ -608,10 +609,12 @@ const handleModalClose = () => {
|
|||||||
*/
|
*/
|
||||||
const handlePanelChange1 = (data: any, type: string) => {
|
const handlePanelChange1 = (data: any, type: string) => {
|
||||||
// TODO: 后续可扩展联动逻辑
|
// TODO: 后续可扩展联动逻辑
|
||||||
if (data.moreSelect) {
|
if (data.moreSelect && data.scopeDate) {
|
||||||
paramsOne.value.value = data.moreSelect;
|
paramsOne.value.value = data.moreSelect;
|
||||||
|
paramsOne.value.tm = data.scopeDate;
|
||||||
|
fetchChartData();
|
||||||
}
|
}
|
||||||
if (type == 'click') {
|
if (data?.selectedNodeExtra?.lttd && data?.selectedNodeExtra?.lgtd) {
|
||||||
const mapClass = MapClass.getInstance();
|
const mapClass = MapClass.getInstance();
|
||||||
mapClass.flyTopanto(
|
mapClass.flyTopanto(
|
||||||
[
|
[
|
||||||
@ -621,8 +624,6 @@ const handlePanelChange1 = (data: any, type: string) => {
|
|||||||
13
|
13
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
paramsOne.value.tm = data.scopeDate;
|
|
||||||
fetchChartData();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -695,7 +696,6 @@ const getselsectData = async () => {
|
|||||||
console.log('res', res);
|
console.log('res', res);
|
||||||
const data = res?.data?.data || res?.data;
|
const data = res?.data?.data || res?.data;
|
||||||
const obj: any = {};
|
const obj: any = {};
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
// 将扁平数据转换为树形结构(按基地分组)
|
// 将扁平数据转换为树形结构(按基地分组)
|
||||||
data.map(e => {
|
data.map(e => {
|
||||||
@ -733,7 +733,6 @@ const getselsectData = async () => {
|
|||||||
return indexA - indexB;
|
return indexA - indexB;
|
||||||
});
|
});
|
||||||
console.log('treeData', treeData);
|
console.log('treeData', treeData);
|
||||||
|
|
||||||
// 更新下拉框选项
|
// 更新下拉框选项
|
||||||
select.value.options = treeData;
|
select.value.options = treeData;
|
||||||
|
|
||||||
|
|||||||
338
frontend/src/modules/fishSurvey/FishChangeTu/index.vue
Normal file
338
frontend/src/modules/fishSurvey/FishChangeTu/index.vue
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
<template>
|
||||||
|
<SidePanelItem
|
||||||
|
title="鱼类变化情况"
|
||||||
|
:datetimePicker="datetimePicker"
|
||||||
|
@update-values="handlePanelChange"
|
||||||
|
>
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<div v-show="!sourceData.length && !loading" class="empty-wrapper">
|
||||||
|
<a-empty description="暂无数据" />
|
||||||
|
</div>
|
||||||
|
<div v-show="sourceData.length" class="chart-wrapper">
|
||||||
|
<div ref="chartContainer" class="chart-container"></div>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</SidePanelItem>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue';
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
import type { EChartsOption } from 'echarts';
|
||||||
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
|
import { sdFprdRGetFtpChangeInfo } from '@/api/yldc';
|
||||||
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
|
defineOptions({
|
||||||
|
name: 'FishChangeTu'
|
||||||
|
});
|
||||||
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
|
// const BASE_ID = '07';
|
||||||
|
|
||||||
|
// 日期选择器配置
|
||||||
|
const datetimePicker = ref({
|
||||||
|
show: true,
|
||||||
|
value: `${new Date().getFullYear()}`,
|
||||||
|
format: 'YYYY',
|
||||||
|
picker: 'year' as const,
|
||||||
|
options: []
|
||||||
|
});
|
||||||
|
|
||||||
|
// 数据状态
|
||||||
|
const sourceData = ref<
|
||||||
|
Array<{
|
||||||
|
year: number;
|
||||||
|
month: number;
|
||||||
|
ftpCount: number;
|
||||||
|
fishCount: number;
|
||||||
|
}>
|
||||||
|
>([]);
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
// 图表实例
|
||||||
|
const chartContainer = ref<HTMLDivElement | null>(null);
|
||||||
|
let chartInstance: echarts.ECharts | null = null;
|
||||||
|
let resizeObserver: ResizeObserver | null = null;
|
||||||
|
|
||||||
|
// 生成从1月到当前月份的月份列表
|
||||||
|
function getMonthsUntilNow(year: number) {
|
||||||
|
const now = new Date();
|
||||||
|
const currentYear = now.getFullYear();
|
||||||
|
const currentMonth = now.getMonth() + 1;
|
||||||
|
const months: Array<{ name: string; value: number }> = [];
|
||||||
|
|
||||||
|
const maxMonth = year >= currentYear ? currentMonth : 12;
|
||||||
|
for (let i = 1; i <= maxMonth; i++) {
|
||||||
|
months.push({
|
||||||
|
name: i + '月',
|
||||||
|
value: i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return months;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理图表数据
|
||||||
|
const fishData = computed(() => {
|
||||||
|
const dates = getMonthsUntilNow(parseInt(datetimePicker.value.value));
|
||||||
|
if (sourceData.value.length > 0) {
|
||||||
|
const speciesCount = dates.map(item => {
|
||||||
|
const info = sourceData.value.find(v => v.month === item.value);
|
||||||
|
return info ? Number(info.ftpCount) : 0;
|
||||||
|
});
|
||||||
|
const fishCount = dates.map(item => {
|
||||||
|
const info = sourceData.value.find(v => v.month === item.value);
|
||||||
|
return info ? Number(info.fishCount) : 0;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
dates: dates.map(item => item.name),
|
||||||
|
speciesCount,
|
||||||
|
fishCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
dates: dates.map(item => item.name),
|
||||||
|
speciesCount: [],
|
||||||
|
fishCount: []
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 图表配置
|
||||||
|
const option = computed<EChartsOption>(() => {
|
||||||
|
const data = fishData.value;
|
||||||
|
const baseFontSize = 12;
|
||||||
|
|
||||||
|
// 动态计算Y轴范围
|
||||||
|
const speciesMax = Math.max(...data.speciesCount, 1);
|
||||||
|
const fishMax = Math.max(...data.fishCount, 1);
|
||||||
|
|
||||||
|
return {
|
||||||
|
yAxisSplit: 5,
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: { type: 'cross' },
|
||||||
|
backgroundColor: 'rgba(50,50,50,0.9)',
|
||||||
|
textStyle: { fontSize: baseFontSize, color: '#fff' }
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['种类', '总数'],
|
||||||
|
top: 0,
|
||||||
|
textStyle: { fontSize: baseFontSize }
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '50px',
|
||||||
|
right: '60px',
|
||||||
|
top: '45px',
|
||||||
|
bottom: '40px'
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: data.dates,
|
||||||
|
axisLine: { lineStyle: { color: '#ccc' } },
|
||||||
|
splitLine: { show: true },
|
||||||
|
axisLabel: {
|
||||||
|
color: '#666',
|
||||||
|
fontSize: baseFontSize,
|
||||||
|
rotate: 0,
|
||||||
|
interval: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
name: '种类',
|
||||||
|
nameTextStyle: { fontSize: baseFontSize },
|
||||||
|
min: 0,
|
||||||
|
max: Math.ceil(speciesMax * 1.2),
|
||||||
|
axisLine: { lineStyle: { color: '#165DFF' } },
|
||||||
|
axisLabel: {
|
||||||
|
color: '#666',
|
||||||
|
fontSize: baseFontSize,
|
||||||
|
formatter: (value: number) => Math.floor(value).toString()
|
||||||
|
},
|
||||||
|
splitLine: { lineStyle: { color: '#eee' } }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
name: '总数',
|
||||||
|
nameTextStyle: { fontSize: baseFontSize },
|
||||||
|
min: 0,
|
||||||
|
max: Math.ceil(fishMax * 1.2),
|
||||||
|
axisLine: { lineStyle: { color: '#36CFC9' } },
|
||||||
|
axisLabel: {
|
||||||
|
color: '#666',
|
||||||
|
fontSize: baseFontSize,
|
||||||
|
formatter: (value: number) => Math.floor(value).toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '种类',
|
||||||
|
type: 'line',
|
||||||
|
data: data.speciesCount,
|
||||||
|
yAxisIndex: 0,
|
||||||
|
color: '#165DFF',
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 8,
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
fontSize: baseFontSize - 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '总数',
|
||||||
|
type: 'line',
|
||||||
|
data: data.fishCount,
|
||||||
|
yAxisIndex: 1,
|
||||||
|
color: '#36CFC9',
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 8,
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
fontSize: baseFontSize - 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取数据
|
||||||
|
const getData = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
const year = datetimePicker.value.value;
|
||||||
|
const res = await sdFprdRGetFtpChangeInfo({
|
||||||
|
baseId: baseid.value,
|
||||||
|
year
|
||||||
|
});
|
||||||
|
if (res && (res as any).data) {
|
||||||
|
sourceData.value = (res as any).data;
|
||||||
|
} else {
|
||||||
|
sourceData.value = [];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取鱼类变化数据失败:', error);
|
||||||
|
sourceData.value = [];
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化图表
|
||||||
|
const initChart = () => {
|
||||||
|
if (!chartContainer.value) return;
|
||||||
|
|
||||||
|
if (chartInstance) {
|
||||||
|
chartInstance.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
chartInstance = echarts.init(chartContainer.value);
|
||||||
|
chartInstance.setOption(option.value, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新图表
|
||||||
|
const updateChart = () => {
|
||||||
|
if (chartInstance) {
|
||||||
|
chartInstance.setOption(option.value, true);
|
||||||
|
chartInstance.resize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理面板变化
|
||||||
|
const handlePanelChange = (data: any) => {
|
||||||
|
if (data.datetime) {
|
||||||
|
datetimePicker.value.value = data.datetime;
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 窗口大小变化时重新调整图表
|
||||||
|
const handleResize = () => {
|
||||||
|
if (chartInstance) {
|
||||||
|
chartInstance.resize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let baseid = ref('');
|
||||||
|
watch(
|
||||||
|
() => JidiSelectEventStore.selectedItem,
|
||||||
|
newVal => {
|
||||||
|
if (newVal && newVal.wbsCode) {
|
||||||
|
baseid.value = newVal.wbsCode;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
onMounted(() => {
|
||||||
|
getData();
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (chartInstance) {
|
||||||
|
chartInstance.dispose();
|
||||||
|
chartInstance = null;
|
||||||
|
}
|
||||||
|
if (resizeObserver) {
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
}
|
||||||
|
window.removeEventListener('resize', handleResize);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听数据变化,数据到达后初始化图表
|
||||||
|
watch(sourceData, newData => {
|
||||||
|
if (newData && newData.length > 0) {
|
||||||
|
// 先销毁旧的 observer
|
||||||
|
if (resizeObserver) {
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 ResizeObserver 等待容器有实际尺寸后再初始化图表
|
||||||
|
resizeObserver = new ResizeObserver(entries => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
const { width, height } = entry.contentRect;
|
||||||
|
if (width > 0 && height > 0) {
|
||||||
|
nextTick(() => {
|
||||||
|
if (!chartInstance && chartContainer.value) {
|
||||||
|
initChart();
|
||||||
|
} else {
|
||||||
|
updateChart();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resizeObserver?.disconnect();
|
||||||
|
resizeObserver = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
if (chartContainer.value) {
|
||||||
|
resizeObserver.observe(chartContainer.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.empty-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-wrapper {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,12 +1,26 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- SidePanelItem.vue -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="鱼类介绍">
|
<SidePanelItem title="鱼类介绍">
|
||||||
<div class="container" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
|
<a-spin :spinning="loading">
|
||||||
|
<template v-if="originalMediaData.length > 0">
|
||||||
|
<div
|
||||||
|
class="container"
|
||||||
|
@mouseenter="handleMouseEnter"
|
||||||
|
@mouseleave="handleMouseLeave"
|
||||||
|
>
|
||||||
<!-- 跑马灯轨道容器 -->
|
<!-- 跑马灯轨道容器 -->
|
||||||
<div class="carousel-track" :class="{ 'no-transition': isTransitioning }"
|
<div
|
||||||
:style="{ transform: `translateX(-${currentIndex * 100}%)` }">
|
class="carousel-track"
|
||||||
|
:class="{ 'no-transition': isTransitioning }"
|
||||||
|
:style="{ transform: `translateX(-${currentIndex * 100}%)` }"
|
||||||
|
>
|
||||||
<!-- 遍历所有媒体项(包含克隆项) -->
|
<!-- 遍历所有媒体项(包含克隆项) -->
|
||||||
<div v-for="(item, index) in renderMediaData" :key="index" class="carousel-item">
|
<div
|
||||||
|
v-for="(item, index) in renderMediaData"
|
||||||
|
:key="index"
|
||||||
|
class="carousel-item"
|
||||||
|
@click="handleItemClick"
|
||||||
|
>
|
||||||
<!-- 图片 -->
|
<!-- 图片 -->
|
||||||
<img :src="item.url" alt="" />
|
<img :src="item.url" alt="" />
|
||||||
<!-- 说明文字(随媒体项移动) -->
|
<!-- 说明文字(随媒体项移动) -->
|
||||||
@ -27,50 +41,58 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 独立的文字说明区域(随跑马灯切换而变化) -->
|
<!-- 独立的文字说明区域(随跑马灯切换而变化) -->
|
||||||
<div class="description-text">
|
<div class="description-text" @click="handleItemClick">
|
||||||
{{ currentDescription }}
|
{{ currentDescription }}
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
<a-empty v-else description="暂无数据" />
|
||||||
|
</a-spin>
|
||||||
</SidePanelItem>
|
</SidePanelItem>
|
||||||
|
|
||||||
|
<!-- 设施详情弹框 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:open="modalVisible"
|
||||||
|
:title="'设施类型介绍'"
|
||||||
|
width="1536px"
|
||||||
|
:footer="null"
|
||||||
|
>
|
||||||
|
<div v-if="currentItem">
|
||||||
|
<ArtsDetail :dataSource="currentItem" :index="getCurrentRealIndex()" />
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onUnmounted, computed } from 'vue';
|
import { ref, onMounted, onUnmounted, computed, watch } from 'vue';
|
||||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
|
import { fishDicGetKendoList, wbsbGetKendoList } from '@/api/yldc';
|
||||||
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
|
import ArtsDetail from '@/components/carouselIntroduce/ArtsDetail.vue';
|
||||||
// 定义组件名(便于调试和递归)
|
// 定义组件名(便于调试和递归)
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'zengZhiZhanJieShaoMod'
|
name: 'zengZhiZhanJieShaoMod'
|
||||||
});
|
});
|
||||||
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
// 媒体类型定义
|
// 媒体类型定义
|
||||||
interface MediaItem {
|
interface MediaItem {
|
||||||
type: 'image' | 'video';
|
type: 'image' | 'video';
|
||||||
url: string;
|
url: string;
|
||||||
text: string;
|
text: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
id?: string;
|
||||||
}
|
}
|
||||||
|
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL;
|
||||||
|
let baseid = ref('');
|
||||||
|
let rvcd = ref('');
|
||||||
|
// 弹框控制
|
||||||
|
const modalVisible = ref(false);
|
||||||
|
const currentItem = ref<any>(null);
|
||||||
|
|
||||||
// 原始媒体数据(3条测试数据)
|
// 加载状态
|
||||||
const originalMediaData = ref<MediaItem[]>([
|
const loading = ref(false);
|
||||||
{
|
|
||||||
type: 'image',
|
// 原始媒体数据(从接口获取)
|
||||||
url: 'https://211.99.26.225:12125/?20230814205611342377136845462200&view=jpg&token=bearer c2e76c28-14db-4a0f-9ff2-10cc3f835920',
|
const originalMediaData = ref<MediaItem[]>([]);
|
||||||
text: '松岗鱼类增殖站',
|
|
||||||
description: '松岗鱼类增殖放流站位于四川省阿坝藏族羌族自治州马尔康市松岗镇,主要服务于大渡河上游的双江口和金川两座水电站,同时还承担多种珍稀特有鱼类的救护和科研任务,实现工程建设与生态环境共同推进、相互促进。'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'video',
|
|
||||||
url: 'https://211.99.26.225:12125/?20230805205848575430105387253710&view=jpg&token=bearer c2e76c28-14db-4a0f-9ff2-10cc3f835920', // 视频URL示例,您可替换为真实地址
|
|
||||||
text: '猴子岩鱼类增殖站',
|
|
||||||
description: '猴子岩水电站鱼类增殖放流站位于猴子岩水电站坝址下游约7.0km(业主营地下游约1.5km),大渡河左岸桃花渣场顶部平台上,紧邻枢纽桃花大桥下游侧,占地面积47.3亩,其中一期工程27.0亩,预留二期工程用地20.3亩(二期工程目前为丹巴、巴底水电站预留工程)增殖放流站工作流程为:亲鱼收集购买、亲鱼驯养培育、人工催产和授精、人工孵化、苗种培育和放流。 猴子岩鱼类增殖放流站近期放流对象中齐口裂腹鱼、重口裂腹鱼、大渡软刺裸裂尻鱼增殖放流技术水平已趋于熟化,中期放流对象大渡软刺裸裂尻鱼人工繁殖技术逐渐趋于熟化'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'image',
|
|
||||||
url: 'https://211.99.26.225:12125/?20230805205924378504010675106305&view=jpg&token=bearer c2e76c28-14db-4a0f-9ff2-10cc3f835920',
|
|
||||||
text: '黑马鱼类增殖站',
|
|
||||||
description: '大渡河黑马鱼类增殖放流站位于四川省甘洛县黑马乡黑马业主营地内,距离甘洛县城45km,区域交通路况较好。主要承担瀑布沟、深溪沟、大岗山、枕头坝一级、沙坪二级等五座水电站鱼类增殖放流的重任。放流鱼类包含:齐口裂腹鱼、重口裂腹鱼、鲈鲤、长薄鳅、白甲鱼、中华倒刺?、长吻脆、稀有鮊鲫、华鲮、侧沟爬岩鳅等10个种类,共计约918.07万尾珍稀特有鱼苗。'
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 克隆首尾项后的渲染数组(用于无缝循环)
|
// 克隆首尾项后的渲染数组(用于无缝循环)
|
||||||
// 结构:[最后一项克隆, 原始数据..., 第一项克隆]
|
// 结构:[最后一项克隆, 原始数据..., 第一项克隆]
|
||||||
@ -157,11 +179,55 @@ const currentDescription = computed(() => {
|
|||||||
return originalMediaData.value[realIndex]?.description || '';
|
return originalMediaData.value[realIndex]?.description || '';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
field: 'isDeleted',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'logo',
|
||||||
|
operator: 'isnotnull'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'introduce',
|
||||||
|
operator: 'isnotnull'
|
||||||
|
},
|
||||||
|
rvcd.value
|
||||||
|
? {
|
||||||
|
field: 'rvcd',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: rvcd.value
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
].filter(Boolean)
|
||||||
|
},
|
||||||
|
select: ['id', 'code', 'name', 'introduce', 'logo']
|
||||||
|
};
|
||||||
|
const res = await fishDicGetKendoList(params);
|
||||||
|
const data = res?.data?.data;
|
||||||
|
originalMediaData.value = data.map((item: any) => ({
|
||||||
|
type: 'image' as const,
|
||||||
|
url: baseUrl + '?' + item.logo + '&view=jpg',
|
||||||
|
text: item.name,
|
||||||
|
description: item.introduce,
|
||||||
|
id: item.id
|
||||||
|
}));
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 页面加载时执行
|
// 页面加载时执行
|
||||||
onMounted(() => {
|
onMounted(async () => {});
|
||||||
initRenderData();
|
|
||||||
startAutoPlay();
|
|
||||||
});
|
|
||||||
|
|
||||||
// 组件卸载时清理
|
// 组件卸载时清理
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@ -187,6 +253,64 @@ const goToSlide = (targetIndex: number) => {
|
|||||||
// 计算目标索引(考虑克隆项偏移)
|
// 计算目标索引(考虑克隆项偏移)
|
||||||
currentIndex.value = targetIndex + 1;
|
currentIndex.value = targetIndex + 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理项目点击事件
|
||||||
|
const handleItemClick = () => {
|
||||||
|
currentItem.value = originalMediaData.value.map((item: any) => ({
|
||||||
|
url: item.url || '',
|
||||||
|
description: item.description || '',
|
||||||
|
title: item.text || ''
|
||||||
|
}));
|
||||||
|
modalVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getrvcd = async baseid => {
|
||||||
|
let params = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
field: 'isDeleted',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'wbsType',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: 'PSB_RVCD'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'objId',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: baseid
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
select: ['wbsCode', 'wbsName']
|
||||||
|
};
|
||||||
|
let res = await wbsbGetKendoList(params);
|
||||||
|
let data = res?.data?.data?.wbsCode;
|
||||||
|
rvcd.value = data;
|
||||||
|
await getData();
|
||||||
|
initRenderData();
|
||||||
|
startAutoPlay();
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => JidiSelectEventStore.selectedItem,
|
||||||
|
newVal => {
|
||||||
|
if (newVal && newVal.wbsCode) {
|
||||||
|
baseid.value = newVal.wbsCode;
|
||||||
|
if (newVal.wbsCode != 'all') {
|
||||||
|
getrvcd(newVal.wbsCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -213,9 +337,11 @@ const goToSlide = (targetIndex: number) => {
|
|||||||
// 单个媒体项
|
// 单个媒体项
|
||||||
.carousel-item {
|
.carousel-item {
|
||||||
min-width: 100%; // 每个项目占满容器宽度
|
min-width: 100%; // 每个项目占满容器宽度
|
||||||
|
max-width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-shrink: 0; // 防止被压缩
|
flex-shrink: 0; // 防止被压缩
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
img,
|
img,
|
||||||
video {
|
video {
|
||||||
@ -251,7 +377,7 @@ const goToSlide = (targetIndex: number) => {
|
|||||||
width: 5px;
|
width: 5px;
|
||||||
height: 5px;
|
height: 5px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: #D8D8D8;
|
background-color: #d8d8d8;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,17 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- SidePanelItem.vue -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="视频" :select="select">
|
<SidePanelItem
|
||||||
|
title="视频"
|
||||||
|
:select="select"
|
||||||
|
@update-values="handlePanelChange1"
|
||||||
|
>
|
||||||
<div class="video-container">
|
<div class="video-container">
|
||||||
|
<LiveVideoBox
|
||||||
|
:videoData="{ url: videoUrl1 }"
|
||||||
|
height="100%"
|
||||||
|
:showTitle="false"
|
||||||
|
monitorType="record"
|
||||||
|
/>
|
||||||
<video
|
<video
|
||||||
ref="videoRef"
|
ref="videoRef"
|
||||||
class="video-player"
|
class="video-player"
|
||||||
@ -11,7 +21,7 @@
|
|||||||
playsinline
|
playsinline
|
||||||
preload="auto"
|
preload="auto"
|
||||||
>
|
>
|
||||||
<source src="" type="video/mp4">
|
<source src="" type="video/mp4" />
|
||||||
您的浏览器不支持视频播放
|
您的浏览器不支持视频播放
|
||||||
</video>
|
</video>
|
||||||
</div>
|
</div>
|
||||||
@ -19,45 +29,103 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onUnmounted, computed } from 'vue';
|
import { ref, onMounted, onUnmounted, computed, reactive,watch } from 'vue';
|
||||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
|
import { msstbprptGetKendoList } from '@/api/yldc';
|
||||||
|
import LiveVideoBox from '@/views/shiPinJianKong/components/LiveVideoBox.vue';
|
||||||
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
// 定义组件名(便于调试和递归)
|
// 定义组件名(便于调试和递归)
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'zengZhiZhanJieShaoMod'
|
name: 'zengZhiZhanJieShaoMod'
|
||||||
});
|
});
|
||||||
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
// 视频元素引用
|
// 视频元素引用
|
||||||
const videoRef = ref<HTMLVideoElement>();
|
const videoRef = ref<HTMLVideoElement>();
|
||||||
|
const videoUrl = ref([]);
|
||||||
// 选择器配置
|
// 选择器配置
|
||||||
const select = ref({
|
const select = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: undefined,
|
value: undefined,
|
||||||
options: [],
|
options: [],
|
||||||
picker: undefined,
|
picker: undefined,
|
||||||
format: undefined
|
format: undefined,
|
||||||
|
width: '200px'
|
||||||
});
|
});
|
||||||
|
const getVideoSelect = async () => {
|
||||||
|
let params = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
field: 'sttpCode',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: 'VD_FPRD'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'isDeleted',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'url',
|
||||||
|
operator: 'isnotnull'
|
||||||
|
},
|
||||||
|
baseid.value != 'all'
|
||||||
|
? {
|
||||||
|
field: 'baseId',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: baseid.value
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
].filter(Boolean)
|
||||||
|
},
|
||||||
|
select: ['stcd', 'stnm', 'url']
|
||||||
|
};
|
||||||
|
let res = await msstbprptGetKendoList(params);
|
||||||
|
let resData = res.data.data; //stnm stcd url
|
||||||
|
select.value.options = resData.map(item => {
|
||||||
|
return {
|
||||||
|
value: item.stcd,
|
||||||
|
label: item.stnm
|
||||||
|
};
|
||||||
|
});
|
||||||
|
select.value.value = resData[0].stcd;
|
||||||
|
videoUrl.value = resData;
|
||||||
|
};
|
||||||
|
const videoUrl1 = ref('');
|
||||||
|
const handlePanelChange1 = async data => {
|
||||||
|
if (data.select) {
|
||||||
|
videoUrl.value.forEach(item => {
|
||||||
|
if (item.stcd === data.select) {
|
||||||
|
videoUrl1.value = item.url;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let baseid = ref('');
|
||||||
|
watch(
|
||||||
|
() => JidiSelectEventStore.selectedItem,
|
||||||
|
newVal => {
|
||||||
|
if (newVal && newVal.wbsCode) {
|
||||||
|
baseid.value = newVal.wbsCode;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
// 页面加载时执行
|
// 页面加载时执行
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
getVideoSelect();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 组件卸载时清理
|
// 组件卸载时清理
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {});
|
||||||
if (videoRef.value) {
|
|
||||||
videoRef.value.pause();
|
|
||||||
videoRef.value.src = '';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.video-container{
|
.video-container {
|
||||||
height: 300px;
|
height: 300px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,407 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:open="visible"
|
||||||
|
title="鱼类调查装置概况详情"
|
||||||
|
width="90%"
|
||||||
|
:body-style="{ height: '700px', padding: '16px' }"
|
||||||
|
:footer="null"
|
||||||
|
@cancel="handleClose"
|
||||||
|
>
|
||||||
|
<div class="modal-content">
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<a-form layout="inline" class="search-form" @finish="handleSearch">
|
||||||
|
<a-form-item label="装置名称">
|
||||||
|
<a-select
|
||||||
|
v-model:value="searchForm.stcd"
|
||||||
|
placeholder="请选择装置名称"
|
||||||
|
style="width: 180px"
|
||||||
|
allow-clear
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
:options="deviceOptions"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="鱼种类">
|
||||||
|
<a-select
|
||||||
|
v-model:value="searchForm.name"
|
||||||
|
placeholder="请选择鱼种类"
|
||||||
|
style="width: 150px"
|
||||||
|
allow-clear
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
:options="fishTypeOptions"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="年份">
|
||||||
|
<a-date-picker
|
||||||
|
v-model:value="searchForm.year"
|
||||||
|
picker="year"
|
||||||
|
format="YYYY"
|
||||||
|
style="width: 120px"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<a-button type="primary" @click="handleSearch">查询</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
<!-- 表格 -->
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<BasicTable
|
||||||
|
ref="tableRef"
|
||||||
|
:columns="columns"
|
||||||
|
:scroll-y="500"
|
||||||
|
:list-url="sdFprdRGetKendoListCust"
|
||||||
|
:search-params="tableSearchParams"
|
||||||
|
:transform-data="customTransform"
|
||||||
|
:enable-sort="true"
|
||||||
|
@sort-change="handleSortChange"
|
||||||
|
>
|
||||||
|
<template #fid="{ column, record }">
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
:disabled="!record.fid"
|
||||||
|
@click="handleViewAttachment(record)"
|
||||||
|
>
|
||||||
|
查看附件
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
<CommonAttachmentModal
|
||||||
|
v-model:open="attachmentModalVisible"
|
||||||
|
:fid="currentAttachmentFid"
|
||||||
|
title="查看附件"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed, reactive, onMounted, nextTick, watch } from 'vue';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import BasicTable from '@/components/BasicTable/index.vue';
|
||||||
|
import { vmsstbprptGetKendoList, sdFprdRGetKendoListCust,sdFprdRgetFishInfo } from '@/api/yldc';
|
||||||
|
import CommonAttachmentModal from '@/components/MapModal/components/CommonAttachmentModal.vue';
|
||||||
|
defineOptions({
|
||||||
|
name: 'DeviceTableModal'
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean;
|
||||||
|
currentYear?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
visible: false,
|
||||||
|
currentYear: () => `${new Date().getFullYear()}`
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:visible', 'close']);
|
||||||
|
const attachmentModalVisible = ref(false);
|
||||||
|
const currentAttachmentFid = ref('');
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = reactive({
|
||||||
|
stcd: undefined,
|
||||||
|
name: undefined,
|
||||||
|
year: props.currentYear ? dayjs().year(parseInt(props.currentYear)) : dayjs()
|
||||||
|
});
|
||||||
|
|
||||||
|
// 排序状态管理
|
||||||
|
const sortInfo = ref<{ field: string; order: string | null }>({
|
||||||
|
field: 'tm',
|
||||||
|
order: 'descend'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 装置选项
|
||||||
|
const deviceOptions = ref<any[]>([]);
|
||||||
|
// 鱼种类选项
|
||||||
|
const fishTypeOptions = ref<any[]>([]);
|
||||||
|
|
||||||
|
// 获取鱼种类列表
|
||||||
|
const fetchFishTypeList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await sdFprdRgetFishInfo();
|
||||||
|
// debugger
|
||||||
|
if (res && res?.data) {
|
||||||
|
fishTypeOptions.value = res.data.map((item: any) => ({
|
||||||
|
label: item,
|
||||||
|
value: item
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取鱼种类列表失败:', error);
|
||||||
|
fishTypeOptions.value = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Select 搜索过滤方法
|
||||||
|
const filterOption = (input: string, option: any) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听弹窗打开,同步年份并刷新表格
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(newVisible) => {
|
||||||
|
if (newVisible && props.currentYear) {
|
||||||
|
// 弹窗打开时,将年份选择器同步为外部传入的年份
|
||||||
|
searchForm.year = dayjs().year(parseInt(props.currentYear));
|
||||||
|
// 等待 DOM 更新后再刷新表格
|
||||||
|
nextTick(() => {
|
||||||
|
handleSearch();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 监听 currentYear 变化(弹窗已打开时外部年份切换)
|
||||||
|
watch(
|
||||||
|
() => props.currentYear,
|
||||||
|
(newYear) => {
|
||||||
|
if (props.visible && newYear) {
|
||||||
|
searchForm.year = dayjs().year(parseInt(newYear));
|
||||||
|
handleSearch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 获取装置列表
|
||||||
|
const fetchDeviceList = async () => {
|
||||||
|
try {
|
||||||
|
let params = {
|
||||||
|
"filter": {
|
||||||
|
"logic": "and",
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"field": "sttpCode",
|
||||||
|
"operator": "eq",
|
||||||
|
"dataType": "string",
|
||||||
|
"value": "FPRD"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sort": [
|
||||||
|
{
|
||||||
|
"field": "baseStepSort",
|
||||||
|
"dir": "asc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "rvcdStepSort",
|
||||||
|
"dir": "asc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "siteStepSort",
|
||||||
|
"dir": "asc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "ttpwr",
|
||||||
|
"dir": "desc"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"group": [],
|
||||||
|
"select": [
|
||||||
|
"stnm",
|
||||||
|
"stcd"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
const res = await vmsstbprptGetKendoList(params);
|
||||||
|
if (res && res?.data?.data) {
|
||||||
|
deviceOptions.value = res.data.data.map((item: any) => ({
|
||||||
|
label: item.stnm,
|
||||||
|
value: item.stcd
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取装置列表失败:', error);
|
||||||
|
deviceOptions.value = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '装置名称',
|
||||||
|
key: 'stnm',
|
||||||
|
dataIndex: 'stnm',
|
||||||
|
merge: true,
|
||||||
|
width: '150px'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '时间',
|
||||||
|
key: 'tm',
|
||||||
|
dataIndex: 'tm',
|
||||||
|
width: '120px',
|
||||||
|
sorter: true, // 启用排序
|
||||||
|
customRender: ({ text }: any) => {
|
||||||
|
if (!text) return '-';
|
||||||
|
return dayjs(text).format('YYYY');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '鱼种类(种)',
|
||||||
|
key: 'name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: '120px'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '数量(尾)',
|
||||||
|
key: 'num',
|
||||||
|
dataIndex: 'num',
|
||||||
|
width: '100px'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '规格(cm)',
|
||||||
|
key: 'fsz',
|
||||||
|
dataIndex: 'fsz',
|
||||||
|
width: '100px'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '附件',
|
||||||
|
key: 'fid',
|
||||||
|
dataIndex: 'fid',
|
||||||
|
width: '100px',
|
||||||
|
slots: { customRender: 'fid' }
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// 构建搜索参数(包含排序)
|
||||||
|
const tableSearchParams = computed(() => {
|
||||||
|
const baseSort =
|
||||||
|
sortInfo.value.field && sortInfo.value.order
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
field: sortInfo.value.field,
|
||||||
|
dir: sortInfo.value.order === 'ascend' ? 'asc' : 'desc'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
sort: baseSort,
|
||||||
|
group: [],
|
||||||
|
select: [],
|
||||||
|
groupResultFlat: false
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理排序变化
|
||||||
|
const handleSortChange = (info: {
|
||||||
|
field: string;
|
||||||
|
order: 'ascend' | 'descend' | null;
|
||||||
|
}) => {
|
||||||
|
sortInfo.value.field = info.field || '';
|
||||||
|
sortInfo.value.order = info.order || null;
|
||||||
|
|
||||||
|
// 等待 DOM 和响应式状态更新完成后再触发搜索
|
||||||
|
nextTick(() => {
|
||||||
|
handleSearch();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 数据转换
|
||||||
|
const customTransform = (res: any) => {
|
||||||
|
return {
|
||||||
|
records: res?.data?.data || [],
|
||||||
|
total: res?.data?.total || 0
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 表格引用
|
||||||
|
const tableRef = ref();
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
const handleClose = () => {
|
||||||
|
emit('update:visible', false);
|
||||||
|
emit('close');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
const handleSearch = () => {
|
||||||
|
const filters: any[] = [];
|
||||||
|
|
||||||
|
if (searchForm.stcd) {
|
||||||
|
filters.push({
|
||||||
|
field: 'stcd',
|
||||||
|
operator: 'eq',
|
||||||
|
value: searchForm.stcd
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchForm.name) {
|
||||||
|
filters.push({
|
||||||
|
field: 'name',
|
||||||
|
operator: 'eq',
|
||||||
|
value: searchForm.name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchForm.year) {
|
||||||
|
const year = dayjs(searchForm.year).format('YYYY');
|
||||||
|
filters.push(
|
||||||
|
{
|
||||||
|
field: 'startTime',
|
||||||
|
operator: 'gte',
|
||||||
|
value: `${year}-01-01 00:00:00`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'endTime',
|
||||||
|
operator: 'lte',
|
||||||
|
value: `${year}-12-31 23:59:59`
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let filter = {
|
||||||
|
logic: 'and',
|
||||||
|
filters: filters.filter((f: any) => f.value !== undefined)
|
||||||
|
};
|
||||||
|
tableRef.value?.getList(filter);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查看附件(保留位置)
|
||||||
|
const handleViewAttachment = (record: any) => {
|
||||||
|
// TODO: 附件预览功能待实现
|
||||||
|
console.log('查看附件:', record.fid);
|
||||||
|
currentAttachmentFid.value = record.fid;
|
||||||
|
attachmentModalVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重置搜索条件
|
||||||
|
const resetSearch = (year: string) => {
|
||||||
|
searchForm.stcd = undefined;
|
||||||
|
searchForm.name = undefined;
|
||||||
|
searchForm.year = year ? dayjs(year) : dayjs();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露方法给父组件调用
|
||||||
|
defineExpose({
|
||||||
|
resetSearch
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
// 初始化获取装置列表和鱼种类列表
|
||||||
|
await Promise.all([fetchDeviceList(), fetchFishTypeList()]);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.modal-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
177
frontend/src/modules/fishSurvey/SurveyQingKuang/index.vue
Normal file
177
frontend/src/modules/fishSurvey/SurveyQingKuang/index.vue
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
<template>
|
||||||
|
<SidePanelItem
|
||||||
|
title="鱼类调查装置概况"
|
||||||
|
:datetimePicker="datetimePicker"
|
||||||
|
@update-values="handlePanelChange"
|
||||||
|
>
|
||||||
|
<div v-loading="loading" class="gird">
|
||||||
|
<div v-for="item in statItems" :key="item.name" class="item" @click="handleCardClick">
|
||||||
|
<div class="item-icon">
|
||||||
|
<img :src="item.icon" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="item-name">{{ item.name }}</div>
|
||||||
|
<div class="item-value">
|
||||||
|
<span class="bold">{{ item.total }}</span>
|
||||||
|
<span class="unit">{{ item.unit }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SidePanelItem>
|
||||||
|
|
||||||
|
<!-- 详情弹窗 -->
|
||||||
|
<DeviceTableModal
|
||||||
|
ref="deviceTableModalRef"
|
||||||
|
:visible="modalVisible"
|
||||||
|
:current-year="datetimePicker.value"
|
||||||
|
@update:visible="handleModalChange"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed, onMounted } from 'vue';
|
||||||
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
|
import DeviceTableModal from './DeviceTableModal.vue';
|
||||||
|
import { getFprdDataInfo } from '@/api/yldc';
|
||||||
|
import fishShuIcon from '@/assets/images/fish_survey_shu.png';
|
||||||
|
import fishZhongIcon from '@/assets/images/fish_survey_zhong.png';
|
||||||
|
import fishLiangIcon from '@/assets/images/fish_survey_liang.png';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'SurveyQingKuang'
|
||||||
|
});
|
||||||
|
|
||||||
|
const BASE_ID = '07';
|
||||||
|
|
||||||
|
// ==================== 统计数据 ====================
|
||||||
|
const loading = ref(false);
|
||||||
|
const sourceData = ref<any>({});
|
||||||
|
|
||||||
|
const datetimePicker = ref({
|
||||||
|
show: true,
|
||||||
|
value: `${new Date().getFullYear()}`,
|
||||||
|
format: 'YYYY',
|
||||||
|
picker: 'year' as const,
|
||||||
|
options: []
|
||||||
|
});
|
||||||
|
|
||||||
|
const statItems = computed(() => [
|
||||||
|
{
|
||||||
|
icon: fishShuIcon,
|
||||||
|
name: '装置数量',
|
||||||
|
unit: '座',
|
||||||
|
total: sourceData.value?.fprdCount || 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: fishZhongIcon,
|
||||||
|
name: '鱼种类',
|
||||||
|
unit: '种',
|
||||||
|
total: sourceData.value?.ftpCount || 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: fishLiangIcon,
|
||||||
|
name: '鱼数量',
|
||||||
|
unit: '尾',
|
||||||
|
total: sourceData.value?.fishCount || 0
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 获取统计数据
|
||||||
|
const fetchStatData = async (year: string) => {
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
const res = await getFprdDataInfo({
|
||||||
|
baseId: BASE_ID,
|
||||||
|
year
|
||||||
|
});
|
||||||
|
if (res && res.data) {
|
||||||
|
sourceData.value = res.data;
|
||||||
|
} else {
|
||||||
|
sourceData.value = {};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取统计数据失败:', error);
|
||||||
|
sourceData.value = {};
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePanelChange = (data: any) => {
|
||||||
|
if (data.datetime) {
|
||||||
|
datetimePicker.value.value = data.datetime;
|
||||||
|
fetchStatData(data.datetime);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 弹窗相关 ====================
|
||||||
|
const modalVisible = ref(false);
|
||||||
|
const deviceTableModalRef = ref();
|
||||||
|
|
||||||
|
// 点击卡片打开弹窗
|
||||||
|
const handleCardClick = () => {
|
||||||
|
modalVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 弹窗关闭
|
||||||
|
const handleModalChange = (visible: boolean) => {
|
||||||
|
modalVisible.value = visible;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始化获取统计数据
|
||||||
|
fetchStatData(datetimePicker.value.value);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.gird {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 190px;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
cursor: pointer;
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
border: 1px solid rgba(108, 164, 247, 0.35);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
align-items: center;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba(108, 164, 247, 0.6);
|
||||||
|
box-shadow: 0 2px 8px rgba(108, 164, 247, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-icon {
|
||||||
|
img {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-name {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-value {
|
||||||
|
.bold {
|
||||||
|
color: #00aeff;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,12 +1,26 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- SidePanelItem.vue -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="鱼类调查装置介绍">
|
<SidePanelItem title="鱼类调查装置介绍">
|
||||||
<div class="container" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
|
<a-spin :spinning="loading">
|
||||||
|
<template v-if="originalMediaData.length > 0">
|
||||||
|
<div
|
||||||
|
class="container"
|
||||||
|
@mouseenter="handleMouseEnter"
|
||||||
|
@mouseleave="handleMouseLeave"
|
||||||
|
>
|
||||||
<!-- 跑马灯轨道容器 -->
|
<!-- 跑马灯轨道容器 -->
|
||||||
<div class="carousel-track" :class="{ 'no-transition': isTransitioning }"
|
<div
|
||||||
:style="{ transform: `translateX(-${currentIndex * 100}%)` }">
|
class="carousel-track"
|
||||||
|
:class="{ 'no-transition': isTransitioning }"
|
||||||
|
:style="{ transform: `translateX(-${currentIndex * 100}%)` }"
|
||||||
|
>
|
||||||
<!-- 遍历所有媒体项(包含克隆项) -->
|
<!-- 遍历所有媒体项(包含克隆项) -->
|
||||||
<div v-for="(item, index) in renderMediaData" :key="index" class="carousel-item">
|
<div
|
||||||
|
v-for="(item, index) in renderMediaData"
|
||||||
|
:key="index"
|
||||||
|
class="carousel-item"
|
||||||
|
@click="handleItemClick"
|
||||||
|
>
|
||||||
<!-- 图片 -->
|
<!-- 图片 -->
|
||||||
<img :src="item.url" alt="" />
|
<img :src="item.url" alt="" />
|
||||||
<!-- 说明文字(随媒体项移动) -->
|
<!-- 说明文字(随媒体项移动) -->
|
||||||
@ -27,16 +41,27 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 独立的文字说明区域(随跑马灯切换而变化) -->
|
<!-- 独立的文字说明区域(随跑马灯切换而变化) -->
|
||||||
<div class="description-text">
|
<div class="description-text" @click="handleItemClick">
|
||||||
{{ currentDescription }}
|
{{ currentDescription }}
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
<a-empty v-else description="暂无数据" />
|
||||||
|
</a-spin>
|
||||||
</SidePanelItem>
|
</SidePanelItem>
|
||||||
|
|
||||||
|
<!-- 设施详情弹框 -->
|
||||||
|
<a-modal v-model:open="modalVisible" :title="'设施类型介绍'" width="1536px" :footer="null">
|
||||||
|
<div v-if="currentItem">
|
||||||
|
<ArtsDetail :dataSource="currentItem" :index="getCurrentRealIndex()" />
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onUnmounted, computed } from 'vue';
|
import { ref, onMounted, onUnmounted, computed } from 'vue';
|
||||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
|
import { msstbprptGetKendoList } from '@/api/yldc';
|
||||||
|
import ArtsDetail from '@/components/carouselIntroduce/ArtsDetail.vue';
|
||||||
// 定义组件名(便于调试和递归)
|
// 定义组件名(便于调试和递归)
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'zengZhiZhanJieShaoMod'
|
name: 'zengZhiZhanJieShaoMod'
|
||||||
@ -49,6 +74,14 @@ interface MediaItem {
|
|||||||
text: string;
|
text: string;
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL;
|
||||||
|
|
||||||
|
// 弹框控制
|
||||||
|
const modalVisible = ref(false);
|
||||||
|
const currentItem = ref<any>(null);
|
||||||
|
|
||||||
|
// 加载状态
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
// 原始媒体数据(3条测试数据)
|
// 原始媒体数据(3条测试数据)
|
||||||
const originalMediaData = ref<MediaItem[]>([
|
const originalMediaData = ref<MediaItem[]>([
|
||||||
@ -56,19 +89,22 @@ const originalMediaData = ref<MediaItem[]>([
|
|||||||
type: 'image',
|
type: 'image',
|
||||||
url: 'https://211.99.26.225:12125/?20230814205611342377136845462200&view=jpg&token=bearer c2e76c28-14db-4a0f-9ff2-10cc3f835920',
|
url: 'https://211.99.26.225:12125/?20230814205611342377136845462200&view=jpg&token=bearer c2e76c28-14db-4a0f-9ff2-10cc3f835920',
|
||||||
text: '松岗鱼类增殖站',
|
text: '松岗鱼类增殖站',
|
||||||
description: '松岗鱼类增殖放流站位于四川省阿坝藏族羌族自治州马尔康市松岗镇,主要服务于大渡河上游的双江口和金川两座水电站,同时还承担多种珍稀特有鱼类的救护和科研任务,实现工程建设与生态环境共同推进、相互促进。'
|
description:
|
||||||
|
'松岗鱼类增殖放流站位于四川省阿坝藏族羌族自治州马尔康市松岗镇,主要服务于大渡河上游的双江口和金川两座水电站,同时还承担多种珍稀特有鱼类的救护和科研任务,实现工程建设与生态环境共同推进、相互促进。'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'video',
|
type: 'video',
|
||||||
url: 'https://211.99.26.225:12125/?20230805205848575430105387253710&view=jpg&token=bearer c2e76c28-14db-4a0f-9ff2-10cc3f835920', // 视频URL示例,您可替换为真实地址
|
url: 'https://211.99.26.225:12125/?20230805205848575430105387253710&view=jpg&token=bearer c2e76c28-14db-4a0f-9ff2-10cc3f835920', // 视频URL示例,您可替换为真实地址
|
||||||
text: '猴子岩鱼类增殖站',
|
text: '猴子岩鱼类增殖站',
|
||||||
description: '猴子岩水电站鱼类增殖放流站位于猴子岩水电站坝址下游约7.0km(业主营地下游约1.5km),大渡河左岸桃花渣场顶部平台上,紧邻枢纽桃花大桥下游侧,占地面积47.3亩,其中一期工程27.0亩,预留二期工程用地20.3亩(二期工程目前为丹巴、巴底水电站预留工程)增殖放流站工作流程为:亲鱼收集购买、亲鱼驯养培育、人工催产和授精、人工孵化、苗种培育和放流。 猴子岩鱼类增殖放流站近期放流对象中齐口裂腹鱼、重口裂腹鱼、大渡软刺裸裂尻鱼增殖放流技术水平已趋于熟化,中期放流对象大渡软刺裸裂尻鱼人工繁殖技术逐渐趋于熟化'
|
description:
|
||||||
|
'猴子岩水电站鱼类增殖放流站位于猴子岩水电站坝址下游约7.0km(业主营地下游约1.5km),大渡河左岸桃花渣场顶部平台上,紧邻枢纽桃花大桥下游侧,占地面积47.3亩,其中一期工程27.0亩,预留二期工程用地20.3亩(二期工程目前为丹巴、巴底水电站预留工程)增殖放流站工作流程为:亲鱼收集购买、亲鱼驯养培育、人工催产和授精、人工孵化、苗种培育和放流。 猴子岩鱼类增殖放流站近期放流对象中齐口裂腹鱼、重口裂腹鱼、大渡软刺裸裂尻鱼增殖放流技术水平已趋于熟化,中期放流对象大渡软刺裸裂尻鱼人工繁殖技术逐渐趋于熟化'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'image',
|
type: 'image',
|
||||||
url: 'https://211.99.26.225:12125/?20230805205924378504010675106305&view=jpg&token=bearer c2e76c28-14db-4a0f-9ff2-10cc3f835920',
|
url: 'https://211.99.26.225:12125/?20230805205924378504010675106305&view=jpg&token=bearer c2e76c28-14db-4a0f-9ff2-10cc3f835920',
|
||||||
text: '黑马鱼类增殖站',
|
text: '黑马鱼类增殖站',
|
||||||
description: '大渡河黑马鱼类增殖放流站位于四川省甘洛县黑马乡黑马业主营地内,距离甘洛县城45km,区域交通路况较好。主要承担瀑布沟、深溪沟、大岗山、枕头坝一级、沙坪二级等五座水电站鱼类增殖放流的重任。放流鱼类包含:齐口裂腹鱼、重口裂腹鱼、鲈鲤、长薄鳅、白甲鱼、中华倒刺?、长吻脆、稀有鮊鲫、华鲮、侧沟爬岩鳅等10个种类,共计约918.07万尾珍稀特有鱼苗。'
|
description:
|
||||||
|
'大渡河黑马鱼类增殖放流站位于四川省甘洛县黑马乡黑马业主营地内,距离甘洛县城45km,区域交通路况较好。主要承担瀑布沟、深溪沟、大岗山、枕头坝一级、沙坪二级等五座水电站鱼类增殖放流的重任。放流鱼类包含:齐口裂腹鱼、重口裂腹鱼、鲈鲤、长薄鳅、白甲鱼、中华倒刺?、长吻脆、稀有鮊鲫、华鲮、侧沟爬岩鳅等10个种类,共计约918.07万尾珍稀特有鱼苗。'
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -156,9 +192,60 @@ const currentDescription = computed(() => {
|
|||||||
const realIndex = getCurrentRealIndex();
|
const realIndex = getCurrentRealIndex();
|
||||||
return originalMediaData.value[realIndex]?.description || '';
|
return originalMediaData.value[realIndex]?.description || '';
|
||||||
});
|
});
|
||||||
|
const getData = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
let params = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
field: 'sttpCode',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: 'FPRD'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'isDeleted',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'logo',
|
||||||
|
operator: 'isnotnull'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'introduce',
|
||||||
|
operator: 'isnotnull'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'baseId',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: '07'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
select: ['stcd', 'introduce', 'stnm', 'logo']
|
||||||
|
};
|
||||||
|
let res = await msstbprptGetKendoList(params);
|
||||||
|
let data = res?.data?.data;
|
||||||
|
originalMediaData.value = data.map((item: any) => {
|
||||||
|
return {
|
||||||
|
url: baseUrl + '?' + item.logo + '&view=jpg',
|
||||||
|
text: item.stnm,
|
||||||
|
description: item.introduce,
|
||||||
|
sctd: item.sctd
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
// 页面加载时执行
|
// 页面加载时执行
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
|
await getData();
|
||||||
initRenderData();
|
initRenderData();
|
||||||
startAutoPlay();
|
startAutoPlay();
|
||||||
});
|
});
|
||||||
@ -187,6 +274,16 @@ const goToSlide = (targetIndex: number) => {
|
|||||||
// 计算目标索引(考虑克隆项偏移)
|
// 计算目标索引(考虑克隆项偏移)
|
||||||
currentIndex.value = targetIndex + 1;
|
currentIndex.value = targetIndex + 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理项目点击事件
|
||||||
|
const handleItemClick = () => {
|
||||||
|
currentItem.value = originalMediaData.value.map((item: any) => ({
|
||||||
|
url: item.url || '',
|
||||||
|
description: item.description || '',
|
||||||
|
title: item.text || ''
|
||||||
|
}));
|
||||||
|
modalVisible.value = true;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -216,6 +313,7 @@ const goToSlide = (targetIndex: number) => {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-shrink: 0; // 防止被压缩
|
flex-shrink: 0; // 防止被压缩
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
img,
|
img,
|
||||||
video {
|
video {
|
||||||
@ -251,7 +349,7 @@ const goToSlide = (targetIndex: number) => {
|
|||||||
width: 5px;
|
width: 5px;
|
||||||
height: 5px;
|
height: 5px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: #D8D8D8;
|
background-color: #d8d8d8;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
|
|
||||||
|
|||||||
@ -308,7 +308,7 @@ const buildFilter = () => {
|
|||||||
|
|
||||||
// 处理排序变化
|
// 处理排序变化
|
||||||
const handleSortChange = (info: { field: string; order: string | null }) => {
|
const handleSortChange = (info: { field: string; order: string | null }) => {
|
||||||
// debugger
|
//
|
||||||
if (info.field && info.order) {
|
if (info.field && info.order) {
|
||||||
sortState.value = info;
|
sortState.value = info;
|
||||||
} else {
|
} else {
|
||||||
@ -356,6 +356,8 @@ const handleViewDetail = (record: any, type: string) => {
|
|||||||
modelStore.params.sttp = lastValue
|
modelStore.params.sttp = lastValue
|
||||||
modelStore.title = record.stnm;
|
modelStore.title = record.stnm;
|
||||||
modelStore.params.stcd = record.stcd;
|
modelStore.params.stcd = record.stcd;
|
||||||
|
modelStore.params.bldsttCcode = record.bldsttCcode;
|
||||||
|
//
|
||||||
}
|
}
|
||||||
// console.log('【查看详情】', record)
|
// console.log('【查看详情】', record)
|
||||||
};
|
};
|
||||||
|
|||||||
@ -609,13 +609,13 @@ const getselsectData = async () => {
|
|||||||
value: item.stcd
|
value: item.stcd
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
// debugger
|
//
|
||||||
};
|
};
|
||||||
const convertDateRange = (dateRange: string[]) => {
|
const convertDateRange = (dateRange: string[]) => {
|
||||||
if (!dateRange || dateRange.length !== 2) {
|
if (!dateRange || dateRange.length !== 2) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// debugger
|
//
|
||||||
|
|
||||||
const [startStr, endStr] = dateRange;
|
const [startStr, endStr] = dateRange;
|
||||||
|
|
||||||
|
|||||||
@ -514,7 +514,7 @@ const convertDateRange = (dateRange: string[]) => {
|
|||||||
if (!dateRange || dateRange.length !== 2) {
|
if (!dateRange || dateRange.length !== 2) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// debugger
|
//
|
||||||
|
|
||||||
const [startStr, endStr] = dateRange;
|
const [startStr, endStr] = dateRange;
|
||||||
|
|
||||||
@ -634,7 +634,7 @@ const getselsectData = async () => {
|
|||||||
value: item.stcd
|
value: item.stcd
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
// debugger
|
//
|
||||||
};
|
};
|
||||||
////
|
////
|
||||||
watch(
|
watch(
|
||||||
|
|||||||
@ -170,7 +170,7 @@ const chartRef = ref<HTMLDivElement>();
|
|||||||
|
|
||||||
// 处理图表点击事件
|
// 处理图表点击事件
|
||||||
const handleChartClick = (params: any) => {
|
const handleChartClick = (params: any) => {
|
||||||
// debugger
|
//
|
||||||
console.log('点击数据点:', params);
|
console.log('点击数据点:', params);
|
||||||
const { name, value, data } = params;
|
const { name, value, data } = params;
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
|
|||||||
@ -404,7 +404,7 @@ const fetchData = async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
data.value = processedData;
|
data.value = processedData;
|
||||||
// debugger
|
//
|
||||||
// 提取唯一的基地名称作为 X 轴数据
|
// 提取唯一的基地名称作为 X 轴数据
|
||||||
echartsXData.value = [
|
echartsXData.value = [
|
||||||
...new Set(processedData.map(item => item.basename))
|
...new Set(processedData.map(item => item.basename))
|
||||||
|
|||||||
@ -37,6 +37,7 @@
|
|||||||
:allow-clear="false"
|
:allow-clear="false"
|
||||||
format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
||||||
style="width: 260px"
|
style="width: 260px"
|
||||||
|
:presets="DateSetting.RangeButton.days1"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
@ -103,7 +104,7 @@ import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
|||||||
import { dayGetKendoListCust, hourGetKendoListCust } from '@/api/stll';
|
import { dayGetKendoListCust, hourGetKendoListCust } from '@/api/stll';
|
||||||
import BasicTable from '@/components/BasicTable/index.vue';
|
import BasicTable from '@/components/BasicTable/index.vue';
|
||||||
import { useModelStore } from '@/store/modules/model';
|
import { useModelStore } from '@/store/modules/model';
|
||||||
|
import { DateSetting } from '@/utils/enumeration';
|
||||||
const modelStore = useModelStore();
|
const modelStore = useModelStore();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
options?: any[];
|
options?: any[];
|
||||||
|
|||||||
@ -244,7 +244,7 @@ const getData = async () => {
|
|||||||
// 根据API响应结构规范处理数据
|
// 根据API响应结构规范处理数据
|
||||||
if (res.data && res.data.list) {
|
if (res.data && res.data.list) {
|
||||||
const apiList = res.data.list;
|
const apiList = res.data.list;
|
||||||
// debugger
|
//
|
||||||
// 只使用前两条数据(type: 1 和 type: 2)
|
// 只使用前两条数据(type: 1 和 type: 2)
|
||||||
if (apiList && apiList.length > 0) {
|
if (apiList && apiList.length > 0) {
|
||||||
dataArr.value = apiList.slice(0, 2).map((item: any) => ({
|
dataArr.value = apiList.slice(0, 2).map((item: any) => ({
|
||||||
@ -311,7 +311,7 @@ const getcont = async () => {
|
|||||||
if (list) {
|
if (list) {
|
||||||
titleData.value = list
|
titleData.value = list
|
||||||
}
|
}
|
||||||
// debugger
|
//
|
||||||
};
|
};
|
||||||
// 页面加载时执行的逻辑
|
// 页面加载时执行的逻辑
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@ -307,7 +307,7 @@ const initChart = () => {
|
|||||||
const handleChartClick = (params: any) => {
|
const handleChartClick = (params: any) => {
|
||||||
const categoryName = params.name;
|
const categoryName = params.name;
|
||||||
const currentIndex = params.dataIndex;
|
const currentIndex = params.dataIndex;
|
||||||
// debugger
|
//
|
||||||
|
|
||||||
// 设置弹框标题
|
// 设置弹框标题
|
||||||
detailModalTitle.value =
|
detailModalTitle.value =
|
||||||
@ -318,7 +318,7 @@ const handleChartClick = (params: any) => {
|
|||||||
// 获取当前点击项的详细数据
|
// 获取当前点击项的详细数据
|
||||||
const baseId = mode.value === 'top' ? chartData.value.baseIds[currentIndex] : undefined;
|
const baseId = mode.value === 'top' ? chartData.value.baseIds[currentIndex] : undefined;
|
||||||
const qecPerformance = mode.value === 'left' ? chartData.value.qecPerformances[currentIndex] : undefined;
|
const qecPerformance = mode.value === 'left' ? chartData.value.qecPerformances[currentIndex] : undefined;
|
||||||
// debugger
|
//
|
||||||
|
|
||||||
const currentItem = {
|
const currentItem = {
|
||||||
baseId: baseId,
|
baseId: baseId,
|
||||||
@ -505,7 +505,7 @@ const getcont = async () => {
|
|||||||
if (list) {
|
if (list) {
|
||||||
titleData.value = list;
|
titleData.value = list;
|
||||||
}
|
}
|
||||||
// debugger
|
//
|
||||||
};
|
};
|
||||||
// 页面加载时执行的逻辑
|
// 页面加载时执行的逻辑
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@ -224,6 +224,7 @@ const getselectData = async () => {
|
|||||||
|
|
||||||
// 合并结果并设置选项
|
// 合并结果并设置选项
|
||||||
select.value.options = [...dataMapNameArr, ...otherArr];
|
select.value.options = [...dataMapNameArr, ...otherArr];
|
||||||
|
//
|
||||||
|
|
||||||
// if (baseid.value == 'all') {
|
// if (baseid.value == 'all') {
|
||||||
// select.value.value = '008640202300001021'
|
// select.value.value = '008640202300001021'
|
||||||
@ -536,7 +537,14 @@ onBeforeUnmount(() => {
|
|||||||
//监听子组件的数据变化
|
//监听子组件的数据变化
|
||||||
const handlePanelChange1 = (data: any, type: string) => {
|
const handlePanelChange1 = (data: any, type: string) => {
|
||||||
console.log('当前所有控件状态:', data);
|
console.log('当前所有控件状态:', data);
|
||||||
if (type == 'click') {
|
// 当选择器或日期变化时,重新加载图表数据
|
||||||
|
if (data.moreSelect || data.datetime) {
|
||||||
|
select.value.value = data.moreSelect;
|
||||||
|
datetimePicker.value.value = data.datetime;
|
||||||
|
//
|
||||||
|
getshuiwenList();
|
||||||
|
}
|
||||||
|
if (data?.selectedNodeExtra?.lttd && data?.selectedNodeExtra?.lgtd) {
|
||||||
const mapClass = MapClass.getInstance();
|
const mapClass = MapClass.getInstance();
|
||||||
mapClass.flyTopanto(
|
mapClass.flyTopanto(
|
||||||
[
|
[
|
||||||
@ -546,13 +554,6 @@ const handlePanelChange1 = (data: any, type: string) => {
|
|||||||
15
|
15
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// 当选择器或日期变化时,重新加载图表数据
|
|
||||||
if (data.moreSelect || data.datetime) {
|
|
||||||
select.value.value = data.moreSelect;
|
|
||||||
datetimePicker.value.value = data.datetime;
|
|
||||||
// debugger
|
|
||||||
getshuiwenList();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
const baseid = ref('');
|
const baseid = ref('');
|
||||||
watch(
|
watch(
|
||||||
|
|||||||
@ -208,7 +208,7 @@ const getDataList = async () => {
|
|||||||
}
|
}
|
||||||
// const params = [data1, data2]
|
// const params = [data1, data2]
|
||||||
const res1 = await msstbprptGetKendoList(data1);
|
const res1 = await msstbprptGetKendoList(data1);
|
||||||
// debugger
|
//
|
||||||
let finalData = facilities.value.map((item) => ({ ...item, count: 0 }))
|
let finalData = facilities.value.map((item) => ({ ...item, count: 0 }))
|
||||||
if (res1.data) {
|
if (res1.data) {
|
||||||
const { data } = res1
|
const { data } = res1
|
||||||
@ -220,7 +220,7 @@ const getDataList = async () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
console.log(finalData)
|
console.log(finalData)
|
||||||
// debugger/
|
// /
|
||||||
const res2 = await msstbprptGetKendoList(data2);
|
const res2 = await msstbprptGetKendoList(data2);
|
||||||
if (res2.data) {
|
if (res2.data) {
|
||||||
const responseData = res2?.data?.data
|
const responseData = res2?.data?.data
|
||||||
@ -231,7 +231,7 @@ const getDataList = async () => {
|
|||||||
}
|
}
|
||||||
facilities.value = finalData
|
facilities.value = finalData
|
||||||
console.log(finalData)
|
console.log(finalData)
|
||||||
// debugger
|
//
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取数据失败:', error);
|
console.error('获取数据失败:', error);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@ -166,7 +166,7 @@ const getSelect = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data && data.length > 0) {
|
if (data && data.length > 0) {
|
||||||
// debugger;
|
// ;
|
||||||
select.value.value = data[0].wbsCode;
|
select.value.value = data[0].wbsCode;
|
||||||
selectData.value = data[0].wbsCode; // 同步选中第一个
|
selectData.value = data[0].wbsCode; // 同步选中第一个
|
||||||
select.value.options = data.map(item => ({
|
select.value.options = data.map(item => ({
|
||||||
|
|||||||
@ -34,6 +34,7 @@ export const useModelStore = defineStore('model', () => {
|
|||||||
sttpMap?: string; // 新增:站点类型映射
|
sttpMap?: string; // 新增:站点类型映射
|
||||||
dtinType?: string;
|
dtinType?: string;
|
||||||
bldstt: string;
|
bldstt: string;
|
||||||
|
bldsttCcode: string;
|
||||||
}>({
|
}>({
|
||||||
sttp: 'eng',
|
sttp: 'eng',
|
||||||
stcd: '',
|
stcd: '',
|
||||||
@ -45,7 +46,8 @@ export const useModelStore = defineStore('model', () => {
|
|||||||
sttpCode: '',
|
sttpCode: '',
|
||||||
sttpMap: '',
|
sttpMap: '',
|
||||||
dtinType: '',
|
dtinType: '',
|
||||||
bldstt: ''
|
bldstt: '',
|
||||||
|
bldsttCcode:''
|
||||||
});
|
});
|
||||||
const filter = ref({
|
const filter = ref({
|
||||||
stllgzlx: '', // 生态流量 - 规则类型
|
stllgzlx: '', // 生态流量 - 规则类型
|
||||||
|
|||||||
@ -1,9 +1,73 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import JidiSelectorMod from "@/modules/jidiSelectorMod.vue";
|
import { ref, onMounted, watch } from 'vue';
|
||||||
import RightDrawer from "@/components/RightDrawer/index.vue";
|
import JidiSelectorMod from '@/modules/jidiSelectorMod.vue';
|
||||||
import ZhuanZhiIntroduce from '@/modules/fishSurvey/ZhuanZhiIntroduce/index.vue'
|
import RightDrawer from '@/components/RightDrawer/index.vue';
|
||||||
import FishIntroduce from '@/modules/fishSurvey/FishIntroduce/index.vue'
|
import ZhuanZhiIntroduce from '@/modules/fishSurvey/ZhuanZhiIntroduce/index.vue';
|
||||||
import FishVideo from '@/modules/fishSurvey/FishVideo/index.vue'
|
import FishIntroduce from '@/modules/fishSurvey/FishIntroduce/index.vue';
|
||||||
|
import FishVideo from '@/modules/fishSurvey/FishVideo/index.vue';
|
||||||
|
import SurveyQingKuang from '@/modules/fishSurvey/SurveyQingKuang/index.vue'; //<!-- 鱼类调查装置概况 -->
|
||||||
|
import FishChangeTu from '@/modules/fishSurvey/FishChangeTu/index.vue'
|
||||||
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
|
import { useUiStore } from '@/store/modules/ui';
|
||||||
|
const uiStore = useUiStore();
|
||||||
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
|
const baseid = ref('');
|
||||||
|
|
||||||
|
// 拖拽相关
|
||||||
|
const fixedPRef = ref<HTMLElement | null>(null);
|
||||||
|
let isDragging = false;
|
||||||
|
let startX = 0;
|
||||||
|
let startY = 0;
|
||||||
|
let initialLeft = 0;
|
||||||
|
let initialTop = 0;
|
||||||
|
|
||||||
|
const onMouseDown = (e: MouseEvent) => {
|
||||||
|
;
|
||||||
|
isDragging = true;
|
||||||
|
startX = e.clientX;
|
||||||
|
startY = e.clientY;
|
||||||
|
const rect = fixedPRef.value?.getBoundingClientRect();
|
||||||
|
initialLeft = rect?.left || 0;
|
||||||
|
initialTop = rect?.top || 0;
|
||||||
|
fixedPRef.value!.style.cursor = 'move';
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', onMouseMove);
|
||||||
|
document.addEventListener('mouseup', onMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseMove = (e: MouseEvent) => {
|
||||||
|
if (!isDragging || !fixedPRef.value) return;
|
||||||
|
const dx = e.clientX - startX;
|
||||||
|
const dy = e.clientY - startY;
|
||||||
|
fixedPRef.value.style.left = `${initialLeft + dx}px`;
|
||||||
|
fixedPRef.value.style.top = `${initialTop + dy}px`;
|
||||||
|
fixedPRef.value.style.bottom = 'auto';
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseUp = () => {
|
||||||
|
isDragging = false;
|
||||||
|
fixedPRef.value!.style.cursor = 'default';
|
||||||
|
document.removeEventListener('mousemove', onMouseMove);
|
||||||
|
document.removeEventListener('mouseup', onMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => JidiSelectEventStore.selectedItem,
|
||||||
|
newVal => {
|
||||||
|
if (newVal && newVal.wbsCode) {
|
||||||
|
baseid.value = newVal.wbsCode;
|
||||||
|
if (newVal.wbsCode == '07') {
|
||||||
|
uiStore.setDrawerOpen(true);
|
||||||
|
} else {
|
||||||
|
uiStore.setDrawerOpen(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
onMounted(() => {
|
||||||
|
JidiSelectEventStore.updataJidiData(11);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -12,12 +76,40 @@ import FishVideo from '@/modules/fishSurvey/FishVideo/index.vue'
|
|||||||
<JidiSelectorMod />
|
<JidiSelectorMod />
|
||||||
</div>
|
</div>
|
||||||
<div class="rightContent">
|
<div class="rightContent">
|
||||||
<RightDrawer>
|
<RightDrawer v-if="baseid == '07'">
|
||||||
<ZhuanZhiIntroduce />
|
<ZhuanZhiIntroduce />
|
||||||
<FishIntroduce />
|
<FishIntroduce />
|
||||||
<FishVideo />
|
<FishVideo />
|
||||||
</RightDrawer>
|
</RightDrawer>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="fixedP" ref="fixedPRef">
|
||||||
|
<div class="zhuai" @mousedown="onMouseDown"></div>
|
||||||
|
<SurveyQingKuang />
|
||||||
|
<FishChangeTu />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss">
|
||||||
|
.fixedP {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
left: 220px;
|
||||||
|
width: 450px;
|
||||||
|
z-index: 999;
|
||||||
|
pointer-events: all;
|
||||||
|
padding: 0px 16px;
|
||||||
|
border: 2px solid rgb(197, 214, 226);
|
||||||
|
box-shadow: rgb(229, 237, 243) 0px 0px 3px 6px;
|
||||||
|
.zhuai {
|
||||||
|
width: 100%;
|
||||||
|
height: 16px;
|
||||||
|
cursor: grab;
|
||||||
|
cursor: grab;
|
||||||
|
user-select: none;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
.zhuai:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -14,6 +14,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :id="playerId" class="video-player"></div>
|
<div :id="playerId" class="video-player"></div>
|
||||||
|
<!-- 自定义错误提示 -->
|
||||||
|
<div v-if="showError" class="custom-error-overlay">
|
||||||
|
<div class="error-content">
|
||||||
|
<i class="iconfont icon-jiankong error-icon"></i>
|
||||||
|
<div class="error-text">数据源错误,无法加载视频</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 无视频时显示占位 -->
|
<!-- 无视频时显示占位 -->
|
||||||
@ -31,6 +38,9 @@ import videojs from 'video.js';
|
|||||||
import 'video.js/dist/video-js.css';
|
import 'video.js/dist/video-js.css';
|
||||||
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL;
|
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL;
|
||||||
|
|
||||||
|
// 错误提示显示状态
|
||||||
|
const showError = ref(false);
|
||||||
|
|
||||||
// 注册 HLS 支持(video.js 8.x 内置了 HLS 支持)
|
// 注册 HLS 支持(video.js 8.x 内置了 HLS 支持)
|
||||||
|
|
||||||
interface VideoData {
|
interface VideoData {
|
||||||
@ -89,6 +99,11 @@ const initPlayer = () => {
|
|||||||
player.dispose();
|
player.dispose();
|
||||||
player = null;
|
player = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重试计数器(用于限制重试次数)
|
||||||
|
let retryCount = 0;
|
||||||
|
const maxRetries = 5;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 创建 video 元素
|
// 创建 video 元素
|
||||||
const videoElement = document.createElement('video');
|
const videoElement = document.createElement('video');
|
||||||
@ -195,36 +210,55 @@ const initPlayer = () => {
|
|||||||
const error = player.error();
|
const error = player.error();
|
||||||
console.error('播放器错误:', error);
|
console.error('播放器错误:', error);
|
||||||
|
|
||||||
// 保持最后一帧画面,不显示错误提示
|
// 检查是否已达到最大重试次数
|
||||||
// 在后台静默重试
|
if (retryCount >= maxRetries) {
|
||||||
let retryCount = 0;
|
console.warn(`已达到最大重试次数(${maxRetries}次),停止重试`);
|
||||||
const maxRetries = 10;
|
// 显示自定义错误提示层
|
||||||
|
showError.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const attemptReload = () => {
|
// 增加重试计数
|
||||||
if (retryCount < maxRetries && player && videoUrl.value) {
|
|
||||||
retryCount++;
|
retryCount++;
|
||||||
|
console.log(`视频加载失败,正在进行第 ${retryCount}/${maxRetries} 次重试...`);
|
||||||
|
|
||||||
|
// 立即显示自定义错误提示层
|
||||||
|
showError.value = true;
|
||||||
|
|
||||||
|
// 延迟后重试
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
try {
|
try {
|
||||||
|
if (player && videoUrl.value) {
|
||||||
|
// 隐藏错误提示层(重试时)
|
||||||
|
showError.value = false;
|
||||||
player.src({
|
player.src({
|
||||||
src: videoUrl.value,
|
src: videoUrl.value,
|
||||||
type: videoType // 指定视频类型
|
type: videoType
|
||||||
});
|
});
|
||||||
player.load();
|
player.load();
|
||||||
player.play();
|
player.play();
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.error('重试加载视频失败:', e);
|
||||||
|
// 如果重试失败且未达到最大次数,继续重试
|
||||||
if (retryCount < maxRetries) {
|
if (retryCount < maxRetries) {
|
||||||
attemptReload();
|
setTimeout(() => {
|
||||||
|
if (player) {
|
||||||
|
player.error(); // 触发下一次错误处理
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
attemptReload();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听加载完成
|
// 监听加载完成
|
||||||
player.on('loadeddata', () => {});
|
player.on('loadeddata', () => {
|
||||||
|
// 加载成功时重置重试计数器
|
||||||
|
retryCount = 0;
|
||||||
|
// 隐藏错误提示
|
||||||
|
showError.value = false;
|
||||||
|
});
|
||||||
|
|
||||||
// 监听播放状态
|
// 监听播放状态
|
||||||
player.on('play', () => {});
|
player.on('play', () => {});
|
||||||
@ -339,6 +373,40 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.custom-error-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
.error-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.error-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
opacity: 0.5;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.video-empty {
|
.video-empty {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@ -12,7 +12,7 @@ watch(
|
|||||||
() => JidiSelectEventStore.selectedItem,
|
() => JidiSelectEventStore.selectedItem,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
console.log(newVal);
|
console.log(newVal);
|
||||||
// debugger
|
//
|
||||||
wbsCode.value = newVal.wbsCode;
|
wbsCode.value = newVal.wbsCode;
|
||||||
},
|
},
|
||||||
{ deep: true, immediate: true }
|
{ deep: true, immediate: true }
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user