1
0

update frontend with bulma css and home&404 pages

This commit is contained in:
2026-05-12 15:32:30 +08:00
parent 46f2d69800
commit 37b08927a7
16 changed files with 383 additions and 50 deletions

61
assets/.nginx.conf Normal file
View File

@@ -0,0 +1,61 @@
# ============================
# 路由 1: /web -> 静态文件
# ============================
location /web {
# 使用 alias 精确映射
# 请求 /web/index.html -> /var/www/static/index.html
alias /var/www/static;
# 静态文件优化
expires 7d;
add_header Cache-Control "public, max-age=604800";
# 尝试返回文件不存在则返回404避免落入其他location
try_files $uri $uri/ =404;
# 可选:启用 gzip 压缩
gzip_static on;
}
# ============================
# 路由 2: /api -> Go 程序 (8848端口)
# ============================
location /api {
# 反向代理到本地 Go 服务
proxy_pass http://127.0.0.1:8848;
# 重要:保留原始请求头
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket 支持(如果 Go 程序需要)
# proxy_http_version 1.1;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "upgrade";
# 超时设置(根据业务调整)
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 缓冲设置(可选,大文件上传时注意调整)
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
# ============================
# 可选:根路径处理
# ============================
location = / {
# 重定向到 /web
return 302 /web/;
}
# 禁止访问隐藏文件
location ~ /\. {
deny all;
return 404;
}

29
assets/dial_plate_gen.py Normal file
View File

@@ -0,0 +1,29 @@
import math
print('Dial Plate Generator')
plateSize = float(input('Plate size: '))
hourInnerRadius = float(input('Hour inner radius percent (float): '))
hourOutterRadius = float(input('Hour outter radius percent (float): '))
minuteRadius = float(input('Minute radius percent (float): '))
halfPlateSize = plateSize / 2
for i in range(24):
rad = math.radians(90 - i * 30)
x = math.cos(rad)
y = math.sin(rad)
radius = halfPlateSize * (hourOutterRadius if i < 12 else hourInnerRadius)
x = x * radius + halfPlateSize
y = (-y * radius) + halfPlateSize
print('<text x="{:.6f}" y="{:.6f}">{}</text>'.format(x, y, i))
print('')
for i in range(12):
rad = math.radians(90 - i * 30)
x = math.cos(rad)
y = math.sin(rad)
radius = minuteRadius * halfPlateSize
x = x * radius + halfPlateSize
y = (-y * radius) + halfPlateSize
print('<text x="{:.6f}" y="{:.6f}">{}</text>'.format(x, y, i * 5))

View File

@@ -33,6 +33,7 @@
"jiti": "^2.6.1",
"npm-run-all2": "^8.0.4",
"oxlint": "~1.60.0",
"sass": "^1.99.0",
"typescript": "~6.0.0",
"vite": "^8.0.8",
"vite-plugin-vue-devtools": "^8.1.1",

231
frontend/pnpm-lock.yaml generated
View File

@@ -35,7 +35,7 @@ importers:
version: 24.12.2
'@vitejs/plugin-vue':
specifier: ^6.0.6
version: 6.0.6(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3))(vue@3.5.33(typescript@6.0.3))
version: 6.0.6(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3))(vue@3.5.33(typescript@6.0.3))
'@vue/eslint-config-typescript':
specifier: ^14.7.0
version: 14.7.0(eslint-plugin-vue@10.8.0(@typescript-eslint/parser@8.59.1(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3))(eslint@10.2.1(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@10.2.1(jiti@2.6.1))))(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)
@@ -60,15 +60,18 @@ importers:
oxlint:
specifier: ~1.60.0
version: 1.60.0
sass:
specifier: ^1.99.0
version: 1.99.0
typescript:
specifier: ~6.0.0
version: 6.0.3
vite:
specifier: ^8.0.8
version: 8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3)
version: 8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3)
vite-plugin-vue-devtools:
specifier: ^8.1.1
version: 8.1.1(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3))(vue@3.5.33(typescript@6.0.3))
version: 8.1.1(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3))(vue@3.5.33(typescript@6.0.3))
vue-tsc:
specifier: ^3.2.6
version: 3.2.7(typescript@6.0.3)
@@ -451,6 +454,94 @@ packages:
cpu: [x64]
os: [win32]
'@parcel/watcher-android-arm64@2.5.6':
resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [android]
'@parcel/watcher-darwin-arm64@2.5.6':
resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [darwin]
'@parcel/watcher-darwin-x64@2.5.6':
resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [darwin]
'@parcel/watcher-freebsd-x64@2.5.6':
resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [freebsd]
'@parcel/watcher-linux-arm-glibc@2.5.6':
resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-arm-musl@2.5.6':
resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
libc: [musl]
'@parcel/watcher-linux-arm64-glibc@2.5.6':
resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-arm64-musl@2.5.6':
resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@parcel/watcher-linux-x64-glibc@2.5.6':
resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-x64-musl@2.5.6':
resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
'@parcel/watcher-win32-arm64@2.5.6':
resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [win32]
'@parcel/watcher-win32-ia32@2.5.6':
resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==}
engines: {node: '>= 10.0.0'}
cpu: [ia32]
os: [win32]
'@parcel/watcher-win32-x64@2.5.6':
resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [win32]
'@parcel/watcher@2.5.6':
resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
engines: {node: '>= 10.0.0'}
'@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
@@ -820,6 +911,10 @@ packages:
caniuse-lite@1.0.30001791:
resolution: {integrity: sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==}
chokidar@4.0.3:
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
engines: {node: '>= 14.16.0'}
chokidar@5.0.0:
resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==}
engines: {node: '>= 20.19.0'}
@@ -1034,6 +1129,9 @@ packages:
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
engines: {node: '>= 4'}
immutable@5.1.5:
resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==}
imurmurhash@0.1.4:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
@@ -1250,6 +1348,9 @@ packages:
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
node-addon-api@7.1.1:
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
node-releases@2.0.38:
resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==}
@@ -1371,6 +1472,10 @@ packages:
resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==}
engines: {node: ^18.17.0 || >=20.5.0}
readdirp@4.1.2:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
readdirp@5.0.0:
resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
engines: {node: '>= 20.19.0'}
@@ -1394,6 +1499,11 @@ packages:
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
sass@1.99.0:
resolution: {integrity: sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==}
engines: {node: '>=14.0.0'}
hasBin: true
scule@1.3.0:
resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
@@ -2011,6 +2121,67 @@ snapshots:
'@oxlint/binding-win32-x64-msvc@1.60.0':
optional: true
'@parcel/watcher-android-arm64@2.5.6':
optional: true
'@parcel/watcher-darwin-arm64@2.5.6':
optional: true
'@parcel/watcher-darwin-x64@2.5.6':
optional: true
'@parcel/watcher-freebsd-x64@2.5.6':
optional: true
'@parcel/watcher-linux-arm-glibc@2.5.6':
optional: true
'@parcel/watcher-linux-arm-musl@2.5.6':
optional: true
'@parcel/watcher-linux-arm64-glibc@2.5.6':
optional: true
'@parcel/watcher-linux-arm64-musl@2.5.6':
optional: true
'@parcel/watcher-linux-x64-glibc@2.5.6':
optional: true
'@parcel/watcher-linux-x64-musl@2.5.6':
optional: true
'@parcel/watcher-win32-arm64@2.5.6':
optional: true
'@parcel/watcher-win32-ia32@2.5.6':
optional: true
'@parcel/watcher-win32-x64@2.5.6':
optional: true
'@parcel/watcher@2.5.6':
dependencies:
detect-libc: 2.1.2
is-glob: 4.0.3
node-addon-api: 7.1.1
picomatch: 4.0.4
optionalDependencies:
'@parcel/watcher-android-arm64': 2.5.6
'@parcel/watcher-darwin-arm64': 2.5.6
'@parcel/watcher-darwin-x64': 2.5.6
'@parcel/watcher-freebsd-x64': 2.5.6
'@parcel/watcher-linux-arm-glibc': 2.5.6
'@parcel/watcher-linux-arm-musl': 2.5.6
'@parcel/watcher-linux-arm64-glibc': 2.5.6
'@parcel/watcher-linux-arm64-musl': 2.5.6
'@parcel/watcher-linux-x64-glibc': 2.5.6
'@parcel/watcher-linux-x64-musl': 2.5.6
'@parcel/watcher-win32-arm64': 2.5.6
'@parcel/watcher-win32-ia32': 2.5.6
'@parcel/watcher-win32-x64': 2.5.6
optional: true
'@polka/url@1.0.0-next.29': {}
'@rolldown/binding-android-arm64@1.0.0-rc.17':
@@ -2174,10 +2345,10 @@ snapshots:
'@typescript-eslint/types': 8.59.1
eslint-visitor-keys: 5.0.1
'@vitejs/plugin-vue@6.0.6(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3))(vue@3.5.33(typescript@6.0.3))':
'@vitejs/plugin-vue@6.0.6(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3))(vue@3.5.33(typescript@6.0.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.13
vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3)
vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3)
vue: 3.5.33(typescript@6.0.3)
'@volar/language-core@2.4.28':
@@ -2411,6 +2582,10 @@ snapshots:
caniuse-lite@1.0.30001791: {}
chokidar@4.0.3:
dependencies:
readdirp: 4.1.2
chokidar@5.0.0:
dependencies:
readdirp: 5.0.0
@@ -2611,6 +2786,8 @@ snapshots:
ignore@7.0.5: {}
immutable@5.1.5: {}
imurmurhash@0.1.4: {}
is-docker@3.0.0: {}
@@ -2769,6 +2946,9 @@ snapshots:
natural-compare@1.4.0: {}
node-addon-api@7.1.1:
optional: true
node-releases@2.0.38: {}
npm-normalize-package-bin@4.0.0: {}
@@ -2899,6 +3079,8 @@ snapshots:
json-parse-even-better-errors: 4.0.0
npm-normalize-package-bin: 4.0.0
readdirp@4.1.2: {}
readdirp@5.0.0: {}
reusify@1.1.0: {}
@@ -2932,6 +3114,14 @@ snapshots:
dependencies:
queue-microtask: 1.2.3
sass@1.99.0:
dependencies:
chokidar: 4.0.3
immutable: 5.1.5
source-map-js: 1.2.1
optionalDependencies:
'@parcel/watcher': 2.5.6
scule@1.3.0: {}
semver@6.3.1: {}
@@ -3022,17 +3212,17 @@ snapshots:
util-deprecate@1.0.2: {}
vite-dev-rpc@1.1.0(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3)):
vite-dev-rpc@1.1.0(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3)):
dependencies:
birpc: 2.9.0
vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3)
vite-hot-client: 2.1.0(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3))
vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3)
vite-hot-client: 2.1.0(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3))
vite-hot-client@2.1.0(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3)):
vite-hot-client@2.1.0(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3)):
dependencies:
vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3)
vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3)
vite-plugin-inspect@11.3.3(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3)):
vite-plugin-inspect@11.3.3(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3)):
dependencies:
ansis: 4.2.0
debug: 4.4.3
@@ -3042,26 +3232,26 @@ snapshots:
perfect-debounce: 2.1.0
sirv: 3.0.2
unplugin-utils: 0.3.1
vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3)
vite-dev-rpc: 1.1.0(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3))
vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3)
vite-dev-rpc: 1.1.0(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3))
transitivePeerDependencies:
- supports-color
vite-plugin-vue-devtools@8.1.1(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3))(vue@3.5.33(typescript@6.0.3)):
vite-plugin-vue-devtools@8.1.1(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3))(vue@3.5.33(typescript@6.0.3)):
dependencies:
'@vue/devtools-core': 8.1.1(vue@3.5.33(typescript@6.0.3))
'@vue/devtools-kit': 8.1.1
'@vue/devtools-shared': 8.1.1
sirv: 3.0.2
vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3)
vite-plugin-inspect: 11.3.3(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3))
vite-plugin-vue-inspector: 5.4.0(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3))
vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3)
vite-plugin-inspect: 11.3.3(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3))
vite-plugin-vue-inspector: 5.4.0(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3))
transitivePeerDependencies:
- '@nuxt/kit'
- supports-color
- vue
vite-plugin-vue-inspector@5.4.0(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3)):
vite-plugin-vue-inspector@5.4.0(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3)):
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0)
@@ -3072,11 +3262,11 @@ snapshots:
'@vue/compiler-dom': 3.5.33
kolorist: 1.8.0
magic-string: 0.30.21
vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3)
vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3)
transitivePeerDependencies:
- supports-color
vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(yaml@2.8.3):
vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.3):
dependencies:
lightningcss: 1.32.0
picomatch: 4.0.4
@@ -3087,6 +3277,7 @@ snapshots:
'@types/node': 24.12.2
fsevents: 2.3.3
jiti: 2.6.1
sass: 1.99.0
yaml: 2.8.3
vscode-uri@3.1.0: {}

View File

@@ -0,0 +1,2 @@
// 导入 Bulma
@use "bulma/sass"

View File

@@ -1,4 +1,9 @@
<script setup lang="ts"></script>
<script setup lang="ts">
import { useLanguageStore } from './stores/global';
const language = useLanguageStore();
</script>
<template>
<nav class="navbar has-shadow is-spaced bd-navbar" role="navigation" aria-label="main navigation">
@@ -33,10 +38,12 @@
</p>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link"></a>
<a v-if="language.isEnglish" class="navbar-link">English</a>
<a v-else-if="language.isSimplifiedChinese" class="navbar-link">简体中文</a>
<div class="navbar-dropdown">
<a language="en-US" class="navbar-item">English</a>
<a language="zh-CN" class="navbar-item">简体中文</a>
<a @click="language.changeToEnglish()" class="navbar-item">English</a>
<a @click="language.changeToSimplifiedChinese()" class="navbar-item">简体中文</a>
</div>
</div>
</div>

View File

@@ -4,6 +4,8 @@ import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import '../public/index.scss'
const app = createApp(App)
app.use(createPinia())

View File

@@ -6,7 +6,7 @@ import Calendar from '@/views/Calendar.vue'
import Todo from '@/views/Todo.vue'
import Admin from '@/views/Admin.vue'
import Page404 from '@/views/Page404.vue'
import NotFound from '@/views/NotFound.vue'
const routes = [
{ path: '/home', component: Home },
@@ -15,7 +15,7 @@ const routes = [
{ path: '/todo', component: Todo},
{ path: '/admin', component: Admin },
{ path: '/404', component: Page404 },
{ path: '/404', component: NotFound },
{ path: '/', redirect: '/home' },
{ path: '/:pathMatch(.*)*', redirect: '/404' },

View File

@@ -1,12 +0,0 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})

View File

@@ -0,0 +1,26 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import { Language } from '@/utils/i18n'
export const useLanguageStore = defineStore('language', {
state: () => ({
language: Language.English
}),
getters: {
isEnglish: (state) => state.language === Language.English,
isSimplifiedChinese: (state) => state.language === Language.SimplifiedChinese,
},
actions: {
changeLanguage(lang: Language) {
this.language = lang;
},
changeToEnglish() {
this.changeLanguage(Language.English);
},
changeToSimplifiedChinese() {
this.changeLanguage(Language.SimplifiedChinese);
},
},
})

View File

@@ -0,0 +1,4 @@
export enum Language {
English,
SimplifiedChinese,
}

View File

@@ -1,8 +1,20 @@
<script setup lang="ts"></script>
<template>
<h1>Congratulations</h1>
<p>This is home.</p>
<div class="container">
<article>
<h1 class="title">coconut-leaf</h1>
<p>A light, self-host and multi-account calendar system.</p>
<p>The original intention of this system is served for yyc12345 personal use.</p>
<br />
<p>See our <a href="https://github.com/yyc12345/coconut-leaf">GitHub project</a> for the source code in detail.</p>
<p>The source code of this project is licensed under <a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL v3</a>.</p>
</article>
</div>
</template>
<style scoped></style>
<style scoped>
div.container {
margin-top: 1.25rem;
}
</style>

View File

@@ -0,0 +1,17 @@
<script setup lang="ts"></script>
<template>
<div class="container">
<article>
<h1 class="title">Oops!</h1>
<p>You are wandering in the desert of coconut-leaf.</p>
<p>Please back to previous page and try again.</p>
</article>
</div>
</template>
<style scoped>
div.container {
margin-top: 1.25rem;
}
</style>

View File

@@ -1,8 +0,0 @@
<script setup lang="ts"></script>
<template>
<h1>Congratulations</h1>
<p>404 Not Found</p>
</template>
<style scoped></style>

View File

@@ -33,5 +33,6 @@ export default defineConfig({
rewrite: (path) => path.replace(/^\/api\//, '/'),
}
},
},
})