Compare commits

..

No commits in common. "ac76131f189e0233f2a540bc7ba8391ea75be53f" and "f966fec0a3eef9e376de69f17b917750e1832380" have entirely different histories.

28 changed files with 491 additions and 772 deletions

File diff suppressed because it is too large Load Diff

View File

@ -12,21 +12,21 @@
"bytes": "^3.1.2", "bytes": "^3.1.2",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"monaco-editor": "^0.45.0", "monaco-editor": "^0.44.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"sass": "^1.69.5", "sass": "^1.69.5",
"vue": "^3.3.11", "vue": "^3.3.9",
"vue-i18n": "^9.8.0", "vue-i18n": "^9.8.0",
"xterm": "^5.3.0", "xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0" "xterm-addon-fit": "^0.8.0"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^4.5.2", "@vitejs/plugin-vue": "^4.5.0",
"naive-ui": "^2.35.0", "naive-ui": "^2.35.0",
"prettier": "^3.1.0", "prettier": "^3.1.0",
"unplugin-auto-import": "^0.17.2", "unplugin-auto-import": "^0.16.7",
"unplugin-icons": "^0.18.1", "unplugin-icons": "^0.17.4",
"unplugin-vue-components": "^0.26.0", "unplugin-vue-components": "^0.25.2",
"vite": "^5.0.6" "vite": "^5.0.2"
} }
} }

View File

@ -1 +1 @@
5d7ef4625da99af1c918196c905459d4 d5ec7e0103cfa8c99bc40c20ffdcb4fb

View File

@ -17,7 +17,8 @@ const handleUpdateValue = (val) => {
<template> <template>
<div style="min-height: 22px"> <div style="min-height: 22px">
<template v-if="props.isEdit"> <template v-if="props.isEdit">
<n-input :input-props="{ spellcheck: 'false' }" :value="props.value" @update:value="handleUpdateValue" /> <!-- TODO: ADD FULL SCREEN EDIT SUPPORT -->
<n-input :value="props.value" @update:value="handleUpdateValue" />
</template> </template>
<template v-else> <template v-else>
{{ props.value }} {{ props.value }}

View File

@ -23,12 +23,7 @@ const handleSelectFile = async () => {
<template> <template>
<n-input-group> <n-input-group>
<n-input <n-input v-model:value="props.value" :disabled="props.disabled" :placeholder="placeholder" clearable />
v-model:value="props.value"
:disabled="props.disabled"
:input-props="{ spellcheck: 'false' }"
:placeholder="placeholder"
clearable />
<n-button :disabled="props.disabled" :focusable="false" @click="handleSelectFile">...</n-button> <n-button :disabled="props.disabled" :focusable="false" @click="handleSelectFile">...</n-button>
</n-input-group> </n-input-group>
</template> </template>

View File

@ -148,7 +148,7 @@ defineExpose({
style="min-width: 100px" /> style="min-width: 100px" />
</n-form-item> </n-form-item>
<n-form-item :label="$t('log.filter_keyword')"> <n-form-item :label="$t('log.filter_keyword')">
<n-input v-model:value="data.keyword" :input-props="{ spellcheck: 'false' }" clearable placeholder="" /> <n-input v-model:value="data.keyword" clearable placeholder="" />
</n-form-item> </n-form-item>
<n-form-item label="&nbsp;"> <n-form-item label="&nbsp;">
<icon-button :icon="Refresh" border t-tooltip="log.refresh" @click="loadHistory" /> <icon-button :icon="Refresh" border t-tooltip="log.refresh" @click="loadHistory" />

View File

@ -164,7 +164,6 @@ const onSave = () => {
<div class="editor-content-item-label">{{ props.fieldLabel }}</div> <div class="editor-content-item-label">{{ props.fieldLabel }}</div>
<n-input <n-input
v-model:value="viewAs.field" v-model:value="viewAs.field"
:input-props="{ spellcheck: 'false' }"
:placeholder="props.field + ''" :placeholder="props.field + ''"
:readonly="props.fieldReadonly" :readonly="props.fieldReadonly"
class="editor-content-item-input" class="editor-content-item-input"

View File

@ -88,7 +88,6 @@ defineExpose({
<slot name="prepend" /> <slot name="prepend" />
<n-input <n-input
v-model:value="inputData.filter" v-model:value="inputData.filter"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('interface.filter')" :placeholder="$t('interface.filter')"
:size="props.small ? 'small' : ''" :size="props.small ? 'small' : ''"
clearable clearable

View File

@ -186,7 +186,7 @@ const infoFilter = ref('')
</n-card> </n-card>
<n-card :title="$t('status.all_info')" embedded> <n-card :title="$t('status.all_info')" embedded>
<template #header-extra> <template #header-extra>
<n-input v-model:value="infoFilter" :input-props="{ spellcheck: 'false' }" clearable placeholder=""> <n-input v-model:value="infoFilter" clearable placeholder="">
<template #prefix> <template #prefix>
<icon-button :icon="Filter" size="18" /> <icon-button :icon="Filter" size="18" />
</template> </template>

View File

@ -176,7 +176,7 @@ const onListLimitChanged = (limit) => {
</n-tooltip> </n-tooltip>
</n-form-item> </n-form-item>
<n-form-item :label="$t('slog.filter')"> <n-form-item :label="$t('slog.filter')">
<n-input v-model:value="data.keyword" :input-props="{ spellcheck: 'false' }" clearable placeholder="" /> <n-input v-model:value="data.keyword" clearable placeholder="" />
</n-form-item> </n-form-item>
</n-form> </n-form>
<div class="content-value fill-height flex-box-h"> <div class="content-value fill-height flex-box-h">

View File

@ -78,7 +78,7 @@ const onCopyKey = () => {
<div class="content-toolbar flex-box-h"> <div class="content-toolbar flex-box-h">
<n-input-group> <n-input-group>
<redis-type-tag :binary-key="binaryKey" :type="props.keyType" size="large" /> <redis-type-tag :binary-key="binaryKey" :type="props.keyType" size="large" />
<n-input v-model:value="props.keyPath" :input-props="{ spellcheck: 'false' }" readonly> <n-input v-model:value="props.keyPath" readonly>
<template #suffix> <template #suffix>
<icon-button <icon-button
:icon="Refresh" :icon="Refresh"

View File

@ -202,11 +202,7 @@ const onClose = () => {
<n-scrollbar style="max-height: 500px"> <n-scrollbar style="max-height: 500px">
<n-form :model="newForm" :show-require-mark="false" label-placement="top" style="padding-right: 15px"> <n-form :model="newForm" :show-require-mark="false" label-placement="top" style="padding-right: 15px">
<n-form-item :label="$t('common.key')" path="key" required> <n-form-item :label="$t('common.key')" path="key" required>
<n-input <n-input v-model:value="newForm.key" placeholder="" readonly />
v-model:value="newForm.key"
:input-props="{ spellcheck: 'false' }"
placeholder=""
readonly />
</n-form-item> </n-form-item>
<component <component
:is="addValueComponent[newForm.type]" :is="addValueComponent[newForm.type]"

View File

@ -271,7 +271,6 @@ const onClose = () => {
required> required>
<n-input <n-input
v-model:value="generalForm.name" v-model:value="generalForm.name"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.connection.name_tip')" /> :placeholder="$t('dialogue.connection.name_tip')" />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi <n-form-item-gi
@ -284,7 +283,6 @@ const onClose = () => {
<n-form-item-gi :label="$t('dialogue.connection.addr')" :span="24" path="addr" required> <n-form-item-gi :label="$t('dialogue.connection.addr')" :span="24" path="addr" required>
<n-input <n-input
v-model:value="generalForm.addr" v-model:value="generalForm.addr"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.connection.addr_tip')" /> :placeholder="$t('dialogue.connection.addr_tip')" />
<n-text style="width: 40px; text-align: center">:</n-text> <n-text style="width: 40px; text-align: center">:</n-text>
<n-input-number <n-input-number
@ -296,7 +294,6 @@ const onClose = () => {
<n-form-item-gi :label="$t('dialogue.connection.pwd')" :span="12" path="password"> <n-form-item-gi :label="$t('dialogue.connection.pwd')" :span="12" path="password">
<n-input <n-input
v-model:value="generalForm.password" v-model:value="generalForm.password"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.connection.pwd_tip')" :placeholder="$t('dialogue.connection.pwd_tip')"
show-password-on="click" show-password-on="click"
type="password" /> type="password" />
@ -304,7 +301,6 @@ const onClose = () => {
<n-form-item-gi :label="$t('dialogue.connection.usr')" :span="12" path="username"> <n-form-item-gi :label="$t('dialogue.connection.usr')" :span="12" path="username">
<n-input <n-input
v-model:value="generalForm.username" v-model:value="generalForm.username"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.connection.usr_tip')" /> :placeholder="$t('dialogue.connection.usr_tip')" />
</n-form-item-gi> </n-form-item-gi>
</n-grid> </n-grid>
@ -326,7 +322,6 @@ const onClose = () => {
path="defaultFilter"> path="defaultFilter">
<n-input <n-input
v-model:value="generalForm.defaultFilter" v-model:value="generalForm.defaultFilter"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.connection.advn.filter_tip')" /> :placeholder="$t('dialogue.connection.advn.filter_tip')" />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi <n-form-item-gi
@ -335,7 +330,6 @@ const onClose = () => {
path="keySeparator"> path="keySeparator">
<n-input <n-input
v-model:value="generalForm.keySeparator" v-model:value="generalForm.keySeparator"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.connection.advn.separator_tip')" /> :placeholder="$t('dialogue.connection.advn.separator_tip')" />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi <n-form-item-gi
@ -392,7 +386,6 @@ const onClose = () => {
v-model:value="dbFilterList" v-model:value="dbFilterList"
:clearable="true" :clearable="true"
:disabled="generalForm.dbFilterType === 'none'" :disabled="generalForm.dbFilterType === 'none'"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.connection.advn.dbfilter_input_tip')" :placeholder="$t('dialogue.connection.advn.dbfilter_input_tip')"
:show="false" :show="false"
:show-arrow="false" :show-arrow="false"
@ -471,7 +464,6 @@ const onClose = () => {
<n-form-item :label="$t('dialogue.connection.addr')" required> <n-form-item :label="$t('dialogue.connection.addr')" required>
<n-input <n-input
v-model:value="generalForm.ssh.addr" v-model:value="generalForm.ssh.addr"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.connection.ssh.addr_tip')" /> :placeholder="$t('dialogue.connection.ssh.addr_tip')" />
<n-text style="width: 40px; text-align: center">:</n-text> <n-text style="width: 40px; text-align: center">:</n-text>
<n-input-number <n-input-number
@ -491,13 +483,11 @@ const onClose = () => {
:label="$t('dialogue.connection.usr')"> :label="$t('dialogue.connection.usr')">
<n-input <n-input
v-model:value="generalForm.ssh.username" v-model:value="generalForm.ssh.username"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.connection.ssh.usr_tip')" /> :placeholder="$t('dialogue.connection.ssh.usr_tip')" />
</n-form-item> </n-form-item>
<n-form-item v-if="sshLoginType === 'pwd'" :label="$t('dialogue.connection.pwd')"> <n-form-item v-if="sshLoginType === 'pwd'" :label="$t('dialogue.connection.pwd')">
<n-input <n-input
v-model:value="generalForm.ssh.password" v-model:value="generalForm.ssh.password"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.connection.ssh.pwd_tip')" :placeholder="$t('dialogue.connection.ssh.pwd_tip')"
show-password-on="click" show-password-on="click"
type="password" /> type="password" />
@ -511,7 +501,6 @@ const onClose = () => {
<n-form-item v-if="sshLoginType === 'pkfile'" :label="$t('dialogue.connection.ssh.passphrase')"> <n-form-item v-if="sshLoginType === 'pkfile'" :label="$t('dialogue.connection.ssh.passphrase')">
<n-input <n-input
v-model:value="generalForm.ssh.passphrase" v-model:value="generalForm.ssh.passphrase"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.connection.ssh.passphrase_tip')" :placeholder="$t('dialogue.connection.ssh.passphrase_tip')"
show-password-on="click" show-password-on="click"
type="password" /> type="password" />
@ -535,7 +524,6 @@ const onClose = () => {
<n-input-group> <n-input-group>
<n-select <n-select
v-model:value="generalForm.sentinel.master" v-model:value="generalForm.sentinel.master"
:input-props="{ spellcheck: 'false' }"
:options="masterNameOptions" :options="masterNameOptions"
filterable filterable
tag /> tag />
@ -547,7 +535,6 @@ const onClose = () => {
<n-form-item :label="$t('dialogue.connection.sentinel.password')"> <n-form-item :label="$t('dialogue.connection.sentinel.password')">
<n-input <n-input
v-model:value="generalForm.sentinel.password" v-model:value="generalForm.sentinel.password"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.connection.sentinel.pwd_tip')" :placeholder="$t('dialogue.connection.sentinel.pwd_tip')"
show-password-on="click" show-password-on="click"
type="password" /> type="password" />
@ -555,7 +542,6 @@ const onClose = () => {
<n-form-item :label="$t('dialogue.connection.sentinel.username')"> <n-form-item :label="$t('dialogue.connection.sentinel.username')">
<n-input <n-input
v-model:value="generalForm.sentinel.username" v-model:value="generalForm.sentinel.username"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.connection.sentinel.usr_tip')" /> :placeholder="$t('dialogue.connection.sentinel.usr_tip')" />
</n-form-item> </n-form-item>
</n-form> </n-form>

View File

@ -109,11 +109,7 @@ const onClose = () => {
v-if="!(deleteForm.key instanceof Array)" v-if="!(deleteForm.key instanceof Array)"
:label="$t('dialogue.key.key_expression')" :label="$t('dialogue.key.key_expression')"
required> required>
<n-input <n-input v-model:value="deleteForm.key" placeholder="" @input="resetAffected" />
v-model:value="deleteForm.key"
:input-props="{ spellcheck: 'false' }"
placeholder=""
@input="resetAffected" />
</n-form-item> </n-form-item>
<!-- <n-form-item :label="$t('dialogue.key.async_delete')" required>--> <!-- <n-form-item :label="$t('dialogue.key.async_delete')" required>-->
<!-- <n-checkbox v-model:checked="deleteForm.async">--> <!-- <n-checkbox v-model:checked="deleteForm.async">-->

View File

@ -112,7 +112,7 @@ const onClose = () => {
:show-require-mark="false" :show-require-mark="false"
label-placement="top"> label-placement="top">
<n-form-item :label="$t('dialogue.group.name')" path="name" required> <n-form-item :label="$t('dialogue.group.name')" path="name" required>
<n-input v-model:value="groupForm.name" :input-props="{ spellcheck: 'false' }" placeholder="" /> <n-input v-model:value="groupForm.name" placeholder="" />
</n-form-item> </n-form-item>
</n-form> </n-form>
</n-modal> </n-modal>

View File

@ -85,7 +85,6 @@ const onClose = () => {
<template #trigger> <template #trigger>
<n-input <n-input
v-model:value="filterForm.pattern" v-model:value="filterForm.pattern"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.filter.filter_pattern')" :placeholder="$t('dialogue.filter.filter_pattern')"
clearable /> clearable />
</template> </template>

View File

@ -178,14 +178,10 @@ const onClose = () => {
label-placement="top" label-placement="top"
style="padding-right: 15px"> style="padding-right: 15px">
<n-form-item :label="$t('common.key')" path="key" required> <n-form-item :label="$t('common.key')" path="key" required>
<n-input v-model:value="newForm.key" :input-props="{ spellcheck: 'false' }" placeholder="" /> <n-input v-model:value="newForm.key" placeholder="" />
</n-form-item> </n-form-item>
<n-form-item :label="$t('dialogue.key.db_index')" path="db" required> <n-form-item :label="$t('dialogue.key.db_index')" path="db" required>
<n-select <n-select v-model:value="newForm.db" :options="dbOptions" filterable />
v-model:value="newForm.db"
:input-props="{ spellcheck: 'false' }"
:options="dbOptions"
filterable />
</n-form-item> </n-form-item>
<n-form-item :label="$t('interface.type')" path="type" required> <n-form-item :label="$t('interface.type')" path="type" required>
<n-select v-model:value="newForm.type" :options="options" :render-label="renderTypeLabel" /> <n-select v-model:value="newForm.type" :options="options" :render-label="renderTypeLabel" />

View File

@ -88,14 +88,12 @@ const onClose = () => {
<n-form-item-gi :label="$t('preferences.general.language')" :span="24" required> <n-form-item-gi :label="$t('preferences.general.language')" :span="24" required>
<n-select <n-select
v-model:value="prefStore.general.language" v-model:value="prefStore.general.language"
:input-props="{ spellcheck: 'false' }"
:options="prefStore.langOption" :options="prefStore.langOption"
filterable /> filterable />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :label="$t('preferences.general.font')" :span="12" required> <n-form-item-gi :label="$t('preferences.general.font')" :span="12" required>
<n-select <n-select
v-model:value="prefStore.general.font" v-model:value="prefStore.general.font"
:input-props="{ spellcheck: 'false' }"
:options="prefStore.fontOption" :options="prefStore.fontOption"
filterable /> filterable />
</n-form-item-gi> </n-form-item-gi>
@ -106,10 +104,7 @@ const onClose = () => {
<n-input-number v-model:value="prefStore.general.scanSize" :min="1" /> <n-input-number v-model:value="prefStore.general.scanSize" :min="1" />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :label="$t('preferences.general.key_icon_style')" :span="12"> <n-form-item-gi :label="$t('preferences.general.key_icon_style')" :span="12">
<n-select <n-select v-model:value="prefStore.general.keyIconStyle" :options="keyOptions" />
v-model:value="prefStore.general.keyIconStyle"
:input-props="{ spellcheck: 'false' }"
:options="keyOptions" />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :label="$t('preferences.general.proxy')" :span="24"> <n-form-item-gi :label="$t('preferences.general.proxy')" :span="24">
<n-space> <n-space>
@ -133,11 +128,7 @@ const onClose = () => {
<n-tab-pane :tab="$t('preferences.editor.name')" display-directive="show" name="editor"> <n-tab-pane :tab="$t('preferences.editor.name')" display-directive="show" name="editor">
<n-form :disabled="loading" :model="prefStore.editor" :show-require-mark="false" label-placement="top"> <n-form :disabled="loading" :model="prefStore.editor" :show-require-mark="false" label-placement="top">
<n-form-item :label="$t('preferences.general.font')" required> <n-form-item :label="$t('preferences.general.font')" required>
<n-select <n-select v-model:value="prefStore.editor.font" :options="prefStore.fontOption" filterable />
v-model:value="prefStore.editor.font"
:input-props="{ spellcheck: 'false' }"
:options="prefStore.fontOption"
filterable />
</n-form-item> </n-form-item>
<n-form-item :label="$t('preferences.general.font_size')"> <n-form-item :label="$t('preferences.general.font_size')">
<n-input-number v-model:value="prefStore.editor.fontSize" :max="65535" :min="1" /> <n-input-number v-model:value="prefStore.editor.fontSize" :max="65535" :min="1" />

View File

@ -74,7 +74,7 @@ const onClose = () => {
label-align="left" label-align="left"
label-placement="top"> label-placement="top">
<n-form-item :label="$t('dialogue.key.new_name')" required> <n-form-item :label="$t('dialogue.key.new_name')" required>
<n-input v-model:value="renameForm.newKey" :input-props="{ spellcheck: 'false' }" /> <n-input v-model:value="renameForm.newKey" />
</n-form-item> </n-form-item>
</n-form> </n-form>
</n-modal> </n-modal>

View File

@ -80,7 +80,7 @@ const onConfirm = async () => {
transform-origin="center"> transform-origin="center">
<n-form :model="ttlForm" :show-require-mark="false" label-placement="top"> <n-form :model="ttlForm" :show-require-mark="false" label-placement="top">
<n-form-item :label="$t('common.key')"> <n-form-item :label="$t('common.key')">
<n-input :input-props="{ spellcheck: 'false' }" :value="ttlForm.key" readonly> <n-input :value="ttlForm.key" readonly>
<template #prefix> <template #prefix>
<n-icon v-if="!!ttlForm.keyCode" :component="Binary" size="20" /> <n-icon v-if="!!ttlForm.keyCode" :component="Binary" size="20" />
</template> </template>

View File

@ -65,7 +65,6 @@ const onUpdate = () => {
<template #default="{ value }"> <template #default="{ value }">
<n-input <n-input
v-model:value="value.value" v-model:value="value.value"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.field.enter_value')" :placeholder="$t('dialogue.field.enter_value')"
type="text" type="text"
@update:value="onUpdate" /> @update:value="onUpdate" />

View File

@ -41,7 +41,7 @@ defineExpose({
<template> <template>
<n-form-item label="ID"> <n-form-item label="ID">
<n-input v-model:value="id" :input-props="{ spellcheck: 'false' }" /> <n-input v-model:value="id" />
</n-form-item> </n-form-item>
<n-form-item :label="$t('common.field') + ':' + $t('common.value')" required> <n-form-item :label="$t('common.field') + ':' + $t('common.value')" required>
<n-dynamic-input <n-dynamic-input

View File

@ -9,7 +9,6 @@ const emit = defineEmits(['update:value'])
<template> <template>
<n-form-item :label="$t('common.value')"> <n-form-item :label="$t('common.value')">
<n-input <n-input
:input-props="{ spellcheck: 'false' }"
:rows="6" :rows="6"
:value="props.value" :value="props.value"
placeholder="" placeholder=""

View File

@ -46,7 +46,6 @@ defineExpose({
<template #default="{ value }"> <template #default="{ value }">
<n-input <n-input
v-model:value="value.value" v-model:value="value.value"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('dialogue.field.enter_member')" :placeholder="$t('dialogue.field.enter_member')"
type="text" type="text"
@update:value="onUpdate" /> @update:value="onUpdate" />

View File

@ -284,7 +284,6 @@ onMounted(() => onReload())
<n-select <n-select
:consistent-menu-width="false" :consistent-menu-width="false"
:filter="(pattern, option) => option.value.toString() === pattern" :filter="(pattern, option) => option.value.toString() === pattern"
:input-props="{ spellcheck: 'false' }"
:options="dbSelectOptions" :options="dbSelectOptions"
:value="props.db" :value="props.db"
filterable filterable

View File

@ -33,11 +33,7 @@ const filterPattern = ref('')
stroke-width="4" stroke-width="4"
t-tooltip="interface.new_group" t-tooltip="interface.new_group"
@click="dialogStore.openNewGroupDialog()" /> @click="dialogStore.openNewGroupDialog()" />
<n-input <n-input v-model:value="filterPattern" :placeholder="$t('interface.filter')" clearable>
v-model:value="filterPattern"
:input-props="{ spellcheck: 'false' }"
:placeholder="$t('interface.filter')"
clearable>
<template #prefix> <template #prefix>
<n-icon :component="Filter" size="20" /> <n-icon :component="Filter" size="20" />
</template> </template>