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:
@@ -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": "已启用",
|
||||
|
||||
Reference in New Issue
Block a user