2023-08-31 21:49:20 +08:00
|
|
|
<template>
|
|
|
|
|
<div id="search-card" v-show="search.visible" @keydown.esc="closeSearch">
|
2024-11-20 22:43:53 +08:00
|
|
|
<el-card :title="$t('components.terminal.search')" size="small">
|
2023-08-31 21:49:20 +08:00
|
|
|
<!-- 搜索框 -->
|
|
|
|
|
<el-input
|
|
|
|
|
class="search-input"
|
|
|
|
|
ref="searchInputRef"
|
2024-11-20 22:43:53 +08:00
|
|
|
:placeholder="$t('components.terminal.serachPlaceholder')"
|
2023-08-31 21:49:20 +08:00
|
|
|
v-model="search.value"
|
|
|
|
|
@keyup.enter.native="searchKeywords(true)"
|
|
|
|
|
clearable
|
|
|
|
|
>
|
|
|
|
|
</el-input>
|
|
|
|
|
<!-- 选项 -->
|
|
|
|
|
<div class="search-options">
|
|
|
|
|
<el-row>
|
|
|
|
|
<el-col :span="12">
|
2024-11-20 22:43:53 +08:00
|
|
|
<el-checkbox class="usn" v-model="search.regex"> {{ $t('components.terminal.regexMatch') }} </el-checkbox>
|
2023-08-31 21:49:20 +08:00
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
2024-11-20 22:43:53 +08:00
|
|
|
<el-checkbox class="usn" v-model="search.words"> {{ $t('components.terminal.fullWordMatching') }} </el-checkbox>
|
2023-08-31 21:49:20 +08:00
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
2024-11-20 22:43:53 +08:00
|
|
|
<el-checkbox class="usn" v-model="search.matchCase"> {{ $t('components.terminal.caseSensitive') }} </el-checkbox>
|
2023-08-31 21:49:20 +08:00
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
2024-11-20 22:43:53 +08:00
|
|
|
<el-checkbox class="usn" v-model="search.incremental"> {{ $t('components.terminal.incrementalSearch') }} </el-checkbox>
|
2023-08-31 21:49:20 +08:00
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 按钮 -->
|
|
|
|
|
<div class="search-buttons">
|
2024-11-20 22:43:53 +08:00
|
|
|
<el-button class="terminal-search-button search-button-prev" type="primary" size="small" @click="searchKeywords(false)">
|
2024-12-13 12:15:24 +08:00
|
|
|
{{ $t('components.terminal.previous') }}
|
2024-11-20 22:43:53 +08:00
|
|
|
</el-button>
|
|
|
|
|
<el-button class="terminal-search-button search-button-next" type="primary" size="small" @click="searchKeywords(true)">
|
2024-12-13 12:15:24 +08:00
|
|
|
{{ $t('components.terminal.next') }}
|
2024-11-20 22:43:53 +08:00
|
|
|
</el-button>
|
|
|
|
|
<el-button class="terminal-search-button search-button-next" type="primary" size="small" @click="closeSearch">
|
|
|
|
|
{{ $t('components.terminal.close') }}
|
|
|
|
|
</el-button>
|
2023-08-31 21:49:20 +08:00
|
|
|
</div>
|
|
|
|
|
</el-card>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
import { ref, toRefs, nextTick, reactive } from 'vue';
|
|
|
|
|
import { ElMessage } from 'element-plus';
|
2025-02-27 19:40:31 +08:00
|
|
|
import { SearchAddon, ISearchOptions } from '@xterm/addon-search';
|
2024-11-20 22:43:53 +08:00
|
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n();
|
2023-08-31 21:49:20 +08:00
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
searchAddon: {
|
|
|
|
|
type: [SearchAddon],
|
|
|
|
|
require: true,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const state = reactive({
|
|
|
|
|
search: {
|
|
|
|
|
visible: false,
|
|
|
|
|
value: '',
|
|
|
|
|
regex: false,
|
|
|
|
|
words: false,
|
|
|
|
|
matchCase: false,
|
|
|
|
|
incremental: false,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const { search } = toRefs(state);
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits(['close']);
|
|
|
|
|
|
|
|
|
|
const searchInputRef: any = ref(null);
|
|
|
|
|
|
|
|
|
|
function open() {
|
|
|
|
|
const visible = state.search.visible;
|
|
|
|
|
state.search.visible = !visible;
|
|
|
|
|
console.log(state.search.visible);
|
|
|
|
|
if (!visible) {
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
searchInputRef.value.focus();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function closeSearch() {
|
|
|
|
|
state.search.visible = false;
|
|
|
|
|
state.search.value = '';
|
|
|
|
|
props.searchAddon?.clearDecorations();
|
|
|
|
|
emit('close');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function searchKeywords(direction: any) {
|
|
|
|
|
if (!state.search.value) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const option = {
|
|
|
|
|
regex: state.search.regex,
|
|
|
|
|
wholeWord: state.search.words,
|
|
|
|
|
caseSensitive: state.search.matchCase,
|
|
|
|
|
incremental: state.search.incremental,
|
|
|
|
|
};
|
|
|
|
|
let res;
|
|
|
|
|
if (direction) {
|
|
|
|
|
res = props.searchAddon?.findNext(state.search.value, getSearchOptions(option));
|
|
|
|
|
} else {
|
|
|
|
|
res = props.searchAddon?.findPrevious(state.search.value, getSearchOptions(option));
|
|
|
|
|
}
|
|
|
|
|
if (!res) {
|
2024-11-20 22:43:53 +08:00
|
|
|
ElMessage.info(t('components.terminal.noMatchMsg'));
|
2023-08-31 21:49:20 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getSearchOptions = (searchOptions?: ISearchOptions): ISearchOptions => {
|
|
|
|
|
return {
|
|
|
|
|
...searchOptions,
|
|
|
|
|
decorations: {
|
|
|
|
|
matchOverviewRuler: '#888888',
|
|
|
|
|
activeMatchColorOverviewRuler: '#ffff00',
|
|
|
|
|
matchBackground: '#888888',
|
|
|
|
|
activeMatchBackground: '#ffff00',
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
defineExpose({ open });
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
#search-card {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 60px;
|
|
|
|
|
right: 20px;
|
|
|
|
|
z-index: 1200;
|
|
|
|
|
width: 270px;
|
|
|
|
|
|
|
|
|
|
.search-input {
|
|
|
|
|
width: 240px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.search-options {
|
|
|
|
|
margin: 12px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.search-buttons {
|
|
|
|
|
margin-top: 5px;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.terminal-search-button {
|
|
|
|
|
margin-left: 10px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|