<script setup>
|
import { ref, reactive, computed, watch } from 'vue';
|
|
const props = defineProps({
|
list: {
|
type: Array,
|
default: () => []
|
},
|
|
});
|
|
|
// 搜索相关状态
|
const searchQuery = ref('');
|
const matches = ref([]);
|
const currentMatchIndex = ref(-1);
|
const recentlyEdited = ref([]);
|
|
// 获取所有分类
|
const categories = computed(() => {
|
return props.list.map((v) => v.almName);
|
});
|
|
const allFields = computed(() => {
|
return props.list || [];
|
});
|
|
// 根据告警ID 创建告警表单
|
function getFieldsByCategory(category) {
|
// TODO
|
return allFields.value.filter(field => field.category === category);
|
}
|
|
// 处理搜索
|
function handleSearch() {
|
if (!searchQuery.value.trim()) {
|
matches.value = [];
|
currentMatchIndex.value = -1;
|
return;
|
}
|
|
const query = searchQuery.value.toLowerCase();
|
matches.value = allFields.value.filter(field =>
|
field.label.toLowerCase().includes(query) ||
|
(field.description && field.description.toLowerCase().includes(query))
|
);
|
|
// 如果有匹配项,高亮第一个
|
if (matches.value.length > 0) {
|
currentMatchIndex.value = 0;
|
scrollToCurrentMatch();
|
}
|
}
|
|
// 清除搜索
|
function clearSearch() {
|
searchQuery.value = '';
|
matches.value = [];
|
currentMatchIndex.value = -1;
|
}
|
|
// 检查字段是否匹配搜索
|
function isMatch(field) {
|
return matches.value.includes(field);
|
}
|
|
// 检查字段是否为当前匹配项
|
function isCurrentMatch(field) {
|
return currentMatchIndex.value >= 0 &&
|
matches.value[currentMatchIndex.value] === field;
|
}
|
|
// 跳转到下一个匹配项
|
function goToNextMatch() {
|
if (matches.value.length === 0) return;
|
currentMatchIndex.value = (currentMatchIndex.value + 1) % matches.value.length;
|
scrollToCurrentMatch();
|
}
|
|
// 跳转到上一个匹配项
|
function goToPrevMatch() {
|
if (matches.value.length === 0) return;
|
currentMatchIndex.value = (currentMatchIndex.value - 1 + matches.value.length) % matches.value.length;
|
scrollToCurrentMatch();
|
}
|
|
// 滚动到当前匹配项
|
function scrollToCurrentMatch() {
|
if (currentMatchIndex.value < 0) return;
|
|
const currentField = matches.value[currentMatchIndex.value];
|
if (!currentField) return;
|
|
// 找到对应的DOM元素并滚动到视图
|
const fieldElement = document.querySelector(`[data-field-key="${currentField.key}"]`);
|
if (fieldElement) {
|
fieldElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
// 自动聚焦到输入框
|
const input = fieldElement.querySelector('input');
|
if (input) input.focus();
|
}
|
}
|
|
// 聚焦到指定字段
|
function focusOnField(field) {
|
// 更新当前匹配项
|
const matchIndex = matches.value.indexOf(field);
|
if (matchIndex >= 0) {
|
currentMatchIndex.value = matchIndex;
|
}
|
|
// 找到对应的DOM元素并聚焦
|
const fieldElement = document.querySelector(`[data-field-key="${field.key}"]`);
|
if (fieldElement) {
|
const input = fieldElement.querySelector('input');
|
if (input) input.focus();
|
}
|
}
|
|
// 检查字段是否最近被编辑
|
function isRecentlyEdited(key) {
|
return recentlyEdited.value.includes(key);
|
}
|
|
// 处理字段变更
|
function handleFieldChange(field) {
|
// 记录字段变更时间
|
const now = new Date().getTime();
|
|
// 检查字段是否已在最近编辑列表中
|
const existingIndex = recentlyEdited.value.findIndex(key => key === field.key);
|
|
if (existingIndex > -1) {
|
// 将字段移到最近编辑列表的顶部
|
recentlyEdited.value.splice(existingIndex, 1);
|
recentlyEdited.value.unshift(field.key);
|
} else {
|
// 添加到最近编辑列表的顶部
|
recentlyEdited.value.unshift(field.key);
|
|
// 限制列表长度
|
if (recentlyEdited.value.length > 5) {
|
recentlyEdited.value.pop();
|
}
|
}
|
}
|
|
|
// 监听搜索变化
|
watch(searchQuery, (newValue, oldValue) => {
|
if (newValue !== oldValue) {
|
handleSearch();
|
}
|
});
|
|
</script>
|
|
<template>
|
<div class="highlight-form">
|
<!-- 顶部工具栏 -->
|
<div class="toolbar">
|
<div class="search-container">
|
<input
|
v-model="searchQuery"
|
placeholder="搜索字段名称或描述..."
|
@keyup="handleSearch"
|
>
|
<button @click="clearSearch">
|
<i class="fa fa-times"></i>
|
</button>
|
</div>
|
|
<div class="quick-actions">
|
<button @click="goToNextMatch">
|
<i class="fa fa-arrow-down"></i> 下一个
|
</button>
|
<button @click="goToPrevMatch">
|
<i class="fa fa-arrow-up"></i> 上一个
|
</button>
|
<span v-if="searchQuery">
|
找到 {{ matches.length }} 个匹配项 (当前 {{ currentMatchIndex + 1 }})
|
</span>
|
</div>
|
</div>
|
|
<!-- 完整表单区域 -->
|
<div class="form-content">
|
<!-- 类别分隔 -->
|
<div v-for="category in categories" :key="category" class="category-section">
|
<h3 class="category-title">{{ category }}</h3>
|
|
<div class="fields-grid">
|
<div
|
v-for="field in getFieldsByCategory(category)"
|
:key="field.key"
|
class="form-field"
|
:class="{
|
'search-match': isMatch(field),
|
'current-match': isCurrentMatch(field),
|
'recently-edited': isRecentlyEdited(field.key)
|
}"
|
@click="focusOnField(field)"
|
>
|
<div class="field-header">
|
<span class="field-label">{{ field.label }}</span>
|
<span v-if="isRecentlyEdited(field.key)" class="recent-tag">最近编辑</span>
|
</div>
|
|
<div class="field-value">
|
<input
|
v-model="formData[field.key]"
|
:type="field.type"
|
@change="handleFieldChange(field)"
|
>
|
</div>
|
|
<div class="field-description" v-if="field.description">
|
{{ field.description }}
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
|
</div>
|
</template>
|
|
<style scoped lang="less">
|
.highlight-form {
|
max-width: 800px;
|
margin: 0 auto;
|
padding: 20px;
|
}
|
|
.toolbar {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20px;
|
}
|
|
.search-container {
|
display: flex;
|
align-items: center;
|
border: 1px solid #ddd;
|
border-radius: 4px;
|
overflow: hidden;
|
}
|
|
.search-container input {
|
padding: 8px;
|
border: none;
|
width: 300px;
|
}
|
|
.search-container button {
|
padding: 8px 12px;
|
border: none;
|
background-color: #f0f0f0;
|
cursor: pointer;
|
}
|
|
.quick-actions {
|
display: flex;
|
align-items: center;
|
gap: 10px;
|
}
|
|
.quick-actions button {
|
padding: 6px 10px;
|
border: 1px solid #ddd;
|
background-color: white;
|
cursor: pointer;
|
border-radius: 4px;
|
}
|
|
.category-section {
|
margin-bottom: 30px;
|
}
|
|
.category-title {
|
font-size: 1.2em;
|
font-weight: bold;
|
margin-bottom: 15px;
|
padding-bottom: 10px;
|
border-bottom: 1px solid #eee;
|
}
|
|
.fields-grid {
|
display: grid;
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
gap: 15px;
|
}
|
|
.form-field {
|
padding: 15px;
|
border: 1px solid #eee;
|
border-radius: 4px;
|
transition: all 0.2s;
|
position: relative;
|
}
|
|
.search-match {
|
border-color: #42b983;
|
background-color: #f9f9f9;
|
}
|
|
.current-match {
|
border-color: #ff7a45;
|
box-shadow: 0 0 0 2px rgba(255, 122, 69, 0.2);
|
z-index: 1;
|
}
|
|
.recently-edited {
|
background-color: #fffbe6;
|
}
|
|
.field-header {
|
display: flex;
|
justify-content: space-between;
|
margin-bottom: 8px;
|
}
|
|
.field-label {
|
font-weight: bold;
|
}
|
|
.recent-tag {
|
padding: 2px 6px;
|
border-radius: 4px;
|
font-size: 0.8em;
|
background-color: #fff7e6;
|
color: #fa8c16;
|
}
|
|
.field-value input {
|
width: 100%;
|
padding: 8px;
|
border: 1px solid #ddd;
|
border-radius: 4px;
|
}
|
|
.field-description {
|
margin-top: 8px;
|
color: #666;
|
font-size: 0.9em;
|
}
|
|
.form-footer {
|
margin-top: 20px;
|
display: flex;
|
justify-content: flex-end;
|
}
|
|
.form-footer button {
|
margin-left: 10px;
|
}
|
</style>
|