bug修改,鱼种类修改,流域基地逻辑修改

This commit is contained in:
扈兆增 2026-04-30 18:07:19 +08:00
parent 86f1030b03
commit 2342a3555e
10 changed files with 583 additions and 300 deletions

View File

@ -88,11 +88,17 @@ export function getLastImportResult() {
method: 'get'
});
}
// 批量保存草稿
export function batchSaveDraft(data:any) {
export function deleteFile(params:any) {
return request({
url: '/data/fishDraft/batchSaveDraft',
url: '/data/fishDraft/deleteFile',
method: 'get',
params
});
}
// 批量保存草稿
export function importBatchSaveDraft(data:any) {
return request({
url: '/data/fishDraft/importBatchSaveDraft',
method: 'post',
data
});

View File

@ -8,6 +8,14 @@ export function getBaseDropdown(data:any) {
data
});
}
// 流域下拉列表
export function getSelectForDropdown(data:any) {
return request({
url: '/env/hbrv/selectForDropdown',
method: 'get',
data
});
}
//电站下拉列表
export function getEngInfoDropdown(data:any) {
return request({
@ -16,10 +24,10 @@ export function getEngInfoDropdown(data:any) {
data
});
}
// 更新导入任务
export function updateImportTask(data:any) {
// 重新验证并更新行数据
export function revalidateAndUpdateRow(data:any) {
return request({
url: '/data/importTask/update',
url: '/data/fishDraft/revalidateAndUpdateRow',
method: 'post',
data
});

View File

@ -74,7 +74,8 @@
<!-- 电站下拉框 -->
<div class="flex gap-[10px]" v-else-if="item.type === 'waterStation'">
<a-form-item-rest>
<a-select
<!-- 基地下拉框 -->
<!-- <a-select
:value="formData.baseId"
placeholder="请选择"
@change="dataDimensionDataChange"
@ -83,7 +84,6 @@
:loading="shuJuTianBaoStore.baseLoading"
:filter-option="filterOption"
style="width: 135px"
>
<a-select-option
v-for="opt in shuJuTianBaoStore.baseOption"
@ -93,7 +93,28 @@
>
{{ opt.basename }}
</a-select-option>
</a-select> -->
<!-- 流域下拉框 -->
<a-select
:value="formData.hbrvcd"
placeholder="请选择"
@change="lyChange"
show-search
allow-clear
:loading="shuJuTianBaoStore.lyLoading"
:filter-option="filterOption"
style="width: 135px"
>
<a-select-option
v-for="opt in shuJuTianBaoStore.lyOption"
:key="opt.hbrvcd"
:value="opt.hbrvcd"
:label="opt.hbrvnm"
>
{{ opt.hbrvnm }}
</a-select-option>
</a-select>
<!-- 电站下拉框 -->
<a-select
:value="formData.rstcd"
placeholder="请选择电站"
@ -246,8 +267,9 @@ const initForm = () => {
validSearchList.value.forEach((item) => {
if (item.type == "waterStation") {
//
shuJuTianBaoStore.getBaseOption();
shuJuTianBaoStore.getEngOption(formData.baseId);
// shuJuTianBaoStore.getBaseOption();
shuJuTianBaoStore.getSelectForOption();
shuJuTianBaoStore.getEngOption(formData.hbrvcd);
}
if (item.fieldProps?.required) {
rules[item.name] = [
@ -270,17 +292,27 @@ const triggerManualValuesChange = (changedKey: string, newValue: any) => {
emit("valuesChange", changedValues, { ...formData });
};
const dataDimensionDataChange = (value: any) => {
formData.baseId = value;
// const dataDimensionDataChange = (value: any) => {
// formData.baseId = value;
// formData.rstcd = "";
// shuJuTianBaoStore.getEngOption(formData.baseId);
// // valuesChange a-form-item-rest
// triggerManualValuesChange("baseId", formData.baseId);
// };
const lyChange = (value: any) => {
formData.hbrvcd = value;
formData.rstcd = "";
shuJuTianBaoStore.getEngOption(formData.baseId);
shuJuTianBaoStore.getEngOption(formData.hbrvcd);
// valuesChange a-form-item-rest
triggerManualValuesChange("baseId", formData.baseId);
triggerManualValuesChange("hbrvcd", formData.hbrvcd);
};
const stcdIdChange = (value: any) => {
formData.rstcd = value;
shuJuTianBaoStore.getFpssOption(formData.hbrvcd, value);
// valuesChange
triggerManualValuesChange("rstcd", formData.rstcd);
};

View File

@ -2,13 +2,13 @@
<div
class="custom-fish-search-container"
ref="containerRef"
@mouseenter="isHovered = true"
@mouseleave="isHovered = false"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
>
<!-- 1. 输入框区域 -->
<div
class="fish-input-wrapper"
:class="{ 'is-focused': isOpen }"
:class="{ 'is-focused': isOpen, disabled: disabled }"
@click="handleWrapperClick"
>
<!--
@ -415,6 +415,14 @@ const handleClickOutside = (event: MouseEvent) => {
closeDropdown();
}
};
const handleMouseEnter = () => {
if (!props.disabled) {
isHovered.value = true;
}
};
const handleMouseLeave = () => {
isHovered.value = false;
};
onMounted(() => {
init();
@ -522,6 +530,15 @@ onBeforeUnmount(() => {
}
}
}
.disabled {
background-color: #f5f5f5;
&:hover {
border-color: #d9d9d9;
}
.single-value {
color: rgba(0, 0, 0, 0.25);
}
}
/* 下拉面板样式 */
.fish-dropdown-panel {

View File

@ -1,15 +1,26 @@
import { defineStore } from 'pinia';
import { ref } from 'vue'; // 使用 ref 更简单直观
import { getBaseDropdown, getEngInfoDropdown, getFpssDropdown } from '@/api/select';
import {
getBaseDropdown,
getSelectForDropdown,
getEngInfoDropdown,
getFpssDropdown
} from '@/api/select';
export const useShuJuTianBaoStore = defineStore('shuJuTianBao', () => {
// 1. 直接使用 ref 定义状态,确保响应式
const fpssOption = ref<any[]>([]);
const fpssLoading = ref(false);
const baseOption = ref<any[]>([]);
// 水电基地下拉列表
const baseLoading = ref(false);
const baseOption = ref<any[]>([]);
// 流域下拉列表
const lyLoading = ref(false);
const lyOption = ref<any[]>([]);
// 电站下拉列表
const engOption = ref<any[]>([]);
const engLoading = ref(false);
// 流域下拉列表
const fpssOption = ref<any[]>([]);
const fpssLoading = ref(false);
// 鱼类名称下拉列表
const fishOption = ref([]);
// 获取水电基地列表
const getBaseOption = async () => {
@ -27,15 +38,35 @@ export const useShuJuTianBaoStore = defineStore('shuJuTianBao', () => {
baseLoading.value = false;
}
};
// 获取流域列表
const getSelectForOption = async () => {
try {
lyLoading.value = true;
const res = await getSelectForDropdown({});
if (res.data && Array.isArray(res.data)) {
const list = [
{ hbrvcd: 'all', hbrvnm: '当前全部', baseid: '01' },
...res.data
];
// 直接赋值给 ref触发响应式更新
lyOption.value = list;
}
} catch (error) {
console.error('获取流域列表失败:', error);
} finally {
lyLoading.value = false;
}
};
// 获取电站列表
const getEngOption = async (baseId: string) => {
const getEngOption = async (hbrvcd: string) => {
try {
engLoading.value = true;
const param = baseId === 'all' ? {} : { baseId };
const param = hbrvcd === 'all' ? {} : { hbrvcd };
const res = await getEngInfoDropdown(param);
if (res.data && Array.isArray(res.data)) {
// 直接赋值给 ref
engOption.value = res.data;
getFpssOption(hbrvcd, '');
}
} catch (error) {
console.error('获取电站列表失败:', error);
@ -44,16 +75,17 @@ export const useShuJuTianBaoStore = defineStore('shuJuTianBao', () => {
}
};
// 获取过鱼设施列表
const getFpssOption = async (baseId: string, rstcd: string) => {
const getFpssOption = async (hbrvcd: string, rstcd: string) => {
try {
fpssLoading.value = true;
const res = await getFpssDropdown({ baseId, rstcd });
console.log(res.data)
const param = hbrvcd === 'all' ? {} : { hbrvcd };
const res = await getFpssDropdown({...param, rstcd: rstcd});
console.log(res.data);
fpssOption.value = res.data;
} catch (error) {
console.log(error);
} finally {
fpssLoading.value = false;
// fpssLoading.value = false;
}
};
const getFishOption = () => {
@ -66,13 +98,16 @@ export const useShuJuTianBaoStore = defineStore('shuJuTianBao', () => {
// 在组件中使用时store.baseOption 会自动解包为数组
return {
fpssOption,
lyOption,
baseOption,
engOption,
fishOption,
fpssLoading,
baseLoading,
lyLoading,
engLoading,
getBaseOption,
getSelectForOption,
getEngOption,
getFpssOption,
getFishOption,

View File

@ -40,3 +40,14 @@
:where(.css-dev-only-do-not-override-ekaqbe).ant-btn > span {
display: inline-flex;
}
.ant-image-preview-root {
.ant-image-preview-wrap {
z-index: 2005 !important;
}
.ant-image-preview-mask {
z-index: 2005 !important;
}
}
.ant-message {
z-index: 2005 !important;
}

View File

@ -2,7 +2,7 @@
<a-modal
:title="isView ? '查看数据' : isEdit ? '编辑数据' : '新增数据'"
v-model:open="modalVisible"
:confirm-loading="loading"
:confirm-loading="localLoading"
width="800px"
:destroy-on-close="true"
:footer="isView ? null : undefined"
@ -18,7 +18,28 @@
>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="流域" name="baseId">
<a-form-item label="流域" name="hbrvcd">
<a-select
v-model:value="formData.hbrvcd"
:loading="hbrvcdLoading"
placeholder="请选择流域"
:disabled="isView"
show-search
allowClear
:filter-option="filterOption"
@change="hbrvcdChange"
>
<a-select-option
v-for="opt in hbrvcdOption"
:key="opt.hbrvcd"
:value="opt.hbrvcd"
:label="opt.hbrvnm"
>
{{ opt.hbrvnm }}
</a-select-option>
</a-select>
</a-form-item>
<!-- <a-form-item label="流域" name="baseId">
<a-select
v-model:value="formData.baseId"
:loading="baseLoading"
@ -38,7 +59,7 @@
{{ opt.basename }}
</a-select-option>
</a-select>
</a-form-item>
</a-form-item> -->
</a-col>
<a-col :span="12">
<a-form-item label="电站名称" name="rstcd">
@ -46,7 +67,7 @@
v-model:value="formData.rstcd"
:loading="engLoading"
placeholder="请选择电站名称"
:disabled="isView || !formData.baseId"
:disabled="isView || !formData.hbrvcd"
show-search
allowClear
:filter-option="filterOption"
@ -218,7 +239,7 @@
/>
</a-form-item>
</a-col>
<a-col :span="12" class="imgupload" :class="{'imgupload_hidden': isView}">
<a-col :span="12" class="imgupload" :class="{ imgupload_hidden: isView }">
<a-form-item label="图片" name="picpth">
<a-upload
v-model:file-list="imageFileList"
@ -258,7 +279,9 @@
上传视频 (MP4)
</a-button>
</a-upload>
<a-button v-else-if="videoFileList.length > 0" @click="handleVideoPreview"> 点击预览视频 </a-button>
<a-button v-else-if="videoFileList.length > 0" @click="handleVideoPreview">
点击预览视频
</a-button>
</a-form-item>
</a-col>
</a-row>
@ -270,12 +293,12 @@
import { ref, reactive, watch, computed } from "vue";
import dayjs from "dayjs";
import { Upload, message } from "ant-design-vue";
import { UploadOutlined, PlusOutlined } from "@ant-design/icons-vue";
import type { Rule } from "ant-design-vue/es/form";
import type { UploadProps } from "ant-design-vue";
import type { Rule } from "ant-design-vue/es/form";
import { UploadOutlined, PlusOutlined } from "@ant-design/icons-vue";
import fishSearch from "@/components/fishSearch/index.vue";
import {
getBaseDropdown,
getSelectForDropdown,
getEngInfoDropdown,
getFpssDropdown,
uploadFile,
@ -289,12 +312,18 @@ interface Props {
loading?: boolean;
isView?: boolean;
}
const baseLoading = ref(false);
//
const localLoading = ref(false);
// const baseLoading = ref(false);
const hbrvcdLoading = ref(false);
const engLoading = ref(false);
const fpssLoading = ref(false);
const baseOption = ref<any[]>([]);
//
// const baseOption = ref<any[]>([]);
const hbrvcdOption = ref<any[]>([]);
const engOption = ref<any[]>([]);
const fpssOption = ref<any[]>([]);
//
const imageFileList = ref<any[]>([]);
const videoFileList = ref<any[]>([]);
@ -303,29 +332,54 @@ const props = withDefaults(defineProps<Props>(), {
initialValues: null,
loading: false,
});
const getBaseDropdownSelect = async () => {
//
// const getBaseDropdownSelect = async () => {
// try {
// baseLoading.value = true;
// const res = await getBaseDropdown({});
// let list = res.data || [];
// if (list && list.length > 0) {
// list = list.filter((item: any) => item.baseId !== "all");
// }
// baseOption.value = list;
// } catch (error) {
// console.error(":", error);
// } finally {
// baseLoading.value = false;
// }
// };
//
const getHbrvcdDropdownSelect = async () => {
try {
baseLoading.value = true;
const res = await getBaseDropdown({});
hbrvcdLoading.value = true;
const res = await getSelectForDropdown({});
let list = res.data || [];
if (list.length > 0) list.shift();
baseOption.value = list;
if (list && list.length > 0) {
list = list.filter((item: any) => item.hbrvcd !== "all");
}
hbrvcdOption.value = list;
} catch (error) {
console.error("获取流域列表失败:", error);
} finally {
baseLoading.value = false;
hbrvcdLoading.value = false;
}
};
const baseChange = async (baseId: string) => {
// const baseChange = async (baseId: string) => {
// formData.rstcd = undefined;
// formData.stcd = undefined;
// await getEngInfoDropdownSelect(baseId);
// await getFpssDropdownSelect(formData.rstcd, baseId);
// };
const hbrvcdChange = async (hbrvcd: string) => {
formData.rstcd = undefined;
formData.stcd = undefined;
await getEngInfoDropdownSelect(baseId);
await getFpssDropdownSelect(formData.rstcd, baseId);
await getEngInfoDropdownSelect(hbrvcd);
// await getFpssDropdownSelect(formData.rstcd, hbrvcd);
};
const getEngInfoDropdownSelect = async (baseId: string) => {
const getEngInfoDropdownSelect = async (hbrvcd: string) => {
try {
engLoading.value = true;
const res = await getEngInfoDropdown({ baseId });
const res = await getEngInfoDropdown({ hbrvcd });
engOption.value = res.data;
} catch (error) {
console.error("获取电站列表失败", error);
@ -335,12 +389,12 @@ const getEngInfoDropdownSelect = async (baseId: string) => {
};
const engChange = async (rstcd: string) => {
formData.stcd = undefined;
await getFpssDropdownSelect(rstcd, formData.baseId);
await getFpssDropdownSelect(rstcd, formData.hbrvcd);
};
const getFpssDropdownSelect = async (rstcd: string, baseId: string) => {
const getFpssDropdownSelect = async (rstcd: string, hbrvcd: string) => {
try {
fpssLoading.value = true;
const res = await getFpssDropdown({ rstcd, baseId });
const res = await getFpssDropdown({ rstcd, hbrvcd });
fpssOption.value = res.data;
} catch (error) {
console.error("获取流量列表失败", error);
@ -369,7 +423,7 @@ const weightError = ref<string>("");
//
const defaultFormData = reactive({
id: undefined,
baseId: undefined,
hbrvcd: undefined,
stcd: undefined,
rstcd: undefined,
strdt: undefined,
@ -394,7 +448,7 @@ const filterOption = (inputValue: string, option: any) => {
};
//
const rules: Record<string, Rule[]> = {
baseId: [{ required: true, message: "请选择流域", trigger: "change" }],
hbrvcd: [{ required: true, message: "请选择流域", trigger: "change" }],
rstcd: [{ required: true, message: "请选择电站", trigger: "change" }],
stcd: [{ required: true, message: "请选择过鱼设施", trigger: "change" }],
strdt: [{ required: true, message: "请选择过鱼时间", trigger: "change" }],
@ -589,46 +643,12 @@ const initForm = () => {
}
};
watch(
() => props.visible,
(newVisible) => {
if (newVisible) {
//
getBaseDropdownSelect();
getEngInfoDropdownSelect(formData.baseId);
getFpssDropdownSelect(formData.rstcd, formData.baseId);
initForm();
}
},
{ immediate: false } // immediate false
);
//
const resetForm = () => {
if (formRef.value) {
formRef.value.resetFields();
}
Object.assign(formData, defaultFormData);
//
bodyLengthError.value = "";
weightError.value = "";
//
imageFileList.value = [];
videoFileList.value = [];
};
//
const handleCancel = () => {
emit("update:visible", false);
emit("cancel");
resetForm();
};
//
const handleVideoPreview = () => {
emit("preview-click", { vdpthList: videoFileList.value }, "formVideo", 0);
};
// ... ...
//
//
const handleImagePreview = async (file: any) => {
if (!props.isView) {
return "";
@ -636,8 +656,6 @@ const handleImagePreview = async (file: any) => {
emit("preview-click", { picpthList: imageFileList.value }, "formImage", 0);
return "";
};
// ... ...
//
const handleOk = async () => {
try {
@ -657,6 +675,7 @@ const handleOk = async () => {
(file) => !file.url && file.originFileObj
);
localLoading.value = true;
if (newImageFiles.length > 0) {
// 使 Promise.all
// uploadFile { data: { url: '...' } }
@ -757,6 +776,45 @@ const handleOk = async () => {
message.error("请检查表单填写是否正确");
}
};
//
const resetForm = () => {
if (formRef.value) {
formRef.value.resetFields();
}
Object.assign(formData, defaultFormData);
//
bodyLengthError.value = "";
weightError.value = "";
//
imageFileList.value = [];
videoFileList.value = [];
};
//
const handleCancel = () => {
emit("update:visible", false);
emit("cancel");
resetForm();
};
watch(
() => props.visible,
(newVisible) => {
if (newVisible) {
//
// getBaseDropdownSelect();//
getHbrvcdDropdownSelect();//
getEngInfoDropdownSelect(formData.hbrvcd);
getFpssDropdownSelect(formData.rstcd, formData.hbrvcd);
initForm();
}
},
{ immediate: false } // immediate false
);
//
defineExpose({
localLoading
});
</script>
<style scoped lang="scss">

View File

@ -104,7 +104,8 @@ const localTypeDate = ref<string>(null);
const basicSearchRef = ref<any>();
const initSearchData = {
baseId: "all",
hbrvcd: "all",
// baseId: "all",
stcd: null,
rstcd: null,
ftp: null,
@ -120,7 +121,7 @@ const searchData = ref<any>({ ...initSearchData });
const searchList: any = computed(() => [
{
type: "waterStation",
name: "baseId",
name: "hbrvcd",
label: "流域",
fieldProps: {
allowClear: true,
@ -191,12 +192,9 @@ const onValuesChange = (changedValues: any, allValues: any) => {
// searchData便使
if (
Object.keys(changedValues)[0] == "rstcd" ||
Object.keys(changedValues)[0] == "baseId"
Object.keys(changedValues)[0] == "baseId"||
Object.keys(changedValues)[0] == "hbrvcd"
) {
shuJuTianBaoStore.getFpssOption(
allValues.baseId == "all" ? "" : allValues.baseId,
allValues.rstcd
);
const formInstance = basicSearchRef.value?.formData;
formInstance.stcd = null;
}

View File

@ -29,8 +29,8 @@
<template
v-else-if="
!isEditing(index) &&
record._warnings &&
record._warnings.includes(column.dataIndexKey)
record.warnings &&
record.warnings.includes(column.dataIndexKey)
"
>
<div style="color: red; display: flex; align-items: center">
@ -46,7 +46,8 @@
isEditing(index) && column.dataIndex != 'picpth' && column.dataIndex != 'vdpth'
"
>
<template v-if="column.dataIndex === 'baseName'">
<!-- 基地 -->
<!-- <template v-if="column.dataIndex === 'baseName'">
<a-select
v-model:value="editingData.baseId"
placeholder="请选择"
@ -66,6 +67,28 @@
{{ opt.basename }}
</a-select-option>
</a-select>
</template> -->
<!-- 流域名称 -->
<template v-if="column.dataIndex === 'hbrvnm'">
<a-select
v-model:value="editingData.hbrvcd"
placeholder="请选择"
show-search
allowClear
:filter-option="filterOption"
:loading="rowStates[index]?.hbrvcdLoading"
style="width: 100%"
@change="(val) => handleHbrvcdChange(val, index, 'input')"
>
<a-select-option
v-for="opt in hbrvcdOptions"
:key="opt.hbrvcd"
:value="opt.hbrvcd"
:label="opt.hbrvnm"
>
{{ opt.hbrvnm }}
</a-select-option>
</a-select>
</template>
<!-- 电站名称 -->
@ -78,7 +101,7 @@
:filter-option="filterOption"
:loading="rowStates[index]?.engLoading"
style="width: 100%"
:disabled="!editingData.baseId"
:disabled="!editingData.hbrvcd"
@change="(val) => handleEngChange(val, index, 'input')"
>
<a-select-option
@ -174,8 +197,10 @@
<!-- 是否鱼苗 -->
<template v-else-if="column.dataIndex === 'isfs'">
<a-radio-group v-model:value="editingData.isfs"
@change="(val) => delWarning(val,'isfs')">
<a-radio-group
v-model:value="editingData.isfs"
@change="(val) => delWarning(val, 'isfs')"
>
<a-radio :value="1"></a-radio>
<a-radio :value="0"></a-radio>
</a-radio-group>
@ -245,24 +270,31 @@
</template>
<script setup lang="ts">
import _ from "lodash";
import { ref, reactive, onMounted, h } from "vue";
import { message, Tag } from "ant-design-vue";
import { ExclamationCircleOutlined } from "@ant-design/icons-vue";
import fishSearch from "@/components/fishSearch/index.vue";
import { getBaseDropdown, getEngInfoDropdown, getFpssDropdown,updateImportTask } from "@/api/select";
import {
getSelectForDropdown,
getEngInfoDropdown,
getFpssDropdown,
revalidateAndUpdateRow,
} from "@/api/select";
const props: any = defineProps({
taskId: { type: String, default: '' },
taskId: { type: String, default: "" },
fileTableData: { type: Array, default: () => [] },
fileLoading: { type: Boolean, default: false },
direction: { type: Array, default: () => [] },
});
const emit = defineEmits(["update:fileTableData", "preview-click"]);
const emit = defineEmits(["update:fileTableData", "preview-click", "update:fileLoading"]);
// --- ---
const editingRowIndex = ref<number | null>(null);
const baseOptions = ref<any[]>([]);
const hbrvcdOptions = ref<any[]>([]);
const rowStates = reactive<Record<number, any>>({});
// 使
@ -270,9 +302,9 @@ const editingData = ref<any>(null);
const modalColumns = ref([
{
dataIndex: "baseName",
key: "baseName",
dataIndexKey: "baseId",
dataIndex: "hbrvnm",
key: "hbrvnm",
dataIndexKey: "hbrvcd",
title: "流域",
width: 140,
},
@ -350,17 +382,33 @@ const modalColumns = ref([
// --- ---
onMounted(() => {
loadBaseOptions();
// loadBaseOptions();
loadHbrvcdOptions();
});
const loadBaseOptions = async () => {
// const loadBaseOptions = async () => {
// try {
// const res = await getBaseDropdown({});
// let list = res.data || [];
// if (list && list.length > 0) {
// list = list.filter((item: any) => item.baseid !== "all");
// }
// baseOptions.value = list;
// } catch (e) {
// console.error("Load base options failed", e);
// }
// };
const loadHbrvcdOptions = async () => {
try {
const res = await getBaseDropdown({});
const res = await getSelectForDropdown({});
let list = res.data || [];
if (list.length > 0) list.shift();
baseOptions.value = list;
if (list && list.length > 0) {
list = list.filter((item: any) => item.hbrvcd !== "all");
}
hbrvcdOptions.value = list;
} catch (e) {
console.error("Load base options failed", e);
console.error("Load hbrvcd options failed", e);
}
};
@ -370,6 +418,7 @@ const ensureRowState = (index: number) => {
engOptions: [],
fpssOptions: [],
baseLoading: false,
hbrvcdLoading: false,
engLoading: false,
fpssLoading: false,
};
@ -378,26 +427,61 @@ const ensureRowState = (index: number) => {
};
// --- ( editingData) ---
const handleBaseChange = async (
baseId: string,
// const handleBaseChange = async (
// hbrvcd: string,
// index: number,
// type: string = "start"
// ) => {
// const state = ensureRowState(index);
// editingData.value.baseName = hbrvcdOptions.value.find(
// (item: any) => item.hbrvcd == hbrvcd
// )?.basename;
// delWarning(hbrvcd, "hbrvcd");
// //
// if (type != "start") {
// editingData.value.rstcd = undefined;
// editingData.value.stcd = undefined;
// editingData.value.ennm = undefined;
// editingData.value.stnm = undefined;
// state.engOptions = [];
// state.fpssOptions = [];
// }
// state.engLoading = true;
// try {
// const res = await getEngInfoDropdown({ hbrvcd });
// state.engOptions = res.data || [];
// } catch (e) {
// message.error("");
// } finally {
// state.engLoading = false;
// }
// };
const handleHbrvcdChange = async (
hbrvcd: string,
index: number,
type: string = "start"
) => {
const state = ensureRowState(index);
editingData.value.baseName = baseOptions.value.find(
(item: any) => item.baseid == baseId
)?.basename;
delWarning(baseId, "baseId");
editingData.value.hbrvnm = hbrvcdOptions.value.find(
(item: any) => item.hbrvcd == hbrvcd
)?.hbrvnm;
delWarning(hbrvcd, "hbrvcd");
if (hbrvcd == null) {
delWarning(null, "hbrvcd");
delWarning(null, "stcd");
}
//
if (type != "start") {
editingData.value.rstcd = undefined;
editingData.value.stcd = undefined;
editingData.value.ennm = undefined;
editingData.value.stnm = undefined;
state.engOptions = [];
state.fpssOptions = [];
}
state.engLoading = true;
try {
const res = await getEngInfoDropdown({ baseId });
const res = await getEngInfoDropdown({ hbrvcd });
state.engOptions = res.data || [];
} catch (e) {
message.error("获取电站列表失败");
@ -412,13 +496,18 @@ const handleEngChange = async (rstcd: string, index: number, type: string = "sta
(item: any) => item.stcd === rstcd
)?.ennm;
delWarning(rstcd, "rstcd");
if (rstcd == null) {
delWarning(null, "stcd");
}
//
if (type != "start") {
editingData.value.stcd = undefined;
editingData.value.stnm = undefined;
state.fpssOptions = [];
}
state.fpssLoading = true;
try {
const res = await getFpssDropdown({ rstcd, baseId: editingData.value.baseId });
const res = await getFpssDropdown({ rstcd, hbrvcd: editingData.value.hbrvcd });
state.fpssOptions = res.data || [];
} catch (e) {
message.error("获取设施列表失败");
@ -436,23 +525,23 @@ const handleFpssChange = (stcd: string, index: number) => {
// /
const delWarning = (val: any, key: string) => {
// _warnings
if (!editingData.value._warnings) {
editingData.value._warnings = [];
// warnings
if (!editingData.value.warnings) {
editingData.value.warnings = [];
}
const warnings = editingData.value._warnings;
const warnings = editingData.value.warnings;
const hasWarning = warnings.includes(key);
if (val !== null && val !== undefined && val !== '') {
if (val !== null && val !== undefined && val !== "") {
// 1.
if (hasWarning) {
editingData.value._warnings = warnings.filter((w: string) => w !== key);
editingData.value.warnings = warnings.filter((w: string) => w !== key);
}
} else {
// 2. (null/undefined/'')
if (!hasWarning) {
editingData.value._warnings.push(key);
editingData.value.warnings.push(key);
}
}
};
@ -462,24 +551,37 @@ const startEdit = (index: number) => {
const originalRecord = props.fileTableData[index];
// 1.
editingData.value = JSON.parse(JSON.stringify(originalRecord));
let copiedData = JSON.parse(JSON.stringify(originalRecord));
editingData.value = copiedData;
// 2. fsz/fwet Min/Max 使
processStringToMinMax(editingData.value);
editingRowIndex.value = index;
if (editingData.value.warnings.includes("hbrvcd")) {
editingData.value.hbrvcd = null;
editingData.value.hbrvnm = null;
editingData.value.rstcd = null;
editingData.value.stcd = null;
delWarning(null, "rstcd");
delWarning(null, "stcd");
}
if (editingData.value.warnings.includes("rstcd")) {
editingData.value.stcd = null;
delWarning(null, "stcd");
}
// 3. ( editingData )
if (editingData.value.baseId == "" || editingData.value.baseId == undefined) {
if (editingData.value.hbrvcd == "" || editingData.value.hbrvcd == undefined) {
if (editingData.value.rstcd) {
handleBaseChange("", index, "start").then(() => {
handleHbrvcdChange("", index, "start").then(() => {
handleEngChange(editingData.value.rstcd, index, "start");
});
} else {
handleEngChange("", index, "start");
}
} else if (editingData.value.baseId != "" && editingData.value.baseId != undefined) {
handleBaseChange(editingData.value.baseId, index, "start").then(() => {
} else if (editingData.value.hbrvcd != "" && editingData.value.hbrvcd != undefined) {
handleHbrvcdChange(editingData.value.hbrvcd, index, "start").then(() => {
handleEngChange(editingData.value.rstcd, index, "start");
});
}
@ -536,24 +638,31 @@ const processMinMaxToString = (data: any) => {
delete data.weightMax;
};
const saveEdit = (index: number) => {
const saveEdit = async (index: number) => {
// 1. Min/Max fsz/fwet
processMinMaxToString(editingData.value);
// 2.
const newData = [...props.fileTableData];
newData[index] = { ...editingData.value };
// 3.
emit("update:fileLoading", true);
try {
const res: any = await revalidateAndUpdateRow({
taskId: props.taskId,
data: newData[index],
});
if (res && res?.code == 0) {
message.success("保存成功");
emit("update:fileTableData", newData);
// 4.
editingRowIndex.value = null;
editingData.value = null;
console.log(newData)
console.log(props.taskId)
// updateImportTask
message.success("保存成功");
emit("update:fileLoading", false);
}
} catch (e) {
message.error("保存失败");
} finally {
emit("update:fileLoading", false);
}
};
const cancelEdit = () => {

View File

@ -32,13 +32,13 @@
<template v-if="column.key === 'action' || column.dataIndex === 'action'">
<div class="flex">
<a-button
v-hasPerm="['sjtb:import-add']"
type="link"
size="small"
@click="handleSubmit([record.id])"
v-if="record.status === 'DRAFT' || record.status === 'REJECTED'"
>提交</a-button
>
<a-button
v-hasPerm="['sjtb:import-add']"
type="link"
@ -52,14 +52,11 @@
type="link"
size="small"
@click="handleEdit(record, 'edit')"
v-if="
record.status === 'PENDING' ||
record.status === 'REJECTED' ||
record.status === 'PENDING'
"
v-if="record.status === 'PENDING'"
>编辑</a-button
>
<a-button
v-hasPerm="['sjtb:import-add']"
type="link"
danger
size="small"
@ -71,7 +68,12 @@
type="link"
size="small"
@click="handleEdit(record, 'view')"
v-if="record.status === 'PENDING' || record.status === 'APPROVED'"
v-if="
(checkPerm(['sjtb:edit-review']) && record.status === 'DRAFT') ||
(checkPerm(['sjtb:import-add']) && record.status === 'PENDING') ||
(checkPerm(['sjtb:edit-review']) && record.status === 'REJECTED') ||
record.status === 'APPROVED'
"
>查看</a-button
>
<a-button
@ -118,10 +120,10 @@
<GuoYuSheShiShuJuTianBaoTable
ref="modalTableRef"
:taskId="taskId"
:fileLoading="fileLoading"
:fileTableData="fileTableData"
:direction="direction"
@preview-click="handlePreviewClick"
v-model:file-loading="fileLoading"
@update:file-table-data="(val) => (fileTableData = val)"
/>
<template #footer>
@ -139,11 +141,11 @@
<!-- 新增/编辑 Modal (对应 React EditModal) -->
<EditModal
ref="editModalRef"
v-model:visible="editModalVisible"
:is-view="isView"
:direction="direction"
:initial-values="currentRecord"
:loading="submitLoading"
@cancel="editModalCancel"
@ok="handleEditSubmit"
@preview-click="handlePreviewClick"
@ -274,15 +276,17 @@ import {
importFishZip,
cancelImportTask,
checkImportStatus,
batchSaveDraft,
importBatchSaveDraft,
getLastImportResult,
markImportTaskSuccess,
deleteFile,
} from "@/api/guoYuSheShiShuJuTianBao";
import { Tag } from "ant-design-vue"; // Tag
import { getDictItemsByCode } from "@/api/dict";
import dayjs from "dayjs";
const baseUrl = import.meta.env.VITE_APP_PREVIEW_URL;
const baseUrlPreview = import.meta.env.VITE_APP_BASE_URL;
// --- ---
interface FormData {
[key: string]: any;
@ -304,8 +308,8 @@ const guoyuStatus = ref<any>([]);
// --- ---
const baseColumnsConfig: ColumnConfig[] = [
{
dataIndex: "baseName",
key: "baseName",
dataIndex: "hbrvnm",
key: "hbrvnm",
title: "流域",
width: 120,
fixed: "left",
@ -364,16 +368,17 @@ const baseColumnsConfig: ColumnConfig[] = [
// --- ---
const visible = ref(false); // Modal
const editModalRef = ref<any>(null);
//
const editModalVisible = ref(false);
const isView = ref(false);
const currentRecord = ref<FormData | null>(null);
const submitLoading = ref(false);
const mediaPreviewVisible = ref(false);
const videoPreviewTitle = ref("视频预览");
interface MediaItem {
id: string;
type: "image" | "video" | "formVideo" | "formImage";
name: string;
url: string;
@ -387,7 +392,6 @@ const currentMediaItem = computed(() => previewList.value[currentMediaIndex.valu
//
const fileTableData = ref<any[]>([]);
const orgFileTableData = ref<any[]>([]);
const batchData = ref<any[]>([]);
const modalTableRef = ref<any>(null);
@ -589,7 +593,7 @@ const editModalCancel = () => {
};
// /-
const handleEditSubmit = async (values: FormData) => {
submitLoading.value = true;
editModalRef.value.localLoading = true;
if (currentRecord.value) {
//
let res: any = await editFishDraft({
@ -600,7 +604,7 @@ const handleEditSubmit = async (values: FormData) => {
editModalVisible.value = false;
tableRef.value?.getList();
}
submitLoading.value = false;
editModalRef.value.localLoading = false;
} else {
//
let res: any = await addFishDraft({
@ -611,57 +615,9 @@ const handleEditSubmit = async (values: FormData) => {
editModalVisible.value = false;
tableRef.value?.getList();
}
submitLoading.value = false;
editModalRef.value.localLoading = false;
}
};
const isRowDataEqual = (oldRow: any, newRow: any, fields: string[]): boolean => {
for (const field of fields) {
// undefined/null
let oldVal = oldRow?.[field];
let newVal = newRow?.[field];
//
if (
(oldVal === undefined || oldVal === null || oldVal === "") &&
(newVal === undefined || newVal === null || newVal === "")
) {
continue;
}
//
if (oldVal !== newVal) {
return false;
}
}
return true;
};
const checkTableDataChanges = () => {
// 1. ( Form )
const oldData = orgFileTableData.value;
const newData = fileTableData.value;
// 2.
if (oldData.length !== newData.length) {
return { hasChanged: true, changedCount: newData.length };
}
let changedCount = 0;
// 3.
for (let i = 0; i < newData.length; i++) {
const oldRow = oldData[i];
const newRow = newData[i];
// +1
if (!isRowDataEqual(oldRow, newRow, ["baseId"])) {
changedCount++;
}
}
return { hasChanged: changedCount > 0, changedCount };
};
//
const handleModalOk = () => {
if (modalTableRef.value.editingData != undefined) {
@ -669,36 +625,40 @@ const handleModalOk = () => {
return;
}
for (let i = 0; i < fileTableData.value.length; i++) {
if (fileTableData.value[i]._warnings?.length > 0) {
if (fileTableData.value[i]._warnings.includes("baseId")) {
if (fileTableData.value[i].warnings?.length > 0) {
// if (fileTableData.value[i].warnings.includes("baseId")) {
// message.warning("");
// return;
// }
if (fileTableData.value[i].warnings.includes("hbrvcd")) {
message.warning("请检查流域,流域填写有误!");
return;
}
if (fileTableData.value[i]._warnings.includes("rstcd")) {
if (fileTableData.value[i].warnings.includes("rstcd")) {
message.warning("请检查电站,电站填写有误!");
return;
}
if (fileTableData.value[i]._warnings.includes("stcd")) {
if (fileTableData.value[i].warnings.includes("stcd")) {
message.warning("请检查过鱼设施,过鱼设施填写有误!");
return;
}
if (fileTableData.value[i]._warnings.includes("strdt")) {
if (fileTableData.value[i].warnings.includes("strdt")) {
message.warning("请检查过鱼时间,过鱼时间填写有误!");
return;
}
if (fileTableData.value[i]._warnings.includes("ftp")) {
if (fileTableData.value[i].warnings.includes("ftp")) {
message.warning("请检查鱼种类,鱼种类填写有误!");
return;
}
if (fileTableData.value[i]._warnings.includes("direction")) {
if (fileTableData.value[i].warnings.includes("direction")) {
message.warning("请检查游向,游向填写有误!");
return;
}
if (fileTableData.value[i]._warnings.includes("isfs")) {
if (fileTableData.value[i].warnings.includes("isfs")) {
message.warning("请检查是否鱼苗,是否鱼苗填写有误!");
return;
}
if (fileTableData.value[i]._warnings.includes("fcnt")) {
if (fileTableData.value[i].warnings.includes("fcnt")) {
message.warning("请检查过鱼数量,过鱼数量填写有误!");
return;
}
@ -715,17 +675,13 @@ const handleModalOk = () => {
fileTableData.value.forEach((item) => {
item.tm = dayjs().format("YYYY-MM-DD HH:mm:ss");
});
// const { hasChanged, changedCount } = checkTableDataChanges();
// // 3.
// if (!hasChanged) {
// message.info("");
// return;
// }
Modal.confirm({
title: "是否提交导入数据?",
onOk: async () => {
let res: any = await batchSaveDraft(fileTableData.value);
let res: any = await importBatchSaveDraft({
taskId: taskId.value,
fishDraftDataList: fileTableData.value,
});
if (res && res?.code == 0) {
message.success("导入成功");
tableRef.value?.getList();
@ -751,6 +707,8 @@ const handleCustomCancel = () => {
message.success("取消成功");
tableRef.value?.getList();
visible.value = false;
} else {
message.error("取消失败");
}
},
});
@ -800,7 +758,11 @@ const fileChange = (file: File) => {
message.error("导入失败");
} else {
taskId.value = res.data.taskId;
if (res.data.summary) {
message.success(res.data.summary);
} else {
message.success("导入成功");
}
fileTableaAnalysis(res, "file");
}
});
@ -847,9 +809,9 @@ const fileTableaAnalysis = (res: any, type: string) => {
let data = [];
let list = [];
if (type == "file") {
list = res.data.failedRows.concat(res.data.successRows);
list = res.data.rows;
} else {
list = res.data.result.failedRowDetails.concat(res.data.result.successRowDetails);
list = res.data.result.rows;
}
list.sort((a: any, b: any) => {
const keyA =
@ -866,11 +828,10 @@ const fileTableaAnalysis = (res: any, type: string) => {
vdpthsWarnings: item.vdpthsWarnings,
picpthList: item.picpthList,
picpthsWarnings: item.picpthsWarnings,
_warnings: item.warnings,
warnings: item.warnings,
});
});
fileTableData.value = data || [];
orgFileTableData.value = JSON.parse(JSON.stringify(fileTableData.value));
fileLoading.value = false;
};
const handleReset = (values) => {
@ -937,12 +898,18 @@ const handleSearchFinish = (values: any) => {
dataType: "string",
value: values.rstcd,
},
values.baseId !== "all" && {
field: "baseId",
values.hbrvcd !== "all" && {
field: "hbrvcd",
operator: "eq",
dataType: "string",
value: values.baseId,
value: values.hbrvcd,
},
// values.baseId !== "all" && {
// field: "baseId",
// operator: "eq",
// dataType: "string",
// value: values.baseId,
// },
].filter(Boolean);
const filter = {
@ -960,9 +927,16 @@ const handlePreviewClick = (record: any, type: string, index: number) => {
const nameList = record.picpthList;
nameList.forEach((item: any) => {
mixedList.push({
id: record.id,
type: "image",
name: item.name,
url: item.value ? `${baseUrl}/?${item.value}` : "",
url:
baseUrlPreview +
"/data/fishDraft/previewFile?filename=" +
item.name +
"&taskId=" +
taskId.value +
"&type=1",
});
});
} else if (type === "video") {
@ -971,9 +945,16 @@ const handlePreviewClick = (record: any, type: string, index: number) => {
const nameList = record.vdpthList;
nameList.forEach((item: any) => {
mixedList.push({
id: record.id,
type: "video",
name: item.name, //
url: item.value ? `${baseUrl}/?${item.value}` : "",
url:
baseUrlPreview +
"/data/fishDraft/previewFile?filename=" +
item.name +
"&taskId=" +
taskId.value +
"&type=2",
});
});
} else if (type === "formImage") {
@ -981,6 +962,7 @@ const handlePreviewClick = (record: any, type: string, index: number) => {
const nameList = JSON.parse(JSON.stringify(record)).picpthList;
nameList.forEach((item: any) => {
mixedList.push({
id: record.id,
type: "formImage",
name: item.name, //
url: item.url,
@ -991,6 +973,7 @@ const handlePreviewClick = (record: any, type: string, index: number) => {
const nameList = JSON.parse(JSON.stringify(record)).vdpthList;
nameList.forEach((item: any) => {
mixedList.push({
id: record.id,
type: "formVideo",
name: item.name, //
url: item.url,
@ -1011,7 +994,7 @@ const prevMedia = () => {
currentMediaIndex.value--;
}
};
//
const nextMedia = () => {
if (currentMediaIndex.value < previewList.value.length - 1) {
currentMediaIndex.value++;
@ -1024,7 +1007,23 @@ const handleDeleteMedia = (item: any, index: number) => {
title: "确认删除",
content: "确定要从预览列表中移除该项吗?",
zIndex: 2002,
onOk: () => {
onOk: async () => {
return new Promise(async (resolve, reject) => {
const params = {
id: item.id,
taskId: taskId.value,
type:
videoPreviewTitle.value == "图片预览"
? 1
: videoPreviewTitle.value == "视频预览"
? 2
: 0,
filename: item.name,
};
try {
let res: any = await deleteFile(params);
if (res && res?.code == 0) {
message.success("删除成功");
previewList.value.splice(index, 1);
if (videoPreviewTitle.value == "图片预览") {
if (previewList.value.length == 0) {
@ -1056,6 +1055,16 @@ const handleDeleteMedia = (item: any, index: number) => {
currentMediaIndex.value = Math.max(0, currentMediaIndex.value - 1);
}
}
resolve(true);
} else {
message.error("删除失败");
reject();
}
} catch (e) {
message.error("删除失败");
reject();
}
}).catch(() => console.log("Oops errors!"));
},
});
};
@ -1067,33 +1076,33 @@ const closeMediaPreview = () => {
currentMediaIndex.value = 0;
});
};
const tableContainerRef = ref<HTMLElement | null>(null)
const tableScrollY = ref<number | undefined>(undefined)
const tableContainerRef = ref<HTMLElement | null>(null);
const tableScrollY = ref<number | undefined>(undefined);
//
const calcTableScrollY = () => {
if (!tableContainerRef.value) return
if (!tableContainerRef.value) return;
//
const containerHeight = tableContainerRef.value.clientHeight
console.log(containerHeight)
const containerHeight = tableContainerRef.value.clientHeight;
console.log(containerHeight);
// 55px + 64px + 10px = 130px
//
const otherHeight = 112
const y = containerHeight - otherHeight
tableScrollY.value = y > 0 ? y : undefined
}
const otherHeight = 112;
const y = containerHeight - otherHeight;
tableScrollY.value = y > 0 ? y : undefined;
};
// resize
const observer = new ResizeObserver(() => {
calcTableScrollY()
})
calcTableScrollY();
});
// --- ---
onMounted(() => {
nextTick(() => {
calcTableScrollY()
calcTableScrollY();
if (tableContainerRef.value) {
observer.observe(tableContainerRef.value)
observer.observe(tableContainerRef.value);
}
})
});
getDictItemsByCode({ dictCode: "direction" }).then((res) => {
direction.value = res.data;
});