更新页面效果

This commit is contained in:
李琦
2026-07-31 12:13:41 +08:00
parent 4630035dd1
commit d060681920
30 changed files with 597 additions and 13 deletions

View File

@@ -121,3 +121,18 @@
实测工具agent-browserplaywright-cli 未装)。坑:后台是 history 模式URL 是 `/admin` 不是 `/#/admin`eval 多行 JS 引号易炸,写临时文件 `eval "$(cat x.js)"` 最稳;截图保存在 daemon 的 cwdD:/myCode/hunliji/dev/_shots 实测工具agent-browserplaywright-cli 未装)。坑:后台是 history 模式URL 是 `/admin` 不是 `/#/admin`eval 多行 JS 引号易炸,写临时文件 `eval "$(cat x.js)"` 最稳;截图保存在 daemon 的 cwdD:/myCode/hunliji/dev/_shots
验证:`vite build` 通过EXIT=0浏览器实测 overlap/single-full/admin 控件均正常。注:沙箱浏览器加载不了 unsplash显示裂图localhost:8080 上传图正常。 验证:`vite build` 通过EXIT=0浏览器实测 overlap/single-full/admin 控件均正常。注:沙箱浏览器加载不了 unsplash显示裂图localhost:8080 上传图正常。
---
## 2026-07-31第八版错落拼贴+图片排序+铺满出血+图标颜色
用户 4 点反馈,全部完成并实测:
1. **错落双图没有错落感**:排查发现总根因——`frames.css``.gf-frame { position:relative }` 在 Tailwind 之后加载,同优先级覆盖元素上的 `absolute`overlap 两图实际一直在静态流里right/bottom 全失效),此前几轮修复都是在治症状。删除 position 后,改拼贴布局:图一左上 72%×78% -2°、图二右下 56%×62% +3° 压角叠放。实测两图 absolute 定位正确左18-276 / 右167-373角落重叠
2. **后台图片排序**AdminEditor 多图版式每张图片加「前移/后移」圆钮(左下/右下moveImage swap实测 36 个按钮渲染正常。
3. **铺满沾满全宽**singleWidth=full 时加负边距出血width:calc(100%+48px); margin:0 -24px 抵消 section p-6。实测 frame 0→390px 完全贴缘。验证方式:直接 POST 改库配置→截图→精确还原original-config.json 备份恢复page0 回 default、scrollMode 回 free
4. **播放图标白色不可见**pause 图标从白色改金色(#a88c6b),激活态按钮改浅金底(border 加深)。实测 3 个控制图标全 gold。
新坑记录agent-browser 的 `network route --body` mock 有时页面自己的首次请求不走拦截eval fetch 却走),不稳定 → 验证配置渲染直接改库+还原更可靠screenshot 不支持 --clip会当成文件路径
验证:`vite build` 通过EXIT=0overlap/single-bleed/admin-sort/controls 截图均确认。

View File

@@ -6,10 +6,11 @@
- 配置存后端单条 JSON前后端均做旧字段兼容musicUrl→musicList、缺省 height/borderStyle/singleWidth/速度参数)。 - 配置存后端单条 JSON前后端均做旧字段兼容musicUrl→musicList、缺省 height/borderStyle/singleWidth/速度参数)。
## 关键坑(不要再踩) ## 关键坑(不要再踩)
1. **滚动容器绝不能加 `scroll-smooth`**CSS smooth 会劫持 JS 的 scrollTop 赋值free 自动滚动会严重卡顿。snap 翻页用自研 rAF 动画(`smoothScrollTo`,时长=pageSwitchDuration 1. **`gf-frame` 绝不能设 position/width/height**frames.css 在 Tailwind 之后加载,同优先级后者胜出——`position:relative` 曾把 overlap 版的 `absolute` 静默冲掉元素退回静态流right/bottom 失效),这是错落双图溢出问题反复出现的总根因。布局定位只交给 Tailwind 工具类
2. **动画摇曳禁用 margin-left**(每帧触发布局):外层跑位移、内层元素跑 transform 摇曳petal/petal-inner、heart-bubble/heart-inner 模式)。 2. **滚动容器绝不能加 `scroll-smooth`**CSS smooth 会劫持 JS 的 scrollTop 赋值free 自动滚动会严重卡顿。snap 翻页用自研 rAF 动画(`smoothScrollTo`,时长=pageSwitchDuration)。
3. **相册页高与图片容器要联动**:页高可能 auto/80vh图片容器写死 vh 会脱钩溢出 → 用 clamp 封顶 + 图片贴容器边缘 3. **动画摇曳禁用 margin-left**(每帧触发布局):外层跑位移、内层元素跑 transform 摇曳petal/petal-inner、heart-bubble/heart-inner 模式)
4. `gf-frame` 不设 width/height尺寸交给版式包装类`gf-natural` 用于保持原始比例 4. **相册页高与图片容器要联动**:页高可能 auto/80vh图片容器写死 vh 会脱钩溢出 → 用 clamp 封顶 + 图片贴容器边缘
5. `gf-natural` 保持原始比例单图铺满用负边距出血width:calc(100%+48px); margin:0 -24px 抵消 section p-6
## 环境 ## 环境
- 本机"安全删除"损坏rm 常失败vite build 用 `--outDir dist-chk-<ts>` 新目录验证,临时目录需手动清。 - 本机"安全删除"损坏rm 常失败vite build 用 `--outDir dist-chk-<ts>` 新目录验证,临时目录需手动清。

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
.admin-tabs[data-v-a7a3ebcb] .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn{color:#a88c6b}.admin-tabs[data-v-a7a3ebcb] .ant-tabs-ink-bar{background:#a88c6b}.admin-tabs[data-v-a7a3ebcb] .ant-tabs-tab:hover .ant-tabs-tab-btn{color:#967b5c}

View File

@@ -0,0 +1 @@
import{x as k,d as y,e as t,q as a,y as o,z as n,A as z,B as A,b as q,k as c,p as B,r as C,C as p}from"./index-B7Bp0mE9.js";const N={class:"min-h-screen w-full flex items-center justify-center bg-[#Fdfbf7] relative overflow-hidden"},S={class:"relative z-10 w-[360px] bg-white/80 backdrop-blur-xl border border-[#a88c6b]/15 rounded-3xl shadow-2xl p-9 animate-fade-up"},V={class:"mt-6 text-center"},U={__name:"AdminLogin",setup(F){const m=z(),b=A(),f=k(),i=C(!1),r=B({account:"",password:""}),x=async()=>{var u,e;i.value=!0;try{await f.login(r),p.success("登录成功"),m.replace(b.query.redirect||"/admin")}catch(s){const l=((e=(u=s==null?void 0:s.response)==null?void 0:u.data)==null?void 0:e.error)||(s==null?void 0:s.message)||"登录失败";p.error(l)}finally{i.value=!1}};return(u,e)=>{const s=n("a-input"),l=n("a-form-item"),g=n("a-input-password"),v=n("a-button"),_=n("a-form"),w=n("router-link");return q(),y("div",N,[e[7]||(e[7]=t("div",{class:"absolute -top-24 -right-24 w-80 h-80 rounded-full bg-[#a88c6b]/10 blur-3xl"},null,-1)),e[8]||(e[8]=t("div",{class:"absolute -bottom-24 -left-24 w-80 h-80 rounded-full bg-[#e8a5b2]/10 blur-3xl"},null,-1)),e[9]||(e[9]=t("div",{class:"absolute inset-0 pointer-events-none opacity-[0.04]",style:{"background-image":"radial-gradient(#a88c6b 1px, transparent 1px)","background-size":"22px 22px"}},null,-1)),t("div",S,[e[6]||(e[6]=t("div",{class:"text-center mb-8"},[t("div",{class:"text-3xl mb-3 text-[#a88c6b] font-serif"},"❀"),t("h1",{class:"text-xl tracking-[0.3em] text-[#2c2c2c] font-light"},"婚礼纪 · 后台"),t("p",{class:"text-[11px] text-[#8A8680] tracking-[0.3em] mt-2 uppercase"},"Admin Console")],-1)),a(_,{model:r,layout:"vertical",onFinish:x},{default:o(()=>[a(l,{name:"account",rules:[{required:!0,message:"请输入账号"}]},{default:o(()=>[a(s,{value:r.account,"onUpdate:value":e[0]||(e[0]=d=>r.account=d),size:"large",placeholder:"管理员账号"},{prefix:o(()=>[...e[2]||(e[2]=[t("i",{class:"fa-solid fa-user text-[#a88c6b]"},null,-1)])]),_:1},8,["value"])]),_:1}),a(l,{name:"password",rules:[{required:!0,message:"请输入密码"}]},{default:o(()=>[a(g,{value:r.password,"onUpdate:value":e[1]||(e[1]=d=>r.password=d),size:"large",placeholder:"密码"},{prefix:o(()=>[...e[3]||(e[3]=[t("i",{class:"fa-solid fa-lock text-[#a88c6b]"},null,-1)])]),_:1},8,["value"])]),_:1}),a(v,{type:"primary","html-type":"submit",size:"large",block:"",loading:i.value,class:"!bg-[#a88c6b] hover:!bg-[#967b5c] !border-[#a88c6b] !rounded-xl !tracking-[0.2em]"},{default:o(()=>[...e[4]||(e[4]=[c(" 登 录 ",-1)])]),_:1},8,["loading"])]),_:1},8,["model"]),t("div",V,[a(w,{to:"/",class:"text-[12px] text-[#a88c6b] hover:underline"},{default:o(()=>[...e[5]||(e[5]=[c("返回请柬预览",-1)])]),_:1})])])])}}};export{U as default};

View File

@@ -0,0 +1 @@
const v=[{value:"none",label:"无边框"},{value:"thin",label:"细金线"},{value:"mat",label:"白卡纸"},{value:"rounded",label:"柔圆角"},{value:"double",label:"双线框"},{value:"gold",label:"古典金框"},{value:"shadow",label:"阴影浮起"},{value:"ink",label:"墨黑描边"},{value:"card",label:"悬浮卡片"},{value:"lace",label:"虚线文艺"},{value:"glow",label:"柔光金晕"}];function t(l){return"gf-frame gf-"+(v.some(a=>a.value===l)?l:"mat")}const b=(l,e)=>{const a=l.__vccOpts||l;for(const[o,u]of e)a[o]=u;return a};export{v as B,b as _,t as b};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>婚礼请柬 · Wedding Invitation</title>
<script type="module" crossorigin src="/assets/index-B7Bp0mE9.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-gh4sePj7.css">
</head>
<body>
<div id="app"></div>
</body>

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -13,12 +13,14 @@
<div :class="singleFrameClass"><img :src="page.img1" /></div> <div :class="singleFrameClass"><img :src="page.img1" /></div>
</div> </div>
<!-- 错落双图高度 clamp 封顶图片贴容器边缘任何页高下不溢出 --> <!-- 错落双图交错拼贴左上大图 + 右下压角叠放微旋转clamp 封顶不溢出 -->
<div v-else-if="page.type === 'overlap'" class="overlap-wrap relative w-full mt-2"> <div v-else-if="page.type === 'overlap'" class="overlap-wrap relative w-full mt-2">
<div v-reveal="{ variant: 'left', delay: 100 }" class="absolute top-0 right-0 w-[68%] h-[72%] z-10" :class="frameClass"> <div v-reveal="{ variant: 'left', delay: 100 }"
class="absolute left-0 top-0 w-[72%] h-[78%] z-10 -rotate-2" :class="frameClass">
<img :src="page.img1" /> <img :src="page.img1" />
</div> </div>
<div v-reveal="{ variant: 'right', delay: 300 }" class="absolute bottom-0 left-0 w-[60%] h-[60%] z-20" :class="frameClass"> <div v-reveal="{ variant: 'right', delay: 300 }"
class="absolute right-0 bottom-0 w-[56%] h-[62%] z-20 rotate-3" :class="frameClass">
<img :src="page.img2" /> <img :src="page.img2" />
</div> </div>
</div> </div>
@@ -96,8 +98,10 @@ const singleWrapClass = computed(() =>
) )
const singleWrapStyle = computed(() => { const singleWrapStyle = computed(() => {
if (props.page.singleWidth === 'full') { if (props.page.singleWidth === 'full') {
// 铺满 + 保持原始比例 → 高度由图片自然撑开;铺满 + 不保持 → 固定 4:5 裁切 // 沾满宽度:负边距抵消 page-section 的 p-624px×2图片贴到屏幕两缘
return props.page.singleKeepRatio ? {} : { aspectRatio: '4 / 5' } const bleed = { width: 'calc(100% + 48px)', marginLeft: '-24px', marginRight: '-24px' }
// 保持原始比例 → 高度由图片自然撑开;否则固定 4:5 裁切
return props.page.singleKeepRatio ? bleed : { ...bleed, aspectRatio: '4 / 5' }
} }
return { aspectRatio: '4 / 5' } return { aspectRatio: '4 / 5' }
}) })

View File

@@ -3,10 +3,12 @@
- 前台 PhotoGallery 按 page.borderStyle 套用 - 前台 PhotoGallery 按 page.borderStyle 套用
- 后台 AdminEditor 预览复用同一套类 - 后台 AdminEditor 预览复用同一套类
结构约定:<div class="gf-frame gf-xxx"><img/></div> 结构约定:<div class="gf-frame gf-xxx"><img/></div>
gf-frame 负责定位与裁切gf-xxx 负责边框/卡纸/投影 gf-frame 负责裁切gf-xxx 负责边框/卡纸/投影
注意:绝不能在此设置 position/width/height —— 本文件在
Tailwind 工具类之后加载,会覆盖元素上的 absolute/尺寸工具类
position:relative 曾把 overlap 版的 absolute 静默冲掉)
============================================================ */ ============================================================ */
.gf-frame { .gf-frame {
position: relative;
background: #fff; background: #fff;
overflow: hidden; overflow: hidden;
} }

View File

@@ -243,6 +243,13 @@
<button v-if="isFlexible(page.type) && page.images.length > 1" <button v-if="isFlexible(page.type) && page.images.length > 1"
class="absolute -top-2 -right-2 w-5 h-5 rounded-full bg-[#a88c6b] text-white text-[10px] leading-none flex items-center justify-center shadow" class="absolute -top-2 -right-2 w-5 h-5 rounded-full bg-[#a88c6b] text-white text-[10px] leading-none flex items-center justify-center shadow"
@click="removeImage(page, iidx)" title="移除该图"></button> @click="removeImage(page, iidx)" title="移除该图"></button>
<!-- 排序前移 / 后移 -->
<button v-if="iidx > 0"
class="absolute -bottom-2 left-1 w-5 h-5 rounded-full bg-white border border-[#a88c6b]/50 text-[#a88c6b] text-[10px] leading-none flex items-center justify-center shadow hover:bg-[#a88c6b] hover:text-white transition-colors"
@click="moveImage(page, iidx, -1)" title="前移"><i class="fa-solid fa-arrow-left"></i></button>
<button v-if="iidx < page.images.length - 1"
class="absolute -bottom-2 right-1 w-5 h-5 rounded-full bg-white border border-[#a88c6b]/50 text-[#a88c6b] text-[10px] leading-none flex items-center justify-center shadow hover:bg-[#a88c6b] hover:text-white transition-colors"
@click="moveImage(page, iidx, 1)" title="后移"><i class="fa-solid fa-arrow-right"></i></button>
</div> </div>
<button v-if="isFlexible(page.type)" <button v-if="isFlexible(page.type)"
class="w-[96px] h-[96px] rounded-xl border border-dashed border-[#a88c6b]/40 text-[#a88c6b] text-[11px] flex items-center justify-center hover:border-[#a88c6b] transition-colors" class="w-[96px] h-[96px] rounded-xl border border-dashed border-[#a88c6b]/40 text-[#a88c6b] text-[11px] flex items-center justify-center hover:border-[#a88c6b] transition-colors"
@@ -396,6 +403,12 @@ function isFlexible(type) {
} }
function addImage(page) { if (!Array.isArray(page.images)) page.images = []; page.images.push('') } function addImage(page) { if (!Array.isArray(page.images)) page.images = []; page.images.push('') }
function removeImage(page, i) { if (page.images.length > 1) page.images.splice(i, 1) } function removeImage(page, i) { if (page.images.length > 1) page.images.splice(i, 1) }
// 图片排序:前移(-1) / 后移(+1)
function moveImage(page, i, dir) {
const j = i + dir
if (!Array.isArray(page.images) || j < 0 || j >= page.images.length) return
const t = page.images[i]; page.images[i] = page.images[j]; page.images[j] = t
}
// 相册页操作 // 相册页操作
function addPage() { cfg.photoPages.push(ensurePageShape({ type: 'single', title: '新页面', subtitle: 'NEW', desc: '', img1: '' })) } function addPage() { cfg.photoPages.push(ensurePageShape({ type: 'single', title: '新页面', subtitle: 'NEW', desc: '', img1: '' })) }

View File

@@ -71,8 +71,9 @@
</div> </div>
</div> </div>
<div @click="toggleAutoScroll" class="control-btn cursor-pointer transition-all duration-300" :class="isAutoScroll ? 'bg-[#a88c6b]' : 'bg-white/60 shadow-sm border-[#a88c6b]/30'"> <div @click="toggleAutoScroll" class="control-btn cursor-pointer transition-all duration-300"
<img v-if="isAutoScroll" src="https://api.iconify.design/lucide:pause.svg?color=%23ffffff" class="w-4 h-4" /> :class="isAutoScroll ? 'bg-[#a88c6b]/15 !border-[#a88c6b]/60 shadow-sm' : 'bg-white/60 shadow-sm border-[#a88c6b]/30'">
<img v-if="isAutoScroll" src="https://api.iconify.design/lucide:pause.svg?color=%23a88c6b" class="w-4 h-4" />
<img v-else src="https://api.iconify.design/lucide:play.svg?color=%23a88c6b" class="w-4 h-4 ml-0.5" /> <img v-else src="https://api.iconify.design/lucide:play.svg?color=%23a88c6b" class="w-4 h-4 ml-0.5" />
</div> </div>
</div> </div>