1027 lines
32 KiB
Vue
1027 lines
32 KiB
Vue
<template>
|
||
<div class="p-2">
|
||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||
<div v-show="showSearch" class="mb-[10px]">
|
||
<el-card shadow="hover">
|
||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||
<el-form-item label="塘口名称" prop="pondName">
|
||
<el-input v-model="queryParams.pondName" placeholder="请输入塘口名称" clearable @keyup.enter="handleQuery" />
|
||
</el-form-item>
|
||
<el-form-item label="用户信息" prop="params.userKeyword">
|
||
<el-input v-model="queryParams.params.userKeyword" placeholder="请输入用户名或手机号" clearable @keyup.enter="handleQuery" />
|
||
</el-form-item>
|
||
<el-form-item label="投苗日期" prop="time">
|
||
<el-date-picker
|
||
v-model="queryParams.time"
|
||
type="daterange"
|
||
range-separator="至"
|
||
start-placeholder="开始时间"
|
||
end-placeholder="结束时间"
|
||
/>
|
||
</el-form-item>
|
||
<!-- <el-form-item label="夜间防止误关" prop="keepNightOpen">
|
||
<el-input v-model="queryParams.keepNightOpen" placeholder="请输入夜间防止误关" clearable @keyup.enter="handleQuery" />
|
||
</el-form-item> -->
|
||
<el-form-item>
|
||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-card>
|
||
</div>
|
||
</transition>
|
||
|
||
<el-card shadow="never">
|
||
<template #header>
|
||
<el-row :gutter="10" class="mb8">
|
||
<el-col :span="1.5">
|
||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['fishery:pond:add']">新增</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['fishery:pond:edit']">修改</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['fishery:pond:remove']"
|
||
>删除</el-button
|
||
>
|
||
</el-col>
|
||
<!-- <el-col :span="1.5">
|
||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['fishery:pond:export']">导出</el-button>
|
||
</el-col> -->
|
||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||
</el-row>
|
||
</template>
|
||
|
||
<el-table v-loading="loading" border :data="pondList" @selection-change="handleSelectionChange">
|
||
<el-table-column type="selection" width="55" align="center" />
|
||
|
||
<el-table-column label="塘口名称" align="center" prop="pondName" />
|
||
<el-table-column label="用户名" align="center" prop="userName" />
|
||
<el-table-column label="手机号" align="center" prop="mobilePhone" />
|
||
<el-table-column label="鱼品种列表" align="center" prop="fishKindNames" />
|
||
<el-table-column label="面积(亩)" align="center" prop="area" />
|
||
<el-table-column label="密度(尾/亩)" align="center" prop="density" />
|
||
<el-table-column label="投苗日期" align="center" prop="placeTime" width="120">
|
||
<template #default="scope">
|
||
<span>{{ parseTime(scope.row.placeTime, '{y}-{m}-{d}') }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="夜间防止误关" align="center" prop="keepNightOpen">
|
||
<template #default="scope">
|
||
<dict-tag :options="open_close" :value="scope.row.keepNightOpen" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="创建时间" align="center" prop="createTime" width="160"/>
|
||
<el-table-column label="更新时间" align="center" prop="updateTime" width="160"/>
|
||
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
|
||
<template #default="scope">
|
||
<el-tooltip content="修改" placement="top">
|
||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['fishery:pond:edit']"></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="删除" placement="top">
|
||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['fishery:pond:remove']"></el-button>
|
||
</el-tooltip>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||
</el-card>
|
||
<!-- 添加或修改塘口对话框 -->
|
||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="550px" append-to-body>
|
||
<el-form ref="pondFormRef" :model="form" :rules="rules" label-width="110px">
|
||
<el-form-item label="用户" prop="userId">
|
||
<el-input
|
||
:value="selectedUser ? `${selectedUser.userName} (${selectedUser.mobilePhone})` : ''"
|
||
placeholder="请选择用户"
|
||
readonly
|
||
@click="openUserSelect"
|
||
style="cursor: pointer;"
|
||
>
|
||
<template #append>
|
||
<el-button icon="Search" @click="openUserSelect">选择</el-button>
|
||
</template>
|
||
</el-input>
|
||
</el-form-item>
|
||
<el-form-item label="塘口名称" prop="pondName">
|
||
<el-input v-model="form.pondName" placeholder="请输入塘口名称" />
|
||
</el-form-item>
|
||
<el-form-item label="鱼品种列表" prop="fishKindIds">
|
||
<div class="fish-selector">
|
||
<el-input
|
||
v-model="selectedFishNames"
|
||
placeholder="请选择鱼品种"
|
||
readonly
|
||
@click="openFishSelect"
|
||
style="cursor: pointer;"
|
||
>
|
||
<template #suffix>
|
||
<div class="input-suffix">
|
||
<span v-if="selectedFishList.length > 0" class="count-badge">
|
||
{{ selectedFishList.length }}
|
||
</span>
|
||
<el-icon class="select-icon"><ArrowDown /></el-icon>
|
||
</div>
|
||
</template>
|
||
</el-input>
|
||
|
||
<!-- 简洁的已选择项目展示 -->
|
||
<div v-if="selectedFishList.length > 0" class="selected-items">
|
||
<div class="selected-summary">
|
||
<span class="summary-text">已选择 {{ selectedFishList.length }} 个品种</span>
|
||
<el-button
|
||
type="text"
|
||
size="small"
|
||
@click="clearAllFish"
|
||
class="clear-btn"
|
||
>
|
||
清空
|
||
</el-button>
|
||
</div>
|
||
|
||
<div class="selected-list-compact">
|
||
<span
|
||
v-for="(fish, index) in selectedFishList.slice(0, 5)"
|
||
:key="fish.id"
|
||
class="fish-item"
|
||
>
|
||
{{ fish.fishName }}<span v-if="index < Math.min(4, selectedFishList.length - 1)">、</span>
|
||
</span>
|
||
<span v-if="selectedFishList.length > 5" class="more-indicator">
|
||
... 及另外{{ selectedFishList.length - 5 }}个品种
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="面积" prop="area">
|
||
<el-input v-model="form.area" placeholder="请输入面积">
|
||
<template #append>亩</template>
|
||
</el-input>
|
||
</el-form-item>
|
||
<el-form-item label="密度" prop="density">
|
||
<el-input v-model="form.density" placeholder="请输入密度">
|
||
<template #append>尾/亩</template>
|
||
</el-input>
|
||
</el-form-item>
|
||
<el-form-item label="投苗日期" prop="placeTime">
|
||
<el-date-picker clearable v-model="form.placeTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择投苗日期">
|
||
</el-date-picker>
|
||
</el-form-item>
|
||
<el-form-item label="夜间防止误关" prop="keepNightOpen">
|
||
<el-select v-model="form.keepNightOpen" placeholder="请选择夜间防止误关">
|
||
<el-option
|
||
v-for="dict in open_close"
|
||
:key="dict.value"
|
||
:label="dict.label"
|
||
:value="parseInt(dict.value)"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="备注" prop="remark">
|
||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||
<el-button @click="cancel">取 消</el-button>
|
||
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 鱼品种选择对话框 -->
|
||
<el-dialog title="选择鱼品种" v-model="fishSelectVisible" width="1200px" append-to-body>
|
||
<el-row :gutter="20">
|
||
<!-- 左侧:鱼类搜索和列表 -->
|
||
<el-col :span="16">
|
||
<!-- Tab页签 -->
|
||
<el-tabs v-model="activeTab" @tab-change="handleTabChange" class="mb-4">
|
||
<el-tab-pane label="全部" name="all"></el-tab-pane>
|
||
<el-tab-pane
|
||
v-for="dict in fish_type"
|
||
:key="dict.value"
|
||
:label="dict.label"
|
||
:name="dict.value"
|
||
></el-tab-pane>
|
||
</el-tabs>
|
||
|
||
<!-- 搜索条件 -->
|
||
<el-form :model="fishQueryParams" :inline="true" class="mb-4">
|
||
<el-form-item label="鱼类名称">
|
||
<el-input
|
||
v-model="fishQueryParams.fishName"
|
||
placeholder="请输入鱼类名称"
|
||
clearable
|
||
style="width: 200px"
|
||
@keyup.enter="handleFishQuery"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" icon="Search" @click="handleFishQuery">搜索</el-button>
|
||
<el-button icon="Refresh" @click="resetFishQuery">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<!-- 鱼品种表格 -->
|
||
<el-table
|
||
ref="fishTableRef"
|
||
:data="fishList"
|
||
@selection-change="handleSelectAllFish"
|
||
height="350px"
|
||
border
|
||
v-loading="loading"
|
||
>
|
||
<el-table-column type="selection" width="55" align="center" />
|
||
<el-table-column label="鱼类名称" align="center" prop="fishName" />
|
||
<el-table-column label="鱼类类型" align="center" prop="fishType" width="100">
|
||
<template #default="scope">
|
||
<dict-tag :options="fish_type" :value="scope.row.fishType" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" align="center" width="80">
|
||
<template #default="scope">
|
||
<el-button
|
||
type="primary"
|
||
size="small"
|
||
@click="quickAddFish(scope.row)"
|
||
:disabled="selectedFishList.some(item => item.id === scope.row.id)"
|
||
>
|
||
添加
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<!-- 数据统计 -->
|
||
<div class="mt-2 text-sm text-gray-500">
|
||
当前显示 {{ fishList.length }} 条数据
|
||
</div>
|
||
</el-col>
|
||
|
||
<!-- 右侧:已选择的鱼类 -->
|
||
<el-col :span="8">
|
||
<div class="selected-fish-panel">
|
||
<div class="panel-header">
|
||
<h4>已选择的鱼类 ({{ selectedFishList.length }})</h4>
|
||
<el-button
|
||
type="danger"
|
||
size="small"
|
||
@click="clearAllFish"
|
||
v-show="selectedFishList.length > 0"
|
||
>
|
||
清空
|
||
</el-button>
|
||
</div>
|
||
<div class="selected-list">
|
||
<div
|
||
v-for="fish in selectedFishList"
|
||
:key="fish.id"
|
||
class="selected-item"
|
||
>
|
||
<span class="fish-name">{{ fish.fishName }}</span>
|
||
<dict-tag :options="fish_type" :value="fish.fishType" size="small" class="ml-2" />
|
||
<el-button
|
||
type="danger"
|
||
size="small"
|
||
icon="Close"
|
||
circle
|
||
@click="removeFish(fish.id)"
|
||
class="ml-2"
|
||
/>
|
||
</div>
|
||
<el-empty
|
||
v-if="selectedFishList.length === 0"
|
||
description="请选择鱼类"
|
||
:image-size="100"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<span class="selected-count">已选择 {{ selectedFishList.length }} 个鱼类</span>
|
||
<el-button type="primary" @click="confirmFishSelect" :disabled="selectedFishList.length === 0">确 定</el-button>
|
||
<el-button @click="cancelFishSelect">取 消</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 用户选择对话框 -->
|
||
<el-dialog title="选择用户" v-model="userSelectVisible" width="900px" append-to-body>
|
||
<!-- 搜索条件 -->
|
||
<el-form :model="userQueryParams" :inline="true" class="mb-4">
|
||
<el-form-item label="用户名">
|
||
<el-input
|
||
v-model="userQueryParams.userName"
|
||
placeholder="请输入用户名"
|
||
clearable
|
||
style="width: 180px"
|
||
@keyup.enter="handleUserQuery"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="手机号">
|
||
<el-input
|
||
v-model="userQueryParams.mobilePhone"
|
||
placeholder="请输入手机号"
|
||
clearable
|
||
style="width: 180px"
|
||
@keyup.enter="handleUserQuery"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" icon="Search" @click="handleUserQuery">搜索</el-button>
|
||
<el-button icon="Refresh" @click="resetUserQuery">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<!-- 用户表格 -->
|
||
<el-table
|
||
:data="aquUserList"
|
||
highlight-current-row
|
||
height="400px"
|
||
border
|
||
>
|
||
<el-table-column label="用户名" align="center" prop="userName" />
|
||
<el-table-column label="手机号" align="center" prop="mobilePhone" />
|
||
<el-table-column label="省份" align="center" prop="province" />
|
||
<el-table-column label="城市" align="center" prop="city" />
|
||
<el-table-column label="区县" align="center" prop="district" />
|
||
<el-table-column label="操作" align="center" width="100">
|
||
<template #default="scope">
|
||
<el-button
|
||
type="primary"
|
||
size="small"
|
||
@click="handleUserSelect(scope.row)"
|
||
>
|
||
选择
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<!-- 分页 -->
|
||
<pagination
|
||
v-show="userTotal > 0"
|
||
:total="userTotal"
|
||
v-model:page="userQueryParams.pageNum"
|
||
v-model:limit="userQueryParams.pageSize"
|
||
@pagination="handleUserPaginationChange"
|
||
class="mt-4"
|
||
/>
|
||
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="cancelUserSelect">取 消</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup name="Pond" lang="ts">
|
||
import { listPond, getPond, delPond, addPond, updatePond } from '@/api/fishery/pond';
|
||
import { listFish, getFish } from '@/api/fishery/fish';
|
||
import { listAquUser } from '@/api/fishery/aquUser';
|
||
import { PondVO, PondQuery, PondForm } from '@/api/fishery/pond/types';
|
||
import { FishVO } from '@/api/fishery/fish/types';
|
||
import { AquUserVO } from '@/api/fishery/aquUser/types';
|
||
import dayjs from 'dayjs';
|
||
|
||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||
const { fish_type,open_close } = toRefs<any>(proxy?.useDict('fish_type','open_close'));
|
||
|
||
const pondList = ref<PondVO[]>([]);
|
||
const fishList = ref<FishVO[]>([]);
|
||
const selectedFishList = ref<FishVO[]>([]);
|
||
const originalSelectedFishList = ref<FishVO[]>([]); // 备份的选择列表,用于取消操作
|
||
const fishSelectVisible = ref(false);
|
||
|
||
// 用户选择相关
|
||
const aquUserList = ref<AquUserVO[]>([]);
|
||
const selectedUser = ref<AquUserVO | null>(null);
|
||
const userSelectVisible = ref(false);
|
||
const userQueryParams = reactive<{
|
||
pageNum: number;
|
||
pageSize: number;
|
||
userName?: string;
|
||
mobilePhone?: string;
|
||
}>({
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
userName: undefined,
|
||
mobilePhone: undefined
|
||
});
|
||
const userTotal = ref(0);
|
||
const fishQueryParams = reactive<{
|
||
fishType?: number;
|
||
fishName?: string;
|
||
}>({
|
||
fishType: undefined,
|
||
fishName: undefined
|
||
});
|
||
const fishTotal = ref(0); // 鱼类数据总数
|
||
const activeTab = ref<string>('all'); // 当前激活的tab
|
||
const buttonLoading = ref(false);
|
||
const loading = ref(true);
|
||
const showSearch = ref(true);
|
||
const ids = ref<Array<string | number>>([]);
|
||
const single = ref(true);
|
||
const multiple = ref(true);
|
||
const total = ref(0);
|
||
|
||
const queryFormRef = ref<ElFormInstance>();
|
||
const pondFormRef = ref<ElFormInstance>();
|
||
const fishTableRef = ref<ElTableInstance>();
|
||
|
||
const dialog = reactive<DialogOption>({
|
||
visible: false,
|
||
title: ''
|
||
});
|
||
|
||
const initFormData: PondForm = {
|
||
id: undefined,
|
||
userId: undefined,
|
||
pondName: undefined,
|
||
fishKindIds: undefined,
|
||
area: undefined,
|
||
density: undefined,
|
||
placeTime: undefined,
|
||
keepNightOpen: undefined,
|
||
remark: undefined
|
||
};
|
||
const data = reactive<PageData<PondForm, PondQuery>>({
|
||
form: { ...initFormData },
|
||
queryParams: {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
time: '',
|
||
userId: undefined,
|
||
pondName: undefined,
|
||
fishKindIds: undefined,
|
||
placeTime: undefined,
|
||
keepNightOpen: undefined,
|
||
params: {}
|
||
},
|
||
rules: {
|
||
id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }],
|
||
userId: [{ required: true, message: '用户id不能为空', trigger: 'blur' }],
|
||
pondName: [{ required: true, message: '塘口名称不能为空', trigger: 'blur' }],
|
||
fishKindIds: [{ required: true, message: '鱼品种列表不能为空', trigger: 'blur' }],
|
||
area: [{ required: true, message: '面积不能为空', trigger: 'blur' }],
|
||
density: [{ required: true, message: '密度不能为空', trigger: 'blur' }],
|
||
keepNightOpen: [{ required: true, message: '夜间防止误关不能为空', trigger: 'change' }]
|
||
}
|
||
});
|
||
|
||
const { queryParams, form, rules } = toRefs(data);
|
||
|
||
// 鱼品种相关状态
|
||
const selectedFishNames = computed(() => {
|
||
const count = selectedFishList.value.length;
|
||
if (count === 0) return '';
|
||
// 简洁显示,不显示具体名称
|
||
return `已选择 ${count} 个鱼类品种`;
|
||
});
|
||
|
||
// 获取完整的鱼类名称列表(用于tooltip显示)
|
||
const fullFishNames = computed(() => {
|
||
return selectedFishList.value.map(fish => fish.fishName).join('、');
|
||
});
|
||
const parseFishIds = (fishKindIds: any): string[] => {
|
||
if (!fishKindIds) return [];
|
||
|
||
try {
|
||
// 解析JSON数组格式:"[29,31,34,4,2]"
|
||
const parsed = JSON.parse(fishKindIds.toString());
|
||
if (Array.isArray(parsed)) {
|
||
return parsed.map(id => id.toString());
|
||
}
|
||
} catch (error) {
|
||
console.warn('fishKindIds格式错误:', fishKindIds);
|
||
}
|
||
|
||
return [];
|
||
};
|
||
|
||
// 获取当前页面中已选中的项目
|
||
const getCurrentPageSelection = () => {
|
||
return fishList.value.filter(fish =>
|
||
selectedFishList.value.some(selected => selected.id === fish.id)
|
||
);
|
||
};
|
||
|
||
/** 根据ID列表获取鱼类信息 */
|
||
const getFishByIds = async (fishIds: string[]) => {
|
||
if (fishIds.length === 0) return [];
|
||
|
||
try {
|
||
// 方案1:如果鱼类数量不多,使用并发单个查询
|
||
if (fishIds.length <= 20) {
|
||
const promises = fishIds.map(id => getFish(id).catch(() => null));
|
||
const results = await Promise.all(promises);
|
||
return results.filter(res => res !== null).map(res => res.data);
|
||
}
|
||
|
||
// 方案2:鱼类数量较多时,使用分批查询
|
||
const allFishList: FishVO[] = [];
|
||
let pageNum = 1;
|
||
let hasMore = true;
|
||
const foundIds = new Set<string>();
|
||
|
||
// 分批获取所有鱼类数据,直到找齐所需的鱼类
|
||
while (hasMore && foundIds.size < fishIds.length) {
|
||
const res = await listFish({
|
||
pageNum: pageNum,
|
||
pageSize: 100, // 每次获取100条
|
||
fishType: undefined,
|
||
fishName: undefined
|
||
});
|
||
|
||
const fishData = res.rows || res.data || [];
|
||
|
||
// 筛选出需要的鱼类
|
||
const neededFish = fishData.filter(fish => {
|
||
const fishIdStr = fish.id.toString();
|
||
if (fishIds.includes(fishIdStr) && !foundIds.has(fishIdStr)) {
|
||
foundIds.add(fishIdStr);
|
||
return true;
|
||
}
|
||
return false;
|
||
});
|
||
|
||
allFishList.push(...neededFish);
|
||
|
||
// 判断是否还需要继续获取
|
||
hasMore = fishData.length === 100 && foundIds.size < fishIds.length;
|
||
pageNum++;
|
||
|
||
// 安全限制,避免无限循环
|
||
if (pageNum > 50) break;
|
||
}
|
||
|
||
return allFishList;
|
||
} catch (error) {
|
||
console.error('获取鱼类信息失败:', error);
|
||
return [];
|
||
}
|
||
};
|
||
const getFishList = async () => {
|
||
const res = await listFish({
|
||
pageNum: 1,
|
||
pageSize: 1000, // 获取所有数据
|
||
fishType: fishQueryParams.fishType,
|
||
fishName: fishQueryParams.fishName
|
||
});
|
||
fishList.value = res.rows || res.data || [];
|
||
fishTotal.value = res.total || 0;
|
||
|
||
// 数据加载完成后,设置当前页面的选中状态
|
||
await nextTick();
|
||
const currentPageSelection = getCurrentPageSelection();
|
||
if (fishTableRef.value && currentPageSelection.length > 0) {
|
||
// 清空当前选中状态
|
||
fishTableRef.value.clearSelection();
|
||
// 设置已选择的项目
|
||
currentPageSelection.forEach(fish => {
|
||
fishTableRef.value!.toggleRowSelection(fish, true);
|
||
});
|
||
}
|
||
};
|
||
|
||
/** 鱼类分页改变 */
|
||
const handleFishPaginationChange = () => {
|
||
getFishList();
|
||
};
|
||
|
||
/** 搜索鱼品种 */
|
||
const handleFishQuery = () => {
|
||
getFishList();
|
||
};
|
||
|
||
/** 重置鱼品种搜索 */
|
||
const resetFishQuery = () => {
|
||
fishQueryParams.fishType = undefined;
|
||
fishQueryParams.fishName = undefined;
|
||
activeTab.value = 'all';
|
||
getFishList();
|
||
};
|
||
|
||
/** 切换tab */
|
||
const handleTabChange = (tabName: string) => {
|
||
activeTab.value = tabName;
|
||
if (tabName === 'all') {
|
||
fishQueryParams.fishType = undefined;
|
||
} else {
|
||
fishQueryParams.fishType = parseInt(tabName);
|
||
}
|
||
fishQueryParams.fishName = undefined; // 切换tab时清空名称搜索
|
||
getFishList();
|
||
};
|
||
|
||
/** 全选/反选鱼类 */
|
||
const handleSelectAllFish = (selection: FishVO[]) => {
|
||
// 先移除当前页面中的所有项目
|
||
const currentPageIds = fishList.value.map(fish => fish.id);
|
||
selectedFishList.value = selectedFishList.value.filter(fish => !currentPageIds.includes(fish.id));
|
||
|
||
// 再添加当前页面选中的项目
|
||
selectedFishList.value.push(...selection);
|
||
};
|
||
|
||
/** 快速添加单个鱼类 */
|
||
const quickAddFish = (fish: FishVO) => {
|
||
if (!selectedFishList.value.find(item => item.id === fish.id)) {
|
||
selectedFishList.value.push(fish);
|
||
}
|
||
};
|
||
|
||
/** 移除已选择的鱼类 */
|
||
const removeFish = (fishId: string | number) => {
|
||
selectedFishList.value = selectedFishList.value.filter(fish => fish.id !== fishId);
|
||
};
|
||
|
||
/** 清空所有已选择的鱼类 */
|
||
const clearAllFish = async () => {
|
||
selectedFishList.value = [];
|
||
// 清空表格选中状态
|
||
if (fishTableRef.value) {
|
||
fishTableRef.value.clearSelection();
|
||
}
|
||
};
|
||
|
||
/** 打开鱼品种选择对话框 */
|
||
const openFishSelect = () => {
|
||
// 备份当前选择状态,用于取消操作
|
||
originalSelectedFishList.value = [...selectedFishList.value];
|
||
|
||
// 仅重置搜索条件,不清空已选择的鱼类
|
||
fishQueryParams.fishType = undefined;
|
||
fishQueryParams.fishName = undefined;
|
||
activeTab.value = 'all';
|
||
getFishList();
|
||
fishSelectVisible.value = true;
|
||
};
|
||
|
||
/** 确认选择鱼品种 */
|
||
const confirmFishSelect = () => {
|
||
// 统一保存为JSON数组格式,新增和编辑都使用此格式
|
||
const fishIds = selectedFishList.value.map(fish => fish.id);
|
||
form.value.fishKindIds = JSON.stringify(fishIds);
|
||
fishSelectVisible.value = false;
|
||
};
|
||
|
||
/** 取消选择鱼品种 */
|
||
const cancelFishSelect = () => {
|
||
// 取消时恢复到原有选择状态
|
||
selectedFishList.value = [...originalSelectedFishList.value];
|
||
fishSelectVisible.value = false;
|
||
};
|
||
|
||
/** 获取用户列表 */
|
||
const getAquUserList = async () => {
|
||
const res = await listAquUser(userQueryParams);
|
||
aquUserList.value = res.rows || res.data || [];
|
||
userTotal.value = res.total || 0;
|
||
};
|
||
|
||
/** 打开用户选择对话框 */
|
||
const openUserSelect = () => {
|
||
// 重置搜索条件
|
||
userQueryParams.pageNum = 1;
|
||
userQueryParams.userName = undefined;
|
||
userQueryParams.mobilePhone = undefined;
|
||
getAquUserList();
|
||
userSelectVisible.value = true;
|
||
};
|
||
|
||
/** 搜索用户 */
|
||
const handleUserQuery = () => {
|
||
userQueryParams.pageNum = 1;
|
||
getAquUserList();
|
||
};
|
||
|
||
/** 重置用户搜索 */
|
||
const resetUserQuery = () => {
|
||
userQueryParams.pageNum = 1;
|
||
userQueryParams.userName = undefined;
|
||
userQueryParams.mobilePhone = undefined;
|
||
getAquUserList();
|
||
};
|
||
|
||
/** 用户分页改变 */
|
||
const handleUserPaginationChange = () => {
|
||
getAquUserList();
|
||
};
|
||
|
||
/** 选择用户 */
|
||
const handleUserSelect = (user: AquUserVO) => {
|
||
selectedUser.value = user;
|
||
form.value.userId = user.id;
|
||
userSelectVisible.value = false;
|
||
};
|
||
|
||
/** 取消选择用户 */
|
||
const cancelUserSelect = () => {
|
||
userSelectVisible.value = false;
|
||
};
|
||
|
||
/** 查询塘口列表 */
|
||
const getList = async () => {
|
||
loading.value = true;
|
||
const timeRange = queryParams.value.time;
|
||
let st = '';
|
||
let et = '';
|
||
if (timeRange && timeRange.length === 2) {
|
||
st = dayjs(timeRange[0]).format('YYYY-MM-DD');
|
||
et = dayjs(timeRange[1]).format('YYYY-MM-DD');
|
||
}
|
||
queryParams.value.params.startTime = st;
|
||
queryParams.value.params.endTime = et;
|
||
const res = await listPond(queryParams.value);
|
||
pondList.value = res.rows;
|
||
total.value = res.total;
|
||
loading.value = false;
|
||
};
|
||
|
||
/** 取消按钮 */
|
||
const cancel = () => {
|
||
reset();
|
||
dialog.visible = false;
|
||
};
|
||
|
||
/** 表单重置 */
|
||
const reset = () => {
|
||
form.value = { ...initFormData };
|
||
// 注释掉清空选择列表,保持弹窗独立性
|
||
// selectedFishList.value = [];
|
||
pondFormRef.value?.resetFields();
|
||
};
|
||
|
||
/** 搜索按钮操作 */
|
||
const handleQuery = () => {
|
||
queryParams.value.pageNum = 1;
|
||
getList();
|
||
};
|
||
|
||
/** 重置按钮操作 */
|
||
const resetQuery = () => {
|
||
queryFormRef.value?.resetFields();
|
||
handleQuery();
|
||
};
|
||
|
||
/** 多选框选中数据 */
|
||
const handleSelectionChange = (selection: PondVO[]) => {
|
||
ids.value = selection.map((item) => item.id);
|
||
single.value = selection.length != 1;
|
||
multiple.value = !selection.length;
|
||
};
|
||
|
||
/** 新增按钮操作 */
|
||
const handleAdd = () => {
|
||
reset();
|
||
selectedFishList.value = []; // 新增时清空选择列表
|
||
selectedUser.value = null; // 清空用户选择
|
||
dialog.visible = true;
|
||
dialog.title = '添加塘口';
|
||
};
|
||
|
||
/** 修改按钮操作 */
|
||
const handleUpdate = async (row?: PondVO) => {
|
||
reset();
|
||
const _id = row?.id || ids.value[0];
|
||
const res = await getPond(_id);
|
||
Object.assign(form.value, res.data);
|
||
|
||
// 解析JSON数组格式的鱼类ID
|
||
if (form.value.fishKindIds) {
|
||
const fishIds = parseFishIds(form.value.fishKindIds);
|
||
selectedFishList.value = await getFishByIds(fishIds);
|
||
} else {
|
||
selectedFishList.value = [];
|
||
}
|
||
|
||
// 回显用户信息 - 优化加载逻辑
|
||
if (form.value.userId) {
|
||
try {
|
||
// 先加载用户列表(使用较大的pageSize确保包含目标用户)
|
||
const userRes = await listAquUser({
|
||
pageNum: 1,
|
||
pageSize: 1000
|
||
});
|
||
const userList = userRes.rows || userRes.data || [];
|
||
const user = userList.find(u => u.id.toString() === form.value.userId.toString());
|
||
if (user) {
|
||
selectedUser.value = user;
|
||
} else {
|
||
console.warn('未找到用户ID:', form.value.userId);
|
||
selectedUser.value = null;
|
||
}
|
||
} catch (error) {
|
||
console.error('加载用户信息失败:', error);
|
||
selectedUser.value = null;
|
||
}
|
||
} else {
|
||
selectedUser.value = null;
|
||
}
|
||
|
||
dialog.visible = true;
|
||
dialog.title = '修改塘口';
|
||
};
|
||
|
||
/** 提交按钮 */
|
||
const submitForm = () => {
|
||
pondFormRef.value?.validate(async (valid: boolean) => {
|
||
if (valid) {
|
||
buttonLoading.value = true;
|
||
if (form.value.id) {
|
||
await updatePond(form.value).finally(() => (buttonLoading.value = false));
|
||
} else {
|
||
await addPond(form.value).finally(() => (buttonLoading.value = false));
|
||
}
|
||
proxy?.$modal.msgSuccess('操作成功');
|
||
dialog.visible = false;
|
||
await getList();
|
||
}
|
||
});
|
||
};
|
||
|
||
/** 删除按钮操作 */
|
||
const handleDelete = async (row?: PondVO) => {
|
||
const _ids = row?.id || ids.value;
|
||
await proxy?.$modal.confirm('是否确认删除塘口编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
|
||
await delPond(_ids);
|
||
proxy?.$modal.msgSuccess('删除成功');
|
||
await getList();
|
||
};
|
||
|
||
/** 导出按钮操作 */
|
||
const handleExport = () => {
|
||
proxy?.download(
|
||
'fishery/pond/export',
|
||
{
|
||
...queryParams.value
|
||
},
|
||
`pond_${new Date().getTime()}.xlsx`
|
||
);
|
||
};
|
||
|
||
onMounted(() => {
|
||
getList();
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.selected-fish-panel {
|
||
border: 1px solid #dcdfe6;
|
||
border-radius: 4px;
|
||
height: 450px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.panel-header {
|
||
padding: 12px 16px;
|
||
border-bottom: 1px solid #ebeef5;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
background-color: #f5f7fa;
|
||
}
|
||
|
||
.panel-header h4 {
|
||
margin: 0;
|
||
font-size: 14px;
|
||
color: #303133;
|
||
}
|
||
|
||
.selected-list {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 8px;
|
||
}
|
||
|
||
.selected-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 8px 12px;
|
||
margin-bottom: 6px;
|
||
background-color: #f0f9ff;
|
||
border: 1px solid #b3d8ff;
|
||
border-radius: 4px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.fish-name {
|
||
flex: 1;
|
||
color: #303133;
|
||
}
|
||
|
||
.selected-count {
|
||
color: #606266;
|
||
margin-right: auto;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.dialog-footer {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
/* 鱼品种选择器样式 */
|
||
.fish-selector {
|
||
width: 100%;
|
||
}
|
||
|
||
.input-suffix {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding-right: 8px;
|
||
}
|
||
|
||
.count-badge {
|
||
background: #409eff;
|
||
color: white;
|
||
border-radius: 10px;
|
||
padding: 2px 8px;
|
||
font-size: 12px;
|
||
min-width: 16px;
|
||
text-align: center;
|
||
}
|
||
|
||
.select-icon {
|
||
color: #c0c4cc;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.selected-items {
|
||
margin-top: 8px;
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 4px;
|
||
background: #f9f9f9;
|
||
}
|
||
|
||
.selected-summary {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid #ebeef5;
|
||
background: #f5f7fa;
|
||
}
|
||
|
||
.summary-text {
|
||
font-size: 13px;
|
||
color: #606266;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.clear-btn {
|
||
color: #f56c6c;
|
||
padding: 0;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.clear-btn:hover {
|
||
color: #f78989;
|
||
}
|
||
|
||
.selected-list-compact {
|
||
padding: 10px 12px;
|
||
line-height: 1.5;
|
||
color: #303133;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.fish-item {
|
||
color: #409eff;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.more-indicator {
|
||
color: #909399;
|
||
font-style: italic;
|
||
}
|
||
|
||
.selected-fish-tags {
|
||
max-height: 100px;
|
||
overflow-y: auto;
|
||
padding: 8px;
|
||
border: 1px solid #dcdfe6;
|
||
border-radius: 4px;
|
||
background-color: #f5f7fa;
|
||
}
|
||
|
||
.selected-fish-tags .el-tag {
|
||
margin-right: 6px;
|
||
margin-bottom: 4px;
|
||
}
|
||
</style>
|