feat: 实现友链页面功能并优化其他页面

添加友链页面表单和展示功能,包括申请友链和显示友链列表
优化首页搜索框自动聚焦和画廊页面图片样式
修复博客列表分页器显示条件
This commit is contained in:
2026-01-07 16:06:44 +08:00
parent e924fcca2b
commit 8a3cc98330
11 changed files with 270 additions and 11 deletions
+181 -8
View File
@@ -1,19 +1,190 @@
<template>
<div class="plink-page">
<h1>友链页</h1>
<!-- 友链内容 -->
<icon-loading class="zhuan text-primary w-12 h-12" />
<div class="plink-page pt-4 flex justify-between" :style="boxStyle">
<div class="left ml-4 ">
<n-card class="card w-100 shadow">
<template #header>
<div class="flex items-center gap-4 text-primary text-xl font-bold">
<icon-loading class="zhuan text-primary w-6 h-6" />
申请友链
</div>
</template>
<div>
<n-form :model="plinkData" ref="formRef" :rules="rules" label-width="80" :inline="false"
feedback-style="font-size: 12px">
<n-form-item path="name">
<template #label>
<div class="flex items-center gap-1 text-sm text-gray-500">
<user-icon></user-icon>
昵称
</div>
</template>
<n-input v-model:value="plinkData.name" placeholder="请输入你的昵称" />
</n-form-item>
<n-form-item path="title">
<template #label>
<div class="flex items-center gap-1 text-sm text-gray-500">
<site-icon></site-icon>
网站名称
</div>
</template>
<n-input v-model:value="plinkData.title" placeholder="请输入你的网站名称" />
</n-form-item>
<n-form-item path="url">
<template #label>
<div class="flex items-center gap-1 text-sm text-gray-500">
<url-icon></url-icon>
站点地址
</div>
</template>
<n-input v-model:value="plinkData.url" placeholder="请输入你的网站地址" />
</n-form-item>
<n-form-item path="avater">
<template #label>
<div class="flex items-center gap-1 text-sm text-gray-500">
<ava-icon></ava-icon>
头像链接
</div>
</template>
<n-input v-model:value="plinkData.avater" placeholder="请输入你的头像链接" />
</n-form-item>
<n-form-item path="desc">
<template #label>
<div class="flex items-center gap-1 text-sm text-gray-500">
<desc-icon></desc-icon>
站点介绍
</div>
</template>
<n-input type="textarea" :autosize="{ minRows: 2, maxRows: 3 }" maxlength="50" v-model:value="plinkData.desc"
placeholder="请用一句话简短的描述你的站点" />
</n-form-item>
<n-form-item path="tagname">
<template #label>
<div class="flex items-center gap-1 text-sm text-gray-500">
<tag-icon></tag-icon>
标签
</div>
</template>
<n-input type="textarea" :autosize="{ minRows: 2, maxRows: 3 }" maxlength="50" v-model:value="plinkData.tagname"
placeholder="输入你的标签,用逗号隔开,注意每个标签最多3个字符,且只显示前3个标签哦~" />
</n-form-item>
<n-form-item>
<n-button class="w-full" type="primary" @click="submitPut">申请友链</n-button>
</n-form-item>
</n-form>
</div>
</n-card>
<div class="mt-4 text-right text-primary text-sm font-italic">申请完不显示是因为需要审核奥~~</div>
</div>
<n-scrollbar class="h-full">
<div class="right h-full grid grid-cols-3 gap-5 flex-1 px-12">
<div
class="rounded-lg bg-white shadow flex flex-col items-center justify-center p-4 cursor-pointer transition-transform duration-300 box-border hover:-translate-y-1.5"
v-for="p in pList" :key="p.pid" @click="gotoWebsite(p.url)">
<div class="ava">
<n-avatar round size="large" :src="p.avater"></n-avatar>
</div>
<div class="text-primary">
{{ p.title }}
</div>
<em class="text-gray-400 text-sm">{{ p.name }}</em>
<div class="flex gap-1 truncate">
<div v-for="i in getTags(p.tagname)" :key="i.tid" class="flex items-center gap-1 text-deepp text-sm">
<icon-tag2 class="w-3 h-3 !text-deepp" />{{ i }}
</div>
</div>
<div class="truncate w-full text-gray-400 text-sm text-center">
{{ p.desc }}
</div>
</div>
</div>
</n-scrollbar>
</div>
</template>
<script setup lang="ts">
import avaIcon from '@/icon/plink/ava.svg';
import descIcon from '@/icon/plink/desc.svg';
import siteIcon from '@/icon/plink/site.svg';
import tagIcon from '@/icon/plink/tag.svg';
import urlIcon from '@/icon/plink/uri.svg';
import userIcon from '@/icon/plink/user.svg';
definePage({
name:'plink',
name: 'plink',
meta: {
title: '友链',
}
})
// 友链页逻辑
const boxStyle: any = ref({})
const nav: any = $store.nav.useNavStore()
const plinkData = reactive<any>({
name: '',
title: '',
avater: '',
url: '',
desc: '',
tagname: '',
});
const rules: any = {
name: [{ required: true, message: '昵称不能为空~~', trigger: ['blur'] }],
title: [{ required: true, message: '网站名称不能为空~~', trigger: ['blur'] }],
url: [{ required: true, message: '站点地址不能为空~~', trigger: ['blur'] }],
avater: [{ required: true, message: '头像链接不能为空~~', trigger: ['blur'] }],
desc: [{ required: true, message: '站点介绍不能为空~~', trigger: ['blur'] }],
tagname: [{ required: true, message: '标签不能为空~~', trigger: ['blur'] }],
}
const pList = ref<any>([])
const formRef = ref<any>(null)
async function getPlinkList() {
const res = await $http.plink.getPlinkList()
pList.value = res.data
// for (let i = 0; i < 20; i++) {
// pList.value.push(res.data[0])
// }
}
function getTags(str: any) {
str = str.replaceAll('', ',')
let tags = str.split(',').slice(0, 3)
tags.forEach((tag: any, idx: number) => {
tags[idx] = tag.trim().slice(0, 3)
});
return tags
}
function gotoWebsite(url: string) {
window.open(url, '_blank')
}
function submitPut() {
formRef.value?.validate(async (valid: boolean) => {
if (valid) {
$msg.error('请完善表单~~')
} else {
const res = await $http.plink.addPlink(plinkData)
if (res?.code !== 200) {
$msg.error(res.msg)
return
}
$msg.success(res.msg)
plinkData.name = ''
plinkData.title = ''
plinkData.avater = ''
plinkData.url = ''
plinkData.desc = ''
plinkData.tagname = ''
getPlinkList()
}
})
}
onMounted(() => {
boxStyle.value = {
height: window.innerHeight - nav.navH - 1 + 'px',
}
getPlinkList()
})
</script>
<style scoped>
@@ -22,6 +193,7 @@ definePage({
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
@@ -34,7 +206,8 @@ definePage({
animation: spin 1.5s linear infinite;
}
:deep(.n-form-item-feedback__line) {
text-align: right;
font-size: 12px !important;
}
</style>