修改鱼种类,修改导入鱼种类报错
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
parent
0f0199fd41
commit
4f34ac0d65
@ -16,6 +16,14 @@ export function getEngInfoDropdown(data:any) {
|
||||
data
|
||||
});
|
||||
}
|
||||
// 更新导入任务
|
||||
export function updateImportTask(data:any) {
|
||||
return request({
|
||||
url: '/data/importTask/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
//过鱼设施下拉列表
|
||||
export function getFpssDropdown(params:any) {
|
||||
return request({
|
||||
@ -31,6 +39,14 @@ export function getFishDictoryDropdown() {
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
// 类似鱼类名称下拉列表
|
||||
export function getSimilarFishDictoryDropdown(params:any) {
|
||||
return request({
|
||||
url: '/env/fishDictory/similar',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
// 上传文件
|
||||
export function uploadFile(data:any) {
|
||||
return request({
|
||||
|
||||
@ -83,6 +83,7 @@
|
||||
:loading="shuJuTianBaoStore.baseLoading"
|
||||
:filter-option="filterOption"
|
||||
style="width: 135px"
|
||||
|
||||
>
|
||||
<a-select-option
|
||||
v-for="opt in shuJuTianBaoStore.baseOption"
|
||||
@ -116,6 +117,9 @@
|
||||
</div>
|
||||
|
||||
<!-- 下拉选择 -->
|
||||
<!-- <div v-else-if="item.type === 'Select'">
|
||||
<div v-for="i in item.options"> {{ i[item.values?.name] }} {{ i[item.values?.value] }}</div>
|
||||
</div> -->
|
||||
<a-select
|
||||
v-else-if="item.type === 'Select'"
|
||||
v-model:value="formData[item.name]"
|
||||
|
||||
@ -1,128 +1,214 @@
|
||||
<template>
|
||||
<a-select
|
||||
ref="selectRef"
|
||||
:style="{ width: width }"
|
||||
:value="modelValue"
|
||||
:options="options"
|
||||
:loading="loading"
|
||||
@change="handleChange"
|
||||
@search="handleSearch"
|
||||
placeholder="鱼种类支持俗名查询"
|
||||
:mode="multiple ? 'multiple' : undefined"
|
||||
show-search
|
||||
:filter-option="false"
|
||||
class="custom-fish-select"
|
||||
:dropdownMatchSelectWidth="false"
|
||||
@dropdownVisibleChange="handleDropdownVisibleChange"
|
||||
:max-tag-count="multiple ? 1 : undefined"
|
||||
:open="open"
|
||||
@update:open="open = $event"
|
||||
:field-names="{ label: 'name', value: 'id' }"
|
||||
<div
|
||||
class="custom-fish-search-container"
|
||||
ref="containerRef"
|
||||
@mouseenter="isHovered = true"
|
||||
@mouseleave="isHovered = false"
|
||||
>
|
||||
<!-- 自定义 Tag 显示名称 (仅在多选时生效) -->
|
||||
<template #tagRender="{ value: tagId, onClose }" v-if="multiple">
|
||||
<a-tag closable @close="onClose" style="margin-right: 3px; max-width: 120px">
|
||||
{{ getFishNameById(tagId) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<!-- 1. 输入框区域 -->
|
||||
<div
|
||||
class="fish-input-wrapper"
|
||||
:class="{ 'is-focused': isOpen }"
|
||||
@click="handleWrapperClick"
|
||||
>
|
||||
<!--
|
||||
核心逻辑:
|
||||
- 当 isOpen 为 true 时,强制显示 input,并聚焦。
|
||||
- 当 isOpen 为 false 时,显示 selectedItem 或 placeholder。
|
||||
-->
|
||||
|
||||
<template #dropdownRender>
|
||||
<div class="custom-dropdown-container">
|
||||
<div class="w-[340px] h-[30px] flex items-center pl-[10px]" @click.stop @mousedown.prevent>
|
||||
<div>查询方式:</div>
|
||||
<div
|
||||
class="text-[12px] font-bold mr-2 cursor-pointer"
|
||||
:class="{ 'text-[#005293]': !isIntelligentQuery }"
|
||||
@click="IntelligentQueryCLick(false)"
|
||||
>
|
||||
相似度
|
||||
</div>
|
||||
<div
|
||||
class="text-[12px] font-bold cursor-pointer"
|
||||
:class="{ 'text-[#005293]': isIntelligentQuery }"
|
||||
@click="IntelligentQueryCLick(true)"
|
||||
>
|
||||
智能查询
|
||||
</div>
|
||||
</div>
|
||||
<!-- 左侧:可滚动的选项列表 -->
|
||||
<div class="dropdown-left-list">
|
||||
<div
|
||||
v-for="opt in filteredOptions"
|
||||
:key="opt.id"
|
||||
class="dropdown-item"
|
||||
:class="{
|
||||
'is-active': isSelected(opt.id),
|
||||
'is-hovered': opt.id === hoveredId,
|
||||
}"
|
||||
@click.stop="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>
|
||||
<!-- 情况 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="请输入搜索..."
|
||||
/>
|
||||
|
||||
<!-- 中间分割线 -->
|
||||
<div class="dropdown-divider"></div>
|
||||
<!-- 情况 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="dropdown-right-detail" @click.stop @mousedown.prevent>
|
||||
<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 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 v-else class="detail-placeholder">请选择或悬停查看</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-select>
|
||||
</transition>
|
||||
</teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, computed, watch } from "vue";
|
||||
import { getFishDictoryDropdown } from "@/api/select";
|
||||
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: string | string[]; // 支持字符串(单选)或数组(多选)
|
||||
modelValue: any; // 单选模式
|
||||
width?: string;
|
||||
multiple?: boolean; // 控制是否多选
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
multiple: false, // 默认单选,根据需求调整
|
||||
placeholder: "鱼种类支持俗名查询",
|
||||
width: "100%",
|
||||
disabled: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "update:modelValue", value: string | string[], opt: any): void;
|
||||
(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 open = ref(false); // 控制下拉框显隐
|
||||
const isIntelligentQuery = ref(true); // 控制是否为智能查询
|
||||
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 (!searchKeyword.value) {
|
||||
if (isIntelligentQuery.value) {
|
||||
return options.value;
|
||||
}
|
||||
|
||||
// 如果是相似度模式,进行本地前端过滤
|
||||
if (!searchKeyword.value) {
|
||||
return allOptions.value; // 无搜索词时显示全量
|
||||
}
|
||||
const lowerKeyword = searchKeyword.value.toLowerCase();
|
||||
return options.value.filter((item: any) => {
|
||||
return allOptions.value.filter((item: any) => {
|
||||
const nameMatch = item.name?.toLowerCase().includes(lowerKeyword);
|
||||
const aliasMatch = item.alias?.toLowerCase().includes(lowerKeyword);
|
||||
return nameMatch || aliasMatch;
|
||||
@ -131,153 +217,370 @@ const filteredOptions = computed(() => {
|
||||
|
||||
const currentDetailData = computed(() => {
|
||||
if (hoveredId.value) {
|
||||
return options.value.find((item: any) => item.id === 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) => {
|
||||
if (props.multiple) {
|
||||
return Array.isArray(props.modelValue) && props.modelValue.includes(id);
|
||||
return props.modelValue === id;
|
||||
};
|
||||
|
||||
const toggleDropdown = () => {
|
||||
if (props.disabled) return;
|
||||
|
||||
if (isOpen.value) {
|
||||
closeDropdown();
|
||||
} else {
|
||||
return props.modelValue === id;
|
||||
openDropdown();
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = (value: string) => {
|
||||
searchKeyword.value = value;
|
||||
const openDropdown = async () => {
|
||||
isOpen.value = true;
|
||||
await nextTick();
|
||||
inputRef.value?.focus();
|
||||
};
|
||||
|
||||
const closeDropdown = () => {
|
||||
isOpen.value = false;
|
||||
hoveredId.value = null;
|
||||
};
|
||||
|
||||
const handleDropdownVisibleChange = (val: boolean) => {
|
||||
open.value = val;
|
||||
if (!val) {
|
||||
hoveredId.value = null;
|
||||
searchKeyword.value = "";
|
||||
// 新增:清空逻辑
|
||||
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 handleSelectOption = (opt: any) => {
|
||||
console.log(props.modelValue)
|
||||
console.log(props.modelValue)
|
||||
if (props.multiple) {
|
||||
// --- 多选逻辑 ---
|
||||
let newValues: string[] = Array.isArray(props.modelValue)
|
||||
? [...props.modelValue]
|
||||
: [];
|
||||
const index = newValues.indexOf(opt.id);
|
||||
|
||||
if (index > -1) {
|
||||
newValues.splice(index, 1); // 取消选中
|
||||
} else {
|
||||
newValues.push(opt.id); // 选中
|
||||
}
|
||||
emit("update:modelValue", newValues, opt);
|
||||
} else {
|
||||
// --- 单选逻辑 ---
|
||||
// 关键:单选模式下,直接发射当前 ID,覆盖旧值
|
||||
// 如果点击的是已选中的项,则清空(可选行为,视需求而定)
|
||||
if (props.modelValue === opt.id) {
|
||||
emit("update:modelValue", "", opt); // 取消选中
|
||||
} else {
|
||||
emit("update:modelValue", opt.id, opt); // 选中新项
|
||||
// 单选模式下,选择后通常希望关闭下拉框
|
||||
open.value = false;
|
||||
}
|
||||
}
|
||||
const handleInputBlur = () => {
|
||||
setTimeout(() => {
|
||||
// 依赖 handleClickOutside 进行最终判断,这里不做强制关闭
|
||||
}, 100);
|
||||
};
|
||||
const IntelligentQueryCLick = (val: boolean) => {
|
||||
isIntelligentQuery.value = val;
|
||||
if (val) {
|
||||
message.success("智能查询已开启");
|
||||
} else {
|
||||
message.success("智能查询已关闭");
|
||||
|
||||
const handleSearchInput = () => {
|
||||
hoveredId.value = null;
|
||||
if (!isOpen.value) {
|
||||
isOpen.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (val: any) => {
|
||||
// 当 a-select 内部触发 change 时(例如删除 Tag)
|
||||
// 在单选模式下,如果用户通过键盘或删除操作改变了值,这里也会捕获
|
||||
emit("update:modelValue", val, "");
|
||||
};
|
||||
// 修改:执行实际的搜索请求
|
||||
const executeSearch = () => {
|
||||
const keyword = searchKeyword.value;
|
||||
|
||||
const getFishNameById = (id: string) => {
|
||||
if (!id) return "";
|
||||
const fish = options.value.find((item: any) => item.id === id);
|
||||
return fish ? fish.name : id;
|
||||
};
|
||||
|
||||
// 监听 multiple 变化,确保模式切换时状态正确
|
||||
watch(
|
||||
() => props.multiple,
|
||||
(newVal) => {
|
||||
// 如果从多选变为单选,且 modelValue 是数组,取第一个值或清空
|
||||
if (!newVal && Array.isArray(props.modelValue)) {
|
||||
emit("update:modelValue", props.modelValue || null, options.value);
|
||||
}
|
||||
// 如果从单选变为多选,且 modelValue 是字符串,转为数组
|
||||
if (newVal && typeof props.modelValue === "string") {
|
||||
emit("update:modelValue", props.modelValue ? [props.modelValue] : [], {});
|
||||
}
|
||||
// 确保下拉框是打开的
|
||||
if (!isOpen.value) {
|
||||
isOpen.value = true;
|
||||
}
|
||||
);
|
||||
const init = () => {
|
||||
|
||||
// 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.length === 0) {
|
||||
if (data && data.length > 0) {
|
||||
allOptions.value = data;
|
||||
options.value = data; // 初始展示全量
|
||||
} else {
|
||||
loading.value = true;
|
||||
getFishDictoryDropdown()
|
||||
.then((res) => {
|
||||
options.value = res.data || [];
|
||||
const list = res.data || [];
|
||||
allOptions.value = list;
|
||||
options.value = list;
|
||||
loading.value = false;
|
||||
shuJuTianBaoStore.setFishOption(options.value);
|
||||
shuJuTianBaoStore.setFishOption(list);
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
} else {
|
||||
options.value = data;
|
||||
}
|
||||
};
|
||||
|
||||
// 根据名字高亮显示匹配项
|
||||
const highlightText = (text) => {
|
||||
if (text == null) {
|
||||
return "暂无别名";
|
||||
}
|
||||
const reg = new RegExp(searchKeyword.value, "g");
|
||||
return text.replace(reg, `<span style="color: red;">${searchKeyword.value}</span>`);
|
||||
// 处理回车键:如果有匹配项,选中第一个
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
document.addEventListener("click", handleClickOutside);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener("click", handleClickOutside);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 样式保持不变 */
|
||||
.custom-fish-select {
|
||||
:deep(.ant-select-dropdown) {
|
||||
padding: 0 !important;
|
||||
min-width: 400px !important;
|
||||
width: auto !important;
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.custom-dropdown-container {
|
||||
width: 401px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
/* 下拉面板样式 */
|
||||
.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;
|
||||
}
|
||||
|
||||
.dropdown-left-list {
|
||||
.list-container {
|
||||
width: 150px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
|
||||
@ -288,6 +591,20 @@ onMounted(() => {
|
||||
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 {
|
||||
@ -295,7 +612,7 @@ onMounted(() => {
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
transition: background-color 0.3s;
|
||||
transition: background-color 0.2s;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@ -315,11 +632,10 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.item-name {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.check-icon {
|
||||
@ -329,22 +645,19 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-divider {
|
||||
.divider {
|
||||
width: 1px;
|
||||
background-color: #e8e8e8;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dropdown-right-detail {
|
||||
.detail-container {
|
||||
width: 250px;
|
||||
height: 100%;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
overflow-y: auto;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
@ -357,7 +670,6 @@ onMounted(() => {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.detail-alias {
|
||||
@ -365,8 +677,10 @@ onMounted(() => {
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
word-break: break-all;
|
||||
span {
|
||||
color: red;
|
||||
|
||||
:deep(span) {
|
||||
color: #ff4d4f;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
@ -374,16 +688,25 @@ onMounted(() => {
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
margin-top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
padding: 10px;
|
||||
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>
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue'; // 使用 ref 更简单直观
|
||||
import { getBaseDropdown, getEngInfoDropdown, getFpssDropdown } from '@/api/select';
|
||||
import { set } from 'lodash';
|
||||
|
||||
export const useShuJuTianBaoStore = defineStore('shuJuTianBao', () => {
|
||||
// 1. 直接使用 ref 定义状态,确保响应式
|
||||
@ -19,10 +18,6 @@ export const useShuJuTianBaoStore = defineStore('shuJuTianBao', () => {
|
||||
const res = await getBaseDropdown({});
|
||||
if (res.data && Array.isArray(res.data)) {
|
||||
const list = [...res.data];
|
||||
list.unshift({
|
||||
baseid: 'all',
|
||||
basename: '当前全部'
|
||||
});
|
||||
// 直接赋值给 ref,触发响应式更新
|
||||
baseOption.value = list;
|
||||
}
|
||||
@ -53,6 +48,7 @@ export const useShuJuTianBaoStore = defineStore('shuJuTianBao', () => {
|
||||
try {
|
||||
fpssLoading.value = true;
|
||||
const res = await getFpssDropdown({ baseId, rstcd });
|
||||
console.log(res.data)
|
||||
fpssOption.value = res.data;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
@ -84,18 +84,87 @@ export function parseTime(time :any, cFormat :any) {
|
||||
})
|
||||
return time_str
|
||||
}
|
||||
export function downloadFile(obj :any, name :any, suffix :any) {
|
||||
const url = window.URL.createObjectURL(new Blob([obj]))
|
||||
const link = document.createElement('a')
|
||||
link.style.display = 'none'
|
||||
link.href = url
|
||||
const fileName = parseTime(new Date(),'') + '-' + name + '.' + suffix
|
||||
link.setAttribute('download', fileName)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
export function downloadFile(obj: any, name: any, suffix: any) {
|
||||
try {
|
||||
const url = window.URL.createObjectURL(new Blob([obj]));
|
||||
const link = document.createElement('a');
|
||||
link.style.display = 'none';
|
||||
link.href = url;
|
||||
|
||||
// 优化文件名生成逻辑,避免多余的中横线
|
||||
const timeStamp = parseTime(new Date(), '{y}{m}{d}{h}{i}{s}') || '';
|
||||
const separator = timeStamp ? '-' : '';
|
||||
const fileName = `${timeStamp}${separator}${name}.${suffix}`;
|
||||
|
||||
link.setAttribute('download', fileName);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
// 延迟移除,确保下载触发
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 100);
|
||||
} catch (error) {
|
||||
console.error('Download failed:', error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 通过 URL 下载文件
|
||||
* @param url 文件地址
|
||||
* @param fileName 文件名(可选,如果不传则尝试从 URL 解析或使用默认名)
|
||||
*/
|
||||
export function downloadFileByUrl(url: string, fileName?: string) {
|
||||
if (!url) return;
|
||||
|
||||
// 如果没有提供文件名,尝试从 URL 中提取
|
||||
let name = fileName;
|
||||
if (!name) {
|
||||
const urlParts = url.split('/');
|
||||
name = urlParts[urlParts.length - 1] || 'download_file';
|
||||
// 去除查询参数
|
||||
name = name.split('?')[0];
|
||||
}
|
||||
|
||||
// 方法 1: 使用 fetch 获取 Blob (推荐,可重命名且强制下载)
|
||||
// 注意:如果 URL 跨域且服务器未配置 CORS,fetch 会失败
|
||||
fetch(url)
|
||||
.then((response) => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.blob();
|
||||
})
|
||||
.then((blob) => {
|
||||
const blobUrl = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.style.display = 'none';
|
||||
link.href = blobUrl;
|
||||
link.setAttribute('download', name);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
// 清理
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(blobUrl);
|
||||
}, 100);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('Fetch download failed (possibly CORS), falling back to direct link:', error);
|
||||
|
||||
// 方法 2: 回退方案 - 直接使用 a 标签跳转
|
||||
// 这种方式对于 PDF/图片等浏览器支持预览的文件,可能会直接在新标签页打开而不是下载
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.target = '_blank';
|
||||
// 如果同源,download 属性生效;如果跨域,大多数浏览器会忽略 download 属性并直接打开
|
||||
if (fileName) {
|
||||
link.setAttribute('download', fileName);
|
||||
}
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
});
|
||||
}
|
||||
const modules = import.meta.glob('@/assets/legend/*.svg', { eager: true });
|
||||
// 图例图标映射
|
||||
export const iconMap: Record<string, string> = {};
|
||||
|
||||
@ -258,7 +258,7 @@
|
||||
上传视频 (MP4)
|
||||
</a-button>
|
||||
</a-upload>
|
||||
<a-button v-else @click="handleVideoPreview"> 点击预览视频 </a-button>
|
||||
<a-button v-else-if="videoFileList.length > 0" @click="handleVideoPreview"> 点击预览视频 </a-button>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@ -307,7 +307,9 @@ const getBaseDropdownSelect = async () => {
|
||||
try {
|
||||
baseLoading.value = true;
|
||||
const res = await getBaseDropdown({});
|
||||
baseOption.value = res.data;
|
||||
let list = res.data || [];
|
||||
if (list.length > 0) list.shift();
|
||||
baseOption.value = list;
|
||||
} catch (error) {
|
||||
console.error("获取流域列表失败:", error);
|
||||
} finally {
|
||||
@ -642,7 +644,6 @@ const handleOk = async () => {
|
||||
// 先执行手动验证
|
||||
const isBodyLenValid = validateBodyLength();
|
||||
const isWeightValid = validateWeight();
|
||||
console.log(isBodyLenValid, isWeightValid);
|
||||
if (!isBodyLenValid || !isWeightValid) {
|
||||
message.error("请检查体长或体重填写是否正确");
|
||||
return;
|
||||
@ -749,7 +750,6 @@ const handleOk = async () => {
|
||||
vdpth: finalVideoPaths.join(","),
|
||||
};
|
||||
if (!formData.id) submitValues.tm = dayjs().format("YYYY-MM-DD HH:mm:ss");
|
||||
console.log(submitValues);
|
||||
// return;
|
||||
emit("ok", submitValues);
|
||||
} catch (error) {
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
@values-change="onValuesChange"
|
||||
>
|
||||
<template #ftp="{ onChange }">
|
||||
<fishSearch v-model="localTypeDate" width="280px" @update:modelValue="onChange" />
|
||||
<fishSearch v-model="localTypeDate" width="200px" @update:modelValue="onChange" />
|
||||
</template>
|
||||
<template #actions>
|
||||
<a-tooltip title="新增">
|
||||
@ -51,13 +51,13 @@
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="下载模板">
|
||||
<a-button v-hasPerm="['sjtb:import-add']">
|
||||
<a-button v-hasPerm="['sjtb:import-add']" @click="downloadTemplate">
|
||||
下载模板
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="leftBottom">
|
||||
<a-tooltip placement="left">
|
||||
<template #title>
|
||||
<div>1.</div>
|
||||
<div>导入须知:仅支持 ZIP 压缩包上传,压缩包内需包含 images、video 文件夹,根目录需放置 xlsx 格式表格,单包大小请勿超过 10MB,Excel 单次最大支持 10000 行数据,超出请分批导入,具体请参考下载模版格式。</div>
|
||||
</template>
|
||||
<a-button>
|
||||
<template #icon><QuestionOutlined /></template>
|
||||
@ -80,6 +80,7 @@ import BasicSearch from "@/components/BasicSearch/index.vue"; // 确保路径正
|
||||
import { DateSetting } from "@/utils/enumeration";
|
||||
import fishSearch from "@/components/fishSearch/index.vue";
|
||||
import { useShuJuTianBaoStore } from "@/store/modules/shuJuTianBao";
|
||||
import { downloadFileByUrl } from "@/utils/index";
|
||||
|
||||
interface Props {
|
||||
direction: any[];
|
||||
@ -130,7 +131,7 @@ const searchList: any = computed(() => [
|
||||
type: "Select",
|
||||
name: "stcd",
|
||||
label: "过鱼设施",
|
||||
values: { name: "stnm", value: "rstcd" },
|
||||
values: { name: "stnm", value: "stcd" },
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
},
|
||||
@ -201,6 +202,12 @@ const onValuesChange = (changedValues: any, allValues: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 下载模板
|
||||
const downloadTemplate = () => {
|
||||
// 20260429090252740092641634938251
|
||||
downloadFileByUrl(import.meta.env.VITE_APP_PREVIEW_URL + "/?20260429090252740092641634938251", "过鱼设施数据填报模板", "zip");
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
localTypeDate.value = null;
|
||||
emit("reset", initSearchData);
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
:columns="modalColumns"
|
||||
:scroll="{ y: 500, x: '100%' }"
|
||||
:pagination="false"
|
||||
:row-key="(_record, index) => index"
|
||||
row-key="id"
|
||||
>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<!-- 1. 操作列 -->
|
||||
@ -249,9 +249,10 @@ 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 } from "@/api/select";
|
||||
import { getBaseDropdown, getEngInfoDropdown, getFpssDropdown,updateImportTask } from "@/api/select";
|
||||
|
||||
const props: any = defineProps({
|
||||
taskId: { type: String, default: '' },
|
||||
fileTableData: { type: Array, default: () => [] },
|
||||
fileLoading: { type: Boolean, default: false },
|
||||
direction: { type: Array, default: () => [] },
|
||||
@ -355,7 +356,9 @@ onMounted(() => {
|
||||
const loadBaseOptions = async () => {
|
||||
try {
|
||||
const res = await getBaseDropdown({});
|
||||
baseOptions.value = res.data || [];
|
||||
let list = res.data || [];
|
||||
if (list.length > 0) list.shift();
|
||||
baseOptions.value = list;
|
||||
} catch (e) {
|
||||
console.error("Load base options failed", e);
|
||||
}
|
||||
@ -467,9 +470,7 @@ const startEdit = (index: number) => {
|
||||
editingRowIndex.value = index;
|
||||
|
||||
// 3. 预加载下拉选项 (基于 editingData 的值)
|
||||
console.log(editingData.value.baseId);
|
||||
if (editingData.value.baseId == "" || editingData.value.baseId == undefined) {
|
||||
console.log(editingData.value.rstcd);
|
||||
if (editingData.value.rstcd) {
|
||||
handleBaseChange("", index, "start").then(() => {
|
||||
handleEngChange(editingData.value.rstcd, index, "start");
|
||||
@ -478,7 +479,6 @@ const startEdit = (index: number) => {
|
||||
handleEngChange("", index, "start");
|
||||
}
|
||||
} else if (editingData.value.baseId != "" && editingData.value.baseId != undefined) {
|
||||
console.log(2);
|
||||
handleBaseChange(editingData.value.baseId, index, "start").then(() => {
|
||||
handleEngChange(editingData.value.rstcd, index, "start");
|
||||
});
|
||||
@ -550,6 +550,9 @@ const saveEdit = (index: number) => {
|
||||
// 4. 重置状态
|
||||
editingRowIndex.value = null;
|
||||
editingData.value = null;
|
||||
console.log(newData)
|
||||
console.log(props.taskId)
|
||||
// updateImportTask
|
||||
message.success("保存成功");
|
||||
};
|
||||
|
||||
|
||||
@ -35,14 +35,27 @@
|
||||
v-if="record.status === 'DRAFT' || record.status === 'REJECTED'"
|
||||
>提交</a-button
|
||||
>
|
||||
|
||||
<a-button
|
||||
v-hasPerm="['sjtb:import-add']"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleEdit(record, 'edit')"
|
||||
v-if="
|
||||
record.status === 'DRAFT' ||
|
||||
record.status === 'REJECTED' ||
|
||||
record.status === 'SUBMITTED'
|
||||
record.status === 'REJECTED'
|
||||
"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button
|
||||
v-hasPerm="['sjtb:edit-review']"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleEdit(record, 'edit')"
|
||||
v-if="
|
||||
record.status === 'PENDING' ||
|
||||
record.status === 'REJECTED'||
|
||||
record.status === 'PENDING'
|
||||
"
|
||||
>编辑</a-button
|
||||
>
|
||||
@ -58,22 +71,24 @@
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleEdit(record, 'view')"
|
||||
v-if="record.status === 'SUBMITTED' || record.status === 'APPROVED'"
|
||||
v-if="record.status === 'PENDING' || record.status === 'APPROVED'"
|
||||
>查看</a-button
|
||||
>
|
||||
<a-button
|
||||
v-hasPerm="['sjtb:edit-review']"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleSuccess([record.id])"
|
||||
v-if="record.status === 'SUBMITTED'"
|
||||
v-if="record.status === 'PENDING'"
|
||||
>审批</a-button
|
||||
>
|
||||
<a-button
|
||||
v-hasPerm="['sjtb:edit-review']"
|
||||
type="link"
|
||||
danger
|
||||
size="small"
|
||||
@click="handleReject(record.id)"
|
||||
v-if="record.status === 'SUBMITTED'"
|
||||
v-if="record.status === 'PENDING'"
|
||||
>驳回</a-button
|
||||
>
|
||||
</div>
|
||||
@ -97,9 +112,11 @@
|
||||
v-model:open="visible"
|
||||
:maskClosable="false"
|
||||
:confirm-loading="fileLoading"
|
||||
@cancel="taskId = ''"
|
||||
>
|
||||
<GuoYuSheShiShuJuTianBaoTable
|
||||
ref="modalTableRef"
|
||||
:taskId="taskId"
|
||||
:fileLoading="fileLoading"
|
||||
:fileTableData="fileTableData"
|
||||
:direction="direction"
|
||||
@ -138,7 +155,7 @@
|
||||
:footer="null"
|
||||
width="900px"
|
||||
@cancel="closeMediaPreview"
|
||||
z-index="2000"
|
||||
:zIndex="2000"
|
||||
>
|
||||
<div class="flex h-[60vh] gap-4">
|
||||
<!-- 左侧:混合列表 (图片+视频) -->
|
||||
@ -245,7 +262,6 @@ import GuoYuSheShiShuJuTianBaoSearch from "./guoYuSheShiShuJuTianBaoSearch.vue";
|
||||
import GuoYuSheShiShuJuTianBaoTable from "./guoYuSheShiShuJuTianBaoTable.vue";
|
||||
import EditModal from "./guoYuSheShiShuJuTianBaoForm.vue";
|
||||
import { checkPerm } from "@/directive/permission";
|
||||
|
||||
import {
|
||||
getFishDraftPage,
|
||||
addFishDraft,
|
||||
@ -264,7 +280,6 @@ import {
|
||||
import { Tag } from "ant-design-vue"; // 确保导入 Tag
|
||||
import { getDictItemsByCode } from "@/api/dict";
|
||||
import dayjs from "dayjs";
|
||||
// import { FileImageOutlined, VideoCameraOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const baseUrl = import.meta.env.VITE_APP_PREVIEW_URL;
|
||||
// --- 类型定义 ---
|
||||
@ -558,15 +573,10 @@ const handleReject = (id: any) => {
|
||||
};
|
||||
// 多选禁用
|
||||
const getCheckboxProps = (record: any) => {
|
||||
console.log(checkPerm(["sjtb:edit-review"]));
|
||||
return {
|
||||
disabled: ["SUBMITTED", "APPROVED"].includes(record.status),
|
||||
disabled: [!checkPerm(['sjtb:edit-review'])&&"PENDING", "APPROVED"].includes(record.status),
|
||||
};
|
||||
};
|
||||
const handleDataLoaded = (params: any, data: any) => {
|
||||
console.log(params, data);
|
||||
return;
|
||||
};
|
||||
// 多选
|
||||
const handleSelectionChange = (keys: any) => {
|
||||
batchData.value = keys;
|
||||
@ -651,9 +661,6 @@ const checkTableDataChanges = () => {
|
||||
|
||||
// 提交导入
|
||||
const handleModalOk = () => {
|
||||
console.log(orgFileTableData.value);
|
||||
console.log(fileTableData.value);
|
||||
console.log(modalTableRef.value.editingData);
|
||||
if (modalTableRef.value.editingData != undefined) {
|
||||
message.warning("请点击保存后提交数据!");
|
||||
return;
|
||||
@ -941,7 +948,6 @@ const handleSearchFinish = (values: any) => {
|
||||
};
|
||||
// 处理预览点击 (由子组件触发)
|
||||
const handlePreviewClick = (record: any, type: string, index: number) => {
|
||||
console.log(record, type);
|
||||
const mixedList: MediaItem[] = [];
|
||||
if (type === "image") {
|
||||
tablePreviewRecord.value = record;
|
||||
@ -989,7 +995,6 @@ const handlePreviewClick = (record: any, type: string, index: number) => {
|
||||
|
||||
mediaPreviewVisible.value = true;
|
||||
currentMediaIndex.value = index;
|
||||
console.log(mixedList);
|
||||
nextTick(() => {
|
||||
previewList.value = mixedList;
|
||||
});
|
||||
@ -1016,7 +1021,6 @@ const handleDeleteMedia = (item: any, index: number) => {
|
||||
zIndex: 2002,
|
||||
onOk: () => {
|
||||
previewList.value.splice(index, 1);
|
||||
console.log(previewList.value);
|
||||
if (videoPreviewTitle.value == "图片预览") {
|
||||
if (previewList.value.length == 0) {
|
||||
tablePreviewRecord.value.picpthList = [];
|
||||
@ -1059,12 +1063,12 @@ const closeMediaPreview = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// --- 生命周期 ---
|
||||
// --- 生命周期 ---
|
||||
onMounted(() => {
|
||||
getDictItemsByCode({ dictCode: "direction" }).then((res) => {
|
||||
direction.value = res.data;
|
||||
});
|
||||
getDictItemsByCode({ dictCode: "guoyuStatus" }).then((res) => {
|
||||
getDictItemsByCode({ dictCode: "approvalStatus" }).then((res) => {
|
||||
guoyuStatus.value = res.data;
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user