Files
par/web/src/views/Sites.vue

163 lines
7.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<!-- 列表模式 -->
<div v-if="!editSiteId">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
<div style="display:flex;align-items:center;gap:12px">
<h2 style="margin:0">站点列表</h2>
<el-badge v-if="pendingCount" :value="pendingCount" type="warning">
<el-button size="small" @click="$router.push('/sites/review')">变更审核</el-button>
</el-badge>
</div>
<div style="display:flex;gap:8px">
<el-button size="small" @click="refresh" :loading="loading">刷新</el-button>
<el-button size="small" type="primary" @click="newSite">新建站点</el-button>
</div>
</div>
<el-table :data="sites" stripe v-loading="loading" border empty-text="暂无站点" style="font-size:13px" :row-style="{height:'44px'}">
<el-table-column prop="siteId" label="站点ID" width="110" />
<el-table-column label="名称" min-width="130">
<template #default="s">{{ (s.row.names||[]).join(' / ') || '-' }}</template>
</el-table-column>
<el-table-column label="地址" min-width="180" show-overflow-tooltip>
<template #default="s">{{ (s.row.urls||[]).join(' / ') }}</template>
</el-table-column>
<el-table-column prop="siteType" label="类型" width="100" />
<el-table-column label="状态" width="80">
<template #default="s">
<el-tag size="small" :type="s.row.status==='active'?'success':'info'">{{ s.row.status||'active' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="适配" width="90">
<template #default="s">
<el-tag v-if="s.row.latestVersion" size="small" type="primary">v{{ s.row.latestVersion }}</el-tag>
<span v-else style="color:#909399">-</span>
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template #default="s">
<el-button size="small" @click="editSite(s.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 编辑模式:全屏双栏 -->
<div v-else style="display:flex;flex-direction:column;height:calc(100vh - 80px)">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
<h2 style="margin:0">站点编辑: {{ editSiteId }}</h2>
<div style="display:flex;gap:8px">
<el-button size="small" type="primary" @click="saveSite" :loading="saving">保存</el-button>
<el-button size="small" @click="editSiteId=''">返回列表</el-button>
</div>
</div>
<div style="display:flex;gap:12px;flex:1;min-height:0">
<div style="flex:0 0 400px;display:flex;flex-direction:column;min-height:0">
<div style="font-size:13px;font-weight:500;margin-bottom:4px">JSON 配置</div>
<div style="flex:1;min-height:0;border-radius:4px;overflow:hidden">
<CodeEditor v-model="editorJson" language="json" />
</div>
</div>
<div style="flex:1;overflow:auto;min-height:0">
<div style="font-size:13px;font-weight:500;margin-bottom:8px;display:flex;gap:8px;align-items:center">
<span>表单编辑(双向同步)</span>
<el-tag v-if="jsonError" type="danger" size="small">JSON解析错误</el-tag>
</div>
<el-form v-if="!jsonError" label-width="80px" label-position="left" size="small">
<el-form-item label="站点ID">
<el-input v-model="form.siteId" @input="formToJson" />
</el-form-item>
<el-form-item label="名称">
<el-input v-model="form.namesStr" type="textarea" :rows="2" @input="formToJson" placeholder="一行一个" />
</el-form-item>
<el-form-item label="地址">
<el-input v-model="form.urlsStr" type="textarea" :rows="2" @input="formToJson" placeholder="一行一个" />
</el-form-item>
<el-form-item label="标签">
<el-input v-model="form.tagsStr" @input="formToJson" placeholder="用逗号分隔" />
</el-form-item>
<el-form-item label="简介">
<el-input v-model="form.desc" type="textarea" :rows="2" @input="formToJson" />
</el-form-item>
<el-form-item label="类型">
<el-select v-model="form.siteType" @change="formToJson" style="width:100%">
<el-option v-for="t in ['nexusphp','mtorrent','gazelle','unit3d','unknown']" :key="t" :label="t" :value="t" />
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="form.status" @change="formToJson" style="width:100%">
<el-option label="活跃" value="active" /><el-option label="弃用" value="deprecated" />
</el-select>
</el-form-item>
</el-form>
</div>
</div>
</div>
</template>
<script setup>
import api from '../api'
const loading = ref(false), saving = ref(false), pendingCount = ref(0)
const sites = ref([]), editSiteId = ref(''), editorJson = ref('{}'), jsonError = ref(false)
const form = reactive({ siteId:'', namesStr:'', urlsStr:'', tagsStr:'', desc:'', siteType:'nexusphp', status:'active' })
let syncing = false
const jsonToForm = () => {
try {
const s = JSON.parse(editorJson.value)
form.siteId = s.siteId||''; form.namesStr = (s.names||[]).join('\n'); form.urlsStr = (s.urls||[]).join('\n')
form.tagsStr = (s.tags||[]).join(','); form.desc = s.description||''; form.siteType = s.siteType||'nexusphp'; form.status = s.status||'active'
jsonError.value = false
} catch { jsonError.value = true }
}
const formToJson = () => {
if (syncing) return; syncing = true
jsonError.value = false
editorJson.value = JSON.stringify({
siteId: form.siteId, names: (form.namesStr||'').split('\n').map(s=>s.trim()).filter(Boolean),
urls: (form.urlsStr||'').split('\n').map(s=>s.trim()).filter(Boolean),
tags: (form.tagsStr||'').split(',').map(s=>s.trim()).filter(Boolean),
description: form.desc||'', siteType: form.siteType, status: form.status,
}, null, 2)
nextTick(() => { syncing = false })
}
const jsonToFormDebounced = null
watch(editorJson, () => { if (!syncing) jsonToForm() })
const refresh = async () => {
const refresh = async () => {
loading.value = true
try {
const r = await api.raw.get('/admin/sites'); sites.value = r?.data || r || []
try { pendingCount.value = ((await api.sites.pendingChanges()).data||[]).length } catch { pendingCount.value=0 }
} catch { ElMessage.error('加载失败') }
finally { loading.value = false }
}
const editSite = (row) => {
editSiteId.value = row.siteId
editorJson.value = JSON.stringify({siteId:row.siteId,names:row.names,urls:row.urls,tags:row.tags||[],description:row.description||'',siteType:row.siteType,status:row.status||'active'}, null, 2)
}
const newSite = () => {
editSiteId.value = '(新建)'
editorJson.value = JSON.stringify({siteId:'',names:[''],urls:[''],siteType:'nexusphp',status:'active'}, null, 2)
}
const saveSite = async () => {
saving.value = true
try {
const s = JSON.parse(editorJson.value)
if (!s.siteId) { ElMessage.warning('siteId不能为空'); return }
await api.raw.put('/admin/sites/'+s.siteId, {names:s.names||[],urls:s.urls||[],tags:s.tags||[],description:s.description||'',siteType:s.siteType||'unknown',status:s.status||'active'})
ElMessage.success('已保存'); editSiteId.value = ''; refresh()
} catch { ElMessage.error('保存失败') }
finally { saving.value = false }
}
onMounted(refresh)
</script>