feat: 站点名称/地址支持多值编辑,动态增删

- Sites.vue: 名称和地址改为动态列表 + 增删按钮
- SiteController: updateSite 接受 names[]/urls[] 数组
- SiteServiceImpl: 更新时直接覆盖整个名称/地址列表
This commit is contained in:
mediabot-pt
2026-06-30 13:17:06 +08:00
parent 1667bdd145
commit 08b5040c74
4 changed files with 41 additions and 29 deletions

View File

@@ -8,6 +8,7 @@ import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.List;
import java.util.Map;
@@ -47,15 +48,17 @@ public class SiteController {
@RequestBody Map<String, Object> payload,
HttpServletRequest request) {
String name = (String) payload.get("name");
String description = (String) payload.get("description");
String baseUrl = (String) payload.get("baseUrl");
@SuppressWarnings("unchecked")
List<String> names = (List<String>) payload.get("names");
@SuppressWarnings("unchecked")
List<String> urls = (List<String>) payload.get("urls");
String siteType = (String) payload.get("siteType");
String status = (String) payload.get("status");
String description = (String) payload.get("description");
Long operatorId = (Long) request.getAttribute("accountId");
try {
Site site = siteService.updateSite(siteId, name, description, baseUrl, siteType, status, operatorId);
Site site = siteService.updateSite(siteId, names, urls, description, siteType, status, operatorId);
return ApiResponse.success(siteService.toDTO(site));
} catch (IllegalArgumentException e) {
return ApiResponse.error(404, e.getMessage());

View File

@@ -43,7 +43,7 @@ public interface SiteService {
/**
* 管理员编辑站点基本信息
*/
Site updateSite(String siteId, String name, String description, String baseUrl,
Site updateSite(String siteId, List<String> names, List<String> urls, String description,
String siteType, String status, Long operatorId);
/** 所有站点列表(管理员用) */

View File

@@ -194,22 +194,16 @@ public class SiteServiceImpl implements SiteService {
}
@Override
public Site updateSite(String siteId, String name, String description, String baseUrl,
public Site updateSite(String siteId, List<String> names, List<String> urls, String description,
String siteType, String status, Long operatorId) {
Site site = siteMapper.selectBySiteId(siteId);
if (site == null) throw new IllegalArgumentException("站点不存在: " + siteId);
if (name != null && !name.isBlank()) {
List<String> names = fromJsonArray(site.getNamesJson());
if (names.isEmpty()) names.add(name);
else names.set(0, name);
site.setNamesJson(toJsonArray(names));
if (names != null && !names.isEmpty()) {
site.setNamesJson(toJsonArray(names.stream().filter(n -> n != null && !n.isBlank()).toList()));
}
if (baseUrl != null && !baseUrl.isBlank()) {
List<String> urls = fromJsonArray(site.getUrlsJson());
if (urls.isEmpty()) urls.add(baseUrl);
else urls.set(0, baseUrl);
site.setUrlsJson(toJsonArray(urls));
if (urls != null && !urls.isEmpty()) {
site.setUrlsJson(toJsonArray(urls.stream().filter(u -> u != null && !u.isBlank()).toList()));
}
if (description != null) site.setDescription(description);
if (siteType != null && !siteType.isBlank()) {

View File

@@ -33,16 +33,24 @@
</el-card>
<!-- 编辑对话框 -->
<el-dialog v-model="editVisible" title="编辑站点" width="500px" :close-on-click-modal="false">
<el-form :model="editForm" label-width="80px" @submit.prevent="handleSave">
<el-dialog v-model="editVisible" title="编辑站点" width="560px" :close-on-click-modal="false">
<el-form label-width="80px">
<el-form-item label="站点ID">
<el-input v-model="editForm.siteId" disabled />
</el-form-item>
<el-form-item label="名称">
<el-input v-model="editForm.name" type="textarea" :rows="3" placeholder="站点名称(一行一个)" />
<div v-for="(n, i) in editForm.names" :key="i" style="display:flex;gap:6px;margin-bottom:4px">
<el-input v-model="editForm.names[i]" placeholder="站点名称" />
<el-button @click="editForm.names.splice(i,1)" :disabled="editForm.names.length===1" size="small">删</el-button>
</div>
<el-button size="small" @click="editForm.names.push('')" style="margin-top:4px">+ 添加名称</el-button>
</el-form-item>
<el-form-item label="地址">
<el-input v-model="editForm.baseUrl" type="textarea" :rows="3" placeholder="站点地址(一行一个)" />
<div v-for="(u, i) in editForm.urls" :key="i" style="display:flex;gap:6px;margin-bottom:4px">
<el-input v-model="editForm.urls[i]" placeholder="站点地址" />
<el-button @click="editForm.urls.splice(i,1)" :disabled="editForm.urls.length===1" size="small">删</el-button>
</div>
<el-button size="small" @click="editForm.urls.push('')" style="margin-top:4px">+ 添加地址</el-button>
</el-form-item>
<el-form-item label="类型">
<el-select v-model="editForm.siteType" placeholder="选择类型" style="width:100%">
@@ -122,7 +130,7 @@ import api from '../api'
const sites = ref([])
const loading = ref(false)
const editVisible = ref(false), saving = ref(false)
const editForm = ref({ id: null, siteId: '', name: '', baseUrl: '', siteType: '', status: '', description: '' })
const editForm = reactive({ id: null, siteId: '', names: [''], urls: [''], siteType: '', status: '', description: '' })
const siteTypes = [
{ value: 'nexusphp', label: 'NexusPHP' }, { value: 'mtorrent', label: 'MTorrent' },
{ value: 'mteam', label: 'M-Team API' }, { value: 'pttime', label: 'PTTime' },
@@ -146,20 +154,27 @@ const fetchAll = async () => {
}
const handleEdit = (row) => {
editForm.value = { id: row.id, siteId: row.siteId, name: (row.names||[]).join('\n'), baseUrl: (row.urls||[]).join('\n'), siteType: row.siteType||'', status: row.status||'active', description: row.description||'' }
editForm.id = row.id
editForm.siteId = row.siteId
editForm.names = (row.names && row.names.length) ? [...row.names] : ['']
editForm.urls = (row.urls && row.urls.length) ? [...row.urls] : ['']
editForm.siteType = row.siteType || ''
editForm.status = row.status || 'active'
editForm.description = row.description || ''
editVisible.value = true
}
const handleSave = async () => {
if (!editForm.value.name.trim()||!editForm.value.baseUrl.trim()) { ElMessage.warning('名称和地址不能为空'); return }
const names = editForm.names.filter(n => n.trim())
const urls = editForm.urls.filter(u => u.trim())
if (names.length === 0 || urls.length === 0) { ElMessage.warning('名称和地址不能为空'); return }
saving.value = true
try {
await api.sites.update(editForm.value.siteId, {
name: editForm.value.name.trim().split('\n').filter(Boolean)[0]||'',
baseUrl: editForm.value.baseUrl.trim().split('\n').filter(Boolean)[0]||'',
siteType: editForm.value.siteType.trim()||'unknown',
status: editForm.value.status||'active',
description: editForm.value.description.trim()||null,
await api.sites.update(editForm.siteId, {
names, urls,
siteType: editForm.siteType.trim()||'unknown',
status: editForm.status||'active',
description: editForm.description.trim()||null,
})
ElMessage.success('保存成功'); editVisible.value = false; fetchAll()
} catch (e) { ElMessage.error(e.response?.data?.message||'保存失败') }