151 lines
7.3 KiB
Vue
151 lines
7.3 KiB
Vue
<template>
|
|
<div>
|
|
<h2>站点审核</h2>
|
|
<el-card style="margin-top: 20px" v-if="submissions.length === 0">
|
|
<div style="text-align:center;padding:40px;color:#909399">暂无待审核提交</div>
|
|
</el-card>
|
|
<el-card style="margin-top: 20px" v-else>
|
|
<el-table :data="submissions" stripe size="small">
|
|
<el-table-column prop="siteId" label="站点ID" width="120" />
|
|
<el-table-column label="名称" width="180">
|
|
<template #default="scope"><div v-for="(n,i) in scope.row.names" :key="i" class="text-xs">{{ n }}</div></template>
|
|
</el-table-column>
|
|
<el-table-column label="地址" min-width="200">
|
|
<template #default="scope"><div v-for="(u,i) in scope.row.urls" :key="i" class="text-xs">{{ u }}</div></template>
|
|
</el-table-column>
|
|
<el-table-column label="站点状态" width="90">
|
|
<template #default="scope">
|
|
<el-tag size="small" :type="scope.row.siteStatus==='正常'?'success':scope.row.siteStatus==='关停'?'danger':'warning'">{{ scope.row.siteStatus }}</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="备注" min-width="120">
|
|
<template #default="scope"><span class="text-xs text-muted">{{ scope.row.notes||'-' }}</span></template>
|
|
</el-table-column>
|
|
<el-table-column prop="createdAt" label="提交时间" width="160" />
|
|
<el-table-column label="操作" width="210">
|
|
<template #default="scope">
|
|
<el-button size="small" type="success" @click="handleApprove(scope.row)">通过</el-button>
|
|
<el-button size="small" type="danger" @click="handleReject(scope.row)">拒绝</el-button>
|
|
<el-button size="small" @click="handleMatch(scope.row)">匹配</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</el-card>
|
|
|
|
<!-- 审核编辑弹窗 -->
|
|
<el-dialog v-model="approveVisible" title="审核通过 - 编辑站点信息" width="500px" :close-on-click-modal="false">
|
|
<el-form label-width="80px">
|
|
<el-form-item label="站点ID"><el-input v-model="approveForm.siteId" disabled /></el-form-item>
|
|
<el-form-item label="名称">
|
|
<el-input v-model="approveForm.namesText" type="textarea" :rows="3" placeholder="站点名称(一行一个)" />
|
|
</el-form-item>
|
|
<el-form-item label="地址">
|
|
<el-input v-model="approveForm.urlsText" type="textarea" :rows="3" placeholder="站点地址(一行一个)" />
|
|
</el-form-item>
|
|
<el-form-item label="类型">
|
|
<el-select v-model="approveForm.siteType" style="width:100%">
|
|
<el-option v-for="t in siteTypes" :key="t.value" :label="t.label" :value="t.value" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="状态">
|
|
<el-select v-model="approveForm.status" style="width:100%">
|
|
<el-option label="活跃" value="active" />
|
|
<el-option label="弃用" value="deprecated" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="备注"><el-input v-model="approveForm.notes" type="textarea" :rows="2" /></el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<el-button @click="approveVisible = false">取消</el-button>
|
|
<el-button type="success" @click="confirmApprove" :loading="saving">确认通过</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
|
|
<el-dialog v-model="matchVisible" title="关联到已有站点" width="500px">
|
|
<el-input v-model="matchQuery" placeholder="搜索站点名称或URL..." clearable @input="handleSearch" style="margin-bottom:12px" />
|
|
<el-table :data="matchResults" stripe size="small" v-loading="matchSearching" max-height="300" @row-click="handleLinkSite" highlight-current-row>
|
|
<el-table-column prop="siteId" label="站点ID" width="120" />
|
|
<el-table-column label="名称" min-width="150"><template #default="scope"><div v-for="(n,i) in scope.row.names" :key="i" class="text-xs">{{ n }}</div></template></el-table-column>
|
|
<el-table-column label="地址" min-width="150"><template #default="scope"><div v-for="(u,i) in scope.row.urls" :key="i" class="text-xs">{{ u }}</div></template></el-table-column>
|
|
</el-table>
|
|
<template #footer><el-button @click="matchVisible = false">取消</el-button></template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import api from '../api'
|
|
|
|
const submissions = ref([])
|
|
const approveVisible = ref(false), saving = ref(false)
|
|
const approveForm = reactive({ id: null, siteId: '', namesText: '', urlsText: '', siteType: '', status: 'active', notes: '' })
|
|
const siteTypes = [
|
|
{ value: 'nexusphp', label: 'NexusPHP' }, { value: 'mtorrent', label: 'MTorrent' },
|
|
{ value: 'gazelle', label: 'Gazelle' }, { value: 'unit3d', label: 'UNIT3D' },
|
|
{ value: 'unknown', label: '未知' },
|
|
]
|
|
const matchVisible = ref(false), matchQuery = ref(''), matchResults = ref([]), matchSearching = ref(false)
|
|
let matchTargetId = null
|
|
|
|
const fetch = async () => {
|
|
try { const r = await api.sites.pendingChanges(); submissions.value = r.data || [] }
|
|
catch { ElMessage.error('获取待审核列表失败') }
|
|
}
|
|
|
|
const handleApprove = (row) => {
|
|
approveForm.id = row.id
|
|
approveForm.siteId = row.siteId
|
|
approveForm.namesText = (row.names || []).join('\n')
|
|
approveForm.urlsText = (row.urls || []).join('\n')
|
|
approveForm.siteType = row.siteType || 'unknown'
|
|
approveForm.status = row.siteStatus === '关停' ? 'deprecated' : 'active'
|
|
approveForm.notes = row.notes || ''
|
|
approveVisible.value = true
|
|
}
|
|
|
|
const confirmApprove = async () => {
|
|
const names = approveForm.namesText.split('\n').map(s => s.trim()).filter(Boolean)
|
|
const urls = approveForm.urlsText.split('\n').map(s => s.trim()).filter(Boolean)
|
|
if (names.length === 0 || urls.length === 0) { ElMessage.warning('名称和地址不能为空'); return }
|
|
saving.value = true
|
|
try {
|
|
await api.sites.approveChange(approveForm.id, {
|
|
names, urls,
|
|
siteType: approveForm.siteType || 'unknown',
|
|
status: approveForm.status,
|
|
notes: approveForm.notes || null,
|
|
})
|
|
ElMessage.success('已通过')
|
|
approveVisible.value = false
|
|
fetch()
|
|
} catch (e) { ElMessage.error(e.response?.data?.message||'操作失败') }
|
|
finally { saving.value = false }
|
|
}
|
|
|
|
const handleReject = async (row) => {
|
|
try {
|
|
await ElMessageBox.confirm(`确认拒绝 "${(row.names||[])[0]||row.siteId}" 的提交?`, '警告', { type: 'warning' })
|
|
await api.sites.rejectChange(row.id); ElMessage.success('已拒绝'); fetch()
|
|
} catch (e) { if (e !== 'cancel') ElMessage.error(e.response?.data?.message||'操作失败') }
|
|
}
|
|
|
|
const handleMatch = (row) => { matchTargetId = 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 r = await api.sites.search(matchQuery.value.trim()); matchResults.value = r.data||[] }
|
|
catch { matchResults.value = [] }
|
|
finally { matchSearching.value = false }
|
|
}
|
|
const handleLinkSite = async (row) => {
|
|
try { await api.sites.linkChange(matchTargetId, row.siteId); ElMessage.success(`已关联到 ${row.siteId}`); matchVisible.value = false; fetch() }
|
|
catch (e) { ElMessage.error(e.response?.data?.message||'关联失败') }
|
|
}
|
|
|
|
onMounted(fetch)
|
|
</script>
|
|
|
|
<style scoped>
|
|
.text-muted { color: #909399; }
|
|
</style>
|