数据管理子系统-问题bug修改
This commit is contained in:
parent
678caccbd7
commit
ac5d153924
@ -277,6 +277,60 @@ export function getFishReleaseMonitorList(data) {
|
||||
});
|
||||
}
|
||||
|
||||
// 过鱼设施人工数据 - 新增
|
||||
export function addFpssRInfo(data) {
|
||||
return request({
|
||||
url: '/data/fpssR/add',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 过鱼设施人工数据 - 编辑
|
||||
export function updateFpssRInfo(data) {
|
||||
return request({
|
||||
url: '/data/fpssR/update',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 过鱼设施人工数据 - 删除
|
||||
export function deleteFpssRInfo(data) {
|
||||
return request({
|
||||
url: '/data/fpssR/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 过鱼设施自动数据 - 编辑
|
||||
export function updateFpssrlRInfo(data) {
|
||||
return request({
|
||||
url: '/base/fpssrlR/update',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 过鱼设施自动数据 - 删除
|
||||
export function deleteFpssrlRInfo(data) {
|
||||
return request({
|
||||
url: '/base/fpssrlR/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 鱼类 放鱼
|
||||
export function getFishReleaseList(data) {
|
||||
return request({
|
||||
|
||||
@ -131,21 +131,13 @@ const searchList: any = [
|
||||
// 表格列配置
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: "recordId",
|
||||
key: "recordName",
|
||||
title: "电站",
|
||||
dataIndex: "recordId",
|
||||
dataIndex: "recordName",
|
||||
visible: true,
|
||||
width: 120,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
key: "tableName",
|
||||
title: "数据类型/所属表名",
|
||||
dataIndex: "tableName",
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
key: "operator",
|
||||
title: "修改人",
|
||||
|
||||
729
frontend-sjgl/src/components/fishSearch/index.vue
Normal file
729
frontend-sjgl/src/components/fishSearch/index.vue
Normal file
@ -0,0 +1,729 @@
|
||||
<template>
|
||||
<div
|
||||
class="custom-fish-search-container"
|
||||
ref="containerRef"
|
||||
@mouseenter="handleMouseEnter"
|
||||
@mouseleave="handleMouseLeave"
|
||||
>
|
||||
<!-- 1. 输入框区域 -->
|
||||
<div
|
||||
class="fish-input-wrapper"
|
||||
:class="{ 'is-focused': isOpen, disabled: disabled }"
|
||||
@click="handleWrapperClick"
|
||||
>
|
||||
<!--
|
||||
核心逻辑:
|
||||
- 当 isOpen 为 true 时,强制显示 input,并聚焦。
|
||||
- 当 isOpen 为 false 时,显示 selectedItem 或 placeholder。
|
||||
-->
|
||||
|
||||
<!-- 情况 A: 显示输入框 (当下拉框打开时) -->
|
||||
<input
|
||||
v-if="isOpen"
|
||||
ref="inputRef"
|
||||
v-model="searchKeyword"
|
||||
type="text"
|
||||
class="visible-input"
|
||||
@focus="handleInputFocus"
|
||||
@input="handleSearchInput"
|
||||
@blur="handleInputBlur"
|
||||
@keydown.stop
|
||||
@keydown.enter="handleEnterKey"
|
||||
:disabled="disabled"
|
||||
placeholder="请输入搜索..."
|
||||
/>
|
||||
|
||||
<!-- 情况 B: 显示选中项或占位符 (当下拉框关闭时) -->
|
||||
<div v-else class="display-value">
|
||||
<span v-if="selectedItem" class="single-value">{{ selectedItem.name }}</span>
|
||||
<span v-else class="placeholder">{{ placeholder }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 右侧图标 -->
|
||||
<div class="suffix-icon">
|
||||
<!-- Loading 状态 -->
|
||||
<span v-if="loading" class="loading-icon">⟳</span>
|
||||
|
||||
<!-- 清除图标:当有值(选中或正在搜索)且鼠标悬停时显示 -->
|
||||
<span
|
||||
v-else-if="(selectedItem || searchKeyword) && isHovered"
|
||||
class="clear-icon"
|
||||
@click.stop="handleClear"
|
||||
title="清空"
|
||||
>
|
||||
<CloseCircleOutlined />
|
||||
</span>
|
||||
|
||||
<!-- 下拉箭头:其他情况显示 -->
|
||||
<span v-else class="arrow-icon" :class="{ rotate: isOpen }"
|
||||
><DownOutlined
|
||||
/></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. 下拉面板 (绝对定位) -->
|
||||
<teleport to="body">
|
||||
<transition name="fade">
|
||||
<div
|
||||
v-show="isOpen"
|
||||
class="fish-dropdown-panel"
|
||||
:style="dropdownStyle"
|
||||
@mousedown.prevent
|
||||
>
|
||||
<!-- 顶部:查询方式切换 -->
|
||||
<div class="dropdown-header">
|
||||
<span class="header-label">查询方式:</span>
|
||||
<span
|
||||
class="mode-btn"
|
||||
:class="{ active: !isIntelligentQuery }"
|
||||
@click="switchQueryMode(false)"
|
||||
>
|
||||
相似度
|
||||
</span>
|
||||
<span
|
||||
class="mode-btn"
|
||||
:class="{ active: isIntelligentQuery }"
|
||||
@click="switchQueryMode(true)"
|
||||
>
|
||||
智能查询
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="dropdown-body">
|
||||
<!-- 左侧:列表 -->
|
||||
<div class="list-container" ref="listContainerRef">
|
||||
<div v-if="loading" class="loading-wrapper">
|
||||
<a-spin size="small" />
|
||||
<span class="loading-text">搜索中...</span>
|
||||
</div>
|
||||
<div
|
||||
v-for="opt in filteredOptions"
|
||||
:key="opt.id"
|
||||
class="dropdown-item"
|
||||
:class="{
|
||||
'is-active': isSelected(opt.id),
|
||||
'is-hovered': hoveredId === opt.id,
|
||||
}"
|
||||
@click="handleSelectOption(opt)"
|
||||
@mouseenter="hoveredId = opt.id"
|
||||
>
|
||||
<span class="item-name" v-html="highlightText(opt.name)"></span>
|
||||
<span v-if="isSelected(opt.id)" class="check-icon">✓</span>
|
||||
</div>
|
||||
<div v-if="filteredOptions.length === 0" class="empty-tip">无匹配数据</div>
|
||||
</div>
|
||||
|
||||
<!-- 中间分割线 -->
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- 右侧:详情预览 -->
|
||||
<div class="detail-container">
|
||||
<div v-if="currentDetailData" class="detail-content">
|
||||
<div
|
||||
class="detail-title"
|
||||
v-html="highlightText(currentDetailData.name)"
|
||||
></div>
|
||||
<div class="detail-alias" :title="currentDetailData.alias">
|
||||
<div
|
||||
v-html="highlightText(currentDetailData.alias || '暂无别名')"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="detail-placeholder">请选择或悬停查看</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, computed, nextTick, onBeforeUnmount } from "vue";
|
||||
import { getFishDictoryDropdown, getSimilarFishDictoryDropdown } from "@/api/select";
|
||||
import { useShuJuTianBaoStore } from "@/store/modules/shuJuTianBao";
|
||||
import { message } from "ant-design-vue";
|
||||
import { CloseCircleOutlined, DownOutlined } from "@ant-design/icons-vue";
|
||||
|
||||
const shuJuTianBaoStore = useShuJuTianBaoStore();
|
||||
|
||||
// --- Props & Emits ---
|
||||
interface Props {
|
||||
modelValue: any; // 单选模式
|
||||
width?: string;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
placeholder: "鱼种类支持俗名查询",
|
||||
width: "100%",
|
||||
disabled: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "update:modelValue", value: string, opt: any): void;
|
||||
}>();
|
||||
|
||||
// --- State ---
|
||||
const loading = ref(false);
|
||||
const options = ref<any[]>([]);
|
||||
const allOptions = ref<any[]>([]);
|
||||
const searchKeyword = ref<string>("");
|
||||
const hoveredId = ref<string | null>(null);
|
||||
const isOpen = ref(false);
|
||||
const isIntelligentQuery = ref(true);
|
||||
const isHovered = ref(false); // 新增:鼠标悬停状态
|
||||
// 新增:防抖定时器
|
||||
let searchTimer: any = null;
|
||||
|
||||
// DOM Refs
|
||||
const containerRef = ref<HTMLElement | null>(null);
|
||||
const inputRef = ref<HTMLInputElement | null>(null);
|
||||
|
||||
// 计算下拉框位置
|
||||
const dropdownStyle = computed(() => {
|
||||
if (!containerRef.value) return {};
|
||||
const rect = containerRef.value.getBoundingClientRect();
|
||||
return {
|
||||
top: `${rect.bottom + window.scrollY + 4}px`,
|
||||
left: `${rect.left + window.scrollX}px`,
|
||||
width: `${Math.max(rect.width, 400)}px`,
|
||||
};
|
||||
});
|
||||
|
||||
// --- Computed ---
|
||||
const selectedItem = computed(() => {
|
||||
if (!props.modelValue) return null;
|
||||
return options.value.find((opt) => opt.id === props.modelValue);
|
||||
});
|
||||
|
||||
const filteredOptions = computed(() => {
|
||||
if (isIntelligentQuery.value) {
|
||||
return options.value;
|
||||
}
|
||||
|
||||
// 如果是相似度模式,进行本地前端过滤
|
||||
if (!searchKeyword.value) {
|
||||
return allOptions.value; // 无搜索词时显示全量
|
||||
}
|
||||
const lowerKeyword = searchKeyword.value.toLowerCase();
|
||||
return allOptions.value.filter((item: any) => {
|
||||
const nameMatch = item.name?.toLowerCase().includes(lowerKeyword);
|
||||
const aliasMatch = item.alias?.toLowerCase().includes(lowerKeyword);
|
||||
return nameMatch || aliasMatch;
|
||||
});
|
||||
});
|
||||
|
||||
const currentDetailData = computed(() => {
|
||||
if (hoveredId.value) {
|
||||
// 优先从当前展示的 options 中找,如果没有再从 allOptions 找
|
||||
return (
|
||||
options.value.find((item: any) => item.id === hoveredId.value) ||
|
||||
allOptions.value.find((item: any) => item.id === hoveredId.value)
|
||||
);
|
||||
}
|
||||
if (props.modelValue) {
|
||||
return allOptions.value.find((item: any) => item.id === props.modelValue);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
// --- Methods ---
|
||||
|
||||
const isSelected = (id: string) => {
|
||||
return props.modelValue === id;
|
||||
};
|
||||
|
||||
const toggleDropdown = () => {
|
||||
if (props.disabled) return;
|
||||
|
||||
if (isOpen.value) {
|
||||
closeDropdown();
|
||||
} else {
|
||||
openDropdown();
|
||||
}
|
||||
};
|
||||
|
||||
const openDropdown = async () => {
|
||||
isOpen.value = true;
|
||||
await nextTick();
|
||||
inputRef.value?.focus();
|
||||
};
|
||||
|
||||
const closeDropdown = () => {
|
||||
isOpen.value = false;
|
||||
hoveredId.value = null;
|
||||
};
|
||||
|
||||
// 新增:清空逻辑
|
||||
const handleClear = () => {
|
||||
// 1. 清空选中的值
|
||||
emit("update:modelValue", "", null);
|
||||
// 2. 清空搜索词
|
||||
searchKeyword.value = "";
|
||||
options.value = allOptions.value; // 恢复全量
|
||||
// 3. 关闭下拉框
|
||||
closeDropdown();
|
||||
};
|
||||
|
||||
const handleWrapperClick = () => {
|
||||
toggleDropdown();
|
||||
};
|
||||
|
||||
const handleInputFocus = () => {
|
||||
if (!isOpen.value) {
|
||||
isOpen.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputBlur = () => {
|
||||
setTimeout(() => {
|
||||
// 依赖 handleClickOutside 进行最终判断,这里不做强制关闭
|
||||
}, 100);
|
||||
};
|
||||
|
||||
const handleSearchInput = () => {
|
||||
hoveredId.value = null;
|
||||
if (!isOpen.value) {
|
||||
isOpen.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
// 修改:执行实际的搜索请求
|
||||
const executeSearch = () => {
|
||||
const keyword = searchKeyword.value;
|
||||
|
||||
// 确保下拉框是打开的
|
||||
if (!isOpen.value) {
|
||||
isOpen.value = true;
|
||||
}
|
||||
|
||||
// 1. 相似度模式:纯前端过滤
|
||||
if (!isIntelligentQuery.value) {
|
||||
if (!keyword) {
|
||||
options.value = allOptions.value;
|
||||
}
|
||||
// 相似度模式下,computed 会自动根据 searchKeyword 过滤 allOptions,无需额外操作
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 智能查询模式:调用接口
|
||||
if (!keyword) {
|
||||
options.value = allOptions.value;
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置 Loading
|
||||
loading.value = true;
|
||||
|
||||
// 调用接口 (不再需要防抖定时器,因为是由用户行为触发的)
|
||||
getSimilarFishDictoryDropdown({ name: keyword })
|
||||
.then((res) => {
|
||||
options.value = res.data || [];
|
||||
loading.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
// 新增:加载默认字典数据的方法
|
||||
const loadDefaultOptions = () => {
|
||||
let data = shuJuTianBaoStore.getFishOption();
|
||||
if (data && data.length > 0) {
|
||||
allOptions.value = data;
|
||||
options.value = data; // 初始展示全量
|
||||
} else {
|
||||
loading.value = true;
|
||||
getFishDictoryDropdown()
|
||||
.then((res) => {
|
||||
const list = res.data || [];
|
||||
allOptions.value = list;
|
||||
options.value = list;
|
||||
loading.value = false;
|
||||
shuJuTianBaoStore.setFishOption(list);
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 处理回车键:如果有匹配项,选中第一个
|
||||
const handleEnterKey = (event: KeyboardEvent) => {
|
||||
// 关键:阻止回车键的默认行为(防止表单提交或输入框失焦)
|
||||
event.preventDefault();
|
||||
|
||||
// 执行搜索
|
||||
executeSearch();
|
||||
};
|
||||
|
||||
const handleSelectOption = (opt: any) => {
|
||||
// 1. 更新值
|
||||
if (props.modelValue === opt.id) {
|
||||
emit("update:modelValue", "", null);
|
||||
} else {
|
||||
emit("update:modelValue", opt.id, opt);
|
||||
}
|
||||
|
||||
// 2. 清空搜索词
|
||||
searchKeyword.value = "";
|
||||
options.value = allOptions.value; // 选中后恢复全量列表,方便下次搜索
|
||||
|
||||
// 3. 关闭下拉框
|
||||
closeDropdown();
|
||||
|
||||
// 4. 让输入框失去焦点
|
||||
inputRef.value?.blur();
|
||||
};
|
||||
|
||||
const switchQueryMode = (val: boolean) => {
|
||||
isIntelligentQuery.value = val;
|
||||
|
||||
message.success(val ? "智能查询已开启" : "相似度查询已开启");
|
||||
if (searchKeyword.value) {
|
||||
executeSearch(); // 切换模式后,用当前的词重新查一次
|
||||
} else {
|
||||
options.value = allOptions.value;
|
||||
}
|
||||
};
|
||||
|
||||
const highlightText = (text: string) => {
|
||||
if (!text) return "暂无别名";
|
||||
if (!searchKeyword.value) return text;
|
||||
|
||||
const escapeRegExp = (str: string) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
const reg = new RegExp(`(${escapeRegExp(searchKeyword.value)})`, "gi");
|
||||
return text.replace(reg, `<span style="color: #ff4d4f; font-weight: bold;">$1</span>`);
|
||||
};
|
||||
|
||||
// 初始化数据
|
||||
const init = () => {
|
||||
loadDefaultOptions();
|
||||
};
|
||||
|
||||
// 点击外部关闭下拉框
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (!isOpen.value) return;
|
||||
|
||||
const target = event.target as HTMLElement;
|
||||
const isInsideContainer = containerRef.value?.contains(target);
|
||||
const isInsideDropdown = target.closest(".fish-dropdown-panel");
|
||||
|
||||
if (!isInsideContainer && !isInsideDropdown) {
|
||||
closeDropdown();
|
||||
}
|
||||
};
|
||||
const handleMouseEnter = () => {
|
||||
if (!props.disabled) {
|
||||
isHovered.value = true;
|
||||
}
|
||||
};
|
||||
const handleMouseLeave = () => {
|
||||
isHovered.value = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
document.addEventListener("click", handleClickOutside);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener("click", handleClickOutside);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.custom-fish-search-container {
|
||||
position: relative;
|
||||
width: v-bind(width);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
|
||||
Arial, sans-serif;
|
||||
}
|
||||
|
||||
.fish-input-wrapper {
|
||||
position: relative;
|
||||
min-height: 32px;
|
||||
padding: 4px 11px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 2px;
|
||||
background-color: #fff;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&:hover {
|
||||
border-color: #40a9ff;
|
||||
}
|
||||
|
||||
&.is-focused {
|
||||
border-color: #40a9ff;
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
.single-value {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.display-value {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
margin-right: 20px;
|
||||
pointer-events: none; /* 让点击事件穿透到 wrapper */
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 输入框样式 */
|
||||
.visible-input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
|
||||
&::placeholder {
|
||||
color: #bfbfbf;
|
||||
}
|
||||
}
|
||||
|
||||
.suffix-icon {
|
||||
color: #bfbfbf;
|
||||
font-size: 12px;
|
||||
pointer-events: none;
|
||||
margin-left: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.rotate {
|
||||
transform: rotate(180deg);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
/* 新增:清除图标样式 */
|
||||
.clear-icon {
|
||||
pointer-events: auto; /* 允许点击 */
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
transition: color 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.disabled {
|
||||
background-color: #f5f5f5;
|
||||
&:hover {
|
||||
border-color: #d9d9d9;
|
||||
}
|
||||
.single-value {
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
}
|
||||
|
||||
/* 下拉面板样式 */
|
||||
.fish-dropdown-panel {
|
||||
position: absolute;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08),
|
||||
0 9px 28px 8px rgba(0, 0, 0, 0.05);
|
||||
z-index: 9999;
|
||||
overflow: hidden;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.dropdown-header {
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
background: #fafafa;
|
||||
user-select: none;
|
||||
|
||||
.header-label {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.mode-btn {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
padding: 2px 6px;
|
||||
border-radius: 2px;
|
||||
color: #666;
|
||||
margin-right: 8px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #005293;
|
||||
background: rgba(0, 82, 147, 0.1);
|
||||
}
|
||||
|
||||
&:hover:not(.active) {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-body {
|
||||
display: flex;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.list-container {
|
||||
width: 150px;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.loading-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
min-height: 100px;
|
||||
color: #999;
|
||||
|
||||
.loading-text {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
transition: background-color 0.2s;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background-color: #e6f7ff;
|
||||
color: #1890ff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.is-hovered {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.check-icon {
|
||||
color: #1890ff;
|
||||
font-weight: bold;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 1px;
|
||||
background-color: #e8e8e8;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.detail-container {
|
||||
width: 250px;
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.detail-alias {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
word-break: break-all;
|
||||
|
||||
:deep(span) {
|
||||
color: #ff4d4f;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-placeholder {
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
margin-top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
padding: 20px 0;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
</style>
|
||||
@ -19,7 +19,7 @@
|
||||
<!-- ===== 概述 ===== -->
|
||||
<a-col :span="24"><div class="form-group-title">概述</div></a-col>
|
||||
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="电站编码" name="stcd">
|
||||
<a-input
|
||||
v-model:value="formData.stcd"
|
||||
@ -29,7 +29,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="电站名称" name="ennm">
|
||||
<a-input
|
||||
v-model:value="formData.ennm"
|
||||
@ -38,7 +38,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="基地" name="baseId">
|
||||
<a-select
|
||||
v-model:value="formData.baseId"
|
||||
@ -58,7 +58,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="所在位置" name="addvcd">
|
||||
<a-select
|
||||
v-model:value="formData.addvcd"
|
||||
@ -77,7 +77,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="详细地址" name="stlc">
|
||||
<a-input
|
||||
v-model:value="formData.stlc"
|
||||
@ -85,7 +85,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="所在河段" name="reachcd">
|
||||
<a-select
|
||||
v-model:value="formData.reachcd"
|
||||
@ -105,7 +105,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="所属公司" name="hycd">
|
||||
<a-select
|
||||
v-model:value="formData.hycd"
|
||||
@ -124,7 +124,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="所属集团" name="topHycd">
|
||||
<a-select
|
||||
v-model:value="formData.topHycd"
|
||||
@ -143,7 +143,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="建设状态" name="bldsttCode">
|
||||
<a-select
|
||||
v-model:value="formData.bldsttCode"
|
||||
@ -159,7 +159,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="建设时间" name="jcdt">
|
||||
<a-date-picker
|
||||
v-model:value="formData.jcdt"
|
||||
@ -170,7 +170,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="第一台机组投产时间" name="piodt">
|
||||
<a-date-picker
|
||||
v-model:value="formData.piodt"
|
||||
@ -181,7 +181,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="全部机组投产时间" name="aiodt">
|
||||
<a-date-picker
|
||||
v-model:value="formData.aiodt"
|
||||
@ -196,7 +196,7 @@
|
||||
<!-- ===== 水文 ===== -->
|
||||
<a-col :span="24"><div class="form-group-title">水文</div></a-col>
|
||||
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="坝址以上流域面积(km²)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -210,7 +210,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="距河源距离(km)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -224,7 +224,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="多年平均降雨量(mm)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -238,7 +238,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="多年平均年径流量(亿m³)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -251,7 +251,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="多年平均流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -264,7 +264,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="1月多年平均流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -278,7 +278,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="2月多年平均流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -292,7 +292,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="3月多年平均流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -306,7 +306,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="4月多年平均流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -320,7 +320,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="5月多年平均流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -334,7 +334,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="6月多年平均流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -348,7 +348,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="7月多年平均流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -362,7 +362,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="8月多年平均流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -376,7 +376,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="9月多年平均流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -390,7 +390,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="10月多年平均流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -404,7 +404,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="11月多年平均流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -418,7 +418,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="12月多年平均流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -432,7 +432,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="实测最大流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -445,7 +445,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="实测最小流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -458,7 +458,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="调查历史最大流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -471,7 +471,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="设计入库洪水流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -484,7 +484,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="设计洪水重现期(年)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -493,7 +493,7 @@
|
||||
<a-input v-model:value="formData.dsrcin" placeholder="请输入" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="校核入库洪水流量(m³/s)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -506,7 +506,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="校核洪水重现期(年)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -515,7 +515,7 @@
|
||||
<a-input v-model:value="formData.chrcin" placeholder="请输入" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="实测最大洪量(三天)(亿m³)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -528,7 +528,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="设计洪量(三天)(亿m³)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -541,7 +541,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="校核洪量(三天)(亿m³)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -554,7 +554,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="多年平均输沙量(万t)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -567,7 +567,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="多年平均含沙量(kg/m³)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -580,7 +580,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="实测最大含沙量(kg/m³)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -597,7 +597,7 @@
|
||||
<!-- ===== 水库 ===== -->
|
||||
<a-col :span="24"><div class="form-group-title">水库</div></a-col>
|
||||
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="校核洪水位(m)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -611,7 +611,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="设计洪水位(m)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -625,7 +625,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="正常蓄水位(m)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -639,7 +639,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="防洪高水位(m)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -653,7 +653,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="防洪限制水位(m)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -667,7 +667,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="年消落水位(m)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -681,7 +681,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="死水位(m)" :labelCol="{ span: 10 }" name="ddz">
|
||||
<a-input-number
|
||||
v-model:value="formData.ddz"
|
||||
@ -691,7 +691,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="正常蓄水位水库面积(km²)"
|
||||
name="nrzar"
|
||||
@ -705,7 +705,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="回水长度(km)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -719,7 +719,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="总库容(亿m³)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -733,7 +733,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="正常蓄水位以下库容(亿m³)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -746,7 +746,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="防洪库容(亿m³)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -759,7 +759,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="调节库容(亿m³)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -772,7 +772,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="死库容(亿m³)"
|
||||
:labelCol="{ span: 10 }"
|
||||
@ -785,7 +785,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="库容系数(%)" :labelCol="{ span: 10 }" name="cpsc">
|
||||
<a-input-number
|
||||
v-model:value="formData.cpsc"
|
||||
@ -794,7 +794,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="调节性能" :labelCol="{ span: 10 }" name="rgcp">
|
||||
<a-select
|
||||
v-model:value="formData.rgcp"
|
||||
@ -816,7 +816,7 @@
|
||||
><div class="form-group-title">下泄流量及相应下游水位</div></a-col
|
||||
>
|
||||
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="设计洪水位时最大下泄流量(m³/s)"
|
||||
:labelCol="{ span: 12 }"
|
||||
@ -829,7 +829,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="设计洪水位时最大下泄流量相应下游水位(m)"
|
||||
:labelCol="{ span: 14 }"
|
||||
@ -842,7 +842,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="校核洪水位时最大下泄流量(m³/s)"
|
||||
:labelCol="{ span: 12 }"
|
||||
@ -855,7 +855,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="校核洪水位时最大下泄流量相应下游水位(m)"
|
||||
:labelCol="{ span: 14 }"
|
||||
@ -868,7 +868,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="枯水期调节流量(m³/s)"
|
||||
:labelCol="{ span: 12 }"
|
||||
@ -881,7 +881,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="枯水期调节流量相应下游水位(m)"
|
||||
:labelCol="{ span: 12 }"
|
||||
@ -894,7 +894,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="发电最大引用流量(m³/s)"
|
||||
:labelCol="{ span: 12 }"
|
||||
@ -907,7 +907,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="发电最大引用流量相应下游水位(m)"
|
||||
:labelCol="{ span: 12 }"
|
||||
@ -926,7 +926,7 @@
|
||||
><div class="form-group-title">工程效益指标</div></a-col
|
||||
>
|
||||
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="装机容量(万kW)" name="ttpwr">
|
||||
<a-input-number
|
||||
v-model:value="formData.ttpwr"
|
||||
@ -936,7 +936,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="装机台数(台)" name="gncnt">
|
||||
<a-input-number
|
||||
v-model:value="formData.gncnt"
|
||||
@ -945,12 +945,12 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="容量构成(台*MW)" name="gntp">
|
||||
<a-input v-model:value="formData.gntp" placeholder="请输入" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="保证出力(MW)" name="grntpwr">
|
||||
<a-input-number
|
||||
v-model:value="formData.grntpwr"
|
||||
@ -960,7 +960,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="多年平均年发电量(亿kW·h)" name="yrge">
|
||||
<a-input-number
|
||||
v-model:value="formData.yrge"
|
||||
@ -969,7 +969,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="年利用小时数(h)" name="ushr">
|
||||
<a-input-number
|
||||
v-model:value="formData.ushr"
|
||||
@ -978,7 +978,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="机组综合出力系数" name="k">
|
||||
<a-input-number
|
||||
v-model:value="formData.k"
|
||||
@ -991,7 +991,7 @@
|
||||
<!-- ===== 大坝 ===== -->
|
||||
<a-col :span="24"><div class="form-group-title">大坝</div></a-col>
|
||||
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="坝型" name="dmtp">
|
||||
<a-input
|
||||
v-model:value="formData.dmtp"
|
||||
@ -1000,7 +1000,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="坝顶高程(m)" name="dmcrel">
|
||||
<a-input-number
|
||||
v-model:value="formData.dmcrel"
|
||||
@ -1010,7 +1010,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="最大坝高(m)" name="mxdmhg">
|
||||
<a-input-number
|
||||
v-model:value="formData.mxdmhg"
|
||||
@ -1020,7 +1020,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="坝顶长度(m)" name="dmlen">
|
||||
<a-input-number
|
||||
v-model:value="formData.dmlen"
|
||||
@ -1034,7 +1034,7 @@
|
||||
<!-- ===== 其他 ===== -->
|
||||
<a-col :span="24"><div class="form-group-title">其他</div></a-col>
|
||||
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="单独年发电量(亿kW·h)" name="sgyge">
|
||||
<a-input-number
|
||||
v-model:value="formData.sgyge"
|
||||
@ -1043,7 +1043,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="联合年发电量(亿kW·h)" name="unyge">
|
||||
<a-input-number
|
||||
v-model:value="formData.unyge"
|
||||
@ -1052,7 +1052,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="利用落差(m)" name="gap">
|
||||
<a-input-number
|
||||
v-model:value="formData.gap"
|
||||
@ -1061,7 +1061,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="开发方式" name="dvtp">
|
||||
<a-select
|
||||
v-model:value="formData.dvtp"
|
||||
@ -1417,7 +1417,12 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
stcd: formData.value.stcd
|
||||
};
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
appendSelectLabels(engInfo, changeOrder.value.map(c => c.field));
|
||||
res = await updatePowerInfo({
|
||||
|
||||
@ -718,7 +718,12 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
// 编辑时只传修改过的字段
|
||||
engInfo.id = formData.value.id;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
// 保留剩余的已有图片 inffile
|
||||
const existingIds = fileList.value
|
||||
|
||||
@ -539,7 +539,12 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateAiboxInfo({
|
||||
engInfo,
|
||||
|
||||
@ -527,7 +527,12 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateOtweInfo({
|
||||
engInfo,
|
||||
|
||||
@ -572,7 +572,12 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateAiInfo({
|
||||
engInfo,
|
||||
|
||||
@ -403,7 +403,14 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
res = await addFpInfo({ engInfo, source: source.trim() || '新增' });
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => { engInfo[item.field] = formData.value[item.field]; });
|
||||
changeOrder.value.forEach(item => {
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
// fpssmn 多选:数组 → 逗号分隔字符串
|
||||
if (Array.isArray(engInfo.fpssmn)) engInfo.fpssmn = engInfo.fpssmn.join(',');
|
||||
res = await updateFpInfo({ engInfo, source: source.trim() || '编辑' });
|
||||
|
||||
@ -450,7 +450,12 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
// 编辑时只传 stcd + 修改过的字段
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateTeInfo({
|
||||
engInfo,
|
||||
|
||||
@ -44,6 +44,15 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="图层编码" name="layerCode">
|
||||
<a-input
|
||||
v-model:value="formData.layerCode"
|
||||
placeholder="请输入图层编码"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="经度(°)" name="lgtd">
|
||||
<a-input-number
|
||||
@ -246,6 +255,7 @@ const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '栖息地站码',
|
||||
stnm: '栖息地名称',
|
||||
stlc: '站址',
|
||||
layerCode: '图层编码',
|
||||
baseId: '水电基地',
|
||||
rstcd: '所属电站',
|
||||
bhdx: '保护对象',
|
||||
@ -518,7 +528,12 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
// bhdx 多选:数组 → 逗号分隔字符串
|
||||
if (Array.isArray(engInfo.bhdx)) engInfo.bhdx = engInfo.bhdx.join(',');
|
||||
|
||||
@ -64,7 +64,7 @@ const operationLogVisible = ref(false);
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{ key: 'stnm', title: '栖息地名称', dataIndex: 'stnm', visible: true, width: 260, ellipsis: true },
|
||||
{ key: 'stcd', title: '栖息地站码', dataIndex: 'stcd', visible: true, width: 320, fixed: 'left', ellipsis: true },
|
||||
{ key: 'stcd', title: '栖息地站码', dataIndex: 'stcd', visible: true, width: 320, ellipsis: true },
|
||||
{ key: 'baseName', title: '水电基地', dataIndex: 'baseName', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'ennm', title: '所属电站', dataIndex: 'ennm', visible: true, width: 150, ellipsis: true },
|
||||
{ key: 'bhdxName', title: '保护对象', dataIndex: 'bhdxName', visible: true, width: 240, ellipsis: true },
|
||||
@ -77,6 +77,7 @@ const columns = ref<any[]>([
|
||||
{ key: 'bhfs', title: '保护方式', dataIndex: 'bhfs', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'inv', title: '投资(亿元)', dataIndex: 'inv', visible: true, width: 120 },
|
||||
{ key: 'stlc', title: '站址', dataIndex: 'stlc', visible: true, ellipsis: true,width: 220 },
|
||||
{ key: 'layerCode', title: '图层编码', dataIndex: 'layerCode', visible: true, ellipsis: true, width: 160 },
|
||||
{ key: 'action', title: '操作', dataIndex: 'action', fixed: 'right', width: 120 },
|
||||
|
||||
]);
|
||||
|
||||
@ -486,7 +486,14 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
res = await addVaInfo({ engInfo, source: source.trim() || '新增' });
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => { engInfo[item.field] = formData.value[item.field]; });
|
||||
changeOrder.value.forEach(item => {
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateVaInfo({ engInfo, source: source.trim() || '编辑' });
|
||||
}
|
||||
if (res?.code == 0 || res?.success) {
|
||||
|
||||
@ -344,7 +344,14 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
res = await addEqInfo({ engInfo, source: source.trim() || '新增' });
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => { engInfo[item.field] = formData.value[item.field]; });
|
||||
changeOrder.value.forEach(item => {
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateEqInfo({ engInfo, source: source.trim() || '编辑' });
|
||||
}
|
||||
if (res?.code == 0 || res?.success) {
|
||||
|
||||
@ -265,7 +265,14 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
res = await addVdInfo({ engInfo, source: source.trim() || '新增' });
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => { engInfo[item.field] = formData.value[item.field]; });
|
||||
changeOrder.value.forEach(item => {
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateVdInfo({ engInfo, source: source.trim() || '编辑' });
|
||||
}
|
||||
if (res?.code == 0 || res?.success) {
|
||||
|
||||
@ -461,7 +461,12 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
// 编辑时只传 stcd + 修改过的字段
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateWeInfo({
|
||||
engInfo,
|
||||
|
||||
@ -454,7 +454,12 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
// 编辑时只传 stcd + 修改过的字段
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateWtInfo({
|
||||
engInfo,
|
||||
|
||||
@ -502,7 +502,12 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateWqInfo({
|
||||
engInfo,
|
||||
|
||||
@ -513,7 +513,12 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateSonarInfo({
|
||||
engInfo,
|
||||
|
||||
@ -525,7 +525,12 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateDwInfo({
|
||||
engInfo,
|
||||
|
||||
@ -534,7 +534,12 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateOtteInfo({
|
||||
engInfo,
|
||||
|
||||
@ -350,7 +350,14 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
res = await addFbInfo({ engInfo, source: source.trim() || '新增' });
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => { engInfo[item.field] = formData.value[item.field]; });
|
||||
changeOrder.value.forEach(item => {
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateFbInfo({ engInfo, source: source.trim() || '编辑' });
|
||||
}
|
||||
if (res?.code == 0 || res?.success) {
|
||||
|
||||
@ -305,7 +305,14 @@ const handleConfirmSubmit = async (source: string) => {
|
||||
res = await addVpInfo({ engInfo, source: source.trim() || '新增' });
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => { engInfo[item.field] = formData.value[item.field]; });
|
||||
changeOrder.value.forEach(item => {
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
res = await updateVpInfo({ engInfo, source: source.trim() || '编辑' });
|
||||
}
|
||||
if (res?.code == 0 || res?.success) {
|
||||
|
||||
@ -0,0 +1,841 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
title="编辑过鱼自动数据"
|
||||
width="70vw"
|
||||
:confirm-loading="confirmLoading"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:label-col="{ span: 8 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
:rules="formRules"
|
||||
class="max-h-[70vh] overflow-y-auto pr-4"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="过鱼设施" name="stcd">
|
||||
<a-select
|
||||
v-model:value="formData.stcd"
|
||||
placeholder="请选择过鱼设施"
|
||||
allow-clear
|
||||
show-search
|
||||
:loading="facilityLoading"
|
||||
:filter-option="filterOption"
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in facilityOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>{{ item.label }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="时间" name="tm">
|
||||
<a-date-picker
|
||||
v-model:value="formData.tm"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择时间"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="鱼种类" name="ftp">
|
||||
<fishSearch v-model="formData.ftp" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="过鱼数量" name="fcnt">
|
||||
<a-input-number
|
||||
v-model:value="formData.fcnt"
|
||||
placeholder="请输入过鱼数量"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="过鱼通道" name="channelno">
|
||||
<a-input
|
||||
v-model:value="formData.channelno"
|
||||
placeholder="请输入过鱼通道"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="鱼游向" name="direction">
|
||||
<a-select
|
||||
v-model:value="formData.direction"
|
||||
placeholder="请选择鱼游向"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="0">上行</a-select-option>
|
||||
<a-select-option :value="1">下行</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="鱼尺寸" name="fsz">
|
||||
<a-select
|
||||
v-model:value="formData.fsz"
|
||||
placeholder="请选择鱼尺寸"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option value="大">大</a-select-option>
|
||||
<a-select-option value="中">中</a-select-option>
|
||||
<a-select-option value="小">小</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="鱼长度(cm)" name="length">
|
||||
<a-input-number
|
||||
v-model:value="formData.length"
|
||||
placeholder="请输入鱼长度"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="鱼宽度(cm)" name="width">
|
||||
<a-input-number
|
||||
v-model:value="formData.width"
|
||||
placeholder="请输入鱼宽度"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="鱼速度(m/s)" name="fishspeed">
|
||||
<a-input
|
||||
v-model:value="formData.fishspeed"
|
||||
placeholder="请输入鱼速度"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="鱼位置" name="fishposition">
|
||||
<a-input-number
|
||||
v-model:value="formData.fishposition"
|
||||
placeholder="请输入鱼位置"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="水温(℃)" name="temperature">
|
||||
<a-input-number
|
||||
v-model:value="formData.temperature"
|
||||
placeholder="请输入水温"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="水位(m)" name="waterlevel">
|
||||
<a-input-number
|
||||
v-model:value="formData.waterlevel"
|
||||
placeholder="请输入水位"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="流速(m/s)" name="speed">
|
||||
<a-input-number
|
||||
v-model:value="formData.speed"
|
||||
placeholder="请输入流速"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="流量(m³/s)" name="q">
|
||||
<a-input-number
|
||||
v-model:value="formData.q"
|
||||
placeholder="请输入流量"
|
||||
:precision="3"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="溶氧(mg/L)" name="dox">
|
||||
<a-input-number
|
||||
v-model:value="formData.dox"
|
||||
placeholder="请输入溶氧"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="浊度(NTU)" name="tu">
|
||||
<a-input-number
|
||||
v-model:value="formData.tu"
|
||||
placeholder="请输入浊度"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="备注" name="remark" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.remark"
|
||||
placeholder="请输入备注"
|
||||
:maxlength="255"
|
||||
show-count
|
||||
:rows="2"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<!-- 图片上传 -->
|
||||
<a-row :gutter="16" class="mt-4">
|
||||
<a-col :span="24">
|
||||
<a-form-item label="图片" name="firstimgurl" :label-col="{ span: 3 }" :wrapper-col="{ span: 21 }">
|
||||
<a-upload
|
||||
v-model:file-list="picFileList"
|
||||
:multiple="false"
|
||||
:max-count="1"
|
||||
list-type="picture-card"
|
||||
:before-upload="handleBeforePicUpload"
|
||||
@remove="handleRemove"
|
||||
>
|
||||
<div v-if="picFileList.length < 1">
|
||||
<plus-outlined />
|
||||
<div class="mt-2">上传图片</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<!-- 视频上传 -->
|
||||
<a-row :gutter="16" class="mt-4">
|
||||
<a-col :span="24">
|
||||
<a-form-item label="视频" name="videourl" :label-col="{ span: 3 }" :wrapper-col="{ span: 21 }">
|
||||
<a-upload
|
||||
v-model:file-list="vdpFileList"
|
||||
:multiple="false"
|
||||
:max-count="1"
|
||||
:before-upload="handleBeforeVdpUpload"
|
||||
@remove="handleRemove"
|
||||
>
|
||||
<a-button v-if="vdpFileList.length < 1">
|
||||
<upload-outlined /> 上传视频
|
||||
</a-button>
|
||||
</a-upload>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<ConfirmModal
|
||||
v-model:open="confirmModalVisible"
|
||||
:diff-list="diffList"
|
||||
:confirm-loading="confirmLoading"
|
||||
@confirm="handleConfirmSubmit"
|
||||
@cancel="handleConfirmCancel"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { message, Upload } from 'ant-design-vue';
|
||||
import { PlusOutlined, UploadOutlined } from '@ant-design/icons-vue';
|
||||
import { updateFpssrlRInfo } from '@/api/DataQueryMenuModule';
|
||||
import { getFishReleaseMonitorSectionList } from '@/api/DataQueryMenuModule';
|
||||
import ConfirmModal from '@/components/ConfirmModal/index.vue';
|
||||
import { useDraggable } from '@/utils/drag';
|
||||
import fishSearch from '@/components/fishSearch/index.vue';
|
||||
import { useShuJuTianBaoStore } from '@/store/modules/shuJuTianBao';
|
||||
|
||||
const shuJuTianBaoStore = useShuJuTianBaoStore();
|
||||
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL;
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean;
|
||||
record?: any;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:open', 'success']);
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.open,
|
||||
set: val => emit('update:open', val)
|
||||
});
|
||||
useDraggable(visible, { boundary: true, resetOnOpen: true });
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const originalLabels = ref<Record<string, string>>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
// 过鱼设施下拉
|
||||
const facilityLoading = ref(false);
|
||||
const facilityOptions = ref<any[]>([]);
|
||||
|
||||
// 图片上传相关
|
||||
const picFileList = ref<any[]>([]);
|
||||
// 视频上传相关
|
||||
const vdpFileList = ref<any[]>([]);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请选择过鱼设施', trigger: 'change' }],
|
||||
tm: [{ required: true, message: '请选择时间', trigger: 'change' }],
|
||||
ftp: [{ required: true, message: '请选择鱼种类', trigger: 'blur' }],
|
||||
fcnt: [{ required: true, message: '请输入过鱼数量', trigger: 'blur' }],
|
||||
channelno: [{ required: true, message: '请输入过鱼通道', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 字段名 → 中文名映射
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '过鱼设施',
|
||||
tm: '时间',
|
||||
ftp: '鱼种类',
|
||||
fcnt: '过鱼数量',
|
||||
fsz: '鱼尺寸',
|
||||
length: '鱼长度(cm)',
|
||||
width: '鱼宽度(cm)',
|
||||
fishspeed: '鱼速度(m/s)',
|
||||
direction: '鱼游向',
|
||||
fishposition: '鱼位置',
|
||||
firstimgurl: '图片',
|
||||
videourl: '视频',
|
||||
temperature: '水温(℃)',
|
||||
waterlevel: '水位(m)',
|
||||
speed: '流速(m/s)',
|
||||
q: '流量(m³/s)',
|
||||
dox: '溶氧(mg/L)',
|
||||
tu: '浊度(NTU)',
|
||||
channelno: '过鱼通道',
|
||||
remark: '备注'
|
||||
};
|
||||
|
||||
// select 选项映射
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
stcd: () => facilityOptions.value,
|
||||
ftp: () =>
|
||||
shuJuTianBaoStore.getFishOption().map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id
|
||||
})),
|
||||
direction: () => [
|
||||
{ label: '上行', value: 0 },
|
||||
{ label: '下行', value: 1 }
|
||||
],
|
||||
fsz: () => [
|
||||
{ label: '大', value: '大' },
|
||||
{ label: '中', value: '中' },
|
||||
{ label: '小', value: '小' }
|
||||
]
|
||||
};
|
||||
|
||||
// 按修改顺序记录变更
|
||||
const changeOrder = ref<
|
||||
{ field: string; label: string; oldValue: string; newValue: string }[]
|
||||
>([]);
|
||||
const diffList = changeOrder;
|
||||
|
||||
const filterOption = (inputValue: string, option: any) => {
|
||||
const label = option.label || '';
|
||||
const keyword = inputValue || '';
|
||||
return label.includes(keyword);
|
||||
};
|
||||
|
||||
const handleBeforePicUpload = (file: any) => {
|
||||
const isImage =
|
||||
file.type === 'image/jpeg' ||
|
||||
file.type === 'image/png' ||
|
||||
file.type === 'image/gif' ||
|
||||
file.type === 'image/webp';
|
||||
if (!isImage) {
|
||||
message.error('只能上传图片文件(JPG/PNG/GIF/WEBP)');
|
||||
return Upload.LIST_IGNORE;
|
||||
}
|
||||
const isLt10M = file.size / 1024 / 1024 < 10;
|
||||
if (!isLt10M) {
|
||||
message.error('图片大小不能超过 10MB');
|
||||
return Upload.LIST_IGNORE;
|
||||
}
|
||||
picFileList.value = [...picFileList.value, file];
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleBeforeVdpUpload = (file: any) => {
|
||||
const isVideo =
|
||||
file.type === 'video/mp4' ||
|
||||
file.type === 'video/quicktime' ||
|
||||
file.type === 'video/x-msvideo' ||
|
||||
file.type === 'video/webm' ||
|
||||
file.type === 'video/x-matroska';
|
||||
if (!isVideo) {
|
||||
message.error('只能上传视频文件(MP4/MOV/AVI/WEBM/MKV)');
|
||||
return Upload.LIST_IGNORE;
|
||||
}
|
||||
const isLt100M = file.size / 1024 / 1024 < 100;
|
||||
if (!isLt100M) {
|
||||
message.error('视频大小不能超过 100MB');
|
||||
return Upload.LIST_IGNORE;
|
||||
}
|
||||
vdpFileList.value = [...vdpFileList.value, file];
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleRemove = () => {
|
||||
return true;
|
||||
};
|
||||
|
||||
// 解析图片/视频 id 列表,过滤 null/-/undefined 等占位字符串
|
||||
const normalizeIds = (val: any): string[] => {
|
||||
if (val === null || val === undefined || val === '') return [];
|
||||
return String(val)
|
||||
.split(',')
|
||||
.map(s => s.trim())
|
||||
.filter(s => s && s !== '-' && s !== 'null' && s !== 'undefined');
|
||||
};
|
||||
|
||||
const resolveSelectLabel = (field: string, value: any): string => {
|
||||
if (value === null || value === undefined || value === '') return '空';
|
||||
const getOptions = selectOptionsMap[field];
|
||||
if (getOptions) {
|
||||
const opt = getOptions().find(
|
||||
(o: any) => String(o.value) === String(value)
|
||||
);
|
||||
if (opt) return opt.label;
|
||||
}
|
||||
return String(value);
|
||||
};
|
||||
|
||||
// 解析原始值的显示名:优先用预解析快照,不依赖实时选项
|
||||
const resolveOriginalLabel = (field: string): string => {
|
||||
return resolveSelectLabel(field, originalRecord.value[field]);
|
||||
};
|
||||
|
||||
// 监听 formData 变化
|
||||
watch(
|
||||
() => formData.value,
|
||||
newData => {
|
||||
const original = originalRecord.value;
|
||||
for (const key in newData) {
|
||||
const oldVal = original[key];
|
||||
const newVal = newData[key];
|
||||
if (oldVal !== newVal) {
|
||||
const existingIdx = changeOrder.value.findIndex(c => c.field === key);
|
||||
const oldNorm =
|
||||
oldVal === null || oldVal === undefined || oldVal === '-'
|
||||
? null
|
||||
: oldVal;
|
||||
const newNorm = newVal === null || newVal === undefined ? null : newVal;
|
||||
if (oldNorm === newNorm) continue;
|
||||
if (
|
||||
oldNorm !== null &&
|
||||
newNorm !== null &&
|
||||
!isNaN(Number(oldNorm)) &&
|
||||
!isNaN(Number(newNorm)) &&
|
||||
Number(oldNorm) === Number(newNorm)
|
||||
)
|
||||
continue;
|
||||
|
||||
const isSelect = !!selectOptionsMap[key];
|
||||
const oldDisplay = isSelect
|
||||
? (originalLabels.value[key] ?? resolveSelectLabel(key, oldNorm))
|
||||
: oldNorm === null
|
||||
? '空'
|
||||
: String(oldNorm);
|
||||
const newDisplay = isSelect
|
||||
? resolveSelectLabel(key, newNorm)
|
||||
: newNorm === null
|
||||
? '空'
|
||||
: String(newNorm);
|
||||
|
||||
const entry = {
|
||||
field: key,
|
||||
label: fieldLabelMap[key] || key,
|
||||
oldValue: oldDisplay,
|
||||
newValue: newDisplay
|
||||
};
|
||||
if (existingIdx >= 0) {
|
||||
changeOrder.value[existingIdx] = entry;
|
||||
} else {
|
||||
changeOrder.value.push(entry);
|
||||
}
|
||||
} else {
|
||||
changeOrder.value = changeOrder.value.filter(c => c.field !== key);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 监听图片列表变化,生成图片增删 diff
|
||||
watch(
|
||||
() => picFileList.value,
|
||||
newList => {
|
||||
const originalIds = normalizeIds(originalRecord.value.firstimgurl);
|
||||
const currentExistingIds = newList
|
||||
.filter((f: any) => f.isExisting)
|
||||
.map((f: any) => f.uid);
|
||||
const currentNewCount = newList.filter((f: any) => f.originFileObj).length;
|
||||
|
||||
const deletedCount = originalIds.filter(
|
||||
(id: string) => !currentExistingIds.includes(id)
|
||||
).length;
|
||||
const addedCount = currentNewCount;
|
||||
|
||||
if (deletedCount > 0 || addedCount > 0) {
|
||||
const existingIdx = changeOrder.value.findIndex(
|
||||
c => c.field === 'firstimgurl'
|
||||
);
|
||||
const oldDisplay = `${originalIds.length}张图片`;
|
||||
const parts: string[] = [];
|
||||
if (deletedCount > 0) parts.push(`删除${deletedCount}张`);
|
||||
if (addedCount > 0) parts.push(`新增${addedCount}张`);
|
||||
const entry = {
|
||||
field: 'firstimgurl',
|
||||
label: '图片',
|
||||
oldValue: oldDisplay,
|
||||
newValue: parts.join(',')
|
||||
};
|
||||
if (existingIdx >= 0) {
|
||||
changeOrder.value[existingIdx] = entry;
|
||||
} else {
|
||||
changeOrder.value.push(entry);
|
||||
}
|
||||
} else {
|
||||
changeOrder.value = changeOrder.value.filter(
|
||||
c => c.field !== 'firstimgurl'
|
||||
);
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 监听视频列表变化,生成视频增删 diff
|
||||
watch(
|
||||
() => vdpFileList.value,
|
||||
newList => {
|
||||
const originalIds = normalizeIds(originalRecord.value.videourl);
|
||||
const currentExistingIds = newList
|
||||
.filter((f: any) => f.isExisting)
|
||||
.map((f: any) => f.uid);
|
||||
const currentNewCount = newList.filter((f: any) => f.originFileObj).length;
|
||||
|
||||
const deletedCount = originalIds.filter(
|
||||
(id: string) => !currentExistingIds.includes(id)
|
||||
).length;
|
||||
const addedCount = currentNewCount;
|
||||
|
||||
if (deletedCount > 0 || addedCount > 0) {
|
||||
const existingIdx = changeOrder.value.findIndex(
|
||||
c => c.field === 'videourl'
|
||||
);
|
||||
const oldDisplay = `${originalIds.length}个视频`;
|
||||
const parts: string[] = [];
|
||||
if (deletedCount > 0) parts.push(`删除${deletedCount}个`);
|
||||
if (addedCount > 0) parts.push(`新增${addedCount}个`);
|
||||
const entry = {
|
||||
field: 'videourl',
|
||||
label: '视频',
|
||||
oldValue: oldDisplay,
|
||||
newValue: parts.join(',')
|
||||
};
|
||||
if (existingIdx >= 0) {
|
||||
changeOrder.value[existingIdx] = entry;
|
||||
} else {
|
||||
changeOrder.value.push(entry);
|
||||
}
|
||||
} else {
|
||||
changeOrder.value = changeOrder.value.filter(
|
||||
c => c.field !== 'videourl'
|
||||
);
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const loadFacilities = async () => {
|
||||
facilityLoading.value = true;
|
||||
try {
|
||||
const res = await getFishReleaseMonitorSectionList({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'mway',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: '2'
|
||||
}
|
||||
]
|
||||
},
|
||||
select: ['stcd', 'stnm', 'mway']
|
||||
});
|
||||
facilityOptions.value = (res.data?.data || []).map((item: any) => ({
|
||||
label: item.stnm,
|
||||
value: item.stcd
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('获取过鱼设施列表失败:', error);
|
||||
} finally {
|
||||
facilityLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 监听 record 变化
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
// 将 "-" 还原为 null,避免控件显示异常
|
||||
for (const key in converted) {
|
||||
if (converted[key] === '-') {
|
||||
converted[key] = null;
|
||||
}
|
||||
}
|
||||
// 数字字段字符串转数字,保证 select/input-number 回显匹配
|
||||
[
|
||||
'direction',
|
||||
'fcnt',
|
||||
'length',
|
||||
'width',
|
||||
'fishposition',
|
||||
'temperature',
|
||||
'waterlevel',
|
||||
'speed',
|
||||
'q',
|
||||
'dox',
|
||||
'tu'
|
||||
].forEach(key => {
|
||||
if (
|
||||
converted[key] !== null &&
|
||||
converted[key] !== undefined &&
|
||||
converted[key] !== ''
|
||||
) {
|
||||
converted[key] = Number(converted[key]);
|
||||
}
|
||||
});
|
||||
// 鱼种类:列表返回名称,fishSearch 需用 fishId(id) 回显
|
||||
const ftpName = converted.ftp;
|
||||
if (converted.fishId) {
|
||||
converted.ftp = converted.fishId;
|
||||
}
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
// 提前解析 select 字段的原始显示名,防止联动刷新选项后丢失 label
|
||||
const labels: Record<string, string> = {};
|
||||
for (const key in converted) {
|
||||
if (selectOptionsMap[key]) {
|
||||
labels[key] = resolveOriginalLabel(key);
|
||||
}
|
||||
}
|
||||
// ftp 优先使用返回的名称,避免选项未加载时显示 id
|
||||
if (ftpName && converted.fishId) {
|
||||
labels.ftp = ftpName;
|
||||
}
|
||||
originalLabels.value = labels;
|
||||
// 处理已有图片
|
||||
const picIds = normalizeIds(converted.firstimgurl);
|
||||
if (picIds.length > 0) {
|
||||
picFileList.value = picIds.map((id: string) => ({
|
||||
uid: id,
|
||||
name: id,
|
||||
status: 'done',
|
||||
url: `${baseUrl}/?${id}&view=jpg`,
|
||||
isExisting: true
|
||||
}));
|
||||
} else {
|
||||
picFileList.value = [];
|
||||
}
|
||||
// 处理已有视频
|
||||
const vdpIds = normalizeIds(converted.videourl);
|
||||
if (vdpIds.length > 0) {
|
||||
vdpFileList.value = vdpIds.map((id: string) => ({
|
||||
uid: id,
|
||||
name: id,
|
||||
status: 'done',
|
||||
url: `${baseUrl}/?${id}`,
|
||||
isExisting: true
|
||||
}));
|
||||
} else {
|
||||
vdpFileList.value = [];
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 选项加载完成后重新解析原始显示名
|
||||
watch(facilityOptions, () => {
|
||||
const original = originalRecord.value;
|
||||
if (!original || Object.keys(original).length === 0) return;
|
||||
const labels: Record<string, string> = {};
|
||||
for (const key in original) {
|
||||
if (selectOptionsMap[key]) {
|
||||
labels[key] = resolveOriginalLabel(key);
|
||||
}
|
||||
}
|
||||
// 保留已解析的 ftp 名称,避免选项未加载时显示 id
|
||||
if (originalLabels.value.ftp) {
|
||||
labels.ftp = originalLabels.value.ftp;
|
||||
}
|
||||
originalLabels.value = labels;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadFacilities();
|
||||
// 编辑时统一初始化图片/视频(无值则清空,效果同新增)
|
||||
const picIds = normalizeIds(props.record?.firstimgurl);
|
||||
picFileList.value = picIds.length
|
||||
? picIds.map((id: string) => ({
|
||||
uid: id,
|
||||
name: id,
|
||||
status: 'done',
|
||||
url: `${baseUrl}/?${id}&view=jpg`,
|
||||
isExisting: true
|
||||
}))
|
||||
: [];
|
||||
const vdpIds = normalizeIds(props.record?.videourl);
|
||||
vdpFileList.value = vdpIds.length
|
||||
? vdpIds.map((id: string) => ({
|
||||
uid: id,
|
||||
name: id,
|
||||
status: 'done',
|
||||
url: `${baseUrl}/?${id}`,
|
||||
isExisting: true
|
||||
}))
|
||||
: [];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
await formRef.value?.validateFields();
|
||||
if (changeOrder.value.length === 0) {
|
||||
message.info('未检测到任何修改');
|
||||
return;
|
||||
}
|
||||
confirmModalVisible.value = true;
|
||||
} catch (error) {
|
||||
console.error('验证失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const buildFormData = (engInfo: Record<string, any>, source: string) => {
|
||||
const formData = new FormData();
|
||||
const data = {
|
||||
engInfo,
|
||||
source: source.trim() || '编辑'
|
||||
};
|
||||
formData.append('data', JSON.stringify(data));
|
||||
// 添加新增的图片文件
|
||||
picFileList.value.forEach((file: any) => {
|
||||
if (file.originFileObj) {
|
||||
formData.append('picFiles', file.originFileObj);
|
||||
}
|
||||
});
|
||||
// 添加新增的视频文件
|
||||
vdpFileList.value.forEach((file: any) => {
|
||||
if (file.originFileObj) {
|
||||
formData.append('vdFiles', file.originFileObj);
|
||||
}
|
||||
});
|
||||
return formData;
|
||||
};
|
||||
|
||||
// 时间字段选择时仅到年月日,提交时统一补上时分秒
|
||||
const formatDateTime = (val: any): any => {
|
||||
if (val === null || val === undefined || val === '') return val;
|
||||
const str = String(val);
|
||||
return str.length === 10 ? `${str} 00:00:00` : str;
|
||||
};
|
||||
|
||||
const handleConfirmSubmit = async (source: string) => {
|
||||
confirmLoading.value = true;
|
||||
try {
|
||||
// 编辑时只传修改过的字段
|
||||
const engInfo: Record<string, any> = {};
|
||||
engInfo.id = formData.value.id;
|
||||
changeOrder.value.forEach(item => {
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
// 保留剩余的已有图片 id
|
||||
const existingPicIds = picFileList.value
|
||||
.filter((f: any) => f.isExisting)
|
||||
.map((f: any) => f.uid);
|
||||
if (existingPicIds.length > 0) {
|
||||
engInfo.firstimgurl = existingPicIds.join(',');
|
||||
} else {
|
||||
delete engInfo.firstimgurl;
|
||||
}
|
||||
// 保留剩余的已有视频 id
|
||||
const existingVdpIds = vdpFileList.value
|
||||
.filter((f: any) => f.isExisting)
|
||||
.map((f: any) => f.uid);
|
||||
if (existingVdpIds.length > 0) {
|
||||
engInfo.videourl = existingVdpIds.join(',');
|
||||
} else {
|
||||
delete engInfo.videourl;
|
||||
}
|
||||
// 时间字段统一补时分秒
|
||||
if (engInfo.tm) {
|
||||
engInfo.tm = formatDateTime(engInfo.tm);
|
||||
}
|
||||
const fd = buildFormData(engInfo, source);
|
||||
const res = await updateFpssrlRInfo(fd);
|
||||
if (res?.code == 0 || res?.success) {
|
||||
message.success('编辑成功');
|
||||
confirmModalVisible.value = false;
|
||||
visible.value = false;
|
||||
emit('success');
|
||||
} else {
|
||||
message.error(res?.msg || '编辑失败');
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('提交失败,请重试');
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirmCancel = () => {
|
||||
confirmModalVisible.value = false;
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
confirmModalVisible.value = false;
|
||||
formRef.value?.resetFields();
|
||||
};
|
||||
</script>
|
||||
@ -0,0 +1,823 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
:title="isAdd ? '新增过鱼人工数据' : '编辑过鱼人工数据'"
|
||||
width="70vw"
|
||||
:confirm-loading="confirmLoading"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:label-col="{ span: 8 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
:rules="formRules"
|
||||
class="max-h-[70vh] overflow-y-auto pr-4"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="过鱼设施" name="stcd">
|
||||
<a-select
|
||||
v-model:value="formData.stcd"
|
||||
placeholder="请选择过鱼设施"
|
||||
allow-clear
|
||||
show-search
|
||||
:loading="facilityLoading"
|
||||
:filter-option="filterOption"
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in facilityOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>{{ item.label }}</a-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="填报时间" name="tm">
|
||||
<a-date-picker
|
||||
v-model:value="formData.tm"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择填报时间"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="鱼种类" name="ftp">
|
||||
<fishSearch v-model="formData.ftp" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="过鱼数量(尾)" name="fcnt">
|
||||
<a-input-number
|
||||
v-model:value="formData.fcnt"
|
||||
placeholder="请输入过鱼数量"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12">
|
||||
<a-form-item label="鱼游向" name="direction">
|
||||
<a-select
|
||||
v-model:value="formData.direction"
|
||||
placeholder="请选择鱼游向"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="0">上行</a-select-option>
|
||||
<a-select-option :value="1">下行</a-select-option>
|
||||
<a-select-option :value="2">上行折返</a-select-option>
|
||||
<a-select-option :value="3">下行折返</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="是否鱼苗" name="isfs">
|
||||
<a-select
|
||||
v-model:value="formData.isfs"
|
||||
placeholder="请选择是否鱼苗"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="0">否</a-select-option>
|
||||
<a-select-option :value="1">是</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="开始日期" name="strdt">
|
||||
<a-date-picker
|
||||
v-model:value="formData.strdt"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择开始日期"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="结束日期" name="enddt">
|
||||
<a-date-picker
|
||||
v-model:value="formData.enddt"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
:disabled-date="disabledEndDate"
|
||||
placeholder="请选择结束日期"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="鱼类规格(cm)" name="fsz">
|
||||
<a-input
|
||||
v-model:value="formData.fsz"
|
||||
placeholder="请输入鱼类规格"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12">
|
||||
<a-form-item label="平均体重(g)" name="fwet">
|
||||
<a-input-number
|
||||
v-model:value="formData.fwet"
|
||||
placeholder="请输入平均体重"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12">
|
||||
<a-form-item label="当日运行次数" name="rcnt">
|
||||
<a-input-number
|
||||
v-model:value="formData.rcnt"
|
||||
placeholder="请输入当日运行次数"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="引用流量" name="fq">
|
||||
<a-input-number
|
||||
v-model:value="formData.fq"
|
||||
placeholder="请输入过鱼设施引用流量"
|
||||
:min="0"
|
||||
:precision="3"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="水温(℃)" name="wtmp">
|
||||
<a-input-number
|
||||
v-model:value="formData.wtmp"
|
||||
placeholder="请输入水温"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<!-- 图片上传 -->
|
||||
<a-row :gutter="16" class="mt-4">
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
label="图片"
|
||||
name="picpth"
|
||||
:label-col="{ span: 3 }"
|
||||
:wrapper-col="{ span: 21 }"
|
||||
>
|
||||
<a-upload
|
||||
v-model:file-list="picFileList"
|
||||
:multiple="false"
|
||||
:max-count="1"
|
||||
list-type="picture-card"
|
||||
:before-upload="handleBeforePicUpload"
|
||||
@remove="handleRemove"
|
||||
>
|
||||
<div v-if="picFileList.length < 1">
|
||||
<plus-outlined />
|
||||
<div class="mt-2">上传图片</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<!-- 视频上传 -->
|
||||
<a-row :gutter="16" class="mt-4">
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
label="视频"
|
||||
name="vdpth"
|
||||
:label-col="{ span: 3 }"
|
||||
:wrapper-col="{ span: 21 }"
|
||||
>
|
||||
<a-upload
|
||||
v-model:file-list="vdpFileList"
|
||||
:multiple="false"
|
||||
:max-count="1"
|
||||
:before-upload="handleBeforeVdpUpload"
|
||||
@remove="handleRemove"
|
||||
>
|
||||
<a-button v-if="vdpFileList.length < 1">
|
||||
<upload-outlined /> 上传视频
|
||||
</a-button>
|
||||
</a-upload>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<ConfirmModal
|
||||
v-model:open="confirmModalVisible"
|
||||
:diff-list="diffList"
|
||||
:confirm-loading="confirmLoading"
|
||||
@confirm="handleConfirmSubmit"
|
||||
@cancel="handleConfirmCancel"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { message, Upload } from 'ant-design-vue';
|
||||
import { PlusOutlined, UploadOutlined } from '@ant-design/icons-vue';
|
||||
import { addFpssRInfo, updateFpssRInfo } from '@/api/DataQueryMenuModule';
|
||||
import { getFishReleaseMonitorSectionList } from '@/api/DataQueryMenuModule';
|
||||
import ConfirmModal from '@/components/ConfirmModal/index.vue';
|
||||
import { useDraggable } from '@/utils/drag';
|
||||
import fishSearch from '@/components/fishSearch/index.vue';
|
||||
import { useShuJuTianBaoStore } from '@/store/modules/shuJuTianBao';
|
||||
|
||||
const shuJuTianBaoStore = useShuJuTianBaoStore();
|
||||
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL;
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean;
|
||||
record?: any;
|
||||
isAdd?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:open', 'success']);
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.open,
|
||||
set: val => emit('update:open', val)
|
||||
});
|
||||
useDraggable(visible, { boundary: true, resetOnOpen: true });
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const originalLabels = ref<Record<string, string>>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
// 过鱼设施下拉
|
||||
const facilityLoading = ref(false);
|
||||
const facilityOptions = ref<any[]>([]);
|
||||
|
||||
// 图片上传相关
|
||||
const picFileList = ref<any[]>([]);
|
||||
// 视频上传相关
|
||||
const vdpFileList = ref<any[]>([]);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请选择过鱼设施', trigger: 'change' }],
|
||||
tm: [{ required: true, message: '请选择填报时间', trigger: 'change' }],
|
||||
ftp: [{ required: true, message: '请输入鱼种类', trigger: 'blur' }],
|
||||
fcnt: [{ required: true, message: '请输入过鱼数量', trigger: 'blur' }],
|
||||
strdt: [{ required: true, message: '请选择开始日期', trigger: 'change' }],
|
||||
direction: [{ required: true, message: '请选择鱼游向', trigger: 'change' }],
|
||||
isfs: [{ required: true, message: '请选择是否鱼苗', trigger: 'change' }]
|
||||
});
|
||||
|
||||
// 字段名 → 中文名映射
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '过鱼设施',
|
||||
tm: '填报时间',
|
||||
ftp: '鱼种类',
|
||||
fsz: '鱼类规格(cm)',
|
||||
fcnt: '过鱼数量(尾)',
|
||||
fwet: '平均体重(g)',
|
||||
strdt: '开始日期',
|
||||
enddt: '结束日期',
|
||||
direction: '鱼游向',
|
||||
rcnt: '当日运行次数',
|
||||
fq: '引用流量',
|
||||
wtmp: '水温(℃)',
|
||||
isfs: '是否鱼苗',
|
||||
picpth: '图片',
|
||||
vdpth: '视频'
|
||||
};
|
||||
|
||||
// select 选项映射
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
stcd: () => facilityOptions.value,
|
||||
ftp: () =>
|
||||
shuJuTianBaoStore.getFishOption().map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id
|
||||
})),
|
||||
direction: () => [
|
||||
{ label: '上行', value: 0 },
|
||||
{ label: '下行', value: 1 },
|
||||
{ label: '上行折返', value: 2 },
|
||||
{ label: '下行折返', value: 3 }
|
||||
],
|
||||
isfs: () => [
|
||||
{ label: '否', value: 0 },
|
||||
{ label: '是', value: 1 }
|
||||
]
|
||||
};
|
||||
|
||||
// 按修改顺序记录变更
|
||||
const changeOrder = ref<
|
||||
{ field: string; label: string; oldValue: string; newValue: string }[]
|
||||
>([]);
|
||||
const diffList = changeOrder;
|
||||
|
||||
const filterOption = (inputValue: string, option: any) => {
|
||||
const label = option.label || '';
|
||||
const keyword = inputValue || '';
|
||||
return label.includes(keyword);
|
||||
};
|
||||
|
||||
const disabledEndDate = (current: any) => {
|
||||
if (formData.value?.strdt) {
|
||||
return current && current < new Date(`${formData.value.strdt} 00:00:00`);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleBeforePicUpload = (file: any) => {
|
||||
const isImage =
|
||||
file.type === 'image/jpeg' ||
|
||||
file.type === 'image/png' ||
|
||||
file.type === 'image/gif' ||
|
||||
file.type === 'image/webp';
|
||||
if (!isImage) {
|
||||
message.error('只能上传图片文件(JPG/PNG/GIF/WEBP)');
|
||||
return Upload.LIST_IGNORE;
|
||||
}
|
||||
const isLt10M = file.size / 1024 / 1024 < 10;
|
||||
if (!isLt10M) {
|
||||
message.error('图片大小不能超过 10MB');
|
||||
return Upload.LIST_IGNORE;
|
||||
}
|
||||
picFileList.value = [...picFileList.value, file];
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleBeforeVdpUpload = (file: any) => {
|
||||
const isVideo =
|
||||
file.type === 'video/mp4' ||
|
||||
file.type === 'video/quicktime' ||
|
||||
file.type === 'video/x-msvideo' ||
|
||||
file.type === 'video/webm' ||
|
||||
file.type === 'video/x-matroska';
|
||||
if (!isVideo) {
|
||||
message.error('只能上传视频文件(MP4/MOV/AVI/WEBM/MKV)');
|
||||
return Upload.LIST_IGNORE;
|
||||
}
|
||||
const isLt100M = file.size / 1024 / 1024 < 100;
|
||||
if (!isLt100M) {
|
||||
message.error('视频大小不能超过 100MB');
|
||||
return Upload.LIST_IGNORE;
|
||||
}
|
||||
vdpFileList.value = [...vdpFileList.value, file];
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleRemove = () => {
|
||||
return true;
|
||||
};
|
||||
|
||||
// 解析图片/视频 id 列表,过滤 null/-/undefined 等占位字符串
|
||||
const normalizeIds = (val: any): string[] => {
|
||||
if (val === null || val === undefined || val === '') return [];
|
||||
return String(val)
|
||||
.split(',')
|
||||
.map(s => s.trim())
|
||||
.filter(s => s && s !== '-' && s !== 'null' && s !== 'undefined');
|
||||
};
|
||||
|
||||
const resolveSelectLabel = (field: string, value: any): string => {
|
||||
if (value === null || value === undefined || value === '') return '空';
|
||||
const getOptions = selectOptionsMap[field];
|
||||
if (getOptions) {
|
||||
const opt = getOptions().find(
|
||||
(o: any) => String(o.value) === String(value)
|
||||
);
|
||||
if (opt) return opt.label;
|
||||
}
|
||||
return String(value);
|
||||
};
|
||||
|
||||
// 解析原始值的显示名:优先用预解析快照,不依赖实时选项
|
||||
const resolveOriginalLabel = (field: string): string => {
|
||||
return resolveSelectLabel(field, originalRecord.value[field]);
|
||||
};
|
||||
|
||||
// 监听 formData 变化
|
||||
watch(
|
||||
() => formData.value,
|
||||
newData => {
|
||||
const original = originalRecord.value;
|
||||
for (const key in newData) {
|
||||
const oldVal = original[key];
|
||||
const newVal = newData[key];
|
||||
if (oldVal !== newVal) {
|
||||
const existingIdx = changeOrder.value.findIndex(c => c.field === key);
|
||||
const oldNorm =
|
||||
oldVal === null || oldVal === undefined || oldVal === '-'
|
||||
? null
|
||||
: oldVal;
|
||||
const newNorm = newVal === null || newVal === undefined ? null : newVal;
|
||||
if (oldNorm === newNorm) continue;
|
||||
if (
|
||||
oldNorm !== null &&
|
||||
newNorm !== null &&
|
||||
!isNaN(Number(oldNorm)) &&
|
||||
!isNaN(Number(newNorm)) &&
|
||||
Number(oldNorm) === Number(newNorm)
|
||||
)
|
||||
continue;
|
||||
|
||||
const isSelect = !!selectOptionsMap[key];
|
||||
const oldDisplay = isSelect
|
||||
? originalLabels.value[key] ?? resolveSelectLabel(key, oldNorm)
|
||||
: oldNorm === null
|
||||
? '空'
|
||||
: String(oldNorm);
|
||||
const newDisplay = isSelect
|
||||
? resolveSelectLabel(key, newNorm)
|
||||
: newNorm === null
|
||||
? '空'
|
||||
: String(newNorm);
|
||||
|
||||
const entry = {
|
||||
field: key,
|
||||
label: fieldLabelMap[key] || key,
|
||||
oldValue: oldDisplay,
|
||||
newValue: newDisplay
|
||||
};
|
||||
if (existingIdx >= 0) {
|
||||
changeOrder.value[existingIdx] = entry;
|
||||
} else {
|
||||
changeOrder.value.push(entry);
|
||||
}
|
||||
} else {
|
||||
changeOrder.value = changeOrder.value.filter(c => c.field !== key);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 监听图片列表变化,生成图片增删 diff
|
||||
watch(
|
||||
() => picFileList.value,
|
||||
newList => {
|
||||
const originalIds = normalizeIds(
|
||||
originalRecord.value.picpth || originalRecord.value.firstimgurl
|
||||
);
|
||||
const currentExistingIds = newList
|
||||
.filter((f: any) => f.isExisting)
|
||||
.map((f: any) => f.uid);
|
||||
const currentNewCount = newList.filter((f: any) => f.originFileObj).length;
|
||||
|
||||
const deletedCount = originalIds.filter(
|
||||
(id: string) => !currentExistingIds.includes(id)
|
||||
).length;
|
||||
const addedCount = currentNewCount;
|
||||
|
||||
if (deletedCount > 0 || addedCount > 0) {
|
||||
const existingIdx = changeOrder.value.findIndex(
|
||||
c => c.field === 'picpth'
|
||||
);
|
||||
const oldDisplay = `${originalIds.length}张图片`;
|
||||
const parts: string[] = [];
|
||||
if (deletedCount > 0) parts.push(`删除${deletedCount}张`);
|
||||
if (addedCount > 0) parts.push(`新增${addedCount}张`);
|
||||
const entry = {
|
||||
field: 'picpth',
|
||||
label: '图片',
|
||||
oldValue: oldDisplay,
|
||||
newValue: parts.join(',')
|
||||
};
|
||||
if (existingIdx >= 0) {
|
||||
changeOrder.value[existingIdx] = entry;
|
||||
} else {
|
||||
changeOrder.value.push(entry);
|
||||
}
|
||||
} else {
|
||||
changeOrder.value = changeOrder.value.filter(c => c.field !== 'picpth');
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 监听视频列表变化,生成视频增删 diff
|
||||
watch(
|
||||
() => vdpFileList.value,
|
||||
newList => {
|
||||
const originalIds = normalizeIds(
|
||||
originalRecord.value.vdpth || originalRecord.value.videourl
|
||||
);
|
||||
const currentExistingIds = newList
|
||||
.filter((f: any) => f.isExisting)
|
||||
.map((f: any) => f.uid);
|
||||
const currentNewCount = newList.filter((f: any) => f.originFileObj).length;
|
||||
|
||||
const deletedCount = originalIds.filter(
|
||||
(id: string) => !currentExistingIds.includes(id)
|
||||
).length;
|
||||
const addedCount = currentNewCount;
|
||||
|
||||
if (deletedCount > 0 || addedCount > 0) {
|
||||
const existingIdx = changeOrder.value.findIndex(c => c.field === 'vdpth');
|
||||
const oldDisplay = `${originalIds.length}个视频`;
|
||||
const parts: string[] = [];
|
||||
if (deletedCount > 0) parts.push(`删除${deletedCount}个`);
|
||||
if (addedCount > 0) parts.push(`新增${addedCount}个`);
|
||||
const entry = {
|
||||
field: 'vdpth',
|
||||
label: '视频',
|
||||
oldValue: oldDisplay,
|
||||
newValue: parts.join(',')
|
||||
};
|
||||
if (existingIdx >= 0) {
|
||||
changeOrder.value[existingIdx] = entry;
|
||||
} else {
|
||||
changeOrder.value.push(entry);
|
||||
}
|
||||
} else {
|
||||
changeOrder.value = changeOrder.value.filter(c => c.field !== 'vdpth');
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const loadFacilities = async () => {
|
||||
facilityLoading.value = true;
|
||||
try {
|
||||
const res = await getFishReleaseMonitorSectionList({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'mway',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: '1'
|
||||
}
|
||||
]
|
||||
},
|
||||
select: ['stcd', 'stnm', 'mway']
|
||||
});
|
||||
facilityOptions.value = (res.data?.data || []).map((item: any) => ({
|
||||
label: item.stnm,
|
||||
value: item.stcd
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('获取过鱼设施列表失败:', error);
|
||||
} finally {
|
||||
facilityLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 监听 record 变化
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
// 将 "-" 还原为 null,避免控件显示异常
|
||||
for (const key in converted) {
|
||||
if (converted[key] === '-') {
|
||||
converted[key] = null;
|
||||
}
|
||||
}
|
||||
// 数字字段字符串转数字,保证 select/input-number 回显匹配
|
||||
['direction', 'isfs', 'fcnt', 'fwet', 'rcnt', 'fq', 'wtmp'].forEach(
|
||||
key => {
|
||||
if (
|
||||
converted[key] !== null &&
|
||||
converted[key] !== undefined &&
|
||||
converted[key] !== ''
|
||||
) {
|
||||
converted[key] = Number(converted[key]);
|
||||
}
|
||||
}
|
||||
);
|
||||
// 鱼种类:列表返回名称,fishSearch 需用 fishId(id) 回显
|
||||
const ftpName = converted.ftp;
|
||||
if (converted.fishId) {
|
||||
converted.ftp = converted.fishId;
|
||||
}
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
// 提前解析 select 字段的原始显示名,防止联动刷新选项后丢失 label
|
||||
const labels: Record<string, string> = {};
|
||||
for (const key in converted) {
|
||||
if (selectOptionsMap[key]) {
|
||||
labels[key] = resolveOriginalLabel(key);
|
||||
}
|
||||
}
|
||||
// ftp 优先使用返回的名称,避免选项未加载时显示 id
|
||||
if (ftpName && converted.fishId) {
|
||||
labels.ftp = ftpName;
|
||||
}
|
||||
originalLabels.value = labels;
|
||||
// 处理已有图片(兼容列表返回字段 firstimgurl)
|
||||
const picIds = normalizeIds(converted.picpth || converted.firstimgurl);
|
||||
if (picIds.length > 0) {
|
||||
picFileList.value = picIds.map((id: string) => ({
|
||||
uid: id,
|
||||
name: id,
|
||||
status: 'done',
|
||||
url: `${baseUrl}/?${id}&view=jpg`,
|
||||
isExisting: true
|
||||
}));
|
||||
} else {
|
||||
picFileList.value = [];
|
||||
}
|
||||
// 处理已有视频(兼容列表返回字段 videourl)
|
||||
const vdpIds = normalizeIds(converted.vdpth || converted.videourl);
|
||||
if (vdpIds.length > 0) {
|
||||
vdpFileList.value = vdpIds.map((id: string) => ({
|
||||
uid: id,
|
||||
name: id,
|
||||
status: 'done',
|
||||
url: `${baseUrl}/?${id}`,
|
||||
isExisting: true
|
||||
}));
|
||||
} else {
|
||||
vdpFileList.value = [];
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 选项加载完成后重新解析原始显示名
|
||||
watch(facilityOptions, () => {
|
||||
const original = originalRecord.value;
|
||||
if (!original || Object.keys(original).length === 0) return;
|
||||
const labels: Record<string, string> = {};
|
||||
for (const key in original) {
|
||||
if (selectOptionsMap[key]) {
|
||||
labels[key] = resolveOriginalLabel(key);
|
||||
}
|
||||
}
|
||||
// 保留已解析的 ftp 名称,避免选项未加载时显示 id
|
||||
if (originalLabels.value.ftp) {
|
||||
labels.ftp = originalLabels.value.ftp;
|
||||
}
|
||||
originalLabels.value = labels;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadFacilities();
|
||||
if (props.isAdd) {
|
||||
formData.value = {};
|
||||
originalRecord.value = {};
|
||||
changeOrder.value = [];
|
||||
picFileList.value = [];
|
||||
vdpFileList.value = [];
|
||||
} else {
|
||||
// 编辑时统一初始化图片/视频(无值则清空,效果同新增)
|
||||
const picIds = normalizeIds(
|
||||
props.record?.picpth || props.record?.firstimgurl
|
||||
);
|
||||
picFileList.value = picIds.length
|
||||
? picIds.map((id: string) => ({
|
||||
uid: id,
|
||||
name: id,
|
||||
status: 'done',
|
||||
url: `${baseUrl}/?${id}&view=jpg`,
|
||||
isExisting: true
|
||||
}))
|
||||
: [];
|
||||
const vdpIds = normalizeIds(
|
||||
props.record?.vdpth || props.record?.videourl
|
||||
);
|
||||
vdpFileList.value = vdpIds.length
|
||||
? vdpIds.map((id: string) => ({
|
||||
uid: id,
|
||||
name: id,
|
||||
status: 'done',
|
||||
url: `${baseUrl}/?${id}`,
|
||||
isExisting: true
|
||||
}))
|
||||
: [];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
await formRef.value?.validateFields();
|
||||
if (!props.isAdd && changeOrder.value.length === 0) {
|
||||
message.info('未检测到任何修改');
|
||||
return;
|
||||
}
|
||||
confirmModalVisible.value = true;
|
||||
} catch (error) {
|
||||
console.error('验证失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const buildFormData = (engInfo: Record<string, any>, source: string) => {
|
||||
const formData = new FormData();
|
||||
const data = {
|
||||
engInfo,
|
||||
source: source.trim() || (props.isAdd ? '新增' : '编辑')
|
||||
};
|
||||
formData.append('data', JSON.stringify(data));
|
||||
// 添加新增的图片文件
|
||||
picFileList.value.forEach((file: any) => {
|
||||
if (file.originFileObj) {
|
||||
formData.append('picFiles', file.originFileObj);
|
||||
}
|
||||
});
|
||||
// 添加新增的视频文件
|
||||
vdpFileList.value.forEach((file: any) => {
|
||||
if (file.originFileObj) {
|
||||
formData.append('vdFiles', file.originFileObj);
|
||||
}
|
||||
});
|
||||
return formData;
|
||||
};
|
||||
|
||||
// 时间字段选择时仅到年月日,提交时统一补上时分秒
|
||||
const formatDateTime = (val: any): any => {
|
||||
if (val === null || val === undefined || val === '') return val;
|
||||
const str = String(val);
|
||||
return str.length === 10 ? `${str} 00:00:00` : str;
|
||||
};
|
||||
|
||||
const handleConfirmSubmit = async (source: string) => {
|
||||
confirmLoading.value = true;
|
||||
try {
|
||||
const engInfo: Record<string, any> = {};
|
||||
let res: any;
|
||||
if (props.isAdd) {
|
||||
// 新增时提交所有字段
|
||||
Object.assign(engInfo, formData.value);
|
||||
} else {
|
||||
// 编辑时只传修改过的字段
|
||||
engInfo.id = formData.value.id;
|
||||
changeOrder.value.forEach(item => {
|
||||
// select 清空后值为 undefined/null,统一转为空字符串,避免序列化时字段被省略
|
||||
let val = formData.value[item.field];
|
||||
if (val === null || val === undefined) {
|
||||
val = '';
|
||||
}
|
||||
engInfo[item.field] = val;
|
||||
});
|
||||
// 保留剩余的已有图片 id
|
||||
const existingPicIds = picFileList.value
|
||||
.filter((f: any) => f.isExisting)
|
||||
.map((f: any) => f.uid);
|
||||
if (existingPicIds.length > 0) {
|
||||
engInfo.picpth = existingPicIds.join(',');
|
||||
} else {
|
||||
delete engInfo.picpth;
|
||||
}
|
||||
// 保留剩余的已有视频 id
|
||||
const existingVdpIds = vdpFileList.value
|
||||
.filter((f: any) => f.isExisting)
|
||||
.map((f: any) => f.uid);
|
||||
if (existingVdpIds.length > 0) {
|
||||
engInfo.vdpth = existingVdpIds.join(',');
|
||||
} else {
|
||||
delete engInfo.vdpth;
|
||||
}
|
||||
}
|
||||
// 时间字段统一补时分秒
|
||||
['tm', 'strdt', 'enddt'].forEach(field => {
|
||||
if (engInfo[field]) {
|
||||
engInfo[field] = formatDateTime(engInfo[field]);
|
||||
}
|
||||
});
|
||||
const fd = buildFormData(engInfo, source);
|
||||
res = props.isAdd ? await addFpssRInfo(fd) : await updateFpssRInfo(fd);
|
||||
if (res?.code == 0 || res?.success) {
|
||||
message.success(props.isAdd ? '新增成功' : '编辑成功');
|
||||
confirmModalVisible.value = false;
|
||||
visible.value = false;
|
||||
emit('success');
|
||||
} else {
|
||||
message.error(res?.msg || (props.isAdd ? '新增失败' : '编辑失败'));
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('提交失败,请重试');
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirmCancel = () => {
|
||||
confirmModalVisible.value = false;
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
confirmModalVisible.value = false;
|
||||
formRef.value?.resetFields();
|
||||
};
|
||||
</script>
|
||||
@ -7,6 +7,7 @@
|
||||
@export-btn="exportBtn"
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@seeEdit="handleSeeEdit"
|
||||
ref="searchRef"
|
||||
/>
|
||||
<!-- 表格组件 -->
|
||||
@ -20,6 +21,18 @@
|
||||
sort: sort
|
||||
}"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 图片预览弹框 -->
|
||||
@ -49,6 +62,24 @@
|
||||
></video>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<!-- 编辑 Modal -->
|
||||
<EditFishDataAutoModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 删除确认 Modal -->
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteFpssrlFn"
|
||||
:label="'过鱼自动数据'"
|
||||
title="删除过鱼自动数据"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<OperationLogModal v-model:open="operationLogVisible" :table-name="'SD_FPSS_A'" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
@ -57,9 +88,12 @@ import dayjs from 'dayjs';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import FishDataSearch from './FishDataSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import { getFishReleaseMonitorList } from '@/api/DataQueryMenuModule';
|
||||
import { getFishReleaseMonitorList, deleteFpssrlRInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
import { useDraggable } from '@/utils/drag';
|
||||
import EditFishDataAutoModal from './EditFishDataAutoModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import OperationLogModal from '@/components/OperationLogModal/index.vue';
|
||||
|
||||
const sort = ref<any>([
|
||||
{
|
||||
@ -89,6 +123,12 @@ const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
const exportLoading = ref(false);
|
||||
|
||||
// 编辑/删除/日志相关
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const deleteModalRef = ref();
|
||||
const operationLogVisible = ref(false);
|
||||
|
||||
// 图片预览
|
||||
const imagePreviewVisible = ref(false);
|
||||
const previewImageUrl = ref('');
|
||||
@ -291,6 +331,13 @@ const tableColumns = ref<any[]>([
|
||||
() => '查看视频'
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
@ -394,6 +441,35 @@ const initTable = (values: any) => {
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
const handleSeeEdit = () => {
|
||||
operationLogVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (record: any) => {
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑成功回调
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
// 删除
|
||||
const deleteFpssrlFn = async (record: any, reason: string) => {
|
||||
return deleteFpssrlRInfo({
|
||||
ids: [record.id],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
@export-btn="exportBtn"
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
@seeEdit="handleSeeEdit"
|
||||
ref="searchRef"
|
||||
/>
|
||||
<!-- 表格组件 -->
|
||||
@ -20,6 +22,18 @@
|
||||
sort: sort
|
||||
}"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 图片预览弹框 -->
|
||||
@ -49,6 +63,25 @@
|
||||
></video>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<!-- 编辑/新增 Modal -->
|
||||
<EditFishDataManualModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 删除确认 Modal -->
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteFpssFn"
|
||||
:label="'过鱼人工数据'"
|
||||
title="删除过鱼人工数据"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<OperationLogModal v-model:open="operationLogVisible" :table-name="'SD_FPSS_R'" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
@ -57,9 +90,12 @@ import dayjs from 'dayjs';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import FishDataSearch from './FishDataSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import { getFishReleaseMonitorList } from '@/api/DataQueryMenuModule';
|
||||
import { getFishReleaseMonitorList, deleteFpssRInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
import { useDraggable } from '@/utils/drag';
|
||||
import EditFishDataManualModal from './EditFishDataManualModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import OperationLogModal from '@/components/OperationLogModal/index.vue';
|
||||
|
||||
const sort = ref<any>([
|
||||
{
|
||||
@ -89,6 +125,13 @@ const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
const exportLoading = ref(false);
|
||||
|
||||
// 编辑/新增相关
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
const operationLogVisible = ref(false);
|
||||
|
||||
// 图片预览
|
||||
const imagePreviewVisible = ref(false);
|
||||
const previewImageUrl = ref('');
|
||||
@ -256,6 +299,13 @@ const tableColumns = ref<any[]>([
|
||||
() => '查看视频'
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
@ -359,6 +409,43 @@ const initTable = (values: any) => {
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
isAdd.value = true;
|
||||
editRecord.value = null;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
const handleSeeEdit = () => {
|
||||
operationLogVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (record: any) => {
|
||||
isAdd.value = false;
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑成功回调
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
// 删除
|
||||
const deleteFpssFn = async (record: any, reason: string) => {
|
||||
return deleteFpssRInfo({
|
||||
ids: [record.id],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
|
||||
@ -57,6 +57,8 @@
|
||||
</a-form-item-rest>
|
||||
</template>
|
||||
<template #actions>
|
||||
<a-button v-if="props.mway === '1'" type="primary" @click="handleAdd">新增</a-button>
|
||||
<a-button type="primary" @click="seeEdit">查看修改</a-button>
|
||||
<a-button :loading="exportLoading" @click="exportBtn">导出</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
@ -75,6 +77,8 @@ const emit = defineEmits<{
|
||||
(e: 'export-btn'): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'add'): void;
|
||||
(e: 'seeEdit'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref<any>();
|
||||
@ -153,7 +157,15 @@ const onSearchFinish = (values: any) => {
|
||||
const exportBtn = () => {
|
||||
emit('export-btn');
|
||||
};
|
||||
const initCrossSectionList = params => {
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
const seeEdit = () => {
|
||||
emit('seeEdit');
|
||||
};
|
||||
const initCrossSectionList = (params, setDefault = false) => {
|
||||
const filters = [];
|
||||
if (params) {
|
||||
if (params.rvcd != '' && params.rvcd != 'all') {
|
||||
@ -173,6 +185,13 @@ const initCrossSectionList = params => {
|
||||
});
|
||||
}
|
||||
}
|
||||
// 根据人工/自动站过滤过鱼设施
|
||||
filters.push({
|
||||
field: 'mway',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: props.mway
|
||||
});
|
||||
crossSectionListLoading.value = true;
|
||||
getFishReleaseMonitorSectionList({
|
||||
filter: {
|
||||
@ -183,6 +202,15 @@ const initCrossSectionList = params => {
|
||||
}).then(res => {
|
||||
if (res.data?.data) {
|
||||
crossSectionList.value = res.data?.data;
|
||||
// 首次加载时默认选中第一条过鱼设施
|
||||
if (setDefault && crossSectionList.value.length > 0) {
|
||||
const firstStcd = crossSectionList.value[0].stcd;
|
||||
initSearchData.stcd = firstStcd;
|
||||
if (basicSearchRef.value?.formData) {
|
||||
basicSearchRef.value.formData.stcd = firstStcd;
|
||||
}
|
||||
handleSearchFinish({ ...initSearchData, stcd: firstStcd });
|
||||
}
|
||||
}
|
||||
crossSectionListLoading.value = false;
|
||||
});
|
||||
@ -233,9 +261,8 @@ onMounted(() => {
|
||||
min: `${now.subtract(5, 'year').format('YYYY-MM-DD')} 00:00:00`,
|
||||
max: `${now.format('YYYY-MM-DD')} 23:59:59`
|
||||
};
|
||||
initCrossSectionList();
|
||||
// 使用 handleSearchFinish 确保包含 jcdt 等完整参数
|
||||
handleSearchFinish({ ...initSearchData });
|
||||
// 首次加载:请求设施列表后默认选中第一条并触发搜索
|
||||
initCrossSectionList(undefined, true);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user