更新页面效果
@@ -121,3 +121,18 @@
|
|||||||
实测工具:agent-browser(playwright-cli 未装)。坑:后台是 history 模式,URL 是 `/admin` 不是 `/#/admin`;eval 多行 JS 引号易炸,写临时文件 `eval "$(cat x.js)"` 最稳;截图保存在 daemon 的 cwd(D:/myCode/hunliji/dev/_shots)。
|
实测工具:agent-browser(playwright-cli 未装)。坑:后台是 history 模式,URL 是 `/admin` 不是 `/#/admin`;eval 多行 JS 引号易炸,写临时文件 `eval "$(cat x.js)"` 最稳;截图保存在 daemon 的 cwd(D:/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=0);overlap/single-bleed/admin-sort/controls 截图均确认。
|
||||||
|
|||||||
@@ -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>` 新目录验证,临时目录需手动清。
|
||||||
|
|||||||
BIN
vite-tailwindcss/_shots/admin-sort.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
vite-tailwindcss/_shots/admin-sort2.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
vite-tailwindcss/_shots/controls.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
vite-tailwindcss/_shots/overlap-collage.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
vite-tailwindcss/_shots/overlap-collage2.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
vite-tailwindcss/_shots/single-bleed.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
@@ -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}
|
||||||
@@ -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};
|
||||||
@@ -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};
|
||||||
514
vite-tailwindcss/dist-chk-1785469523/assets/index-B7Bp0mE9.js
Normal file
14
vite-tailwindcss/dist-chk-1785469523/index.html
Normal 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>
|
||||||
1
vite-tailwindcss/dist-chk-1785469523/vite.svg
Normal 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 |
@@ -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-6(24px×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' }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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: '' })) }
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||