Compare commits

...

4 Commits

Author SHA1 Message Date
kkfluous
36371ab601 chore: ⬆️ daisyui ^5.5.14 -> ^5.5.19
Some checks are pending
ci/woodpecker/push/woodpecker Pipeline is running
ci/woodpecker/manual/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:05:44 +08:00
kkfluous
6889fdee1f feat: 标题文案与样式焕新「星耀时刻,致敬奋斗者」
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- 中英文默认标题更新为「星耀时刻,致敬奋斗者」/「Stellar Moments · Tribute to the Driven」
- 在 main.ts 添加一次性迁移,自动覆盖本地缓存中的旧标题
- HeaderTitle 与 PrizeDraw 标题改用羚牛 logo 配色:深炭黑 + 翡翠绿 + 金色高光的渐变与流光动画
- 标题左右增加渐变细线 + 金/绿光点装饰,按钮统一为绿金品牌色
- 标题强制单行不换行,避免窄容器下折行

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:59:56 +08:00
kkfluous
30ea764c44 chore: 🐳 docker-compose 端口对齐为 8029
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
与 demo-ctx 反向代理实际转发目标保持一致。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 16:57:38 +08:00
kkfluous
c7025db514 chore: 🐳 新增 Portainer 部署用 docker-compose
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
外部端口 8088 → 容器 80,镜像 tag 跟 Woodpecker 构建输出一致。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 16:49:09 +08:00
7 changed files with 304 additions and 30 deletions

16
docker-compose.yml Normal file
View File

@@ -0,0 +1,16 @@
version: '3.8'
services:
log-lottery:
image: harbor.lnh2e.com/lingniu-v1/log-lottery:main-0.6.0-5
ports:
- "8029:80"
deploy:
replicas: 1
restart_policy:
condition: on-failure
placement:
constraints: [node.role == manager]
labels:
- portainer.hide=false
- project=lingniu

View File

@@ -83,7 +83,7 @@
"autoprefixer": "^10.4.23",
"baseline-browser-mapping": "^2.9.11",
"child_process": "^1.0.2",
"daisyui": "^5.5.14",
"daisyui": "^5.5.19",
"eslint": "^9.39.2",
"eslint-plugin-vue": "^10.6.2",
"fast-glob": "^3.3.3",

10
pnpm-lock.yaml generated
View File

@@ -196,8 +196,8 @@ importers:
specifier: ^1.0.2
version: 1.0.2
daisyui:
specifier: ^5.5.14
version: 5.5.14
specifier: ^5.5.19
version: 5.5.19
eslint:
specifier: ^9.39.2
version: 9.39.2(jiti@2.6.1)
@@ -2448,8 +2448,8 @@ packages:
csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
daisyui@5.5.14:
resolution: {integrity: sha512-L47rvw7I7hK68TA97VB8Ee0woHew+/ohR6Lx6Ah/krfISOqcG4My7poNpX5Mo5/ytMxiR40fEaz6njzDi7cuSg==}
daisyui@5.5.19:
resolution: {integrity: sha512-pbFAkl1VCEh/MPCeclKL61I/MqRIFFhNU7yiXoDDRapXN4/qNCoMxeCCswyxEEhqL5eiTTfwHvucFtOE71C9sA==}
data-urls@6.0.0:
resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==}
@@ -7638,7 +7638,7 @@ snapshots:
csstype@3.2.3: {}
daisyui@5.5.14: {}
daisyui@5.5.19: {}
data-urls@6.0.0:
dependencies:

View File

@@ -12,7 +12,7 @@ export const dataEn = {
operation: 'Operation',
delete: 'Delete',
removePerson: 'Remove the Person',
defaultTitle: 'The Prelude to the Six Ministries of the Ming Dynasty Cabinet',
defaultTitle: 'Stellar Moments · Tribute to the Driven',
xlsxName: 'personListTemplate-en.xlsx',
readmeName: 'readme-en.md',
}
@@ -31,7 +31,7 @@ export const dataZhCn = {
operation: '操作',
delete: '删除',
removePerson: '移入未中奖名单',
defaultTitle: '「氢」春正好,牛人闪耀',
defaultTitle: '星耀时刻,致敬奋斗者',
xlsxName: '人口登记表-zhCn.xlsx',
readmeName: 'readme-zhCn.md',
}

View File

@@ -38,6 +38,22 @@ import 'virtual:svg-icons-register'
// 更新CSS变量
document.documentElement.style.setProperty('--app-font-family', `"${globalConfig.theme.font}", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif`)
}
// 一次性迁移旧的默认标题,确保已有用户也能看到新文案
const legacyTitles = new Set([
'「氢」春正好,牛人闪耀',
'The Prelude to the Six Ministries of the Ming Dynasty Cabinet',
])
if (globalConfig.topTitle && legacyTitles.has(globalConfig.topTitle)) {
const isEn = globalConfig.language === 'en'
globalConfig.topTitle = isEn
? 'Stellar Moments · Tribute to the Driven'
: '星耀时刻,致敬奋斗者'
if (storageData.globalConfig) {
storageData.globalConfig = globalConfig
}
localStorage.setItem('globalConfig', JSON.stringify(storageData.globalConfig ? storageData : globalConfig))
}
}
}
catch (e) {

View File

@@ -40,22 +40,26 @@ const { t } = useI18n()
<template>
<div class="absolute z-10 flex flex-col items-center justify-center -translate-x-1/2 left-1/2">
<div class="lingniu-title-row pt-12 mb-12">
<span v-if="!isTextColor" class="title-ornament title-ornament--left" aria-hidden="true" />
<h2
class="pt-12 m-0 mb-12 tracking-wide text-center leading-12"
:class="{ 'animate-pulse bg-linear-to-r from-primary via-secondary to-accent bg-clip-text text-transparent': !isTextColor }"
class="lingniu-title-text m-0 text-center"
:class="{ 'lingniu-title-gradient': !isTextColor }"
:style="titleStyle"
>
{{ topTitle }}
</h2>
<span v-if="!isTextColor" class="title-ornament title-ornament--right" aria-hidden="true" />
</div>
<div v-if="isInitialDone" class="flex gap-3">
<button
v-if="tableData.length <= 0" class="cursor-pointer btn btn-outline btn-secondary btn-lg"
v-if="tableData.length <= 0" class="cursor-pointer btn btn-outline btn-lg lingniu-btn"
@click="router.push('config')"
>
{{ t('button.noInfoAndImport') }}
</button>
<button
v-if="tableData.length <= 0" class="cursor-pointer btn btn-outline btn-secondary btn-lg"
v-if="tableData.length <= 0" class="cursor-pointer btn btn-outline btn-lg lingniu-btn"
@click="setDefaultPersonList"
>
{{ t('button.useDefault') }}
@@ -70,11 +74,132 @@ const { t } = useI18n()
</template>
<style scoped lang="scss">
.header-title {
.lingniu-title-row {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
gap: 28px;
white-space: nowrap;
-webkit-animation: tracking-in-expand-fwd 0.8s cubic-bezier(0.215, 0.610, 0.355, 1.000) both;
animation: tracking-in-expand-fwd 0.8s cubic-bezier(0.215, 0.610, 0.355, 1.000) both;
}
.lingniu-title-text {
line-height: 1.2;
font-weight: 700;
letter-spacing: 0.22em;
padding-inline: 0.22em; // 防止letter-spacing截断尾字渐变
white-space: nowrap;
}
.lingniu-title-gradient {
background: linear-gradient(
110deg,
#2F2828 0%,
#007143 18%,
#12a86b 36%,
#f5d27a 50%,
#12a86b 64%,
#007143 82%,
#2F2828 100%
);
background-size: 220% 100%;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
filter: drop-shadow(0 6px 24px rgba(0, 113, 67, 0.35));
animation: lingniu-shine 6s linear infinite;
}
.title-ornament {
--ornament-w: clamp(56px, 12vw, 140px);
position: relative;
display: inline-block;
width: var(--ornament-w);
height: 1px;
background: linear-gradient(to right, transparent 0%, rgba(0, 113, 67, 0.05) 10%, #007143 50%, rgba(0, 113, 67, 0.05) 90%, transparent 100%);
flex: none;
&::before,
&::after {
content: '';
position: absolute;
top: 50%;
width: 8px;
height: 8px;
border-radius: 50%;
transform: translateY(-50%) rotate(45deg);
background: linear-gradient(135deg, #f5d27a 0%, #d4a960 100%);
box-shadow:
0 0 14px rgba(0, 113, 67, 0.55),
0 0 4px rgba(245, 210, 122, 0.7);
}
&--left {
&::before {
left: 50%;
margin-left: -4px;
background: linear-gradient(135deg, #007143 0%, #12a86b 100%);
}
&::after {
right: -4px;
}
}
&--right {
&::before {
left: -4px;
}
&::after {
right: 50%;
margin-right: -4px;
background: linear-gradient(135deg, #007143 0%, #12a86b 100%);
}
}
}
.lingniu-btn {
--btn-color: #007143;
--btn-color-hover: #12a86b;
color: #fff;
border-color: rgba(0, 113, 67, 0.6);
background: rgba(0, 113, 67, 0.08);
backdrop-filter: blur(6px);
box-shadow:
0 4px 18px rgba(0, 113, 67, 0.18),
inset 0 0 0 1px rgba(245, 210, 122, 0.12);
transition: transform 0.25s ease, box-shadow 0.25s ease, background 0.25s ease, border-color 0.25s ease;
&:hover {
background: linear-gradient(135deg, rgba(0, 113, 67, 0.85), rgba(18, 168, 107, 0.85));
border-color: rgba(245, 210, 122, 0.7);
box-shadow:
0 10px 28px rgba(0, 113, 67, 0.45),
inset 0 0 0 1px rgba(245, 210, 122, 0.45);
transform: translateY(-1px);
}
}
@media (max-width: 640px) {
.lingniu-title-row {
gap: 12px;
}
.title-ornament {
--ornament-w: 36px;
}
}
@keyframes lingniu-shine {
0% {
background-position: 200% 50%;
}
100% {
background-position: -200% 50%;
}
}
@-webkit-keyframes tracking-in-expand-fwd {
0% {
letter-spacing: -0.5em;

View File

@@ -5,13 +5,17 @@
<!-- 顶部标题 -->
<div class="header-wrapper">
<div class="lingniu-title-row">
<span v-if="!isTextColor" class="title-ornament title-ornament--left" aria-hidden="true" />
<h2
class="page-title"
:class="{ 'animate-pulse bg-linear-to-r from-primary via-secondary to-accent bg-clip-text text-transparent': !isTextColor }"
class="page-title lingniu-title-text"
:class="{ 'lingniu-title-gradient': !isTextColor }"
:style="titleStyle"
>
{{ topTitle }}
</h2>
<span v-if="!isTextColor" class="title-ornament title-ornament--right" aria-hidden="true" />
</div>
</div>
<!-- 抽奖进度和重置按钮 -->
@@ -775,14 +779,127 @@ onUnmounted(() => {
justify-content: center;
}
.page-title {
.lingniu-title-row {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
white-space: nowrap;
gap: 28px;
padding-top: 48px;
margin: 0;
margin-bottom: 48px;
letter-spacing: 0.05em;
-webkit-animation: tracking-in-expand-fwd 0.8s cubic-bezier(0.215, 0.610, 0.355, 1.000) both;
animation: tracking-in-expand-fwd 0.8s cubic-bezier(0.215, 0.610, 0.355, 1.000) both;
}
.page-title {
margin: 0;
text-align: center;
line-height: 3rem;
font-weight: normal;
line-height: 1.2;
}
.lingniu-title-text {
font-weight: 700;
letter-spacing: 0.22em;
padding-inline: 0.22em;
white-space: nowrap;
}
.lingniu-title-gradient {
background: linear-gradient(
110deg,
#2F2828 0%,
#007143 18%,
#12a86b 36%,
#f5d27a 50%,
#12a86b 64%,
#007143 82%,
#2F2828 100%
);
background-size: 220% 100%;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
filter: drop-shadow(0 6px 24px rgba(0, 113, 67, 0.35));
animation: lingniu-shine 6s linear infinite;
}
.title-ornament {
--ornament-w: clamp(56px, 12vw, 140px);
position: relative;
display: inline-block;
width: var(--ornament-w);
height: 1px;
flex: none;
background: linear-gradient(
to right,
transparent 0%,
rgba(0, 113, 67, 0.05) 10%,
#007143 50%,
rgba(0, 113, 67, 0.05) 90%,
transparent 100%
);
}
.title-ornament::before,
.title-ornament::after {
content: '';
position: absolute;
top: 50%;
width: 8px;
height: 8px;
border-radius: 50%;
transform: translateY(-50%) rotate(45deg);
background: linear-gradient(135deg, #f5d27a 0%, #d4a960 100%);
box-shadow:
0 0 14px rgba(0, 113, 67, 0.55),
0 0 4px rgba(245, 210, 122, 0.7);
}
.title-ornament--left::before {
left: 50%;
margin-left: -4px;
background: linear-gradient(135deg, #007143 0%, #12a86b 100%);
}
.title-ornament--left::after {
right: -4px;
}
.title-ornament--right::before {
left: -4px;
}
.title-ornament--right::after {
right: 50%;
margin-right: -4px;
background: linear-gradient(135deg, #007143 0%, #12a86b 100%);
}
@media (max-width: 640px) {
.lingniu-title-row {
gap: 12px;
}
.title-ornament {
--ornament-w: 36px;
}
}
@keyframes lingniu-shine {
0% { background-position: 200% 50%; }
100% { background-position: -200% 50%; }
}
@keyframes tracking-in-expand-fwd {
0% {
letter-spacing: -0.5em;
transform: translateZ(-700px);
opacity: 0;
}
40% { opacity: 0.6; }
100% {
transform: translateZ(0);
opacity: 1;
}
}
.prize-info {