docs(ui05,ui06): admin analysis+HTML preview, app-preview evidence, UI06 unified visual plan

- admin-preview-2026-09-22/ 20页新版后台HTML原型+截图+validation(UI05 交付)
- app-preview-2026-09-22/ 截图/validation/check 脚本入库及小改
- admin-redesign-concept 概念图
- UI05 管理后台分析与新版设计说明、用户列表分页计划文档
- UI06 全端视觉统一实施方案(以两个 HTML 预览为唯一视觉基准)
- services/api g4-sync-once 手动同步脚本

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-22 22:42:00 +08:00
parent dbf958613f
commit 37d88da4bc
85 changed files with 1149 additions and 3 deletions
+2 -2
View File
@@ -4,8 +4,8 @@ const paths={back:'m15 18-6-6 6-6',next:'m9 18 6-6-6-6',close:'m6 6 12 12M6 18 1
function icon(n){return `<svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="${paths[n]||paths.photo}"/></svg>`}
let lang='en',page=1,returnPage=2,albumTitle='Canton Fair · Autumn',editTab='access',uploadFilter='all',photoFilter='all',currentPhoto=1,categoryNames=['Exhibition','People','Highlights'],members=['Emma Chen','Daniel Lee'],extraTasks=0,hiddenPhotos=new Set([6]),removedPhotos=new Set(),retried=false,publicAccess=true,uploadAllowed=true,downloads=true,original=false,draftLanguage='en',toastTimer;
const T=(en,zh)=>lang==='zh'?zh:en, esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const pics=[[262,486,230,128],[845,248,358,435],[264,661,224,122],[506,656,185,142],[265,824,224,126],[504,823,181,129],[260,100,477,193],[848,300,345,378]];
function photo(i,cls='',height=''){const r=pics[((i%pics.length)+pics.length)%pics.length],[x,y,w,h]=r;return `<div class="art ${cls}" style="aspect-ratio:${w}/${h};${height?`height:${height};`:''}"><img src="assets/reference.png" alt="" style="width:${1536/w*100}%;height:${1024/h*100}%;left:${-x/w*100}%;top:${-y/h*100}%"></div>`}
const pics=[[262,530,230,105],[845,248,358,435],[264,661,224,122],[506,656,185,142],[265,824,224,126],[504,823,181,129],[260,100,477,193],[848,300,345,378]];
function photo(i,cls='',height=''){const r=pics[((i%pics.length)+pics.length)%pics.length],[x,y,w,h]=r;return `<div class="art ${cls}" style="aspect-ratio:${w}/${h};${height?`height:${height};`:''}"><svg width="100%" height="100%" viewBox="${x} ${y} ${w} ${h}" preserveAspectRatio="xMidYMid slice" aria-hidden="true" style="display:block"><image href="assets/reference.png" width="1536" height="1024"/></svg></div>`}
function button(label,action,cls='primary',ic=''){return `<button class="btn ${cls}" data-action="${action}">${ic?icon(ic):''}${label}</button>`}
function row(ic,label,sub,action='',trailing=''){return `<${action?'button':'div'} class="row" ${action?`data-action="${action}"`:''}>${icon(ic)}<span class="grow"><strong>${label}</strong>${sub?`<p>${sub}</p>`:''}</span>${trailing?`<span class="trailing">${trailing}</span>`:''}${action?`<span class="arrow">${icon('next')}</span>`:''}</${action?'button':'div'}>`}
function bar(title,back=5,right='',root=false){return `<div class="safe-top"></div><header class="bar ${root?'root':''} ${[13,18,20].includes(page)?'dark':''}">${root?`<h1>${title}</h1>`:`<button class="back" data-go="${back}" aria-label="${T('Back','返回')}">${icon('back')}</button><span class="bar-title">${title}</span>`}${right}</header>`}
@@ -0,0 +1,5 @@
const fs=require('fs'),path=require('path');
const {chromium}=require(require.resolve('@playwright/test',{paths:[path.resolve(__dirname,'../../../apps/viewer')]}));
(async()=>{const browser=await chromium.launch({headless:true});const page=await browser.newPage({viewport:{width:390,height:844}});const errors=[];page.on('pageerror',e=>errors.push(e.message));const files=fs.readdirSync(__dirname).filter(x=>/^\d\d-.*\.html$/.test(x)).sort();fs.mkdirSync(path.join(__dirname,'screenshots'),{recursive:true});const rows=[];for(const file of files){await page.goto('file:///'+path.join(__dirname,file).replaceAll('\\','/'));await page.locator('#app').waitFor();await page.evaluate(()=>Promise.all([...document.images].map(i=>i.decode().catch(()=>{}))));await page.screenshot({path:path.join(__dirname,'screenshots',file.slice(0,2)+'.png')});rows.push({file,overflow:await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth),buttons:await page.locator('button').count()})}
await page.goto('file:///'+path.join(__dirname,'01-login.html').replaceAll('\\','/'));await page.locator('#loginForm button[type=submit]').click();await page.waitForURL('**#05');await page.locator('.album-card').first().click();await page.waitForURL('**#06');await page.locator('[data-action="go:13"]').click();await page.waitForURL('**#13');await page.locator('.shutter').click();if(!(await page.locator('#cameraNote').textContent()).includes('1'))throw Error('Capture mock queue failed');await page.evaluate(()=>location.hash='20');await page.locator('[data-action="confirmHide"]').click();await page.waitForURL('**#18');await page.waitForFunction(()=>document.querySelector('.photo-meta')?.textContent.includes('Hidden'));await page.locator('[data-action="publish"]').click();await page.evaluate(()=>location.hash='04');await page.locator('input[value="zh"]').check();await page.locator('[data-action="saveLanguage"]').click();await page.waitForURL('**#02');await page.waitForFunction(()=>document.querySelector('h1')?.textContent.includes('设置'));
const narrow=[];await page.setViewportSize({width:320,height:568});for(const id of [1,9,14,18,19]){await page.goto('file:///'+path.join(__dirname,files[id-1]).replaceAll('\\','/'));narrow.push({id,overflow:await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth)})}const result={date:'2026-09-22',type:'HTML prototype only',pages:rows,narrow,errors,interactions:['login','album navigation','simulated capture','hide','publish','language'],nativeAppTested:false};fs.writeFileSync(path.join(__dirname,'validation.json'),JSON.stringify(result,null,2));console.log(JSON.stringify(result));await browser.close();if(errors.length||rows.some(x=>x.overflow)||narrow.some(x=>x.overflow))process.exitCode=1})().catch(e=>{console.error(e);process.exit(1)});
Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

File diff suppressed because one or more lines are too long
@@ -0,0 +1,138 @@
{
"date": "2026-09-22",
"type": "HTML prototype only",
"pages": [
{
"file": "01-login.html",
"overflow": false,
"buttons": 3
},
{
"file": "02-settings.html",
"overflow": false,
"buttons": 7
},
{
"file": "03-password.html",
"overflow": false,
"buttons": 5
},
{
"file": "04-language.html",
"overflow": false,
"buttons": 8
},
{
"file": "05-albums.html",
"overflow": false,
"buttons": 7
},
{
"file": "06-album-detail.html",
"overflow": false,
"buttons": 18
},
{
"file": "07-album-actions.html",
"overflow": false,
"buttons": 23
},
{
"file": "08-empty-album.html",
"overflow": false,
"buttons": 4
},
{
"file": "09-create-album.html",
"overflow": false,
"buttons": 2
},
{
"file": "10-edit-album.html",
"overflow": false,
"buttons": 5
},
{
"file": "11-members.html",
"overflow": false,
"buttons": 5
},
{
"file": "12-categories.html",
"overflow": false,
"buttons": 9
},
{
"file": "13-camera.html",
"overflow": false,
"buttons": 6
},
{
"file": "14-uploads.html",
"overflow": false,
"buttons": 7
},
{
"file": "15-upload-errors.html",
"overflow": false,
"buttons": 10
},
{
"file": "16-upload-history.html",
"overflow": false,
"buttons": 1
},
{
"file": "17-photos.html",
"overflow": false,
"buttons": 22
},
{
"file": "18-photo-detail.html",
"overflow": false,
"buttons": 6
},
{
"file": "19-share.html",
"overflow": false,
"buttons": 3
},
{
"file": "20-hide-photo.html",
"overflow": false,
"buttons": 8
}
],
"narrow": [
{
"id": 1,
"overflow": false
},
{
"id": 9,
"overflow": false
},
{
"id": 14,
"overflow": false
},
{
"id": 18,
"overflow": false
},
{
"id": 19,
"overflow": false
}
],
"errors": [],
"interactions": [
"login",
"album navigation",
"simulated capture",
"hide",
"publish",
"language"
],
"nativeAppTested": false
}