├── public ├── _robots.txt ├── icp.png ├── beian.png ├── logo.png ├── favicon.ico └── logo.svg ├── .npmrc ├── server ├── tsconfig.json └── api │ ├── websites │ ├── index.delete.ts │ ├── index.post.ts │ ├── index.put.ts │ └── index.get.ts │ └── categorys │ ├── index.post.ts │ ├── index.delete.ts │ ├── index.put.ts │ └── index.get.ts ├── app ├── assets │ ├── images │ │ ├── dark.png │ │ ├── light.png │ │ ├── caretorys.png │ │ ├── websites.png │ │ └── vercel.svg │ ├── css │ │ └── main.css │ └── icons │ │ └── logo.svg ├── layouts │ └── default.vue ├── lib │ ├── enum.ts │ ├── constant.ts │ ├── utils.ts │ └── type.ts ├── pages │ ├── confirm │ │ └── index.vue │ ├── admin │ │ ├── index.vue │ │ └── _components │ │ │ ├── categorys │ │ │ ├── components │ │ │ │ └── EditModal.vue │ │ │ └── index.vue │ │ │ └── websites │ │ │ ├── components │ │ │ └── EditModal.vue │ │ │ └── index.vue │ ├── index.vue │ └── login │ │ └── index.vue ├── components │ ├── SocialIcon │ │ └── index.vue │ ├── SiteImage │ │ └── index.vue │ ├── JumpAdmin │ │ └── index.vue │ ├── Logout │ │ └── index.vue │ ├── Footer │ │ └── index.vue │ ├── UserAvatar │ │ └── index.vue │ ├── BackTop │ │ └── index.vue │ ├── Header │ │ └── index.vue │ ├── FullLoading │ │ └── index.vue │ ├── ColorMode │ │ └── index.vue │ ├── AnimatedContent │ │ └── index.vue │ ├── BlurText │ │ └── index.vue │ ├── SplitText │ │ └── index.vue │ ├── Squares │ │ └── index.vue │ └── SplashCursor │ │ └── index.vue └── app.vue ├── .gitignore ├── tsconfig.json ├── LICENSE ├── eslint.config.mjs ├── .release-it.json ├── package.json ├── initSupabase.sql ├── nuxt.config.ts ├── README.md └── CHANGELOG.md /public/_robots.txt: -------------------------------------------------------------------------------- 1 | User-Agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | node-linker=hoisted 3 | -------------------------------------------------------------------------------- /public/icp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baiwumm/dream-site/HEAD/public/icp.png -------------------------------------------------------------------------------- /public/beian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baiwumm/dream-site/HEAD/public/beian.png -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baiwumm/dream-site/HEAD/public/logo.png -------------------------------------------------------------------------------- /server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baiwumm/dream-site/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /app/assets/images/dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baiwumm/dream-site/HEAD/app/assets/images/dark.png -------------------------------------------------------------------------------- /app/assets/images/light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baiwumm/dream-site/HEAD/app/assets/images/light.png -------------------------------------------------------------------------------- /app/assets/images/caretorys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baiwumm/dream-site/HEAD/app/assets/images/caretorys.png -------------------------------------------------------------------------------- /app/assets/images/websites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baiwumm/dream-site/HEAD/app/assets/images/websites.png -------------------------------------------------------------------------------- /app/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | !.env.example 25 | 26 | .vscode 27 | -------------------------------------------------------------------------------- /app/lib/enum.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description: 响应状态码 3 | */ 4 | export enum RESPONSE_STATUS_CODE { 5 | SUCCESS = 200, // 请求成功 6 | FAIL = 400, // 请求失败 7 | UNAUTHORIZED = 401, // 未授权 8 | FORBIDDEN = 403, // 禁止访问 9 | NOT_FOUND = 404, // 请求资源不存在 10 | TIMEOUT = 408, // 请求超时 11 | SERVER_ERROR = 500, // 服务器异常 12 | SERVICE_UNAVAILABLE = 503, // 服务不可用 13 | GATEWAY_TIMEOUT = 504 // 网关超时 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "files": [], 4 | "references": [ 5 | { 6 | "path": "./.nuxt/tsconfig.app.json" 7 | }, 8 | { 9 | "path": "./.nuxt/tsconfig.server.json" 10 | }, 11 | { 12 | "path": "./.nuxt/tsconfig.shared.json" 13 | }, 14 | { 15 | "path": "./.nuxt/tsconfig.node.json" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /app/pages/confirm/index.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 24 | -------------------------------------------------------------------------------- /app/components/SocialIcon/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 13 | 14 | 30 | -------------------------------------------------------------------------------- /app/components/SiteImage/index.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 25 | -------------------------------------------------------------------------------- /app/components/JumpAdmin/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 28 | 31 | -------------------------------------------------------------------------------- /app/pages/admin/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 36 | -------------------------------------------------------------------------------- /app/components/Logout/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 27 | 42 | -------------------------------------------------------------------------------- /app/lib/constant.ts: -------------------------------------------------------------------------------- 1 | import type { Social } from '~/lib/type' 2 | import pkg from '../../package.json' 3 | 4 | /** 5 | * @description: 底部备案 6 | */ 7 | export const FooterRecord: Social[] = [ 8 | { 9 | icon: 'icp.png', 10 | url: 'https://beian.miit.gov.cn/#/Integrated/index', 11 | tip: '粤ICP备2023007649号-3' 12 | }, 13 | { 14 | icon: 'beian.png', 15 | url: 'https://beian.mps.gov.cn/#/query/webSearch', 16 | tip: '粤公网安备44030002003295号' 17 | } 18 | ] 19 | 20 | /** 21 | * @description: 社交图标 22 | */ 23 | export const FooterSocial: Social[] = [ 24 | { 25 | icon: 'ri:bar-chart-2-line', 26 | url: 'https://um.baiwumm.com/share/3kXaMUYbKgUFkphU', 27 | tip: '网站统计' 28 | }, 29 | { icon: 'i-ri-github-line', url: `https://github.com/${pkg.author.name}`, tip: 'Github' }, 30 | { icon: 'i-ri-mail-line', url: `mailto:${pkg.author.email}`, tip: 'Email' }, 31 | { icon: 'i-ri-quill-pen-line', url: pkg.author.url, tip: '博客' }, 32 | { icon: 'mdi:api', url: 'https://api.baiwumm.com', tip: 'Easy Api' } 33 | ] 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 谢明伟 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /server/api/websites/index.delete.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: 白雾茫茫丶 3 | * @Date: 2024-06-13 13:39:14 4 | * @LastEditors: 白雾茫茫丶 5 | * @LastEditTime: 2024-06-17 17:42:48 6 | * @Description: 删除站点 7 | */ 8 | import type { Response, WebsiteEdit, WebsiteList } from '~/lib/type' 9 | import { serverSupabaseClient } from '#supabase/server' 10 | import { RESPONSE_STATUS_CODE } from '~/lib/enum' 11 | 12 | export default defineEventHandler(async (event): Promise> => { 13 | const client = await serverSupabaseClient(event) 14 | // 得到请求体 15 | const { id }: WebsiteEdit = await readBody(event) 16 | 17 | if (!id) { 18 | return { 19 | code: RESPONSE_STATUS_CODE.FAIL, 20 | msg: 'id不能为空!' 21 | } 22 | } 23 | 24 | // 删除数据 25 | const { error } = await client.from('ds_websites').delete().eq('id', id) 26 | 27 | // 判断请求结果 28 | if (error) { 29 | throw createError({ 30 | statusCode: RESPONSE_STATUS_CODE.FAIL, 31 | statusMessage: error.message 32 | }) 33 | } 34 | 35 | // 请求成功 36 | return { 37 | code: RESPONSE_STATUS_CODE.SUCCESS, 38 | msg: '请求成功' 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import withNuxt from "./.nuxt/eslint.config.mjs"; 3 | import importPlugin from "eslint-plugin-import"; 4 | 5 | export default withNuxt( 6 | // Your custom configs here 7 | { 8 | plugins: { 9 | import: importPlugin, 10 | }, 11 | settings: { 12 | "import/resolver": { 13 | typescript: { 14 | project: "./tsconfig.json", 15 | }, 16 | node: { 17 | extensions: [".js", ".ts", ".vue", ".tsx"], 18 | }, 19 | }, 20 | }, 21 | rules: { 22 | // 核心验证规则 23 | "import/no-unresolved": "error", 24 | "import/named": "error", 25 | "import/default": "error", 26 | "import/namespace": "error", 27 | "import/export": "error", 28 | 29 | // 代码风格规则 30 | "import/order": [ 31 | "error", 32 | { 33 | groups: ["builtin", "external", "internal", "parent", "sibling", "index"], 34 | "newlines-between": "always", 35 | alphabetize: { order: "asc", caseInsensitive: true }, 36 | }, 37 | ], 38 | "import/no-duplicates": "error", 39 | "import/no-absolute-path": "error", 40 | "import/no-cycle": "warn", 41 | }, 42 | } 43 | ); 44 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": { 3 | "@release-it/conventional-changelog": { 4 | "preset": { 5 | "name": "conventionalcommits", 6 | "types": [ 7 | { "type": "feat", "section": "✨ Features | 新功能" }, 8 | { "type": "fix", "section": "🐛 Bug Fixes | Bug 修复" }, 9 | { "type": "chore", "section": "🎫 Chores | 其他更新" }, 10 | { "type": "docs", "section": "📝 Documentation | 文档" }, 11 | { "type": "style", "section": "💄 Styles | 风格" }, 12 | { "type": "refactor", "section": "♻ Code Refactoring | 代码重构" }, 13 | { "type": "perf", "section": "⚡ Performance Improvements | 性能优化" }, 14 | { "type": "test", "section": "✅ Tests | 测试" }, 15 | { "type": "revert", "section": "⏪ Reverts | 回退" }, 16 | { "type": "build", "section": "👷‍ Build System | 构建" }, 17 | { "type": "ci", "section": "🔧 Continuous Integration | CI 配置" }, 18 | { "type": "config", "section": "🔨 CONFIG | 配置" } 19 | ] 20 | }, 21 | "infile": "CHANGELOG.md", 22 | "ignoreRecommendedBump": true, 23 | "strictSemVer": true 24 | } 25 | }, 26 | "git": { 27 | "commitMessage": "chore: Release v${version}" 28 | }, 29 | "github": { 30 | "release": true, 31 | "draft": false 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /server/api/websites/index.post.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: 白雾茫茫丶 3 | * @Date: 2024-06-06 10:14:37 4 | * @LastEditors: 白雾茫茫丶 5 | * @LastEditTime: 2024-06-14 15:17:24 6 | * @Description: 新增站点列表 7 | */ 8 | import type { Response, WebsiteEdit, WebsiteList } from '~/lib/type' 9 | import { serverSupabaseClient, serverSupabaseUser } from '#supabase/server' 10 | import { RESPONSE_STATUS_CODE } from '~/lib/enum' 11 | 12 | export default defineEventHandler(async (event): Promise> => { 13 | const client = await serverSupabaseClient(event) 14 | const user = await serverSupabaseUser(event) 15 | // 得到请求体 16 | const body: WebsiteEdit = await readBody(event) 17 | 18 | // 插入数据 19 | const { data, error } = await client 20 | .from('ds_websites') 21 | .insert({ ...body, email: user?.email }) 22 | .select() 23 | 24 | // 判断请求结果 25 | if (error) { 26 | // 23505 是 PostgreSQL 的唯一性违反错误码 27 | if (error.code === '23505') { 28 | return { 29 | code: RESPONSE_STATUS_CODE.FAIL, 30 | msg: '站点名称已存在!' 31 | } 32 | } else { 33 | throw createError({ 34 | statusCode: RESPONSE_STATUS_CODE.FAIL, 35 | statusMessage: error.message 36 | }) 37 | } 38 | } 39 | 40 | // 请求成功 41 | return { 42 | code: RESPONSE_STATUS_CODE.SUCCESS, 43 | msg: '请求成功', 44 | data: data 45 | } 46 | }) 47 | -------------------------------------------------------------------------------- /server/api/categorys/index.post.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: 白雾茫茫丶 3 | * @Date: 2024-06-06 10:14:37 4 | * @LastEditors: 白雾茫茫丶 5 | * @LastEditTime: 2024-06-13 15:08:30 6 | * @Description: 新增分类列表 7 | */ 8 | import type { Response, CategoryEdit, CategoryList } from '~/lib/type' 9 | import { serverSupabaseClient, serverSupabaseUser } from '#supabase/server' 10 | import { RESPONSE_STATUS_CODE } from '~/lib/enum' 11 | 12 | export default defineEventHandler(async (event): Promise> => { 13 | const client = await serverSupabaseClient(event) 14 | const user = await serverSupabaseUser(event) 15 | // 得到请求体 16 | const body: CategoryEdit = await readBody(event) 17 | 18 | // 插入数据 19 | const { data, error } = await client 20 | .from('ds_categorys') 21 | .insert({ ...body, email: user?.email }) 22 | .select() 23 | 24 | // 判断请求结果 25 | if (error) { 26 | // 23505 是 PostgreSQL 的唯一性违反错误码 27 | if (error.code === '23505') { 28 | return { 29 | code: RESPONSE_STATUS_CODE.FAIL, 30 | msg: '分类名称已存在!' 31 | } 32 | } else { 33 | throw createError({ 34 | statusCode: RESPONSE_STATUS_CODE.FAIL, 35 | statusMessage: error.message 36 | }) 37 | } 38 | } 39 | 40 | // 请求成功 41 | return { 42 | code: RESPONSE_STATUS_CODE.SUCCESS, 43 | msg: '请求成功', 44 | data: data 45 | } 46 | }) 47 | -------------------------------------------------------------------------------- /server/api/categorys/index.delete.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: 白雾茫茫丶 3 | * @Date: 2024-06-13 13:39:14 4 | * @LastEditors: 白雾茫茫丶 5 | * @LastEditTime: 2025-07-18 14:15:00 6 | * @Description: 删除网站分类 7 | */ 8 | import type { Response, CategoryEdit, CategoryList } from '~/lib/type' 9 | import { serverSupabaseClient } from '#supabase/server' 10 | import { RESPONSE_STATUS_CODE } from '~/lib/enum' 11 | 12 | export default defineEventHandler(async (event): Promise> => { 13 | const client = await serverSupabaseClient(event) 14 | // 得到请求体 15 | const { id }: CategoryEdit = await readBody(event) 16 | 17 | if (!id) { 18 | return { 19 | code: RESPONSE_STATUS_CODE.FAIL, 20 | msg: 'id不能为空!' 21 | } 22 | } 23 | 24 | // 删除数据 25 | const { error } = await client.from('ds_categorys').delete().eq('id', id) 26 | 27 | // 判断请求结果 28 | if (error) { 29 | // 23503 是 PostgreSQL 的 waiting_resource_violation 错误码,表示外键约束被违反。 30 | if (error.code === '23503') { 31 | return { 32 | code: RESPONSE_STATUS_CODE.FAIL, 33 | msg: '该网站分类存在子站点,请删除全部站点后,再来删除!' 34 | } 35 | } else { 36 | throw createError({ 37 | statusCode: RESPONSE_STATUS_CODE.FAIL, 38 | statusMessage: error.message 39 | }) 40 | } 41 | } 42 | 43 | // 请求成功 44 | return { 45 | code: RESPONSE_STATUS_CODE.SUCCESS, 46 | msg: '请求成功' 47 | } 48 | }) 49 | -------------------------------------------------------------------------------- /app/components/Footer/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 46 | 49 | -------------------------------------------------------------------------------- /app/components/UserAvatar/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 52 | 55 | -------------------------------------------------------------------------------- /server/api/websites/index.put.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: 白雾茫茫丶 3 | * @Date: 2024-06-12 17:45:42 4 | * @LastEditors: 白雾茫茫丶 5 | * @LastEditTime: 2024-06-17 17:43:27 6 | * @Description: 更新站点列表 7 | */ 8 | import type { Response, WebsiteEdit, WebsiteList } from '~/lib/type' 9 | import { serverSupabaseClient } from '#supabase/server' 10 | import { RESPONSE_STATUS_CODE } from '~/lib/enum' 11 | 12 | export default defineEventHandler(async (event): Promise> => { 13 | const client = await serverSupabaseClient(event) 14 | // 得到请求体 15 | const { id, ...body }: WebsiteEdit = await readBody(event) 16 | 17 | if (!id) { 18 | return { 19 | code: RESPONSE_STATUS_CODE.FAIL, 20 | msg: 'id不能为空!' 21 | } 22 | } 23 | 24 | // 插入数据 25 | const { data, error } = await client 26 | .from('ds_websites') 27 | .update({ ...body, updated_at: new Date() }) 28 | .eq('id', id) 29 | .select() 30 | 31 | // 判断请求结果 32 | if (error) { 33 | // 23505 是 PostgreSQL 的唯一性违反错误码 34 | if (error.code === '23505') { 35 | return { 36 | code: RESPONSE_STATUS_CODE.FAIL, 37 | msg: '站点名称已存在!' 38 | } 39 | } else { 40 | throw createError({ 41 | statusCode: RESPONSE_STATUS_CODE.FAIL, 42 | statusMessage: error.message 43 | }) 44 | } 45 | } 46 | 47 | // 请求成功 48 | return { 49 | code: RESPONSE_STATUS_CODE.SUCCESS, 50 | msg: '请求成功', 51 | data: data 52 | } 53 | }) 54 | -------------------------------------------------------------------------------- /server/api/categorys/index.put.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: 白雾茫茫丶 3 | * @Date: 2024-06-12 17:45:42 4 | * @LastEditors: 白雾茫茫丶 5 | * @LastEditTime: 2024-06-13 17:47:25 6 | * @Description: 更新分类列表 7 | */ 8 | import type { Response, CategoryEdit, CategoryList } from '~/lib/type' 9 | import { serverSupabaseClient } from '#supabase/server' 10 | import { RESPONSE_STATUS_CODE } from '~/lib/enum' 11 | 12 | export default defineEventHandler(async (event): Promise> => { 13 | const client = await serverSupabaseClient(event) 14 | // 得到请求体 15 | const { id, ...body }: CategoryEdit = await readBody(event) 16 | 17 | if (!id) { 18 | return { 19 | code: RESPONSE_STATUS_CODE.FAIL, 20 | msg: 'id不能为空!' 21 | } 22 | } 23 | 24 | // 插入数据 25 | const { data, error } = await client 26 | .from('ds_categorys') 27 | .update({ ...body, updated_at: new Date() }) 28 | .eq('id', id) 29 | .select() 30 | 31 | // 判断请求结果 32 | if (error) { 33 | // 23505 是 PostgreSQL 的唯一性违反错误码 34 | if (error.code === '23505') { 35 | return { 36 | code: RESPONSE_STATUS_CODE.FAIL, 37 | msg: '分类名称已存在!' 38 | } 39 | } else { 40 | throw createError({ 41 | statusCode: RESPONSE_STATUS_CODE.FAIL, 42 | statusMessage: error.message 43 | }) 44 | } 45 | } 46 | 47 | // 请求成功 48 | return { 49 | code: RESPONSE_STATUS_CODE.SUCCESS, 50 | msg: '请求成功', 51 | data: data 52 | } 53 | }) 54 | -------------------------------------------------------------------------------- /app/components/BackTop/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 50 | 51 | 62 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dream-site", 3 | "private": true, 4 | "type": "module", 5 | "version": "2.2.6", 6 | "author": { 7 | "name": "baiwumm", 8 | "email": "me@baiwumm.com", 9 | "url": "https://baiwumm.com" 10 | }, 11 | "scripts": { 12 | "build": "nuxt build", 13 | "dev": "nuxt dev", 14 | "generate": "nuxt generate", 15 | "preview": "nuxt preview", 16 | "postinstall": "nuxt prepare", 17 | "release": "release-it" 18 | }, 19 | "dependencies": { 20 | "@nuxt/eslint": "1.6.0", 21 | "@nuxt/icon": "1.15.0", 22 | "@nuxt/image": "1.10.0", 23 | "@nuxt/scripts": "0.11.10", 24 | "@nuxt/ui": "^3.2.0", 25 | "@nuxtjs/color-mode": "3.5.2", 26 | "@nuxtjs/seo": "3.1.0", 27 | "@nuxtjs/supabase": "1.5.3", 28 | "@unhead/vue": "^2.0.3", 29 | "@vercel/analytics": "^1.5.0", 30 | "dayjs-nuxt": "2.1.11", 31 | "eslint": "^9.0.0", 32 | "gsap": "^3.13.0", 33 | "motion-v": "^1.5.0", 34 | "nuxt": "^4.0.0", 35 | "nuxt-clarity-analytics": "0.0.9", 36 | "nuxt-gtag": "3.0.3", 37 | "nuxt-umami": "3.2.0", 38 | "vue": "^3.5.17", 39 | "vue-router": "^4.5.1", 40 | "zod": "^4.0.5" 41 | }, 42 | "packageManager": "pnpm@8.15.5+sha1.a58c038faac410c947dbdb93eb30994037d0fce2", 43 | "devDependencies": { 44 | "@iconify-json/lucide": "^1.2.57", 45 | "@iconify-json/ri": "^1.2.5", 46 | "@release-it/conventional-changelog": "^10.0.1", 47 | "eslint-plugin-import": "^2.32.0", 48 | "release-it": "^19.0.4", 49 | "sass": "^1.89.2", 50 | "typescript": "^5.8.3", 51 | "vue-tsc": "^3.0.2" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /initSupabase.sql: -------------------------------------------------------------------------------- 1 | create table public.ds_categorys ( 2 | id uuid not null default gen_random_uuid (), 3 | name text not null, 4 | "desc" text null, 5 | created_at timestamp with time zone not null default now(), 6 | updated_at timestamp with time zone not null default now(), 7 | user_id uuid null default auth.uid (), 8 | email text null, 9 | sort smallint null, 10 | icon text null default ''::text, 11 | constraint site_category_pkey primary key (id), 12 | constraint site_category_name_key unique (name) 13 | ) TABLESPACE pg_default; 14 | 15 | create table public.ds_websites ( 16 | id uuid not null default gen_random_uuid (), 17 | created_at timestamp with time zone not null default now(), 18 | user_id uuid null default auth.uid (), 19 | email text null, 20 | name text null, 21 | "desc" text null, 22 | logo text null, 23 | tags text[] null, 24 | sort smallint null, 25 | updated_at timestamp with time zone null default now(), 26 | url text null, 27 | pinned boolean null default false, 28 | vpn boolean null default false, 29 | category_id uuid null default gen_random_uuid (), 30 | recommend boolean null default false, 31 | color text null default ''::text, 32 | "visitCount" integer null default 0, 33 | "commonlyUsed" boolean null default false, 34 | constraint websites_pkey primary key (id), 35 | constraint ds_websites_category_id_fkey foreign KEY (category_id) references ds_categorys (id) 36 | ) TABLESPACE pg_default; 37 | 38 | create index IF not exists idx_ds_websites_id on public.ds_websites using btree (id) TABLESPACE pg_default; -------------------------------------------------------------------------------- /app/components/Header/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 47 | 53 | -------------------------------------------------------------------------------- /server/api/websites/index.get.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: 白雾茫茫丶 3 | * @Date: 2024-05-29 14:39:50 4 | * @LastEditors: 白雾茫茫丶 5 | * @LastEditTime: 2025-07-15 17:54:40 6 | * @Description: 获取站点列表 7 | */ 8 | import type { Response, PageResponse, WebsiteList, WebsiteParams } from '~/lib/type' 9 | import { serverSupabaseClient } from '#supabase/server' 10 | import { RESPONSE_STATUS_CODE } from '~/lib/enum' 11 | 12 | export default defineEventHandler(async (event): Promise>> => { 13 | const client = await serverSupabaseClient(event) 14 | // 获取请求参数 15 | const { current, pageSize, name = '', category_id = '' } = getQuery(event) as WebsiteParams 16 | // 判断参数 17 | if (!current || !pageSize) { 18 | return { code: RESPONSE_STATUS_CODE.FAIL, msg: '参数错误' } 19 | } 20 | 21 | // 计算分页 22 | const start = (current - 1) * pageSize 23 | const end = current * pageSize - 1 24 | 25 | // 查询 sql 26 | let sqlQuery = client 27 | .from('ds_websites') 28 | .select('*,ds_categorys(*)', { count: 'exact' }) 29 | .range(start, end) 30 | .order('pinned', { 31 | ascending: false 32 | }) 33 | .order('sort', { 34 | ascending: false 35 | }) 36 | .order('recommend', { 37 | ascending: false 38 | }) 39 | .order('created_at', { 40 | ascending: false 41 | }) 42 | 43 | // 判断查询参数 44 | if (name) { 45 | sqlQuery = sqlQuery.like('name', `%${name}%`) 46 | } 47 | if (category_id) { 48 | sqlQuery = sqlQuery.eq('category_id', category_id) 49 | } 50 | 51 | // 请求列表 52 | const { data, error, count } = await sqlQuery 53 | 54 | // 判断请求结果 55 | if (error) { 56 | throw createError({ 57 | statusCode: RESPONSE_STATUS_CODE.FAIL, 58 | statusMessage: error.message 59 | }) 60 | } 61 | 62 | // 请求成功 63 | return { 64 | code: RESPONSE_STATUS_CODE.SUCCESS, 65 | msg: '请求成功', 66 | data: { 67 | list: data, 68 | total: count 69 | } 70 | } 71 | }) 72 | -------------------------------------------------------------------------------- /app/lib/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Pick a list of properties from an object 3 | * into a new object 4 | */ 5 | export const pick = ( 6 | obj: T, 7 | keys: TKeys[] 8 | ): Pick => { 9 | if (!obj) return {} as Pick 10 | return keys.reduce((acc, key) => { 11 | if (Object.prototype.hasOwnProperty.call(obj, key)) acc[key] = obj[key] 12 | return acc 13 | }, {} as Pick) 14 | } 15 | 16 | /** 17 | * Omit a list of properties from an object 18 | * returning a new object with the properties 19 | * that remain 20 | */ 21 | export const omit = ( 22 | obj: T, 23 | keys: TKeys[] 24 | ): Omit => { 25 | if (!obj) return {} as Omit 26 | if (!keys || keys.length === 0) return obj as Omit 27 | return keys.reduce( 28 | (acc, key) => { 29 | // Gross, I know, it's mutating the object, but we 30 | // are allowing it in this very limited scope due 31 | // to the performance implications of an omit func. 32 | // Not a pattern or practice to use elsewhere. 33 | delete acc[key] 34 | return acc 35 | }, 36 | { ...obj } 37 | ) 38 | } 39 | 40 | /** 41 | * Dynamically get a nested value from an array or 42 | * object with a string. 43 | * 44 | * @example get(person, 'friends[0].name') 45 | */ 46 | export const get = ( 47 | value: any, 48 | path: string, 49 | defaultValue?: TDefault 50 | ): TDefault => { 51 | const segments = path.split(/[\.\[\]]/g) 52 | let current: any = value 53 | for (const key of segments) { 54 | if (current === null) return defaultValue as TDefault 55 | if (current === undefined) return defaultValue as TDefault 56 | const dequoted = key.replace(/['"]/g, '') 57 | if (dequoted.trim() === '') continue 58 | current = current[dequoted] 59 | } 60 | if (current === undefined) return defaultValue as TDefault 61 | return current 62 | } -------------------------------------------------------------------------------- /app/components/FullLoading/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 69 | -------------------------------------------------------------------------------- /app/app.vue: -------------------------------------------------------------------------------- 1 | 45 | 70 | 71 | 82 | -------------------------------------------------------------------------------- /app/components/ColorMode/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 49 | 50 | 65 | 66 | 83 | -------------------------------------------------------------------------------- /server/api/categorys/index.get.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: 白雾茫茫丶 3 | * @Date: 2024-05-29 14:39:50 4 | * @LastEditors: 白雾茫茫丶 5 | * @LastEditTime: 2025-07-18 14:12:03 6 | * @Description: 获取分类列表 7 | */ 8 | import type { Response, PageResponse, CategoryList, CategoryParams } from '~/lib/type' 9 | import { serverSupabaseClient } from '#supabase/server' 10 | import { RESPONSE_STATUS_CODE } from '~/lib/enum' 11 | 12 | export default defineEventHandler(async (event): Promise>> => { 13 | const client = await serverSupabaseClient(event) 14 | // 获取请求参数 15 | const { current, pageSize, name } = getQuery(event) as CategoryParams 16 | // 判断参数 17 | if (!current || !pageSize) { 18 | return { code: RESPONSE_STATUS_CODE.FAIL, msg: '参数错误' } 19 | } 20 | 21 | // 计算分页 22 | const start = (current - 1) * pageSize 23 | const end = current * pageSize - 1 24 | 25 | // 查询 sql 26 | let sqlQuery = client 27 | .from('ds_categorys') 28 | .select('*,ds_websites(*)', { count: 'exact' }) 29 | .range(start, end) 30 | .order('sort', { 31 | ascending: false 32 | }) 33 | .order('created_at', { 34 | ascending: false 35 | }) 36 | 37 | // 判断查询参数 38 | if (name) { 39 | sqlQuery = sqlQuery.like('name', `%${name}%`) 40 | } 41 | 42 | // 请求列表 43 | const { data, error, count } = await sqlQuery 44 | 45 | // 判断请求结果 46 | if (error) { 47 | throw createError({ 48 | statusCode: RESPONSE_STATUS_CODE.FAIL, 49 | statusMessage: error.message 50 | }) 51 | } 52 | 53 | if (data) { 54 | data.forEach((category: CategoryList) => { 55 | category?.ds_websites.sort((a, b) => { 56 | // 2. 再按 pinned 降序 (true 排在前面) 57 | if (a.pinned !== b.pinned) return b.pinned ? 1 : -1 58 | 59 | // 1. 先按 sort 降序 (b - a) 60 | if (b.sort !== a.sort) return b.sort - a.sort 61 | 62 | // 3. 然后按 recommend 降序 (true 排在前面) 63 | if (a.recommend !== b.recommend) return b.recommend ? 1 : -1 64 | 65 | // 4. 最后按 created_at 降序 (新日期在前) 66 | return new Date(b.created_at).getTime() - new Date(a.created_at).getTime() 67 | }) 68 | }) 69 | } 70 | 71 | // 请求成功 72 | return { 73 | code: RESPONSE_STATUS_CODE.SUCCESS, 74 | msg: '请求成功', 75 | data: { 76 | list: data, 77 | total: count 78 | } 79 | } 80 | }) 81 | -------------------------------------------------------------------------------- /app/assets/css/main.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | @import "@nuxt/ui"; 3 | 4 | body { 5 | font-family: 'Maple Mono CN', system-ui, Avenir, Helvetica, Arial, sans-serif; 6 | } 7 | 8 | @keyframes border-spin { 9 | 0% { 10 | clip-path: inset(0 100% 100% 0); 11 | opacity: 0; 12 | } 13 | 50% { 14 | clip-path: inset(0 0 100% 0); 15 | opacity: 1; 16 | } 17 | 100% { 18 | clip-path: inset(0 0 0 0); 19 | opacity: 1; 20 | } 21 | } 22 | 23 | @keyframes border-unspin { 24 | 0% { 25 | clip-path: inset(0 0 0 0); 26 | opacity: 1; 27 | } 28 | 50% { 29 | clip-path: inset(100% 0 0 0); 30 | opacity: 1; 31 | } 32 | 100% { 33 | clip-path: inset(100% 0 0 100%); 34 | opacity: 0; 35 | } 36 | } 37 | 38 | /* 添加通用卡片样式 */ 39 | @layer components { 40 | /* 基础卡片样式 */ 41 | .card-base { 42 | @apply p-4 rounded-xl bg-white dark:bg-gray-800/50 43 | border border-gray-200/60 dark:border-gray-700/60 44 | transition-all duration-300; 45 | } 46 | 47 | /* 统一的边框动画样式 */ 48 | .animated-border { 49 | @apply relative; 50 | } 51 | 52 | .animated-border::after { 53 | @apply absolute inset-0 rounded-xl border-2 opacity-0 pointer-events-none; 54 | content: ''; 55 | } 56 | 57 | /* 只在悬浮时显示进入动画 */ 58 | .animated-border:hover::after { 59 | animation: border-spin 0.5s ease-out forwards; 60 | } 61 | 62 | /* 只在悬浮后的元素显示退出动画 */ 63 | .animated-border.hovered:not(:hover)::after { 64 | animation: border-unspin 0.5s ease-out forwards; 65 | } 66 | 67 | .inner-card { 68 | @apply bg-gray-50 dark:bg-gray-800/50 rounded-lg p-4 69 | border border-gray-100/60 dark:border-gray-700/60; 70 | } 71 | } 72 | 73 | :root { 74 | --scrollbar-bg:rgba(0, 0, 0, .15); 75 | --scrollbar-hover: rgba(0, 0, 0, .25); 76 | } 77 | 78 | .dark{ 79 | --scrollbar-bg:rgba(255, 255, 255, .45); 80 | --scrollbar-hover: rgba(255, 255, 255, .25); 81 | } 82 | 83 | /* 修改滚动条样式 */ 84 | *::-webkit-scrollbar { 85 | width: 5px; 86 | height: 5px; 87 | } 88 | 89 | *::-webkit-scrollbar-thumb { 90 | background: var(--scrollbar-bg); 91 | border-radius: 2.5px; 92 | transition: .35s background-color; 93 | } 94 | 95 | *::-webkit-scrollbar-thumb:hover { 96 | background-color: var(--scrollbar-hover); 97 | } 98 | 99 | *::-webkit-scrollbar-track { 100 | background-color: transparent; 101 | } -------------------------------------------------------------------------------- /app/lib/type.ts: -------------------------------------------------------------------------------- 1 | import type { RESPONSE_STATUS_CODE } from '~/lib/enum' 2 | 3 | /** 4 | * @description: 响应体结构 5 | */ 6 | export type Response = { 7 | code: RESPONSE_STATUS_CODE // 状态码 8 | msg: string // 状态信息 9 | data?: T // 数据 10 | } 11 | 12 | /** 13 | * @description: 分页列表 14 | */ 15 | export type PageResponse = { 16 | list: T[] 17 | total: number | null // 总页数 18 | } 19 | 20 | /** 21 | * @description: 分页参数 22 | */ 23 | export type PaginationParams = { 24 | current: number // 当前页 25 | pageSize: number // 每页条数 26 | } 27 | 28 | /** 29 | * @description: 分类列表 30 | */ 31 | export type CategoryList = { 32 | id: string // uuid 33 | name: string // 分类名称 34 | desc: string // 分类描述 35 | icon: string // 分类图标 36 | user_id: string // 用户 id 37 | email: string // 用户邮箱 38 | sort: number // 排序 39 | ds_websites: WebsiteList[]; // 网站列表 40 | created_at: Date // 创建时间 41 | updated_at: Date // 更新时间 42 | } 43 | 44 | /** 45 | * @description: 站点列表 46 | */ 47 | export type WebsiteList = { 48 | id: string // uuid 49 | category_id: string // 所属分类 50 | name: string // 站点名称 51 | desc: string // 站点描述 52 | url: string // 站点 url 53 | logo: string // logo url 54 | color: string // 图标颜色 55 | tags: string[] // 站点标签 56 | pinned: boolean // 是否置顶 57 | vpn: boolean // 是否需要 vpn 58 | recommend: boolean // 是否推荐 59 | user_id: string // 用户 id 60 | email: string // 用户邮箱 61 | sort: number // 排序 62 | commonlyUsed: boolean; // 是否常用 63 | visitCount: number; // 访问量 64 | created_at: Date // 创建时间 65 | updated_at: Date // 更新时间 66 | ds_categorys: CategoryList; 67 | } 68 | 69 | /** 70 | * @description: 分类查询参数 71 | */ 72 | export type CategoryParams = PaginationParams & Partial> 73 | 74 | /** 75 | * @description: 新增/编辑参数 76 | */ 77 | export type CategoryEdit = Pick & 78 | Partial> 79 | 80 | /** 81 | * @description: 站点查询参数 82 | */ 83 | export type WebsiteParams = PaginationParams & Partial> 84 | 85 | /** 86 | * @description: 新增/编辑参数 87 | */ 88 | export type WebsiteEdit = Pick< 89 | WebsiteList, 90 | 'category_id' | 'name' | 'url' | 'logo' | 'tags' | 'pinned' | 'vpn' | 'recommend' | 'commonlyUsed' | 'sort' 91 | > & 92 | Partial> 93 | 94 | /** 95 | * @description: 社交图标类型 96 | */ 97 | export type Social = { 98 | icon: string // 图标 99 | url?: string // 跳转地址 100 | tip?: string // tip 文案 101 | } 102 | -------------------------------------------------------------------------------- /nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | compatibilityDate: '2025-07-15', 4 | devtools: { enabled: true }, 5 | devServer: { 6 | port: 5173 7 | }, 8 | runtimeConfig: { 9 | public: { 10 | baseUrl: process.env.NODE_ENV === 'production' ? process.env.NUXT_SITE_URL : 'http://localhost:5173', 11 | siteTitle: process.env.NUXT_SITE_NAME, 12 | siteDescription: process.env.NUXT_SITE_DESCRIPTION, 13 | siteKeywords: process.env.NUXT_SITE_KEYWORDS, 14 | } 15 | }, 16 | app: { 17 | pageTransition: { name: 'page', mode: 'out-in' }, // 页面过渡效果 18 | head: { 19 | link: [ 20 | { 21 | rel: "stylesheet", 22 | href: "https://cdn.baiwumm.com/fonts/MapleMono-CN-Regular/result.css" 23 | } 24 | ] 25 | } 26 | }, 27 | modules: [ 28 | '@nuxt/eslint', 29 | '@nuxt/icon', 30 | '@nuxt/image', 31 | '@nuxt/scripts', 32 | '@nuxtjs/supabase', 33 | '@nuxtjs/color-mode', 34 | '@nuxtjs/seo', 35 | 'nuxt-umami', 36 | 'dayjs-nuxt', 37 | '@nuxt/ui', 38 | 'nuxt-gtag', 39 | 'nuxt-clarity-analytics' 40 | ], 41 | // supabase Auth 42 | supabase: { 43 | redirectOptions: { 44 | login: '/login', 45 | callback: '/confirm', 46 | exclude: ['/'] 47 | } 48 | }, 49 | // nuxt-umami Umami 统计 50 | umami: { 51 | id: '87f94791-c0a5-424f-a3f4-e0171d82352b', 52 | host: 'https://um.baiwumm.com', 53 | autoTrack: true 54 | }, 55 | // nuxt-gtag 谷歌统计 56 | gtag: { 57 | enabled: process.env.NODE_ENV === 'production', 58 | id: 'G-76RP7KMHMQ', 59 | config: { 60 | page_title: process.env.NUXT_SITE_NAME 61 | }, 62 | }, 63 | // dayjs 插件 64 | dayjs: { 65 | locales: ['zh-cn'], 66 | plugins: ['relativeTime', 'utc', 'timezone'], 67 | defaultLocale: 'zh-cn', 68 | defaultTimezone: 'Asia/Shanghai', 69 | }, 70 | // @nuxtjs/color-mode 配置 71 | colorMode: { 72 | classSuffix: '' 73 | }, 74 | // @nuxt/icon 配置 75 | icon: { 76 | customCollections: [ 77 | { 78 | prefix: 'my-icon', 79 | dir: './app/assets/icons' 80 | }, 81 | ], 82 | }, 83 | // @nuxt/ui 配置 84 | ui: { 85 | fonts: false 86 | }, 87 | // 站点地图配置 88 | sitemap: { 89 | xslColumns: [ 90 | { label: 'URL', width: '50%' }, 91 | { label: 'Last Modified', select: 'sitemap:lastmod', width: '25%' }, 92 | { label: 'Priority', select: 'sitemap:priority', width: '12.5%' }, 93 | { label: 'Change Frequency', select: 'sitemap:changefreq', width: '12.5%' }, 94 | ], 95 | }, 96 | experimental: { 97 | viewTransition: true 98 | }, 99 | css: ['~/assets/css/main.css'] 100 | }) -------------------------------------------------------------------------------- /app/assets/icons/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | logo 3 |

Dream Site

4 |

一个简约、精美、现代化的个人站点导航

5 |
6 | 7 | 24 | 25 | ## ☘️ 项目简介 26 | 27 | `Dream Site` 是一个现代化的个人站点导航系统,旨在为用户提供美观、高效的个人网站收藏与管理体验。无论你是开发者、设计师还是内容创作者,都可以用它来组织和展示你喜爱的网站资源。 28 | 29 | ## 🌿 在线体验 30 | 31 | ➡️ [点击打开](https://site.baiwumm.com/) 32 | 33 | ## 🪴 技术栈 34 | 35 | - **前端框架**: [Nuxt 4.0](https://nuxt.com/) (基于Vue 3) 36 | - **UI组件**: [NuxtUI](https://ui.nuxt.com/) 37 | - **样式方案**: [Tailwind CSS](https://www.tailwindcss.cn/) 38 | - **后端服务**: [Supabase](https://supabase.com/) (开源Firebase替代品) 39 | - **部署平台**: 支持 `Vercel` 等多种部署方式 40 | 41 | ## ✨ 特性 42 | - 🚀 **高性能**: 基于最新前端技术栈,极速响应 43 | - 🌓 **主题切换**: 完善的亮色/暗黑模式支持 44 | - 🔍 **SEO友好**: 支持SSR渲染,优化搜索引擎收录 45 | - 📱 **响应式设计**: 适配各种设备屏幕 46 | - 🔒 **安全认证**: 基于Supabase的完整用户系统 47 | - 🧩 **模块化架构**: 清晰的目录结构,便于二次开发 48 | 49 | ## 🪴 项目截图 50 | 51 | | 亮色模式 | 暗色模式 | 52 | |----------|----------| 53 | | ![亮色模式](./app/assets/images/light.png) | ![暗色模式](./app/assets/images/dark.png) | 54 | 55 | | 分类管理 | 站点列表 | 56 | |----------|----------| 57 | | ![分类列表](./app/assets/images/caretorys.png) | ![站点列表](./app/assets/images/websites.png) | 58 | 59 | ## 🚀 快速开始 60 | 61 | ### 🌳 环境要求 62 | - Node.js ≥ 18.17 (推荐最新LTS版本) 63 | - pnpm (推荐) 或 npm/yarn 64 | 65 | ### 🧑‍💻 本地开发 66 | ```bash 67 | # 克隆项目 68 | git clone https://github.com/baiwumm/dream-site.git 69 | 70 | # 进入项目目录 71 | cd dream-site 72 | 73 | # 安装依赖 74 | pnpm install 75 | 76 | # 启动开发服务器 77 | pnpm dev 78 | ``` 79 | 80 | ### 📝 初始化数据库 81 | 1. 进入 [supabase 控制台](https://supabase.com/dashboard),创建项目,并获取`SUPABASE_URL`和`SUPABASE_KEY` 82 | 2. 导入数据库结构,将 `initSupabase.sql` 导入到 `Supabase SQL` 编辑器中执行。 83 | 3. 在根目录下创建`.env`文件,并添加以下内容: 84 | ```env 85 | SUPABASE_URL="xxx" 86 | SUPABASE_KEY="xxx" 87 | ``` 88 | 4. 进入 `Authentication` ,自行配置 `Policies` 和 `Sign In / Providers` 89 | 90 | ## ⚙️ Vercel 一键部署 91 | 1. `Fork` 本项目,在 `Vercel` 官网点击 `New Project` 92 | 2. 点击 `Import Git Repository` 并选择你 fork 的此项目并点击 `import` 93 | 3. `PROJECT NAME`自己填,`FRAMEWORK PRESET` 选 `Other` 然后直接点 `Deploy` 接着等部署完成即可 94 | 95 | 96 | vercel 部署 97 | 98 | 99 | ## 📜 许可证 100 | 本项目采用 [MIT](LICENSE) 许可证。 101 | 102 | ## ⭐ Star History 103 | 104 | [![Star History Chart](https://api.star-history.com/svg?repos=baiwumm/dream-site&type=Date)](https://star-history.com/#baiwumm/dream-site&Date) -------------------------------------------------------------------------------- /app/components/AnimatedContent/index.vue: -------------------------------------------------------------------------------- 1 | 130 | 131 | 136 | 137 | 140 | -------------------------------------------------------------------------------- /app/components/BlurText/index.vue: -------------------------------------------------------------------------------- 1 | 127 | 128 | 148 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [2.2.6](https://github.com/baiwumm/dream-site/compare/2.2.5...2.2.6) (2025-09-04) 4 | 5 | ### ✨ Features | 新功能 6 | 7 | * 细节调整 ([f3b79f6](https://github.com/baiwumm/dream-site/commit/f3b79f6967345c7e65ec4bf56bb18c0113e53b38)) 8 | 9 | ## [2.2.5](https://github.com/baiwumm/dream-site/compare/2.2.4...2.2.5) (2025-08-28) 10 | 11 | ### 💄 Styles | 风格 12 | 13 | * 新增全站字体 ([cdc2f98](https://github.com/baiwumm/dream-site/commit/cdc2f982dba0b4ad860204ce0d038a3deaacd9bc)) 14 | 15 | ### ⚡ Performance Improvements | 性能优化 16 | 17 | * 细节优化 ([02cfc11](https://github.com/baiwumm/dream-site/commit/02cfc11056240559c10a5ea6a33a7c7717b37ebe)) 18 | 19 | ## [2.2.4](https://github.com/baiwumm/dream-site/compare/2.2.3...2.2.4) (2025-08-01) 20 | 21 | ### 💄 Styles | 风格 22 | 23 | * 细节调整 ([2d817b3](https://github.com/baiwumm/dream-site/commit/2d817b35ce658676e032c15e56079fab328a9fce)) 24 | 25 | ## [2.2.3](https://github.com/baiwumm/dream-site/compare/2.2.2...2.2.3) (2025-07-30) 26 | 27 | ### ✨ Features | 新功能 28 | 29 | * update README.md ([0728fa6](https://github.com/baiwumm/dream-site/commit/0728fa6dde0997089226da939cd6959e960d98a6)) 30 | 31 | ### ⚡ Performance Improvements | 性能优化 32 | 33 | * 修改站点描述、细节优化 ([856cfe1](https://github.com/baiwumm/dream-site/commit/856cfe1644198f1978853963150d57a032685299)) 34 | 35 | ## [2.2.2](https://github.com/baiwumm/dream-site/compare/2.2.1...2.2.2) (2025-07-30) 36 | 37 | ### ✨ Features | 新功能 38 | 39 | * update README.md ([dc0ec95](https://github.com/baiwumm/dream-site/commit/dc0ec9520db46b37cbff3c8d47f43cfb373d9ae8)) 40 | 41 | ### 💄 Styles | 风格 42 | 43 | * 细节优化 ([122a89c](https://github.com/baiwumm/dream-site/commit/122a89c4733b269f7f4e38210cbfe1a91f9f5cd7)) 44 | 45 | ## [2.2.1](https://github.com/baiwumm/dream-site/compare/2.2.0...2.2.1) (2025-07-29) 46 | 47 | ### ✨ Features | 新功能 48 | 49 | * 修复编辑成功后没有清空数据的问题 ([3ecf0b1](https://github.com/baiwumm/dream-site/commit/3ecf0b1054b877bc41b6e5ba034126e707a2903d)) 50 | * 修复新增时数据没有清空的问题 ([cbacdba](https://github.com/baiwumm/dream-site/commit/cbacdbae67ef61a6a96f18a2a255ecf152b87e18)) 51 | 52 | ## [2.2.0](https://github.com/baiwumm/dream-site/compare/2.1.1...2.2.0) (2025-07-24) 53 | 54 | ### ✨ Features | 新功能 55 | 56 | * 添加 OgImage ([5ad0743](https://github.com/baiwumm/dream-site/commit/5ad07437d3811f792857d1561f41eaa18c862c60)) 57 | * 添加邮箱注册登录 ([923f545](https://github.com/baiwumm/dream-site/commit/923f5455ad2d10563901d1e86aee68ad2e4c3491)) 58 | * 完成谷歌登录逻辑 ([62997aa](https://github.com/baiwumm/dream-site/commit/62997aadfabf1f8d36816e8da29ff18236a285b9)) 59 | * 细节优化调整 ([530ce96](https://github.com/baiwumm/dream-site/commit/530ce96de6b2082b1211343a72653a227e39d13b)) 60 | 61 | ## [2.1.1](https://github.com/baiwumm/dream-site/compare/2.1.0...2.1.1) (2025-07-23) 62 | 63 | ### ✨ Features | 新功能 64 | 65 | * 后台列表搜索添加重置按钮 ([077f365](https://github.com/baiwumm/dream-site/commit/077f36506aef294bed06c59bc11cf12bb6637619)) 66 | * 添加 BackTop 回到顶部按钮组件 ([8ed86a4](https://github.com/baiwumm/dream-site/commit/8ed86a4795a5112729fad6902d884e777f86ed7e)) 67 | * 修改社交图标不能跳转的问题 ([958edb9](https://github.com/baiwumm/dream-site/commit/958edb9f658fca3d42e887f47d5e809bc1806f71)) 68 | * update README.md ([4b1e57e](https://github.com/baiwumm/dream-site/commit/4b1e57e4dae1b3c8ef36734a2dd404475f0fffc7)) 69 | 70 | ### 💄 Styles | 风格 71 | 72 | * 调整按钮图标大小 ([65b657f](https://github.com/baiwumm/dream-site/commit/65b657f7f13715081885d7a4f94ad0e4d1a872ee)) 73 | 74 | ### ⚡ Performance Improvements | 性能优化 75 | 76 | * 优化首页元素初次加载没有铺满窗口的问题 ([904e835](https://github.com/baiwumm/dream-site/commit/904e835b69747c96d53f468e1c843b24dd509f48)) 77 | 78 | ## [2.1.0](https://github.com/baiwumm/dream-site/compare/2.0.0...2.1.0) (2025-07-22) 79 | 80 | ### ✨ Features | 新功能 81 | 82 | * 新增@nuxt/ui,删除 @nuxt/ui 中集成的包 ([1fa8145](https://github.com/baiwumm/dream-site/commit/1fa81456ad91440cb8e74a7cbef7fc1de8174ed8)) 83 | * **categorys:** 完成网站分类列表和新增/编辑逻辑的开发 ([b00d337](https://github.com/baiwumm/dream-site/commit/b00d337b83660320e36bf70540c4562d5afd2ebd)) 84 | * update README.md ([694dceb](https://github.com/baiwumm/dream-site/commit/694dceb6c8ec6fc0a588007e3cc32654d50a63e4)) 85 | * **websites:** 完成分类站点列表和新增/编辑逻辑的开发 ([0b57fa1](https://github.com/baiwumm/dream-site/commit/0b57fa1704b8a3b1a753118ceb8af0624971229c)) 86 | 87 | ### 💄 Styles | 风格 88 | 89 | * 修改 eslint 配置 ([bdc27cc](https://github.com/baiwumm/dream-site/commit/bdc27cc6d26986ada748ece6675f3d9d0bfffc2c)) 90 | * 样式细节调整 ([fb677cd](https://github.com/baiwumm/dream-site/commit/fb677cd2a0b7bcc6e4f0c21ac7018448fd955d23)) 91 | 92 | ### ♻ Code Refactoring | 代码重构 93 | 94 | * 全面基于 @nuxt/ui 重构 ([8159866](https://github.com/baiwumm/dream-site/commit/8159866f05c8fc31da1e9c97dd3ec2dcef6dc0bd)) 95 | 96 | ### ⚡ Performance Improvements | 性能优化 97 | 98 | * 删除没用的包 ([4732759](https://github.com/baiwumm/dream-site/commit/4732759bf4bee21ee9c16acd000811fc3acb7a6f)) 99 | 100 | ## [2.0.0](https://github.com/baiwumm/dream-site/compare/1.5.2...2.0.0) (2025-07-21) 101 | 102 | ### ♻ Code Refactoring | 代码重构 103 | 104 | * **app:** 使用 Nuxt4.0 版本全面重构 ([757504d](https://github.com/baiwumm/dream-site/commit/757504d7d6f14890d7c0b50c1f7c82e31b9b13ab)) 105 | -------------------------------------------------------------------------------- /app/assets/images/vercel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/pages/admin/_components/categorys/components/EditModal.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 178 | -------------------------------------------------------------------------------- /app/pages/index.vue: -------------------------------------------------------------------------------- 1 | 90 | 91 | 139 | -------------------------------------------------------------------------------- /app/components/SplitText/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 195 | -------------------------------------------------------------------------------- /app/components/Squares/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 200 | -------------------------------------------------------------------------------- /app/pages/login/index.vue: -------------------------------------------------------------------------------- 1 | 170 | 171 | 248 | -------------------------------------------------------------------------------- /app/pages/admin/_components/websites/components/EditModal.vue: -------------------------------------------------------------------------------- 1 | 115 | 116 | 258 | -------------------------------------------------------------------------------- /app/pages/admin/_components/categorys/index.vue: -------------------------------------------------------------------------------- 1 | 87 | 371 | -------------------------------------------------------------------------------- /app/pages/admin/_components/websites/index.vue: -------------------------------------------------------------------------------- 1 | 101 | 524 | -------------------------------------------------------------------------------- /app/components/SplashCursor/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 1268 | --------------------------------------------------------------------------------