From bec5961f2cf7907719d9bc2caf1f0848c199eced Mon Sep 17 00:00:00 2001 From: zyh Date: Thu, 28 Aug 2025 12:46:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20Vite=20=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=20API=20=E7=9B=AE=E6=A0=87=E5=9C=B0=E5=9D=80?= =?UTF-8?q?=E4=B8=BA=E6=9C=AC=E5=9C=B0=E5=9C=B0=E5=9D=80=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=8C=89=E7=8A=B6=E6=80=81=E6=89=B9=E9=87=8F=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E9=93=BE=E6=8E=A5=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E5=8C=85=E5=90=AB=E7=9B=B8=E5=85=B3=E5=AF=B9=E8=AF=9D=E6=A1=86?= =?UTF-8?q?=E5=92=8C=E8=A1=A8=E5=8D=95=E9=AA=8C=E8=AF=81=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E7=A7=BB=E5=8A=A8=E7=AB=AF=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E4=BB=A5=E6=8F=90=E5=8D=87=E7=94=A8=E6=88=B7=E4=BD=93?= =?UTF-8?q?=E9=AA=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/按狀態批量刪除鏈接前端實現說明.md | 138 +++++++++++++++++ src/api/links.js | 6 + src/views/links/LinkGenerate.vue | 199 ++++++++++++++++++++++++- vite.config.js | 2 +- 4 files changed, 343 insertions(+), 2 deletions(-) create mode 100644 docs/按狀態批量刪除鏈接前端實現說明.md diff --git a/docs/按狀態批量刪除鏈接前端實現說明.md b/docs/按狀態批量刪除鏈接前端實現說明.md new file mode 100644 index 0000000..37e9c6f --- /dev/null +++ b/docs/按狀態批量刪除鏈接前端實現說明.md @@ -0,0 +1,138 @@ +# 按狀態批量刪除鏈接前端實現說明 + +## 功能概述 +在現有的鏈接生成頁面中新增了按狀態批量刪除鏈接的功能,用戶可以根據指定的狀態批量刪除自己的鏈接。 + +## 實現內容 + +### 1. API 接口實現 +**文件:** `src/api/links.js` + +新增了 `batchDeleteByStatus` 方法: +```javascript +// 按状态批量删除链接 +export function batchDeleteByStatus(payload) { + // payload: { statusList: string[], confirmDelete: boolean } + return http.post('/api/link/batch-delete-by-status', payload) +} +``` + +### 2. UI 界面實現 +**文件:** `src/views/links/LinkGenerate.vue` + +#### 2.1 操作入口 +- **桌面端:** 在鏈接列表卡片頭部新增「按狀態刪除」按鈕 +- **移動端:** 在移動端操作按鈕組中新增相同功能按鈕 + +#### 2.2 對話框界面 +- 使用 `el-dialog` 組件創建批量刪除對話框 +- 包含狀態選擇(多選框組)和確認刪除選項 +- 支持響應式設計,移動端優化顯示 + +#### 2.3 狀態選項 +支持的鏈接狀態: +- **NEW**: 新建 +- **USING**: 使用中 +- **LOGGED_IN**: 已登錄 +- **COMPLETED**: 已完成 +- **REFUNDED**: 已退款 +- **EXPIRED**: 已過期 + +### 3. 業務邏輯實現 + +#### 3.1 表單驗證 +- 狀態列表不能為空 +- 必須勾選確認刪除選項 +- 使用 Element Plus 的表單驗證規則 + +#### 3.2 安全確認機制 +- 表單提交前進行驗證 +- 二次確認對話框顯示要刪除的狀態 +- 防止誤操作 + +#### 3.3 結果處理 +- 顯示刪除結果統計(成功/失敗數量) +- 失敗時顯示詳細錯誤信息 +- 自動刷新鏈接列表 + +### 4. 響應式數據結構 + +```javascript +const batchDeleteByStatusDialog = reactive({ + visible: false, // 對話框顯示狀態 + loading: false, // 刪除操作加載狀態 + form: { + statusList: [], // 選中的狀態列表 + confirmDelete: false // 確認刪除選項 + }, + rules: { + // 表單驗證規則 + statusList: [...], + confirmDelete: [...] + } +}) +``` + +### 5. 主要方法 + +#### 5.1 `showBatchDeleteByStatusDialog()` +- 顯示批量刪除對話框 +- 重置表單狀態 + +#### 5.2 `closeBatchDeleteByStatusDialog()` +- 關閉對話框 +- 清理表單數據和狀態 + +#### 5.3 `handleBatchDeleteByStatus()` +- 執行批量刪除操作 +- 包含完整的驗證、確認、調用API、結果處理流程 + +## 用戶操作流程 + +1. **打開功能:** 點擊「按狀態刪除」按鈕 +2. **選擇狀態:** 勾選要刪除的鏈接狀態(可多選) +3. **確認操作:** 勾選確認刪除選項 +4. **提交請求:** 點擊「確認刪除」按鈕 +5. **二次確認:** 在彈出的確認對話框中確認操作 +6. **查看結果:** 系統顯示刪除結果並自動刷新列表 + +## 錯誤處理 + +### 客戶端驗證 +- 狀態列表為空時提示用戶選擇 +- 未確認刪除時阻止提交 +- 表單驗證失敗時顯示相應錯誤信息 + +### 服務端錯誤 +- 網絡錯誤時顯示通用錯誤信息 +- 服務端返回的具體錯誤信息會顯示給用戶 +- 部分成功時會顯示成功和失敗的統計信息 + +## 移動端優化 + +### 界面適配 +- 對話框寬度自適應移動端屏幕 +- 狀態選擇框採用縱向佈局 +- 按鈕尺寸和間距針對觸摸操作優化 + +### 樣式優化 +- 選擇框使用卡片式設計,易於點擊 +- 選中狀態有明顯的視覺反饋 +- 支持深色模式適配 + +## 安全特性 + +1. **權限控制:** 用戶只能刪除自己創建的鏈接 +2. **操作確認:** 雙重確認機制防止誤操作 +3. **參數驗證:** 前後端雙重驗證確保數據安全 +4. **狀態限制:** 限制可選擇的狀態類型和數量 + +## 技術特點 + +- **響應式設計:** 支持桌面端和移動端 +- **用戶體驗:** 操作流程清晰,反饋及時 +- **錯誤處理:** 完善的錯誤捕獲和用戶提示 +- **代碼復用:** 複用現有的 API 和組件結構 +- **維護性:** 代碼結構清晰,易於維護和擴展 + +這個實現為用戶提供了一個安全、易用的批量刪除功能,大大提高了鏈接管理的效率。 diff --git a/src/api/links.js b/src/api/links.js index bbf0d59..68f1bbb 100644 --- a/src/api/links.js +++ b/src/api/links.js @@ -32,4 +32,10 @@ export function getLinkStatus(codeNo) { return http.get(`/api/link/${codeNo}/status`) } +// 按状态批量删除链接 +export function batchDeleteByStatus(payload) { + // payload: { statusList: string[], confirmDelete: boolean } + return http.post('/api/link/batch-delete-by-status', payload) +} + diff --git a/src/views/links/LinkGenerate.vue b/src/views/links/LinkGenerate.vue index 0b8dc9f..d977587 100644 --- a/src/views/links/LinkGenerate.vue +++ b/src/views/links/LinkGenerate.vue @@ -155,6 +155,15 @@ > 导出CSV + + 按状态删除 + @@ -178,6 +187,15 @@ 导出CSV + + + 按状态删除 + @@ -414,6 +432,54 @@ + + + + + + + {{ status.label }} + + +
选择要删除的链接状态,可多选
+
+ + + + 我确认要删除选中状态的所有链接,此操作不可恢复 + + +
+ + +
@@ -425,7 +491,7 @@ import { Plus, Minus, Connection, Refresh, Download, DocumentCopy, Delete } from '@element-plus/icons-vue' -import { generateLinks, fetchLinks, deleteLink, batchDeleteLinks } from '@/api/links' +import { generateLinks, fetchLinks, deleteLink, batchDeleteLinks, batchDeleteByStatus } from '@/api/links' import { formatLinkStatus, getLinkStatusType, generateQRCodeUrl, downloadImage, copyToClipboard as copyText, exportToCSV as exportCSV, exportToExcel } from '@/utils/links' import { LINK_CONFIG, STATUS_CONFIG, EXPORT_CONFIG } from '@/config/links' @@ -447,6 +513,46 @@ const selectedRows = ref([]) const showBatchActions = computed(() => selectedRows.value.length > 0) const tableRef = ref() +// 按状态批量删除相关状态 +const batchDeleteFormRef = ref() +const batchDeleteByStatusDialog = reactive({ + visible: false, + loading: false, + form: { + statusList: [], + confirmDelete: false + }, + rules: { + statusList: [ + { required: true, message: '请选择要删除的状态', trigger: 'change' }, + { type: 'array', min: 1, message: '至少选择一个状态', trigger: 'change' } + ], + confirmDelete: [ + { required: true, message: '请确认删除操作', trigger: 'change' }, + { + validator: (rule, value, callback) => { + if (!value) { + callback(new Error('必须确认删除操作')) + } else { + callback() + } + }, + trigger: 'change' + } + ] + } +}) + +// 状态选项 +const statusOptions = [ + { value: 'NEW', label: '新建' }, + { value: 'USING', label: '使用中' }, + { value: 'LOGGED_IN', label: '已登錄' }, + { value: 'COMPLETED', label: '已完成' }, + { value: 'REFUNDED', label: '已退款' }, + { value: 'EXPIRED', label: '已過期' } +] + // 生成表单 const generateForm = reactive({ times: null, @@ -810,6 +916,77 @@ const toggleSelection = (item) => { } } +// 按状态批量删除相关方法 +const showBatchDeleteByStatusDialog = () => { + batchDeleteByStatusDialog.visible = true + batchDeleteByStatusDialog.form.statusList = [] + batchDeleteByStatusDialog.form.confirmDelete = false +} + +const closeBatchDeleteByStatusDialog = () => { + batchDeleteByStatusDialog.visible = false + batchDeleteByStatusDialog.loading = false + batchDeleteByStatusDialog.form.statusList = [] + batchDeleteByStatusDialog.form.confirmDelete = false + batchDeleteFormRef.value?.resetFields() +} + +const handleBatchDeleteByStatus = async () => { + try { + // 表单验证 + await batchDeleteFormRef.value.validate() + + const { statusList, confirmDelete } = batchDeleteByStatusDialog.form + + // 二次确认 + await ElMessageBox.confirm( + `確定要刪除狀態為 "${statusList.map(s => statusOptions.find(opt => opt.value === s)?.label).join('、')}" 的所有鏈接嗎?此操作不可恢復!`, + '批量刪除確認', + { + confirmButtonText: '確定刪除', + cancelButtonText: '取消', + type: 'warning' + } + ) + + batchDeleteByStatusDialog.loading = true + + // 調用API + const response = await batchDeleteByStatus({ + statusList, + confirmDelete + }) + + const result = response.data + + // 顯示結果 + if (result.allSuccess) { + ElMessage.success(`成功刪除 ${result.successCount} 個鏈接`) + } else { + ElMessage.warning( + `刪除完成:成功 ${result.successCount} 個,失敗 ${result.failedCount} 個` + ) + + // 如果有失敗的,顯示詳細信息 + if (result.failedCount > 0 && result.failedReasons.length > 0) { + console.warn('刪除失敗的原因:', result.failedReasons) + } + } + + // 關閉對話框並刷新列表 + closeBatchDeleteByStatusDialog() + await getLinkList() + + } catch (error) { + if (error !== 'cancel') { + console.error('按狀態批量刪除失敗:', error) + ElMessage.error(error.response?.data?.message || '批量刪除失敗') + } + } finally { + batchDeleteByStatusDialog.loading = false + } +} + // 页面加载时获取数据 onMounted(() => { checkMobile() @@ -1259,6 +1436,26 @@ onUnmounted(() => { padding: 0 20px 20px 20px; text-align: center; } + + /* 按状态删除对话框移动端优化 */ + .batch-delete-dialog :deep(.el-checkbox-group) { + display: flex; + flex-direction: column; + gap: 12px; + } + + .batch-delete-dialog :deep(.el-checkbox) { + margin-right: 0; + padding: 8px 12px; + border: 1px solid #e4e7ed; + border-radius: 8px; + background: #f8f9fa; + } + + .batch-delete-dialog :deep(.el-checkbox.is-checked) { + background: #ecf5ff; + border-color: #409eff; + } } @media (max-width: 480px) { diff --git a/vite.config.js b/vite.config.js index f1e8e51..7da7c15 100644 --- a/vite.config.js +++ b/vite.config.js @@ -27,7 +27,7 @@ export default defineConfig({ server: { proxy: { '/api': { - target: 'http://192.140.164.137:18080', + target: 'http://127.0.0.1:18080', changeOrigin: true, rewrite: (p) => p.replace(/^\/api/, ''), },