feat(admin): 首页编辑支持链接选择器(本站页面/他站首页/文件)与试跳

Made-with: Cursor
This commit is contained in:
whm
2026-03-19 16:47:40 +08:00
parent 6df5cf029d
commit 88f9d42f91
3 changed files with 170 additions and 72 deletions

View File

@@ -30,9 +30,21 @@
</el-form-item>
<el-form-item label="导航链接">
<div v-for="(link, i) in form.nav_links" :key="i" style="display: flex; gap: 8px; margin-bottom: 8px">
<el-input v-model="link.label" placeholder="Label" style="width: 120px" />
<el-input v-model="link.url" placeholder="URL" style="flex: 1" />
<div
v-for="(link, i) in form.nav_links"
:key="i"
style="display: flex; gap: 8px; margin-bottom: 8px; align-items: center; flex-wrap: wrap"
>
<el-input v-model="link.label" placeholder="显示文字" style="width: 120px" />
<el-input v-model="link.url" placeholder="路径或外链,可点「选择链接」" style="flex: 1; min-width: 160px" />
<el-button type="primary" link @click="openLinkPicker({ type: 'nav', index: i })">选择链接</el-button>
<el-link
v-if="previewReady(link.url)"
type="primary"
:href="previewHref(link.url)"
target="_blank"
rel="noopener noreferrer"
>试跳</el-link>
<el-button link type="danger" @click="form.nav_links.splice(i, 1)">删除</el-button>
</div>
<el-button link type="primary" @click="form.nav_links.push({ label: '', url: '#' })">+ 添加链接</el-button>
@@ -43,16 +55,32 @@
<el-input v-model="form.download_text" placeholder="START EXPLORING" />
</el-form-item>
<el-form-item label="按钮链接">
<el-input v-model="form.download_url" placeholder="#" style="flex: 1" />
<el-button type="primary" link @click="openFilePicker('download')">选择可下载文件</el-button>
<div style="display: flex; gap: 8px; align-items: center; flex-wrap: wrap; width: 100%">
<el-input v-model="form.download_url" placeholder="#" style="flex: 1; min-width: 200px" />
<el-button type="primary" link @click="openLinkPicker({ type: 'download' })">选择链接</el-button>
<el-link
v-if="previewReady(form.download_url)"
type="primary"
:href="previewHref(form.download_url)"
target="_blank"
rel="noopener noreferrer"
>试跳</el-link>
</div>
</el-form-item>
<el-divider content-position="left">平台轨道</el-divider>
<el-form-item label="平台列表">
<div v-for="(p, i) in form.platforms" :key="i" style="display: flex; gap: 8px; margin-bottom: 8px; align-items: center">
<el-input v-model="p.name" placeholder="如 WINDOWS" style="width: 140px" />
<el-input v-model="p.url" placeholder="链接" style="flex: 1" />
<el-button type="primary" link @click="openFilePicker('platform', i)">选择文件</el-button>
<el-input v-model="p.url" placeholder="链接" style="flex: 1; min-width: 140px" />
<el-button type="primary" link @click="openLinkPicker({ type: 'platform', index: i })">选择链接</el-button>
<el-link
v-if="previewReady(p.url)"
type="primary"
:href="previewHref(p.url)"
target="_blank"
rel="noopener noreferrer"
>试跳</el-link>
<el-button link type="danger" @click="form.platforms.splice(i, 1)">删除</el-button>
</div>
<el-button link type="primary" @click="form.platforms.push({ name: '', url: '#' })">+ 添加平台</el-button>
@@ -87,41 +115,29 @@
<el-empty v-else description="请先选择站点" />
</el-card>
<el-dialog v-model="filePickerVisible" title="选择可下载文件" width="640px">
<el-table
v-loading="fileListLoading"
:data="downloadableFiles"
highlight-current-row
@current-change="onSelectFile"
>
<el-table-column property="name" label="文件名" min-width="180" />
<el-table-column property="file_path" label="路径" min-width="200" show-overflow-tooltip />
<el-table-column label="操作" width="100">
<template #default="{ row }">
<el-button type="primary" link @click="confirmSelectFile(row)">选择</el-button>
</template>
</el-table-column>
</el-table>
<el-empty v-if="!fileListLoading && downloadableFiles.length === 0" description="暂无可下载文件,请在文件管理中上传并勾选“允许下载”" />
</el-dialog>
<LinkPickerDialog
v-model="linkPickerVisible"
:site-id="siteId"
@select="onLinkPicked"
/>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { getSites, getOfficialSite, getHomepage, updateHomepage, getDownloadableAssets } from '../../api/admin'
import { getSites, getOfficialSite, getHomepage, updateHomepage } from '../../api/admin'
import { useAuthStore } from '../../stores/auth'
import LinkPickerDialog from '../../components/LinkPickerDialog.vue'
const siteId = ref('')
const sites = ref([])
const saving = ref(false)
const downloading = ref(false)
const formRef = ref(null)
const filePickerVisible = ref(false)
const fileListLoading = ref(false)
const downloadableFiles = ref([])
const filePickerTarget = ref({ type: 'download' }) // { type: 'download' } | { type: 'platform', index: number }
const linkPickerVisible = ref(false)
/** @type {import('vue').Ref<{ type: 'nav' | 'download' | 'platform'; index?: number }>} */
const linkPickTarget = ref({ type: 'download' })
const defaultForm = () => ({
logo_text: 'YUHENG ONE',
@@ -227,43 +243,34 @@ const handleDownload = async () => {
}
}
function openFilePicker(type, index) {
filePickerTarget.value = type === 'platform' ? { type: 'platform', index } : { type: 'download' }
filePickerVisible.value = true
fetchDownloadableFiles()
function openLinkPicker(target) {
linkPickTarget.value = target
linkPickerVisible.value = true
}
async function fetchDownloadableFiles() {
if (!siteId.value) return
fileListLoading.value = true
try {
const res = await getDownloadableAssets(siteId.value)
downloadableFiles.value = res.list || []
} catch (e) {
ElMessage.error(e.message)
downloadableFiles.value = []
} finally {
fileListLoading.value = false
}
}
function buildDownloadUrl(asset) {
return `/api/web/sites/${siteId.value}/assets/${asset.id}/download`
}
function confirmSelectFile(asset) {
const url = buildDownloadUrl(asset)
if (filePickerTarget.value.type === 'download') {
function onLinkPicked(url) {
const t = linkPickTarget.value
if (t.type === 'nav' && typeof t.index === 'number') {
form.nav_links[t.index].url = url
} else if (t.type === 'download') {
form.download_url = url
} else {
form.platforms[filePickerTarget.value.index].url = url
} else if (t.type === 'platform' && typeof t.index === 'number') {
form.platforms[t.index].url = url
}
filePickerVisible.value = false
ElMessage.success('已选择:' + asset.name)
ElMessage.success('已填入链接')
}
function onSelectFile(row) {
if (row) confirmSelectFile(row)
function previewReady(url) {
const u = (url || '').trim()
return Boolean(u && u !== '#')
}
/** 后台试跳:相对路径用当前浏览器域名拼接 */
function previewHref(url) {
const u = (url || '').trim()
if (/^https?:\/\//i.test(u)) return u
if (u.startsWith('/')) return `${window.location.origin}${u}`
return u
}
onMounted(() => {