bug修复

This commit is contained in:
JenniferW 2025-12-19 16:13:21 +08:00
parent d575852b50
commit fd4f0c891c
7 changed files with 46032 additions and 45803 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,10 @@
<template>
<div id="tags-view-container" class="tags-view-container">
<scroll-pane ref="scrollPaneRef" class="tags-view-wrapper" @scroll="handleScroll">
<scroll-pane
ref="scrollPaneRef"
class="tags-view-wrapper"
@scroll="handleScroll"
>
<router-link
v-for="tag in visitedViews"
:key="tag.path"
@ -14,39 +18,46 @@
>
{{ tag.title }}
<span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)">
<close class="el-icon-close" style="width: 1em; height: 1em;vertical-align: middle;" />
<close
class="el-icon-close"
style="width: 1em; height: 1em; vertical-align: middle"
/>
</span>
</router-link>
</scroll-pane>
<ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu">
<ul
v-show="visible"
:style="{ left: left + 'px', top: top + 'px' }"
class="contextmenu"
>
<li @click="refreshSelectedTag(selectedTag)">
<refresh-right style="width: 1em; height: 1em;" /> 刷新页面
<refresh-right style="width: 1em; height: 1em" /> 刷新页面
</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
<close style="width: 1em; height: 1em;" /> 关闭当前
<close style="width: 1em; height: 1em" /> 关闭当前
</li>
<li @click="closeOthersTags">
<circle-close style="width: 1em; height: 1em;" /> 关闭其他
<circle-close style="width: 1em; height: 1em" /> 关闭其他
</li>
<li v-if="!isFirstView()" @click="closeLeftTags">
<back style="width: 1em; height: 1em;" /> 关闭左侧
<back style="width: 1em; height: 1em" /> 关闭左侧
</li>
<li v-if="!isLastView()" @click="closeRightTags">
<right style="width: 1em; height: 1em;" /> 关闭右侧
<right style="width: 1em; height: 1em" /> 关闭右侧
</li>
<li @click="closeAllTags(selectedTag)">
<circle-close style="width: 1em; height: 1em;" /> 全部关闭
<circle-close style="width: 1em; height: 1em" /> 全部关闭
</li>
</ul>
</div>
</template>
<script setup>
import ScrollPane from './ScrollPane'
import { getNormalPath } from '@/utils/ruoyi'
import useTagsViewStore from '@/store/modules/tagsView'
import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
import ScrollPane from "./ScrollPane";
import { getNormalPath } from "@/utils/ruoyi";
import useTagsViewStore from "@/store/modules/tagsView";
import useSettingsStore from "@/store/modules/settings";
import usePermissionStore from "@/store/modules/permission";
const visible = ref(false);
const top = ref(0);
@ -64,75 +75,81 @@ const routes = computed(() => usePermissionStore().routes);
const theme = computed(() => useSettingsStore().theme);
watch(route, () => {
addTags()
moveToCurrentTag()
})
addTags();
moveToCurrentTag();
});
watch(visible, (value) => {
if (value) {
document.body.addEventListener('click', closeMenu)
document.body.addEventListener("click", closeMenu);
} else {
document.body.removeEventListener('click', closeMenu)
document.body.removeEventListener("click", closeMenu);
}
})
});
onMounted(() => {
initTags()
addTags()
})
initTags();
addTags();
});
function isActive(r) {
return r.path === route.path
return r.path === route.path;
}
function activeStyle(tag) {
if (!isActive(tag)) return {};
return {
"background-color": theme.value,
"border-color": theme.value
"border-color": theme.value,
};
}
function isAffix(tag) {
return tag.meta && tag.meta.affix
return tag.meta && tag.meta.affix;
}
function isFirstView() {
try {
return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath
return (
selectedTag.value.fullPath === "/index" ||
selectedTag.value.fullPath === visitedViews.value[1].fullPath
);
} catch (err) {
return false
return false;
}
}
function isLastView() {
try {
return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath
return (
selectedTag.value.fullPath ===
visitedViews.value[visitedViews.value.length - 1].fullPath
);
} catch (err) {
return false
return false;
}
}
function filterAffixTags(routes, basePath = '') {
let tags = []
routes.forEach(route => {
function filterAffixTags(routes, basePath = "") {
let tags = [];
routes.forEach((route) => {
if (route.meta && route.meta.affix) {
const tagPath = getNormalPath(basePath + '/' + route.path)
const tagPath = getNormalPath(basePath + "/" + route.path);
tags.push({
fullPath: tagPath,
path: tagPath,
name: route.name,
meta: { ...route.meta }
})
meta: { ...route.meta },
});
}
if (route.children) {
const tempTags = filterAffixTags(route.children, route.path)
const tempTags = filterAffixTags(route.children, route.path);
if (tempTags.length >= 1) {
tags = [...tags, ...tempTags]
tags = [...tags, ...tempTags];
}
}
})
return tags
});
return tags;
}
function initTags() {
@ -141,15 +158,15 @@ function initTags() {
for (const tag of res) {
// Must have tag name
if (tag.name) {
useTagsViewStore().addVisitedView(tag)
useTagsViewStore().addVisitedView(tag);
}
}
}
function addTags() {
const { name } = route
const { name } = route;
if (name) {
useTagsViewStore().addView(route)
useTagsViewStore().addView(route);
}
}
@ -160,11 +177,11 @@ function moveToCurrentTag() {
scrollPaneRef.value.moveToTarget(r);
// when query is different then update
if (r.fullPath !== route.fullPath) {
useTagsViewStore().updateVisitedView(route)
useTagsViewStore().updateVisitedView(route);
}
}
}
})
});
}
function refreshSelectedTag(view) {
@ -177,83 +194,83 @@ function refreshSelectedTag(view) {
function closeSelectedTag(view) {
proxy.$tab.closePage(view).then(({ visitedViews }) => {
if (isActive(view)) {
toLastView(visitedViews, view)
toLastView(visitedViews, view);
}
})
});
}
function closeRightTags() {
proxy.$tab.closeRightPage(selectedTag.value).then(visitedViews => {
if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
toLastView(visitedViews)
proxy.$tab.closeRightPage(selectedTag.value).then((visitedViews) => {
if (!visitedViews.find((i) => i.fullPath === route.fullPath)) {
toLastView(visitedViews);
}
})
});
}
function closeLeftTags() {
proxy.$tab.closeLeftPage(selectedTag.value).then(visitedViews => {
if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
toLastView(visitedViews)
proxy.$tab.closeLeftPage(selectedTag.value).then((visitedViews) => {
if (!visitedViews.find((i) => i.fullPath === route.fullPath)) {
toLastView(visitedViews);
}
})
});
}
function closeOthersTags() {
router.push(selectedTag.value).catch(() => { });
router.push(selectedTag.value).catch(() => {});
proxy.$tab.closeOtherPage(selectedTag.value).then(() => {
moveToCurrentTag()
})
moveToCurrentTag();
});
}
function closeAllTags(view) {
proxy.$tab.closeAllPage().then(({ visitedViews }) => {
if (affixTags.value.some(tag => tag.path === route.path)) {
return
if (affixTags.value.some((tag) => tag.path === route.path)) {
return;
}
toLastView(visitedViews, view)
})
toLastView(visitedViews, view);
});
}
function toLastView(visitedViews, view) {
const latestView = visitedViews.slice(-1)[0]
const latestView = visitedViews.slice(-1)[0];
if (latestView) {
router.push(latestView.fullPath)
router.push(latestView.fullPath);
} else {
// now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs.
if (view.name === 'Dashboard') {
if (view.name === "Dashboard") {
// to reload home page
router.replace({ path: '/redirect' + view.fullPath })
router.replace({ path: "/redirect" + view.fullPath });
} else {
router.push('/')
router.push("/");
}
}
}
function openMenu(tag, e) {
const menuMinWidth = 105
const offsetLeft = proxy.$el.getBoundingClientRect().left // container margin left
const offsetWidth = proxy.$el.offsetWidth // container width
const maxLeft = offsetWidth - menuMinWidth // left boundary
const l = e.clientX - offsetLeft + 15 // 15: margin right
const menuMinWidth = 105;
const offsetLeft = proxy.$el.getBoundingClientRect().left; // container margin left
const offsetWidth = proxy.$el.offsetWidth; // container width
const maxLeft = offsetWidth - menuMinWidth; // left boundary
const l = e.clientX - offsetLeft + 15; // 15: margin right
if (l > maxLeft) {
left.value = maxLeft
left.value = maxLeft;
} else {
left.value = l
left.value = l;
}
top.value = e.clientY
visible.value = true
selectedTag.value = tag
top.value = e.clientY;
visible.value = true;
selectedTag.value = tag;
}
function closeMenu() {
visible.value = false
visible.value = false;
}
function handleScroll() {
closeMenu()
closeMenu();
}
</script>
@ -261,9 +278,9 @@ function handleScroll() {
.tags-view-container {
height: 34px;
width: 100%;
background: var(--tags-bg, #fff);
background: #fff;
border-bottom: 1px solid var(--tags-item-border, #d8dce5);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
.tags-view-wrapper {
.tags-view-item {
@ -294,7 +311,7 @@ function handleScroll() {
border-color: #42b983;
&::before {
content: '';
content: "";
background: #fff;
display: inline-block;
width: 8px;
@ -318,7 +335,7 @@ function handleScroll() {
font-size: 12px;
font-weight: 400;
color: var(--tags-item-text, #333);
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
border: 1px solid var(--el-border-color-light, #e4e7ed);
li {
@ -344,11 +361,11 @@ function handleScroll() {
vertical-align: 2px;
border-radius: 50%;
text-align: center;
transition: all .3s cubic-bezier(.645, .045, .355, 1);
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
transform-origin: 100% 50%;
&:before {
transform: scale(.6);
transform: scale(0.6);
display: inline-block;
vertical-align: -3px;
}

View File

@ -486,6 +486,9 @@ const currentInput = ref("");
const searchInput = ref(null);
const inputWidth = ref(600);
const selectedConditionIndex = ref(-1);
const lastInputValue = ref(""); //
const isConditionMoved = ref(false); // 使
const disableLocalSuggestions = ref(false); // smart
//
const suggestions = ref([]);
@ -784,12 +787,19 @@ const mergeFieldMetadata = (fields = []) => {
fieldMapCache.set(key, { key, label });
}
if (Array.isArray(item.fieldValues) && item.fieldValues.length > 0) {
// fieldValues
// -
// -
if (Array.isArray(item.fieldValues)) {
if (item.fieldValues.length === 0) {
fieldValueMap.value[key] = [];
} else {
const existingValues = fieldValueMap.value[key] || [];
fieldValueMap.value[key] = Array.from(
new Set([...existingValues, ...item.fieldValues])
);
}
}
});
allFields.value = Array.from(fieldMapCache.values());
@ -955,6 +965,14 @@ const focusConditionTag = (index) => {
//
const triggerConditionSuggestions = (conditionText) => {
// smart
if (disableLocalSuggestions.value) {
suggestions.value = [];
possibleFields.value = [];
showSuggestions.value = false;
return;
}
if (!conditionText) {
suggestions.value = [];
possibleFields.value = [];
@ -1100,6 +1118,14 @@ const triggerConditionSuggestions = (conditionText) => {
//
const updateSuggestionsFromValue = (value) => {
// smart
if (disableLocalSuggestions.value) {
suggestions.value = [];
possibleFields.value = [];
showSuggestions.value = false;
return;
}
const normalizedValue = value ?? "";
//
const parts = normalizedValue.split(/[;]/);
@ -1270,20 +1296,109 @@ const updateSuggestionsFromValue = (value) => {
//
const handleInput = (value, options = {}) => {
const normalizedValue = value ?? "";
//
//
if (options.skipReorder) {
lastInputValue.value = normalizedValue;
return;
}
//
if (lastInputValue.value && normalizedValue !== lastInputValue.value) {
const lastParts = lastInputValue.value
.split(/[;]/)
.map((p) => p.trim())
.filter((p) => p);
const currentParts = normalizedValue
.split(/[;]/)
.map((p) => p.trim())
.filter((p) => p);
const cursorIndex = findCursorConditionIndex();
//
if (cursorIndex >= 0 && cursorIndex < currentParts.length - 1) {
const lastPart = lastParts[cursorIndex] || "";
const currentPart = currentParts[cursorIndex] || "";
//
if (lastPart !== currentPart) {
//
const editedPart = currentPart;
//
const newParts = currentParts.filter(
(_, index) => index !== cursorIndex
);
newParts.push(editedPart);
const newValue = newParts.join(";");
// 使
isConditionMoved.value = true;
//
lastInputValue.value = newValue;
// 使 skipReorder
currentInput.value = newValue;
// 使
parsedConditions.value = parseConditions(newValue);
syncPreciseConditionsIndex();
//
suggestions.value = [];
possibleFields.value = [];
fieldsFromApi.value = false;
//
nextTick(() => {
if (searchInput.value) {
const inputEl = searchInput.value.$el.querySelector("input");
if (inputEl) {
//
const cursorPos = newValue.length;
inputEl.setSelectionRange(cursorPos, cursorPos);
}
}
// 使
setTimeout(() => {
isConditionMoved.value = false;
}, 100);
});
//
const finalPart = editedPart;
if (finalPart && !options.skipFetch) {
scheduleSearchHint(finalPart);
}
return;
}
}
}
//
lastInputValue.value = normalizedValue;
//
if (!normalizedValue.trim()) {
parsedConditions.value = [];
preciseConditions.value = [];
disableLocalSuggestions.value = false; // 使
isConditionMoved.value = false; //
fieldsFromApi.value = false;
possibleFields.value = [];
}
updateSuggestionsFromValue(normalizedValue);
if (!normalizedValue.trim()) {
suggestions.value = [];
keyDiffFields.value = [];
showKeyDiffHint.value = false;
if (differenceTimer.value) {
clearTimeout(differenceTimer.value);
differenceTimer.value = null;
}
return;
}
updateSuggestionsFromValue(normalizedValue);
if (options.skipFetch) {
return;
}
@ -1342,23 +1457,18 @@ const fetchSearchHints = async (keyword) => {
? res
: [];
//
// smart 退
if (!hintList.length) {
if (!allFields.value.length) {
disableLocalSuggestions.value = true; // smart
suggestions.value = [];
possibleFields.value = [];
allFields.value = [];
allValues.value = [];
fieldValueMap.value = {};
fieldsFromApi.value = false;
showSuggestions.value = false;
return;
}
//
updateSuggestionsFromValue(currentInput.value);
showSuggestions.value = suggestions.value.length > 0;
return;
}
// smart
disableLocalSuggestions.value = false;
//
mergeFieldMetadata(hintList);
@ -1500,7 +1610,9 @@ const fetchSearchHints = async (keyword) => {
uniqueSuggestions.push(s);
});
suggestions.value = uniqueSuggestions;
showSuggestions.value = uniqueSuggestions.length > 0;
//
showSuggestions.value =
uniqueSuggestions.length > 0 || possibleFields.value.length > 0;
} catch (error) {
console.error("searchHint error:", error);
}
@ -1554,8 +1666,14 @@ const selectPossibleField = (field) => {
targetIndex = parts.length - 1;
} else {
//
// :
const trimmedPart = (targetPart || "").trim();
if (!trimmedPart || trimmedPart === field.label) {
parts[targetIndex] = `${field.label}:`;
} else {
parts[targetIndex] = `${field.label}:${targetPart}`;
}
}
//
currentInput.value = parts.join(";").replace(/;;+/g, ";").trim();
@ -1826,20 +1944,60 @@ const syncPreciseConditionsIndex = () => {
const buildFieldConditionsPayload = () => {
const preciseIndexSet = getPreciseIndexSet();
return parsedConditions.value
.filter((condition) => condition.value)
//
const cursorIndex = findCursorConditionIndex();
const parts = currentInput.value.split(/[;]/);
const activeIndex =
cursorIndex >= 0 && cursorIndex < parts.length
? cursorIndex
: parts.length - 1;
//
let conditionsToProcess = [...parsedConditions.value];
if (activeIndex >= 0 && activeIndex < conditionsToProcess.length - 1) {
const [editedCondition] = conditionsToProcess.splice(activeIndex, 1);
conditionsToProcess.push(editedCondition);
}
return conditionsToProcess
.filter((condition) => {
// ":"
return condition.value || (condition.fieldLabel && condition.field);
})
.map((condition, index) => {
const queryType = preciseIndexSet.has(index) ? "EXACT" : "FUZZY";
//
const originalIndex =
activeIndex >= 0 && activeIndex < parsedConditions.value.length - 1
? index === conditionsToProcess.length - 1
? activeIndex
: parsedConditions.value.findIndex(
(c) =>
c.fieldLabel === condition.fieldLabel &&
c.value === condition.value &&
c.field === condition.field
)
: index;
const queryType = preciseIndexSet.has(originalIndex) ? "EXACT" : "FUZZY";
if (condition.valid && condition.field) {
return {
fieldName: condition.field,
fieldValue: condition.value,
fieldValue: condition.value || "",
keyword: "",
queryType,
};
}
// ":"使 keyword
if (condition.fieldLabel && !condition.value) {
return {
fieldName: "",
fieldValue: "",
keyword: condition.fieldLabel,
queryType,
};
}
const keyword = condition.fieldLabel
? `${condition.fieldLabel}:${condition.value}`.replace(/:$/, "")
: condition.value;
@ -1855,19 +2013,47 @@ const buildFieldConditionsPayload = () => {
// searchHint fieldConditions
const buildFieldConditionsForHint = () => {
return parsedConditions.value
.filter((condition) => condition.value)
//
const cursorIndex = findCursorConditionIndex();
const parts = currentInput.value.split(/[;]/);
const activeIndex =
cursorIndex >= 0 && cursorIndex < parts.length
? cursorIndex
: parts.length - 1;
//
let conditionsToProcess = [...parsedConditions.value];
if (activeIndex >= 0 && activeIndex < conditionsToProcess.length - 1) {
const [editedCondition] = conditionsToProcess.splice(activeIndex, 1);
conditionsToProcess.push(editedCondition);
}
return conditionsToProcess
.filter((condition) => {
// ":"
return condition.value || (condition.fieldLabel && condition.field);
})
.map((condition) => {
// field valid使 fieldName fieldValue
if (condition.field && condition.valid) {
return {
fieldName: condition.field, // 使 fieldKey
fieldValue: condition.value, // value
fieldValue: condition.value || "", // value
keyword: "",
queryType: "FUZZY",
};
}
// ":"使 keyword
if (condition.fieldLabel && !condition.value) {
return {
fieldName: "",
fieldValue: "",
keyword: condition.fieldLabel,
queryType: "FUZZY",
};
}
// label value keyword
return {
fieldName: "",
@ -2087,6 +2273,8 @@ const removeCondition = (index) => {
//
const clearAll = () => {
currentInput.value = "";
lastInputValue.value = ""; //
isConditionMoved.value = false; //
parsedConditions.value = [];
preciseConditions.value = [];
filteredData.value = [];
@ -3035,6 +3223,28 @@ function handleConfirm() {
vertical-align: middle;
}
/* 强制本页卡片始终使用浅色背景,避免受全局暗色模式影响 */
.result-card {
background-color: #fff !important;
}
::deep(.el-card.result-card) {
background-color: #fff !important;
}
/* 对比弹窗及其他弹窗的头部、底部统一浅色背景,避免暗色主题干扰 */
.compare-dialog {
::deep(.el-dialog__header),
::deep(.el-dialog__footer) {
background-color: #fff !important;
}
}
::deep(.el-dialog__header),
::deep(.el-dialog__footer) {
background-color: #fff;
}
/* 对比表头可拖拽的鼠标样式 */
.compare-header-draggable {
display: flex;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff