refactor: improve UI layout and interaction for list item management (#4002)

* refactor: improve UI layout and interaction for list item management

* feat: enhance list configuration UI with batch import functionality

* feat: add internationalization support for list configuration UI
This commit is contained in:
Soulter
2025-12-11 18:55:56 +08:00
committed by GitHub
parent ec408a2aff
commit c09d57a820
3 changed files with 142 additions and 43 deletions
@@ -2,7 +2,7 @@
<div class="d-flex align-center justify-space-between">
<div>
<span v-if="!modelValue || modelValue.length === 0" style="color: rgb(var(--v-theme-primaryText));">
暂无项目
{{ t('core.common.list.noItems') }}
</span>
<div v-else class="d-flex flex-wrap ga-2">
<v-chip v-for="item in displayItems" :key="item" size="x-small" label color="primary">
@@ -14,7 +14,7 @@
</div>
</div>
<v-btn size="small" color="primary" variant="tonal" @click="openDialog">
{{ buttonText }}
{{ buttonText || t('core.common.list.modifyButton') }}
</v-btn>
</div>
@@ -22,17 +22,43 @@
<v-dialog v-model="dialog" max-width="600px">
<v-card>
<v-card-title class="text-h3 py-4" style="font-weight: normal;">
{{ dialogTitle }}
{{ dialogTitle || t('core.common.list.editTitle') }}
</v-card-title>
<!-- Add new item section - moved to top -->
<v-card-text class="pa-4 pb-2">
<div class="d-flex align-center ga-2">
<v-text-field
v-model="newItem"
:label="t('core.common.list.addItemPlaceholder')"
@keyup.enter="addItem"
clearable
hide-details
variant="outlined"
density="compact"
:placeholder="t('core.common.list.inputPlaceholder')"
class="flex-grow-1">
</v-text-field>
<v-btn
@click="showBatchImport = true"
variant="tonal"
color="primary"
size="small">
<v-icon size="small">mdi-import</v-icon>
{{ t('core.common.list.batchImport') }}
</v-btn>
</div>
</v-card-text>
<v-card-text class="pa-0" style="max-height: 400px; overflow-y: auto;">
<v-list v-if="localItems.length > 0" density="compact">
<v-list-item
v-for="(item, index) in localItems"
:key="index"
rounded="md"
class="ma-1">
<v-list-item-title v-if="editIndex !== index">
class="ma-1 list-item-clickable"
@click="startEdit(index, item)">
<v-list-item-title v-if="editIndex !== index" class="item-text">
{{ item }}
</v-list-item-title>
<v-text-field
@@ -43,23 +69,27 @@
density="compact"
@keyup.enter="saveEdit"
@keyup.esc="cancelEdit"
@click.stop
autofocus
></v-text-field>
<template v-slot:append>
<div v-if="editIndex !== index" class="d-flex">
<v-btn @click="startEdit(index, item)" variant="plain" icon size="small">
<v-icon>mdi-pencil</v-icon>
</v-btn>
<v-btn @click="removeItem(index)" variant="plain" icon size="small">
<v-icon>mdi-close</v-icon>
</v-btn>
</div>
<div v-else class="d-flex">
<v-btn @click="saveEdit" variant="plain" color="success" icon size="small">
<div class="d-flex">
<v-btn
v-if="editIndex === index"
@click.stop="saveEdit"
variant="plain"
color="success"
icon
size="small">
<v-icon>mdi-check</v-icon>
</v-btn>
<v-btn @click="cancelEdit" variant="plain" color="error" icon size="small">
<v-btn
@click.stop="editIndex === index ? cancelEdit() : removeItem(index)"
variant="plain"
:color="editIndex === index ? 'error' : 'default'"
icon
size="small">
<v-icon>mdi-close</v-icon>
</v-btn>
</div>
@@ -69,34 +99,43 @@
<div v-else class="text-center py-8">
<v-icon size="64" color="grey-lighten-1">mdi-format-list-bulleted</v-icon>
<p class="text-grey mt-4">暂无项目</p>
</div>
</v-card-text>
<!-- Add new item section -->
<v-card-text class="pa-4">
<div class="d-flex align-center ga-2">
<v-text-field
v-model="newItem"
:label="t('core.common.list.addItemPlaceholder')"
@keyup.enter="addItem"
clearable
hide-details
variant="outlined"
density="compact"
class="flex-grow-1">
</v-text-field>
<v-btn @click="addItem" variant="tonal" color="primary">
<v-icon>mdi-plus</v-icon>
{{ t('core.common.list.addButton') }}
</v-btn>
<p class="text-grey mt-4">{{ t('core.common.list.noItemsHint') }}</p>
</div>
</v-card-text>
<v-card-actions class="pa-4">
<v-spacer></v-spacer>
<v-btn variant="text" @click="cancelDialog">取消</v-btn>
<v-btn color="primary" @click="confirmDialog">确认</v-btn>
<v-btn variant="text" @click="cancelDialog">{{ t('core.common.cancel') }}</v-btn>
<v-btn color="primary" @click="confirmDialog">{{ t('core.common.confirm') }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- Batch Import Dialog -->
<v-dialog v-model="showBatchImport" max-width="600px">
<v-card>
<v-card-title class="text-h3 py-4" style="font-weight: normal;">
{{ t('core.common.list.batchImportTitle') }}
</v-card-title>
<v-card-text>
<v-textarea
v-model="batchImportText"
:label="t('core.common.list.batchImportLabel')"
:placeholder="t('core.common.list.batchImportPlaceholder')"
rows="10"
variant="outlined"
:hint="t('core.common.list.batchImportHint')"
persistent-hint
></v-textarea>
</v-card-text>
<v-card-actions class="pa-4">
<v-spacer></v-spacer>
<v-btn variant="text" @click="cancelBatchImport">{{ t('core.common.cancel') }}</v-btn>
<v-btn color="primary" @click="confirmBatchImport">
{{ t('core.common.list.batchImportButton', { count: batchImportPreviewCount }) }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
@@ -139,12 +178,24 @@ const originalItems = ref([])
const newItem = ref('')
const editIndex = ref(-1)
const editItem = ref('')
const showBatchImport = ref(false)
const batchImportText = ref('')
// 计算要显示的项目
const displayItems = computed(() => {
return props.modelValue.slice(0, props.maxDisplayItems)
})
// 计算批量导入的项目数量
const batchImportPreviewCount = computed(() => {
if (!batchImportText.value) return 0
return batchImportText.value
.split('\n')
.map(line => line.trim())
.filter(line => line.length > 0)
.length
})
// 监听 modelValue 变化,同步到 localItems
watch(() => props.modelValue, (newValue) => {
localItems.value = [...(newValue || [])]
@@ -199,6 +250,24 @@ function cancelDialog() {
newItem.value = ''
dialog.value = false
}
function confirmBatchImport() {
if (batchImportText.value.trim()) {
const newItems = batchImportText.value
.split('\n')
.map(line => line.trim())
.filter(line => line.length > 0)
localItems.value.push(...newItems)
batchImportText.value = ''
showBatchImport.value = false
}
}
function cancelBatchImport() {
batchImportText.value = ''
showBatchImport.value = false
}
</script>
<style scoped>
@@ -206,8 +275,16 @@ function cancelDialog() {
transition: all 0.2s ease;
}
.v-list-item:hover {
background-color: rgba(var(--v-theme-primary), 0.04);
.list-item-clickable {
cursor: pointer;
}
.list-item-clickable:hover {
background-color: rgba(var(--v-theme-primary), 0.08);
}
.item-text {
user-select: none;
}
.v-chip {
@@ -66,7 +66,18 @@
},
"list": {
"addItemPlaceholder": "Add new item, press Enter to confirm",
"addButton": "Add"
"addButton": "Add",
"batchImport": "Batch Import",
"batchImportTitle": "Batch Import",
"batchImportLabel": "One item per line",
"batchImportPlaceholder": "Example:\nItem 1\nItem 2\nItem 3\nItem 4",
"batchImportHint": "Each line will be treated as a separate item, empty lines will be ignored",
"batchImportButton": "Import {count} items",
"noItems": "No items",
"noItemsHint": "No items yet, type in the input above and press Enter to add",
"inputPlaceholder": "Type and press Enter to add",
"editTitle": "Edit List Items",
"modifyButton": "Modify"
},
"itemCard": {
"enabled": "Enabled",
@@ -66,7 +66,18 @@
},
"list": {
"addItemPlaceholder": "添加新项,按回车确认添加",
"addButton": "添加"
"addButton": "添加",
"batchImport": "批量导入",
"batchImportTitle": "批量导入",
"batchImportLabel": "每行一个项目",
"batchImportPlaceholder": "例如:\n项目1\n项目2\n项目3\n项目4",
"batchImportHint": "每行将作为一个单独的项目,空行会被自动忽略",
"batchImportButton": "导入 {count} 项",
"noItems": "暂无项目",
"noItemsHint": "暂无项目,在上方输入框输入后按回车添加",
"inputPlaceholder": "输入后按回车添加",
"editTitle": "修改列表项",
"modifyButton": "修改"
},
"itemCard": {
"enabled": "已启用",