From 1664033c536213bd44ed9b4e1b2347cbfed251d6 Mon Sep 17 00:00:00 2001
From: mediabot-pt <295750538+mediabot-pt@users.noreply.github.com>
Date: Tue, 30 Jun 2026 11:25:01 +0800
Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=AB=99=E7=82=B9=E5=88=97?=
=?UTF-8?q?=E8=A1=A8=E5=92=8C=E7=AB=99=E7=82=B9=E5=AE=A1=E6=A0=B8=E6=8B=86?=
=?UTF-8?q?=E5=88=86=E4=B8=BA=E4=B8=A4=E4=B8=AA=E7=8B=AC=E7=AB=8B=E9=A1=B5?=
=?UTF-8?q?=E9=9D=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Sites.vue: 精简为纯站点列表(编辑/配置/变更)
- SiteReview.vue: 新文件,站点审核队列(通过/拒绝/匹配)
- router: 新增 /sites/review 路由
- Layout: 导航栏新增「站点审核」菜单项
---
web/src/router/index.js | 8 +-
web/src/views/Layout.vue | 6 +-
web/src/views/SiteReview.vue | 91 ++++++++++++
web/src/views/Sites.vue | 280 ++++++++---------------------------
4 files changed, 162 insertions(+), 223 deletions(-)
create mode 100644 web/src/views/SiteReview.vue
diff --git a/web/src/router/index.js b/web/src/router/index.js
index f702deb..dca7835 100644
--- a/web/src/router/index.js
+++ b/web/src/router/index.js
@@ -22,7 +22,13 @@ const routes = [
path: 'sites',
name: 'Sites',
component: () => import('../views/Sites.vue'),
- meta: { title: '站点管理' }
+ meta: { title: '站点列表' }
+ },
+ {
+ path: 'sites/review',
+ name: 'SiteReview',
+ component: () => import('../views/SiteReview.vue'),
+ meta: { title: '站点审核' }
},
{
path: 'configs',
diff --git a/web/src/views/Layout.vue b/web/src/views/Layout.vue
index 8f2e027..d93be69 100644
--- a/web/src/views/Layout.vue
+++ b/web/src/views/Layout.vue
@@ -16,7 +16,11 @@
- 站点管理
+ 站点列表
+
+
+
+ 站点审核
diff --git a/web/src/views/SiteReview.vue b/web/src/views/SiteReview.vue
new file mode 100644
index 0000000..f26adb4
--- /dev/null
+++ b/web/src/views/SiteReview.vue
@@ -0,0 +1,91 @@
+
+
+
站点审核
+
+ 暂无待审核提交
+
+
+
+
+
+ {{ n }}
+
+
+ {{ u }}
+
+
+
+ {{ scope.row.siteStatus }}
+
+
+
+ {{ scope.row.notes||'-' }}
+
+
+
+
+ 通过
+ 拒绝
+ 匹配
+
+
+
+
+
+
+
+
+
+ {{ n }}
+ {{ u }}
+
+ 取消
+
+
+
+
+
+
+
diff --git a/web/src/views/Sites.vue b/web/src/views/Sites.vue
index 8874078..6ec79a1 100644
--- a/web/src/views/Sites.vue
+++ b/web/src/views/Sites.vue
@@ -1,49 +1,8 @@
-
站点管理
-
-
-
- 待审核
-
-
-
-
- {{ n }}
-
-
-
-
- {{ u }}
-
-
-
-
-
- {{ scope.row.siteStatus }}
-
-
-
-
-
- {{ scope.row.notes || '-' }}
-
-
-
-
-
- 通过
- 拒绝
- 匹配
-
-
-
-
-
-
+
站点列表
- 已审核站点
-
+
@@ -148,26 +107,6 @@
-
-
-
-
-
-
-
- {{ n }}
-
-
-
-
- {{ u }}
-
-
-
-
- 取消
-
-
@@ -175,182 +114,81 @@
import api from '../api'
const sites = ref([])
-const submissions = ref([])
const loading = ref(false)
-const editVisible = ref(false)
-const saving = ref(false)
+const editVisible = ref(false), saving = ref(false)
const editForm = ref({ id: null, siteId: '', name: '', baseUrl: '', siteType: '', description: '' })
const siteTypes = [
- { value: 'nexusphp', label: 'NexusPHP' },
- { value: 'mtorrent', label: 'MTorrent' },
- { value: 'mteam', label: 'M-Team API' },
- { value: 'pttime', label: 'PTTime' },
- { value: 'gazelle', label: 'Gazelle' },
- { value: 'unit3d', label: 'UNIT3D' },
- { value: 'jsonapi', label: 'JSON API' },
- { value: 'unknown', label: '未知' },
+ { value: 'nexusphp', label: 'NexusPHP' }, { value: 'mtorrent', label: 'MTorrent' },
+ { value: 'mteam', label: 'M-Team API' }, { value: 'pttime', label: 'PTTime' },
+ { value: 'gazelle', label: 'Gazelle' }, { value: 'unit3d', label: 'UNIT3D' },
+ { value: 'jsonapi', label: 'JSON API' }, { value: 'unknown', label: '未知' },
]
-// 配置查看
-const matchVisible = ref(false)
-const matchQuery = ref('')
-const matchResults = ref([])
-const matchSearching = ref(false)
-let matchTargetId = ref(null)
-
-const handleMatch = (row) => {
- matchTargetId.value = row.id
- matchQuery.value = (row.names || [])[0] || ''
- matchVisible.value = true
- handleSearch()
-}
-
-const handleSearch = async () => {
- if (!matchQuery.value.trim()) { matchResults.value = []; return }
- matchSearching.value = true
- try {
- const res = await api.sites.search(matchQuery.value.trim())
- matchResults.value = res.data || []
- } catch { matchResults.value = [] }
- finally { matchSearching.value = false }
-}
-
-const handleLinkSite = async (row) => {
- try {
- await api.sites.linkChange(matchTargetId.value, row.siteId)
- ElMessage.success(`已关联到 ${row.siteId}`)
- matchVisible.value = false
- fetchAll()
- } catch (e) {
- ElMessage.error(e.response?.data?.message || '关联失败')
- }
-}
-
-const historyVisible = ref(false)
-const historyLoading = ref(false)
-const historyList = ref([])
-const historySiteId = ref('')
-
-const tagForStatus = (s) => {
- if (s === '正常') return 'success'
- if (s === '关停') return 'danger'
- return 'warning'
-}
-
-const handleViewHistory = async (row) => {
- historySiteId.value = row.siteId
- historyList.value = []
- historyVisible.value = true
- historyLoading.value = true
- try {
- const res = await api.sites.changesBySite(row.siteId)
- historyList.value = res.data || []
- } catch (e) {
- ElMessage.error('获取变更记录失败')
- } finally {
- historyLoading.value = false
- }
-}
-
-const configVisible = ref(false)
-const configLoading = ref(false)
-const configError = ref('')
-const configContent = ref('')
-const configSiteId = ref('')
+const configVisible = ref(false), configLoading = ref(false), configError = ref(''), configContent = ref(''), configSiteId = ref('')
+const historyVisible = ref(false), historyLoading = ref(false), historyList = ref([]), historySiteId = ref('')
const highlightedJson = computed(() => {
- try {
- const obj = JSON.parse(configContent.value)
- return syntaxHighlight(JSON.stringify(obj, null, 2))
- } catch {
- return escapeHtml(configContent.value)
- }
+ try { return syntaxHighlight(JSON.stringify(JSON.parse(configContent.value), null, 2)) }
+ catch { return escapeHtml(configContent.value) }
})
-const handleViewConfig = async (row) => {
- configVisible.value = true
- configSiteId.value = row.siteId
- configLoading.value = true
- configError.value = ''
- configContent.value = ''
- try {
- const res = await api.configs.bySite(row.siteId)
- const configs = res.data || []
- if (configs.length === 0) {
- configContent.value = ''
- } else {
- const latest = configs[0] // already sorted by created_at desc
- const resp = await api.configs.content(latest.siteId, latest.version)
- const content = resp.data
- configContent.value = typeof content === 'string' ? content : JSON.stringify(content, null, 2)
- }
- } catch (e) {
- configError.value = e.response?.data?.message || '获取配置失败'
- } finally {
- configLoading.value = false
- }
-}
-
-// syntax highlighting (reused from Configs.vue)
-const syntaxHighlight = (json) => {
- return escapeHtml(json).replace(
- /("(\\u[a-fA-F0-9]{4}|\\[^u]|[^"\\])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
- (match) => {
- let cls = 'number'
- if (/^"/.test(match)) {
- cls = /:$/.test(match) ? 'key' : 'string'
- } else if (/true|false/.test(match)) {
- cls = 'boolean'
- } else if (/null/.test(match)) {
- cls = 'null'
- }
- return `${match}`
- }
- )
-}
-
-const escapeHtml = (str) => {
- return str.replace(/&/g, '&').replace(//g, '>')
-}
-
const fetchAll = async () => {
loading.value = true
- try {
- const [siteRes, subRes] = await Promise.all([
- api.sites.adminList(),
- api.sites.pendingChanges(),
- ])
- sites.value = siteRes.data || []
- submissions.value = subRes.data || []
- } catch (e) {
- ElMessage.error('获取数据失败')
- } finally {
- loading.value = false
- }
+ try { const r = await api.sites.adminList(); sites.value = r.data || [] }
+ catch { ElMessage.error('获取站点列表失败') }
+ finally { loading.value = false }
}
-const handleApprove = async (row) => {
- try {
- await api.sites.approveChange(row.id)
- ElMessage.success('已通过')
- fetchAll()
- } catch (e) {
- ElMessage.error(e.response?.data?.message || '操作失败')
- }
+const handleEdit = (row) => {
+ editForm.value = { id: row.id, siteId: row.siteId, name: (row.names||[]).join('\n'), baseUrl: (row.urls||[]).join('\n'), siteType: row.siteType||'', description: row.description||'' }
+ editVisible.value = true
}
-const handleReject = async (row) => {
+const handleSave = async () => {
+ if (!editForm.value.name.trim()||!editForm.value.baseUrl.trim()) { ElMessage.warning('名称和地址不能为空'); return }
+ saving.value = true
try {
- const name = (row.names || [])[0] || row.siteId
- await ElMessageBox.confirm(`确认拒绝 "${name}" 的提交?`, '警告', { type: 'warning' })
- await api.sites.rejectChange(row.id)
- ElMessage.success('已拒绝')
- fetchAll()
- } catch (e) {
- if (e !== 'cancel') ElMessage.error(e.response?.data?.message || '操作失败')
- }
+ 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',
+ description: editForm.value.description.trim()||null,
+ })
+ ElMessage.success('保存成功'); editVisible.value = false; fetchAll()
+ } catch (e) { ElMessage.error(e.response?.data?.message||'保存失败') }
+ finally { saving.value = false }
}
+const handleViewConfig = async (row) => {
+ configVisible.value = true; configSiteId.value = row.siteId; configLoading.value = true; configError.value = ''; configContent.value = ''
+ try {
+ const r = await api.configs.bySite(row.siteId)
+ const configs = r.data || []
+ if (configs.length) {
+ const resp = await api.configs.content(configs[0].siteId, configs[0].version)
+ configContent.value = typeof resp.data==='string' ? resp.data : JSON.stringify(resp.data,null,2)
+ }
+ } catch (e) { configError.value = e.response?.data?.message||'获取配置失败' }
+ finally { configLoading.value = false }
+}
+
+const handleViewHistory = async (row) => {
+ historySiteId.value = row.siteId; historyList.value = []; historyVisible.value = true; historyLoading.value = true
+ try { const r = await api.sites.changesBySite(row.siteId); historyList.value = r.data || [] }
+ catch { ElMessage.error('获取变更记录失败') }
+ finally { historyLoading.value = false }
+}
+
+const syntaxHighlight = (json) => escapeHtml(json).replace(/("(\\u[a-fA-F0-9]{4}|\\[^u]|[^"\\])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, (match) => {
+ let cls = 'number'
+ if (/^"/.test(match)) cls = /:$/.test(match) ? 'key' : 'string'
+ else if (/true|false/.test(match)) cls = 'boolean'
+ else if (/null/.test(match)) cls = 'null'
+ return `${match}`
+})
+
+const escapeHtml = (str) => str.replace(/&/g,'&').replace(//g,'>')
+
const handleEdit = (row) => {
editForm.value = {
id: row.id,