fix key index issue

This commit is contained in:
stkevintan
2025-12-08 17:43:49 +08:00
parent e21c3ec507
commit 8aba3cbe00
+44 -24
View File
@@ -85,22 +85,16 @@ function parseStoragePath(path: string): [prefix: string, suffix: string] {
} }
// 更新存储路径前缀 // 更新存储路径前缀
function updateStoragePrefix(index: number, storage: string) { function updateStoragePrefix(row: PathMappingRow, storage: string) {
if (!downloaderInfo.value.path_mapping) return const [, currentSuffix] = parseStoragePath(row.storage)
const currentMapping = downloaderInfo.value.path_mapping[index]
const currentPath = currentMapping[0] || ''
const [, currentSuffix] = parseStoragePath(currentPath)
const prefix = storage2Prefix(storage) const prefix = storage2Prefix(storage)
currentMapping[0] = prefix + currentSuffix row.storage = prefix + currentSuffix
} }
// 更新存储路径后缀 // 更新存储路径后缀
function updateStorageSuffix(index: number, suffix: string) { function updateStorageSuffix(row: PathMappingRow, suffix: string) {
if (!downloaderInfo.value.path_mapping) return const [currentPrefix] = parseStoragePath(row.storage)
const currentMapping = downloaderInfo.value.path_mapping[index] row.storage = currentPrefix + suffix
const currentPath = currentMapping[0] || ''
const [currentPrefix] = parseStoragePath(currentPath)
currentMapping[0] = currentPrefix + suffix
} }
const pathValidationRules = [ const pathValidationRules = [
@@ -115,8 +109,24 @@ const downloaderInfo = ref<DownloaderConf>({
default: false, default: false,
enabled: false, enabled: false,
config: {}, config: {},
path_mapping: [],
}) })
// 路径映射行定义
interface PathMappingRow {
id: string
storage: string
download: string
}
// 路径映射行数据
const pathMappingRows = ref<PathMappingRow[]>([])
// 生成随机ID
function generateId() {
return Math.random().toString(36).substring(2, 9)
}
// 下载器是否应该刷新数据的计算属性 // 下载器是否应该刷新数据的计算属性
const shouldRefresh = computed(() => props.allowRefresh && props.downloader.enabled) const shouldRefresh = computed(() => props.allowRefresh && props.downloader.enabled)
@@ -148,6 +158,12 @@ async function loadDownloaderInfo() {
function openDownloaderInfoDialog() { function openDownloaderInfoDialog() {
// 深复制 // 深复制
downloaderInfo.value = cloneDeep(props.downloader) downloaderInfo.value = cloneDeep(props.downloader)
// 初始化路径映射行数据
pathMappingRows.value = (downloaderInfo.value.path_mapping || []).map(item => ({
id: generateId(),
storage: item[0],
download: item[1],
}))
downloaderInfoDialog.value = true downloaderInfoDialog.value = true
} }
@@ -157,6 +173,9 @@ async function saveDownloaderInfo() {
const { valid } = await downloaderForm.value?.validate() const { valid } = await downloaderForm.value?.validate()
if (!valid) return if (!valid) return
// 同步路径映射数据
downloaderInfo.value.path_mapping = pathMappingRows.value.map(row => [row.storage, row.download])
// 为空不保存,跳出警告框 // 为空不保存,跳出警告框
if (!downloaderInfo.value.name) { if (!downloaderInfo.value.name) {
$toast.error(t('downloader.nameRequired')) $toast.error(t('downloader.nameRequired'))
@@ -196,15 +215,16 @@ const getIcon = computed(() => {
// 添加路径映射 // 添加路径映射
function addPathMapping() { function addPathMapping() {
if (!downloaderInfo.value.path_mapping) { pathMappingRows.value.push({
downloaderInfo.value.path_mapping = [] id: generateId(),
} storage: '',
downloaderInfo.value.path_mapping.push(['', '']) download: ''
})
} }
// 移除路径映射 // 移除路径映射
function removePathMapping(index: number) { function removePathMapping(index: number) {
downloaderInfo.value.path_mapping?.splice(index, 1) pathMappingRows.value.splice(index, 1)
} }
// 按钮点击 // 按钮点击
@@ -482,30 +502,30 @@ onUnmounted(() => {
<VCol cols="12"> <VCol cols="12">
<VLabel class="mb-2">{{ t('downloader.pathMapping') }}</VLabel> <VLabel class="mb-2">{{ t('downloader.pathMapping') }}</VLabel>
<VRow <VRow
v-for="(mapping, index) in downloaderInfo.path_mapping" v-for="(row, index) in pathMappingRows"
:key="mapping[0] + '-' + mapping[1]" :key="row.id"
class="align-start flex-wrap pm-row" class="align-start flex-wrap pm-row"
> >
<VCol cols="12" md="6" class="pl-0 pr-0"> <VCol cols="12" md="6" class="pl-0 pr-0">
<div class="d-flex flex-nowrap align-start"> <div class="d-flex flex-nowrap align-start">
<VTextField <VTextField
class="rp-input" class="rp-input"
:model-value="parseStoragePath(mapping[0])[1]" :model-value="parseStoragePath(row.storage)[1]"
:placeholder="t('downloader.storagePath')" :placeholder="t('downloader.storagePath')"
density="compact" density="compact"
hide-details="auto" hide-details="auto"
:rules="pathValidationRules" :rules="pathValidationRules"
@update:model-value="v => updateStorageSuffix(index, v)" @update:model-value="v => updateStorageSuffix(row, v)"
append-icon="mdi-arrow-right" append-icon="mdi-arrow-right"
> >
<template v-slot:prepend> <template v-slot:prepend>
<VSelect <VSelect
class="rp-select" class="rp-select"
:model-value="getStorageType(mapping[0])" :model-value="getStorageType(row.storage)"
:items="prefixOptions" :items="prefixOptions"
density="compact" density="compact"
hide-details hide-details
@update:model-value="v => updateStoragePrefix(index, v)" @update:model-value="v => updateStoragePrefix(row, v)"
/> />
</template> </template>
</VTextField> </VTextField>
@@ -513,7 +533,7 @@ onUnmounted(() => {
</VCol> </VCol>
<VCol cols="12" md="6" class="pl-0 pr-0"> <VCol cols="12" md="6" class="pl-0 pr-0">
<VTextField <VTextField
v-model="mapping[1]" v-model="row.download"
:placeholder="t('downloader.downloadPath')" :placeholder="t('downloader.downloadPath')"
density="compact" density="compact"
hide-details="auto" hide-details="auto"