Compare commits

...

3 Commits

Author SHA1 Message Date
6e6bb8cad2 如果缩略图比原图文件大则替换为原图 2025-03-04 22:21:24 +08:00
d5d74cd9a3 处理缩略图 2025-03-04 22:04:17 +08:00
986472d76d windicss 替换为 unocss 2025-02-25 22:49:45 +08:00
10 changed files with 80 additions and 49 deletions

16
app.vue
View File

@@ -6,11 +6,11 @@
nav.container.mx-auto.flex.text-gray-400.text-lg.font-bold nav.container.mx-auto.flex.text-gray-400.text-lg.font-bold
template(v-for="item in navbar") template(v-for="item in navbar")
NuxtLink.px-4.py-6.transition-all.duration-150( NuxtLink.px-4.py-6.transition-all.duration-150(
class="hover:text-gray-800", class="text-gray-500 hover:text-dark-800",
:class="{ 'text-black': item.active }", :class="{ 'text-dark-800': item.active }",
:to="item.path" :to="item.path"
) {{ item.title }} ) {{ item.title }}
NuxtLink.px-4.py-6.ml-auto(to="/account" v-if="!account.padding") {{ account.online ? account.name : '登录' }} NuxtLink.px-4.py-6.ml-auto.text-gray-500(to="/account" v-if="!account.padding") {{ account.online ? account.name : '登录' }}
NuxtPage NuxtPage
</template> </template>
@@ -71,6 +71,16 @@ onMounted(() => {
</script> </script>
<style> <style>
/** 去除链接默认下划线 */
a {
text-decoration: none;
}
/** 全局字体 */
body {
font-family: 'Noto Sans SC', 'Microsoft YaHei', sans-serif;
}
/** 全局滚动条取消空间占用, 防止渲染时闪烁 */ /** 全局滚动条取消空间占用, 防止渲染时闪烁 */
::-webkit-scrollbar { ::-webkit-scrollbar {
display: none; display: none;

View File

@@ -1,9 +1,7 @@
import { defineNuxtConfig } from 'nuxt/config' import { defineNuxtConfig } from 'nuxt/config'
import { PugExtractor } from 'vite-plugin-windicss'
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({ export default defineNuxtConfig({
modules: ['nuxt-windicss'], modules: ['@unocss/nuxt'],
app: { app: {
head: { head: {
title: 'satori', title: 'satori',
@@ -12,16 +10,6 @@ export default defineNuxtConfig({
] ]
}, },
}, },
windicss: {
config: {
extract: {
extractors: [{
extractor: PugExtractor,
extensions: ['vue', 'pug'],
}]
},
},
},
nitro: { nitro: {
storage: { storage: {
blog: { blog: {
@@ -51,5 +39,6 @@ export default defineNuxtConfig({
clientPort: 3000 clientPort: 3000
} }
} }
} },
compatibilityDate: '2025-02-25'
}) })

View File

@@ -8,11 +8,13 @@
"update": "ssh satori 'cd satori; git pull; npm i; npm run build; pm2 restart satori'" "update": "ssh satori 'cd satori; git pull; npm i; npm run build; pm2 restart satori'"
}, },
"devDependencies": { "devDependencies": {
"@unocss/extractor-pug": "^66.0.0",
"@unocss/nuxt": "^66.0.0",
"@vue/compiler-sfc": "^3.3.4", "@vue/compiler-sfc": "^3.3.4",
"nuxt": "3.7.4", "nuxt": "3.15.4",
"nuxt-windicss": "^2.6.0",
"pug": "^3.0.2", "pug": "^3.0.2",
"vite-plugin-windicss": "^1.9.3" "pug-plain-loader": "^1.1.0",
"unocss": "^66.0.0"
}, },
"dependencies": { "dependencies": {
"@formkit/auto-animate": "^0.8.0", "@formkit/auto-animate": "^0.8.0",

View File

@@ -56,7 +56,7 @@ div.container.mx-auto.py-24.flex.gap-8.w-full(
span.mr-4 {{ content }} span.mr-4 {{ content }}
time.text-xs(:datetime="updatedAt || createdAt") {{ date }} time.text-xs(:datetime="updatedAt || createdAt") {{ date }}
// 右侧栏(aside) // 右侧栏(aside)
aside.w-64.py-2.flex.flex-col.gap-8(class="<sm:hidden") aside.w-64.py-2.flex.flex-col.gap-8
div(v-if="!tagPending && tags.length") div(v-if="!tagPending && tags.length")
span.font-bold # TAG span.font-bold # TAG
ul.flex.flex-wrap.gap-2.py-1 ul.flex.flex-wrap.gap-2.py-1
@@ -68,7 +68,7 @@ div.container.mx-auto.py-24.flex.gap-8.w-full(
span.font-bold # 归档 span.font-bold # 归档
template(v-for="item in data.filter(x => x.title)" :key="item.id") template(v-for="item in data.filter(x => x.title)" :key="item.id")
NuxtLink.block.truncate( NuxtLink.block.truncate(
class="hover:text-pink-500" class="text-dark-600 hover:text-pink-500 visited:text-gray-500"
:to="`/blog/${item.id}`" :to="`/blog/${item.id}`"
) {{ item.title }} ) {{ item.title }}
// 弹出层编辑器(编辑日志) // 弹出层编辑器(编辑日志)

View File

@@ -27,7 +27,7 @@
) )
NuxtLink(:to="`/gallery/${item.id}`") NuxtLink(:to="`/gallery/${item.id}`")
//img.w-full(:src="`${item.image}?w=${WIDTH}`" loading="lazy") //img.w-full(:src="`${item.image}?w=${WIDTH}`" loading="lazy")
img.w-full(:src="item.image" loading="lazy") img.w-full(:src="item.image+'@w480.webp'" loading="lazy")
.absolute.top-0.left-0.right-0.text-white.overflow-hidden.break-words.pb-4( .absolute.top-0.left-0.right-0.text-white.overflow-hidden.break-words.pb-4(
style="background: linear-gradient(to top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.25))" style="background: linear-gradient(to top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.25))"
) )

View File

@@ -1,4 +1,4 @@
<template lang="pug"> <template lang="pug">
main.container.mx-auto.h-screen.flex.justify-center.align-center.items-center main.container.mx-auto.h-screen.flex.justify-center.align-center.items-center
div.text-red-100.text-8xl.cursor-pointer satori div.text-red-100.text-8xl.cursor-pointer.ma3 satori
</template> </template>

View File

@@ -1,6 +1,5 @@
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import { getQuery } from 'h3'
import webp from 'webp-converter' import webp from 'webp-converter'
// 如果当前文件夹是 .output 则使用上一级路径 // 如果当前文件夹是 .output 则使用上一级路径
@@ -17,31 +16,42 @@ export default defineEventHandler(async event => {
// 处理 GET 请求 // 处理 GET 请求
if (event.node.req.method === 'GET') { if (event.node.req.method === 'GET') {
const file_name = decodeURI(event.context.params.id) // 正则提取文件名后缀 1ac7eb2155fa09851c4eeca14.png@w320.webp
const file_path = path.resolve(dirPath, file_name) const filename = decodeURI(event.context.params.id)
const match = filename.match(/(.*)@w(\d+)\.(\w+)/)
// 检查文件是否存在
if (!fs.existsSync(file_path)) {
event.node.res.statusCode = 404
return { error: 'file not found' }
}
// 浏览器缓存一个月
event.node.res.setHeader('Cache-Control', 'max-age=2629746,immutable')
// 是否请求缩略图(如果文件不存在则创建)
const { w } = getQuery(event)
if (w) {
const tmp_path = path.resolve(tmpPath, file_name + `?w=${w}`)
if (!fs.existsSync(tmp_path)) {
const origin = file_path.replace(/\.thumbnail$/, '')
await webp.cwebp(origin, tmp_path, `-lossless -resize ${w} 0`)
}
return fs.readFileSync(tmp_path)
}
// 请求原图 // 请求原图
return fs.readFileSync(file_path) if (!match) {
event.node.res.setHeader('Cache-Control', 'max-age=2629746,immutable')
return fs.readFileSync(path.resolve(dirPath, filename))
}
const [name, origin, w, format] = match
// 返回缩略图(如果已经生成)
if (fs.existsSync(path.resolve(tmpPath, name))) {
event.node.res.setHeader('Cache-Control', 'max-age=2629746,immutable')
return fs.readFileSync(path.resolve(tmpPath, name))
}
// 生成缩略图(如果源文件存在)
if (fs.existsSync(path.resolve(dirPath, origin))) {
await webp.cwebp(path.resolve(dirPath, origin), path.resolve(tmpPath, name) , `-lossless -resize ${w} 0`)
// 如果缩略图比原图文件大则替换为原图
const originSize = fs.statSync(path.resolve(dirPath, origin)).size
const thumbSize = fs.statSync(path.resolve(tmpPath, name)).size
if (thumbSize > originSize) {
fs.unlinkSync(path.resolve(tmpPath, name))
fs.copyFileSync(path.resolve(dirPath, origin), path.resolve(tmpPath, name))
}
event.node.res.setHeader('Cache-Control', 'max-age=2629746,immutable')
return fs.readFileSync(path.resolve(tmpPath, name))
}
event.node.res.statusCode = 404
return { error: 'file not found' }
} }
// 处理 DELETE 请求 // 处理 DELETE 请求

View File

@@ -13,6 +13,7 @@ if (!fs.existsSync(dirPath)) {
} }
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
// 处理 GET 请求(读取image中的图片文件列表) // 处理 GET 请求(读取image中的图片文件列表)
if (event.node.req.method === 'GET') { if (event.node.req.method === 'GET') {
const files = await fs.promises.readdir(dirPath) const files = await fs.promises.readdir(dirPath)
@@ -30,6 +31,7 @@ export default defineEventHandler(async event => {
return { list } return { list }
} }
// 处理 POST 请求(FromData上传图片) // 处理 POST 请求(FromData上传图片)
if (event.node.req.method === 'POST') { if (event.node.req.method === 'POST') {
const form = formidable({ multiples: true, uploadDir: dirPath }) const form = formidable({ multiples: true, uploadDir: dirPath })
@@ -55,4 +57,5 @@ export default defineEventHandler(async event => {
return { list } return { list }
} }
}) })

17
uno.config.ts Normal file
View File

@@ -0,0 +1,17 @@
import { defineConfig, presetWind3, presetWebFonts } from 'unocss'
import extractorPug from '@unocss/extractor-pug'
export default defineConfig({
presets: [
presetWind3(),
presetWebFonts({
fonts: {
sans: 'Roboto',
mono: ['Fira Code', 'Fira Mono:400,700']
}
})
],
extractors: [
extractorPug(),
],
})