作者 xiaoqiu

优化了seo搜索引擎

# API地址
NUXT_API_URL="/dev-api"
\ No newline at end of file
NUXT_API_URL="/dev-api"
NUXT_BASE_URL="http://localhost:3666"
\ No newline at end of file
... ...
# API地址
NUXT_API_URL="http://htai.aiboxgo.com"
\ No newline at end of file
NUXT_API_URL="http://htai.aiboxgo.com"
NUXT_BASE_URL="http://htai.aiboxgo.com"
\ No newline at end of file
... ...
不能预览此文件类型
// 获取广告信息
import { useGet } from "~/utils/request";
export const getAdList = async () => {
const {rows} = await useGet('/dh/ad/listFrontAd')
return rows
}
\ No newline at end of file
... ...
export interface adListType {
id: number
code: string
name: string
width: number
height: number
frontAdVos: frontAdVosType[]
}
export interface frontAdVosType {
id: number
positions: number
link: string
image: string
title: string
sort: number
}
\ No newline at end of file
... ...
... ... @@ -25,6 +25,7 @@ export interface appDetail {
link: string
isRecommend?: string
types: Types[]
updateTime? :string
}
export interface Types{
... ...
... ... @@ -14,13 +14,6 @@ const sortList = useState<classifyType[]>("sortTree");
webSite.value = await getWebSite();
sortList.value = await getClassifyList();
useHead({
title: webSite.value.webname,
meta: [
{ name: "description", content: webSite.value.webdescription },
{ name: "keywords", content: webSite.value.webkeywords },
],
});
</script>
<style>
... ...
<template>
<!-- 轮播容器,固定1920x300尺寸 -->
<div
v-if="adSwiperList && adSwiperList.frontAdVos.length > 0 && isShowAD"
class="banner-container"
>
<el-carousel
:height="`${height}px`"
indicator-position="none"
autoplay
:interval="5000"
arrow="never"
>
<!-- 遍历轮播数据 -->
<el-carousel-item v-for="item in adSwiperList.frontAdVos" :key="item.id">
<a :href="item.link" target="_blank" class="banner-link">
<img
:src="config.public.apiUrl + item.image"
:alt="item.title"
class="banner-image aspect-[4/1] max-[768px]:aspect-[2/1]"
loading="lazy"
/>
</a>
</el-carousel-item>
</el-carousel>
<!-- 关闭广告按钮 -->
<div class="close-button" @click="isShowAD = false">
<span class="close-icon">x</span>
<span class="close-text">关闭</span>
</div>
</div>
</template>
<script setup lang="ts">
import type { adListType } from "~/api/types/ad";
defineProps<{
adSwiperList: adListType | null;
}>();
// 定义轮播数据类型
interface BannerItem {
title: string;
link: string;
image: string;
}
const isShowAD = ref(true);
const config = useRuntimeConfig();
// 响应式宽度
const windowWidth = ref(0);
// 监听窗口大小变化
onMounted(() => {
windowWidth.value = window.innerWidth;
window.addEventListener("resize", () => {
windowWidth.value = window.innerWidth;
});
});
onUnmounted(() => {
window.removeEventListener("resize", () => {
windowWidth.value = window.innerWidth;
});
});
// 计算属性:高度 = 浏览器宽度 * 1/4
const height = computed(() => {
if (windowWidth.value < 768) {
return Math.floor((windowWidth.value * 1) / 2);
} else {
return Math.floor((windowWidth.value * 1) / 4);
}
});
</script>
<style scoped lang="less">
/* 容器样式:适配1920宽度,居中显示 */
.banner-container {
width: 100%;
max-width: 1920px;
margin: 0 auto;
overflow: hidden;
margin-bottom: 30px;
position: relative;
}
/* 广告链接样式 */
.banner-link {
display: block;
width: 100%;
height: 100%;
}
/* 图片样式:适配轮播容器,保持1920x300比例 */
.banner-image {
width: 100%;
height: 100%;
object-fit: cover; /* 保证图片不变形 */
}
.close-button {
position: absolute;
top: 4px;
right: 4px;
display: flex;
align-items: center;
font-size: 14px;
padding: 2px 12px;
background-color: rgba(255, 255, 255, 0.35);
border-radius: 10px;
vertical-align: middle;
.close-icon {
font-size: 20px;
margin-right: 2px;
color: red;
margin-top: -2px;
}
}
@media screen and (max-width: 768px) {
.banner-image {
object-fit: contain;
}
}
</style>
... ...
... ... @@ -4,7 +4,7 @@
<div class="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-4 gap-8">
<div>
<h3 class="text-xl font-bold mb-4">
{{ webSite.webname }}
{{ webSite.webname.slice(0, 7) }}
</h3>
<p class="text-gray-400">
提供安全、快速的网址跳转服务,保护您的隐私安全。
... ...
... ... @@ -5,10 +5,18 @@
>
<div class="mx-auto md:px-6 px-3 py-3 flex items-center justify-between">
<NuxtLink to="/" class="flex items-center space-x-2">
<el-icon :size="24"><Promotion /></el-icon>
<h1 class="md:text-xl text-base font-bold">
<img
width="30"
height="30"
src="../../assets/Annie-logo.jpg"
alt="Annie-logo"
/>
<h1 style="font-size: 0px">
{{ webSite.webname }}
</h1>
<h2 class="md:text-xl text-base font-bold">
{{ webSite.webname.slice(0, 7) }}
</h2>
</NuxtLink>
<div class="flex items-center gap-2">
... ...
<template>
<nav
class="max-[768px]:flex-[0] scroll-container w-56 bg-white shadow-lg h-[calc(100vh-4rem)] sticky top-16 overflow-y-auto"
class="max-[768px]:flex-[0] flex-shrink-0 scroll-container w-56 bg-white shadow-lg h-[calc(100vh-4rem)] sticky top-16 overflow-y-auto"
>
<div class="md:p-4 p-2">
<h2 class="text-lg font-semibold mb-4 text-gray-700">工具分类</h2>
<ul class="space-y-1">
<li v-for="(category, index) in sortList" :key="index">
<ul id="menu" class="space-y-1">
<li
:id="`menu-${category.id}`"
v-for="(category, index) in sortList"
:key="index"
class="menu-item"
>
<a
:href="`#term-${category.id}`"
@click.stop="toggleCategory($event, category.id, index)"
... ... @@ -41,6 +46,7 @@
<li
v-for="(subItem, subIndex) in category.children"
:key="subItem.id"
:id="`menu-${category.id}-${subItem.id}`"
>
<a
:href="`#term-${category.id}-${subItem.id}`"
... ...
... ... @@ -40,12 +40,12 @@
<div class="flex-auto"></div>
<a
class="hidden md:block text-xs ml-2 text-[#282a2d] hover:text-[#5961f9]"
:href="`/category/${childAlias}`"
:href="`${config.public.baseUrl}/category/${childAlias}`"
>查看更多 &gt;&gt;</a
>
<a
class="md:hidden text-xs ml-2 text-[#282a2d] hover:text-[#5961f9]"
:href="`/category/${childAlias}`"
:href="`${config.public.baseUrl}/category/${childAlias}`"
>&gt;&gt;</a
>
</div>
... ... @@ -76,14 +76,15 @@
>
<template #reference>
<a
:href="'/details/' + appItem.id"
:href="config.public.baseUrl + '/site-details/' + appItem.id"
target="_blank"
@click.stop="onNuxtLink"
>
<div class="group p-3">
<div class="flex items-start space-x-4">
<img
:src="config.public.baseUrl + appItem.image"
loading="lazy"
:src="config.public.apiUrl + appItem.image"
:alt="appItem.title"
class="w-10 h-10 md:w-14 md:h-14 object-cover rounded-lg"
/>
... ... @@ -104,11 +105,16 @@
</a>
</template>
</el-popconfirm>
<a v-else :href="'/details/' + appItem.id" target="_blank">
<a
v-else
:href="config.public.baseUrl + '/site-details/' + appItem.id"
target="_blank"
>
<div class="group p-3">
<div class="flex items-start space-x-4">
<img
:src="config.public.baseUrl + appItem.image"
loading="lazy"
:src="config.public.apiUrl + appItem.image"
:alt="appItem.title"
class="w-10 h-10 md:w-14 md:h-14 object-cover rounded-lg"
/>
... ... @@ -146,7 +152,7 @@ function onNuxtLink(event: any) {
}
// 点击确认跳转
function onConfirm(id: number) {
window.open(`/details/${id}`);
window.open(`/site-details/${id}`);
}
// 导航样式内容
const currentFilter = ref(0);
... ...
... ... @@ -13,12 +13,12 @@
<div class="flex-auto"></div>
<a
class="hidden md:block text-xs ml-2 text-[#282a2d] hover:text-[#5961f9]"
:href="`/category/${childData.alias}`"
:href="`${config.public.baseUrl}/category/${childData.alias}`"
>查看更多 &gt;&gt;</a
>
<a
class="md:hidden text-xs ml-2 text-[#282a2d] hover:text-[#5961f9]"
:href="`/category/${childData.alias}`"
:href="`${config.public.baseUrl}/category/${childData.alias}`"
>&gt;&gt;</a
>
</div>
... ... @@ -46,14 +46,15 @@
>
<template #reference>
<a
:href="'/details/' + item.id"
:href="'/site-details/' + item.id"
target="_blank"
@click.stop="onNuxtLink"
>
<div class="group p-3">
<div class="flex items-start space-x-4">
<img
:src="config.public.baseUrl + item.image"
loading="lazy"
:src="config.public.apiUrl + item.image"
:alt="item.title"
class="w-10 h-10 md:w-14 md:h-14 object-cover rounded-lg"
/>
... ... @@ -74,11 +75,12 @@
</a>
</template>
</el-popconfirm>
<a v-else :href="'/details/' + item.id" target="_blank">
<a v-else :href="'/site-details/' + item.id" target="_blank">
<div class="group p-3">
<div class="flex items-start space-x-4">
<img
:src="config.public.baseUrl + item.image"
loading="lazy"
:src="config.public.apiUrl + item.image"
:alt="item.title"
class="w-10 h-10 md:w-14 md:h-14 object-cover rounded-lg"
/>
... ... @@ -113,6 +115,6 @@ function onNuxtLink(event: any) {
}
// 点击确认跳转
function onConfirm(id: number) {
window.open(`/details/${id}`);
window.open(`/site-details/${id}`);
}
</script>
... ...
... ... @@ -52,14 +52,15 @@
>
<template #reference>
<a
:href="'/details/' + item.id"
:href="config.public.baseUrl + '/site-details/' + item.id"
target="_blank"
@click.stop="onNuxtLink"
>
<div class="group p-3">
<div class="flex items-start space-x-4">
<img
:src="config.public.baseUrl + item.image"
loading="lazy"
:src="config.public.apiUrl + item.image"
:alt="item.title"
class="w-10 h-10 md:w-14 md:h-14 object-cover rounded-lg"
/>
... ... @@ -80,11 +81,16 @@
</a>
</template>
</el-popconfirm>
<a v-else :href="'/details/' + item.id" target="_blank">
<a
v-else
:href="config.public.baseUrl + '/site-details/' + item.id"
target="_blank"
>
<div class="group p-3">
<div class="flex items-start space-x-4">
<img
:src="config.public.baseUrl + item.image"
loading="lazy"
:src="config.public.apiUrl + item.image"
:alt="item.title"
class="w-10 h-10 md:w-14 md:h-14 object-cover rounded-lg"
/>
... ... @@ -122,6 +128,6 @@ function onNuxtLink(event: any) {
}
// 点击确认跳转
function onConfirm(id: number) {
window.open(`/details/${id}`);
window.open(`/site-details/${id}`);
}
</script>
... ...
... ... @@ -4,7 +4,9 @@
<AppSidebar />
<div class="w-full flex-1">
<slot></slot>
<main class="w-full">
<slot></slot>
</main>
<AppFooter />
</div>
</div>
... ...
... ... @@ -3,22 +3,29 @@ export default defineNuxtConfig({
ssr: true,
runtimeConfig: {
public: {
baseUrl: process.env.NUXT_API_URL,
baseUrl: process.env.NUXT_BASE_URL,
apiUrl: process.env.NUXT_API_URL,
}
},
devtools: { enabled: true },
devtools: { enabled: false },
modules: [
'@nuxtjs/tailwindcss',
'@element-plus/nuxt'
],
features: {
inlineStyles: false,
devLogs: false,
},
devServer: {
host: 'localhost',
port: 3666
},
nitro: {
compressPublicAssets: false,
devProxy: {
'/dev-api': {
target: 'http://192.168.2.15:35273/',
// target: 'http://192.168.2.15:35273/',
target: 'http://htai.aiboxgo.com/',
changeOrigin: true,
}
... ... @@ -26,14 +33,15 @@ export default defineNuxtConfig({
// 该配置用于服务端请求转发
routeRules: {
'/dev-api/**': {
proxy: 'http://192.168.2.15:35273/**'
// proxy: 'http://192.168.2.15:35273/**'
proxy: 'http://htai.aiboxgo.com/**'
},
},
prerender: {
routes: ['/sitemap.xml'],
// 从预渲染中排除 sitemap.xml,让它动态生成
ignore: ['/sitemap.xml']
}
},
},
css: [
'~/assets/iconfonts/iconfont.css',
... ... @@ -43,25 +51,28 @@ export default defineNuxtConfig({
],
app: {
head: {
title: 'Annie网站',
title: 'Annie网站 - 优质 AI 工具集合、AI 资源网站、AI 导航平台',
htmlAttrs: {
lang: 'en'
lang: 'zh-CN'
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '提供市面上最简洁的导航系统' },
{ name: 'format-detection', content: 'telephone=no' },
{ name: 'keywords', content: '提供市面上最简洁的导航系统,一个完全免费的导航站'}
{ name: "renderer", content: "webkit"},
{ name: "force-rendering", content: "webkit"},
{ "http-equiv":"X-UA-Compatible", content:"IE=edge, chrome=1" },
{ name: 'viewport', content:"width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" },
{ name:'robots', content:'max-image-preview:large'}
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
{ rel: 'canonical', href: 'https://aiboxgo.com/' }
],
script: [
{
src: "/js/translate.js",
},
]
}
script: [{ src: "/js/translate.js"}]
},
rootId: 'annie',
},
build: {
transpile: []
}
})
... ...
<script lang="ts" setup>
import type { appListType, appType } from "~/api/types/app";
import type { adListType } from "~/api/types/ad";
import { getAppList, getAllApp } from "~/api/app";
import type { webSiteType } from "~/api/types/webSite";
import { getAdList } from "~/api/ad";
const recommendList = ref<appType[]>([]);
const appList = ref<appListType[]>([]);
const adList = ref<adListType | null>(null);
// 获取轮播广告
const adRes = await getAdList();
adList.value = adRes[0];
// 获取推荐应用
const recommendRes = await getAppList({
pageSize: 10,
... ... @@ -13,11 +20,54 @@ recommendList.value = recommendRes.rows;
// 获取全部应用
const allRes = await getAllApp();
appList.value = allRes.data;
const webSite = useState<webSiteType>("webSite");
useHead({
title: webSite.value.webname,
meta: [
{ name: "description", content: webSite.value.webdescription },
{ name: "keywords", content: webSite.value.webkeywords },
{ name: "format-detection", content: "telephone=no" },
{ name: "referrer", content: "origin-when-cross-origin" },
{
name: "robots",
content:
"follow, index, max-snippet:-1, max-video-preview:-1, max-image-preview:large",
},
{ property: "og:locale", content: "zh_CN" },
{
property: "og:title",
content: "Annie网站 - 优质 AI 工具集合、AI 资源网站、AI 导航平台",
},
{
property: "og:description",
content:
"专业 AI 工具导航网站,汇集全网优质 AI 工具,包含 AI 写作、AI 绘画、AI 视频、AI 对话、AI 代码、AI 设计、AI 办公等各类 AI 资源,让你快速找到好用的 AI 工具。",
},
// { property: 'og:image', content: 'https://aiboxgo.com/images/logo.png'},
{ property: "og:url", content: "https://aiboxgo.com/" },
{ property: "og:site_name", content: "Annie网站" },
{ property: "og:type", content: "website" },
{ property: "twitter:card", content: "summary_large_image" },
{
property: "twitter:title",
content: "Annie网站 - 优质 AI 工具集合、AI 资源网站、AI 导航平台",
},
{
property: "twitter:description",
content:
"专业 AI 工具导航网站,汇集全网优质 AI 工具,包含 AI 写作、AI 绘画、AI 视频、AI 对话、AI 代码、AI 设计、AI 办公等各类 AI 资源,让你快速找到好用的 AI 工具。",
},
],
});
</script>
<template>
<div class="flex flex-col min-h-screen bg-white">
<main class="flex-grow md:p-6 p-2 bg-white">
<!-- 轮播广告区域 -->
<ADSwiperCarousel :adSwiperList="adList" />
<!-- Banner 区域 -->
<HomeBanner />
<!-- 广告区域 -->
... ...
... ... @@ -49,6 +49,8 @@ const detailRes = await getAppDetail(Number(route.params.id));
DetailData.value = detailRes.data;
DetailData.value.types = mergeDuplicates(detailRes.data.types);
console.log("详情数据", DetailData.value);
useHead({
title: DetailData.value.popupContent
? `${DetailData.value.title} - ${DetailData.value.popupContent}`
... ... @@ -56,16 +58,35 @@ useHead({
meta: [
{ name: "description", content: DetailData.value.description },
{
name: "og:title",
content: `${DetailData.value.title}-${DetailData.value.popupContent}`,
property: "og:title",
content: `${DetailData.value.title}${
DetailData.value.popupContent != null
? ` - ${DetailData.value.popupContent}`
: `- ${DetailData.value.description} | ${webSite.value.webname.slice(
0,
7
)}`
}`,
},
{ name: "og:description", content: DetailData.value.description },
{ property: "og:description", content: DetailData.value.description },
{ property: "og:type", content: "article" },
{
name: "og:image",
property: "og:image",
content: config.public.baseUrl + DetailData.value.image,
},
{ name: "og:url", content: route.fullPath },
{ name: "og:site_name", content: webSite.value.webname },
{ property: "og:url", content: config.public.baseUrl + route.fullPath },
{ property: "og:site_name", content: webSite.value.webname.slice(0, 7) },
{ property: "og:updated_time", content: DetailData.value.updateTime },
{
property: "article:published_time",
content: DetailData.value.updateTime,
},
],
link: [
{
rel: "canonical",
href: config.public.baseUrl + route.fullPath,
},
],
});
</script>
... ... @@ -80,7 +101,7 @@ useHead({
>
<div class="flex items-center space-x-4">
<img
:src="config.public.baseUrl + DetailData.image"
:src="config.public.apiUrl + DetailData.image"
:alt="DetailData.title"
class="w-16 h-16 object-contain"
/>
... ... @@ -156,7 +177,7 @@ useHead({
</div>
</div> -->
<div class="md:max-w-5xl mx-auto md:p-8 p-2 w-full">
<div v-html="DetailData.content"></div>
<article v-html="DetailData.content"></article>
</div>
</main>
</main>
... ...
不能预览此文件类型
... ... @@ -6,7 +6,7 @@ const useMyfetch = async (url: any, options?: any, headers?: any) => {
try {
loadingInstance = ElLoading.service()
const config = useRuntimeConfig() // 3.0正式版环境变量要从useRuntimeConfig里的public拿
const reqUrl = config.public.baseUrl + url // 你的接口地址
const reqUrl = config.public.apiUrl + url // 你的接口地址
// 不设置key,始终拿到的都是第一个请求的值,参数一样则不会进行第二次请求
// 可以设置默认headers例如
... ...