设计完成30%
This commit is contained in:
@@ -89,13 +89,13 @@ foreach ($requiredVisualAnchor in @(
|
||||
'margin-top: 141rpx',
|
||||
'top: 70rpx',
|
||||
'width: 64rpx',
|
||||
'inset: 6rpx',
|
||||
'entry-button__content',
|
||||
'translateX(-20rpx)',
|
||||
'auth-button-corner.png',
|
||||
'auth-divider-knot.png',
|
||||
'auth-title-cloud.png',
|
||||
'button-corner-asset',
|
||||
'a01-primary-button.png',
|
||||
'a01-secondary-button.png',
|
||||
'entry-button__skin',
|
||||
'align-items: center',
|
||||
'white-space: nowrap',
|
||||
'font-size: 20rpx',
|
||||
@@ -104,7 +104,7 @@ foreach ($requiredVisualAnchor in @(
|
||||
)) {
|
||||
Assert-Contains -Content $entry -Expected $requiredVisualAnchor -Message "Missing A-01 calibrated visual anchor: $requiredVisualAnchor"
|
||||
}
|
||||
foreach ($rejectedVisualConstruction in @('link-line', 'button-corner--legacy', 'class="auth-button primary-button"')) {
|
||||
foreach ($rejectedVisualConstruction in @('link-line', 'button-corner--legacy', 'class="auth-button primary-button"', 'auth-button-corner.png', 'button-corner-asset', '.entry-button::after')) {
|
||||
Assert-NotContains -Content $entry -Unexpected $rejectedVisualConstruction -Message "A-01 retains rejected visual construction: $rejectedVisualConstruction"
|
||||
}
|
||||
foreach ($forbiddenClass in @('entryGuideVisual', 'guide-art', 'entry-hit', 'width: 750rpx', 'min-height: 1625rpx')) {
|
||||
@@ -113,12 +113,18 @@ foreach ($forbiddenClass in @('entryGuideVisual', 'guide-art', 'entry-hit', 'wid
|
||||
Assert-NotContains -Content $entry -Unexpected '/static/assets/icons/action/create-cloud-v2.png' -Message 'A-01 must not reuse the mismatched create-cloud asset for its title or divider ornaments'
|
||||
Assert-Contains -Content $entry -Expected 'filter: brightness(0) invert(1)' -Message 'A-01 primary login icon must render in white'
|
||||
Assert-NotContains -Content $entry -Unexpected $dividerGlyph -Message 'A-01 divider must not use a text glyph as ornament'
|
||||
if (([regex]::Matches($entry, 'button-corner-asset button-corner-asset--')).Count -ne 8) {
|
||||
throw 'A-01 must render the generated ornamental corner asset in all four corners of both entry buttons'
|
||||
if (([regex]::Matches($entry, 'class="entry-button__skin"')).Count -ne 2) {
|
||||
throw 'A-01 must render one complete opaque skin image for each entry button'
|
||||
}
|
||||
if (([regex]::Matches($entry, 'auth-divider-knot\.png')).Count -ne 2) {
|
||||
throw 'A-01 must render the dedicated divider-knot asset above and below the subtitle'
|
||||
}
|
||||
if ($entry -match '(?s)\.entry-button\s*\{[^}]*\bborder\s*:') {
|
||||
throw 'A-01 entry buttons must not draw their final border with CSS'
|
||||
}
|
||||
if ($entry -match '(?s)\.entry-button--(?:primary|secondary)\s*\{[^}]*\bbackground\s*:') {
|
||||
throw 'A-01 entry buttons must not draw their final surface with CSS'
|
||||
}
|
||||
if ($entry -notmatch '(?s)\.agreement-row\s*\{[^}]*align-items:\s*center') {
|
||||
throw 'A-01 agreement checkbox and copy must align on one horizontal center line'
|
||||
}
|
||||
@@ -129,9 +135,40 @@ if ($entry -match '(?s)\.auth-page\s*\{[^}]*overflow\s*:\s*hidden') {
|
||||
throw 'A-01 must allow vertical scrolling on small screens'
|
||||
}
|
||||
|
||||
foreach ($requiredLoginCopy in @($accountPasswordLogin, $smsLogin, $forgotPassword, $getCode, $loginPending, $returnToEntry, $forgotPasswordPending, $getCodePending)) {
|
||||
foreach ($requiredLoginCopy in @($accountPasswordLogin, $smsLogin, $forgotPassword, $getCode, $loginPending, $returnToEntry, $getCodePending)) {
|
||||
Assert-Contains -Content $login -Expected $requiredLoginCopy -Message "Missing A-02 copy: $requiredLoginCopy"
|
||||
}
|
||||
Assert-Contains -Content $login -Expected "url: '/pages/auth/a05-reset-password'" -Message 'A-02 forgot-password entry must navigate to A-05'
|
||||
|
||||
foreach ($requiredLoginVisualAnchor in @(
|
||||
'auth-title-cloud.png',
|
||||
'login-title-cloud',
|
||||
'a02-login-panel.png',
|
||||
'login-panel__skin',
|
||||
'a01-primary-button.png',
|
||||
'login-submit__skin',
|
||||
'login-submit__content'
|
||||
)) {
|
||||
Assert-Contains -Content $login -Expected $requiredLoginVisualAnchor -Message "Missing A-02 calibrated visual anchor: $requiredLoginVisualAnchor"
|
||||
}
|
||||
if (([regex]::Matches($login, 'class="login-submit__skin"')).Count -ne 1) {
|
||||
throw 'A-02 must render one complete opaque skin image for its login button'
|
||||
}
|
||||
if (([regex]::Matches($login, 'class="login-panel__skin"')).Count -ne 1) {
|
||||
throw 'A-02 must render one complete opaque skin image for its login panel'
|
||||
}
|
||||
if ($login -match '(?s)\.login-panel\s*\{[^}]*\bborder\s*:') {
|
||||
throw 'A-02 login panel must not draw its final frame with CSS'
|
||||
}
|
||||
if ($login -match '(?s)\.login-panel\s*\{[^}]*\bbackground\s*:') {
|
||||
throw 'A-02 login panel must not draw its final paper surface with CSS'
|
||||
}
|
||||
if ($login -match '(?s)\.login-submit\s*\{[^}]*\bborder\s*:') {
|
||||
throw 'A-02 login button must not draw its final border with CSS'
|
||||
}
|
||||
if ($login -match '(?s)\.login-submit\s*\{[^}]*\bbackground\s*:') {
|
||||
throw 'A-02 login button must not draw its final surface with CSS'
|
||||
}
|
||||
|
||||
if ($login -match '(?s)\.auth-page\s*\{[^}]*overflow\s*:\s*hidden') {
|
||||
throw 'A-02 must allow vertical scrolling on small screens'
|
||||
|
||||
@@ -11,12 +11,15 @@ $backgrounds = @(
|
||||
$icons = @(
|
||||
'static/assets/foundation/transparent/auth-login-outline.png',
|
||||
'static/assets/foundation/transparent/auth-wechat.png',
|
||||
'static/assets/foundation/transparent/auth-button-corner.png',
|
||||
'static/assets/foundation/transparent/auth-divider-knot.png',
|
||||
'static/assets/foundation/transparent/auth-title-cloud.png'
|
||||
)
|
||||
$buttons = @(
|
||||
'static/assets/foundation/opaque/a01-primary-button.png',
|
||||
'static/assets/foundation/opaque/a01-secondary-button.png'
|
||||
)
|
||||
|
||||
foreach ($asset in $backgrounds + $icons) {
|
||||
foreach ($asset in $backgrounds + $icons + $buttons) {
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $root $asset))) {
|
||||
throw "Missing A01 asset: $asset"
|
||||
}
|
||||
@@ -41,7 +44,6 @@ foreach ($asset in $icons) {
|
||||
$bitmap.Dispose()
|
||||
|
||||
$expectedSize = switch ($asset) {
|
||||
'static/assets/foundation/transparent/auth-button-corner.png' { @(160, 160) }
|
||||
'static/assets/foundation/transparent/auth-divider-knot.png' { @(160, 96) }
|
||||
'static/assets/foundation/transparent/auth-title-cloud.png' { @(200, 120) }
|
||||
default { @(96, 96) }
|
||||
@@ -52,4 +54,30 @@ foreach ($asset in $icons) {
|
||||
Write-Output "PASS $asset transparent corners and visible artwork"
|
||||
}
|
||||
|
||||
foreach ($asset in $buttons) {
|
||||
$bitmap = [System.Drawing.Bitmap]::FromFile((Join-Path $root $asset))
|
||||
$width = [int]$bitmap.Width
|
||||
$height = [int]$bitmap.Height
|
||||
$corners = @(
|
||||
$bitmap.GetPixel(0, 0).A,
|
||||
$bitmap.GetPixel(($width - 1), 0).A,
|
||||
$bitmap.GetPixel(0, ($height - 1)).A,
|
||||
$bitmap.GetPixel(($width - 1), ($height - 1)).A
|
||||
)
|
||||
$chromaPixels = 0
|
||||
for ($y = 0; $y -lt $height; $y++) {
|
||||
for ($x = 0; $x -lt $width; $x++) {
|
||||
$pixel = $bitmap.GetPixel($x, $y)
|
||||
if ($pixel.G - [Math]::Max($pixel.R, $pixel.B) -gt 80 -and $pixel.G -gt 120) { $chromaPixels++ }
|
||||
}
|
||||
}
|
||||
$bitmap.Dispose()
|
||||
|
||||
$ratio = [double]$width / $height
|
||||
if ([Math]::Abs($ratio - 4.492) -gt 0.01) { throw "$asset must keep the approved 4.492:1 button ratio." }
|
||||
if (($corners | Where-Object { $_ -ne 255 }).Count -ne 0) { throw "$asset must have opaque outer corners." }
|
||||
if ($chromaPixels -ne 0) { throw "$asset retains chroma-key green pixels." }
|
||||
Write-Output "PASS $asset opaque corners and approved button ratio"
|
||||
}
|
||||
|
||||
Write-Output 'A01-ASSET-ALPHA-AUDIT PASS'
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
$asset = Join-Path $root 'static/assets/modules/auth/opaque/a02-login-panel.png'
|
||||
|
||||
if (-not (Test-Path -LiteralPath $asset)) {
|
||||
throw 'Missing A02 login-panel asset'
|
||||
}
|
||||
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
$bitmap = [System.Drawing.Bitmap]::FromFile($asset)
|
||||
$width = [int]$bitmap.Width
|
||||
$height = [int]$bitmap.Height
|
||||
$corners = @(
|
||||
$bitmap.GetPixel(0, 0).A,
|
||||
$bitmap.GetPixel(($width - 1), 0).A,
|
||||
$bitmap.GetPixel(0, ($height - 1)).A,
|
||||
$bitmap.GetPixel(($width - 1), ($height - 1)).A
|
||||
)
|
||||
$bitmap.Dispose()
|
||||
|
||||
$ratio = [double]$width / $height
|
||||
if ($width -ne 940 -or $height -ne 1672) { throw 'A02 login-panel asset must remain 940 x 1672px.' }
|
||||
if ([Math]::Abs($ratio - 0.5622) -gt 0.001) { throw 'A02 login-panel asset must retain its approved panel ratio.' }
|
||||
if (($corners | Where-Object { $_ -ne 255 }).Count -ne 0) { throw 'A02 login-panel asset must have opaque outer corners.' }
|
||||
|
||||
Write-Output 'A02-ASSET-ALPHA-AUDIT PASS'
|
||||
@@ -1,7 +1,7 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
$requiredFiles = @('utils/config.js', 'utils/session.js', 'utils/genealogy-context.js', 'utils/api.js', 'pages/genealogy/g04-first-ancestor.vue', 'pages/skeleton/content-list-skeleton.vue', 'pages/family/f02-publish-feed.vue')
|
||||
$requiredFiles = @('utils/config.js', 'utils/session.js', 'utils/genealogy-context.js', 'utils/api.js', 'pages/genealogy/g04-first-ancestor.vue', 'pages/family/f04-article-list.vue', 'pages/family/f02-publish-feed.vue')
|
||||
foreach ($file in $requiredFiles) {
|
||||
if (-not (Test-Path (Join-Path $root $file))) {
|
||||
throw "Missing core-flow file: $file"
|
||||
@@ -49,7 +49,7 @@ $pages = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages.json')
|
||||
if ($pages -notmatch 'pages/genealogy/g04-first-ancestor') {
|
||||
throw 'Missing first-person page route'
|
||||
}
|
||||
foreach ($route in @('pages/skeleton/content-list-skeleton', 'pages/family/f02-publish-feed')) {
|
||||
foreach ($route in @('pages/family/f04-article-list', 'pages/family/f02-publish-feed')) {
|
||||
if ($pages -notmatch [regex]::Escape($route)) {
|
||||
throw "Missing content page route: $route"
|
||||
}
|
||||
@@ -91,7 +91,7 @@ foreach ($method in @('markNotificationRead', 'markAllNotificationsRead')) {
|
||||
}
|
||||
|
||||
$family = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/family/f01-family-feed.vue')
|
||||
foreach ($token in @('genealogyContext', 'openSection', 'pages/skeleton/content-list-skeleton', 'pages/family/f02-publish-feed')) {
|
||||
foreach ($token in @('genealogyContext', 'openSection', 'pages/family/f04-article-list', 'pages/family/f07-album-list', 'pages/records/r05-ritual-list', 'pages/records/r10-memo-list', 'pages/family/f02-publish-feed')) {
|
||||
if ($family -notmatch [regex]::Escape($token)) {
|
||||
throw "Missing family content flow: $token"
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ Test-ExpectedPage -ExpectedPath 'pages/tree/t01-tree-overview' -PageId 'T-01'
|
||||
Test-ExpectedPage -ExpectedPath 'pages/tree/t03-member-profile' -PageId 'T-03'
|
||||
Test-ExpectedPage -ExpectedPath 'pages/family/f01-family-feed' -PageId 'F-01'
|
||||
Test-ExpectedPage -ExpectedPath 'pages/family/f02-publish-feed' -PageId 'F-02'
|
||||
Test-ExpectedPage -ExpectedPath 'pages/skeleton/content-list-skeleton' -PageId 'S-01'
|
||||
Test-ExpectedPage -ExpectedPath 'pages/notification/n01-message-center' -PageId 'N-01'
|
||||
Test-ExpectedPage -ExpectedPath 'pages/profile/m01-profile-home' -PageId 'M-01'
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function ConvertFrom-Utf8Base64 {
|
||||
param([string]$Value)
|
||||
return [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($Value))
|
||||
}
|
||||
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
$pagesPath = Join-Path $root 'pages.json'
|
||||
$pageHeader = ConvertFrom-Utf8Base64 '6aG16Z2i57yW5Y+377ya'
|
||||
$expectedPages = @(
|
||||
'pages/auth/a01-entry', 'pages/auth/a02-login', 'pages/auth/a03-mobile-verify', 'pages/auth/a04-register', 'pages/auth/a05-reset-password', 'pages/auth/a06-auth-status',
|
||||
'pages/genealogy/g01-my-genealogies', 'pages/genealogy/g02-empty-genealogies', 'pages/genealogy/g03-create-genealogy', 'pages/genealogy/g04-first-ancestor', 'pages/genealogy/g05-genealogy-overview', 'pages/genealogy/g06-search-genealogies', 'pages/genealogy/g07-search-result', 'pages/genealogy/g08-join-application', 'pages/genealogy/g09-my-applications', 'pages/genealogy/g10-application-review', 'pages/genealogy/g11-genealogy-settings', 'pages/genealogy/g12-generation-poems',
|
||||
'pages/tree/t01-tree-overview', 'pages/tree/t02-tree-states', 'pages/tree/t03-member-profile', 'pages/tree/t04-add-relative', 'pages/tree/t05-edit-member', 'pages/tree/t06-edit-relationship', 'pages/tree/t07-member-directory', 'pages/tree/t08-member-states',
|
||||
'pages/family/f01-family-feed', 'pages/family/f02-publish-feed', 'pages/family/f03-feed-detail', 'pages/family/f04-article-list', 'pages/family/f05-article-detail', 'pages/family/f06-article-editor', 'pages/family/f07-album-list', 'pages/family/f08-album-detail', 'pages/family/f09-media-upload', 'pages/family/f10-video-list',
|
||||
'pages/records/r01-people-list', 'pages/records/r02-person-detail', 'pages/records/r03-gift-list', 'pages/records/r04-gift-editor', 'pages/records/r05-ritual-list', 'pages/records/r06-ritual-detail', 'pages/records/r07-ritual-editor', 'pages/records/r08-growth-journal', 'pages/records/r09-life-events', 'pages/records/r10-memo-list', 'pages/records/r11-merit-records',
|
||||
'pages/notification/n01-message-center', 'pages/notification/n02-message-detail',
|
||||
'pages/profile/m01-profile-home', 'pages/profile/m02-edit-profile', 'pages/profile/m03-security-settings', 'pages/profile/m04-change-password', 'pages/profile/m05-change-phone', 'pages/profile/m06-help-center', 'pages/profile/m07-feedback', 'pages/profile/m08-promotion', 'pages/profile/m09-vip-orders', 'pages/profile/m10-about-settings'
|
||||
)
|
||||
|
||||
$pages = Get-Content -LiteralPath $pagesPath -Raw -Encoding utf8 | ConvertFrom-Json
|
||||
$actualPaths = @($pages.pages.path)
|
||||
if ($actualPaths.Count -ne 59) {
|
||||
throw "Expected 59 final visual routes, found $($actualPaths.Count)."
|
||||
}
|
||||
if (Compare-Object -ReferenceObject $expectedPages -DifferenceObject $actualPaths) {
|
||||
throw 'pages.json routes do not match the 59-page visual delivery contract.'
|
||||
}
|
||||
|
||||
foreach ($page in $expectedPages) {
|
||||
$pageFile = Join-Path $root ($page + '.vue')
|
||||
if (-not (Test-Path -LiteralPath $pageFile)) {
|
||||
throw "Missing planned page file: $page.vue"
|
||||
}
|
||||
$content = Get-Content -LiteralPath $pageFile -Raw -Encoding utf8
|
||||
if ($content -notmatch [regex]::Escape($pageHeader)) {
|
||||
throw "Missing Chinese page header: $page.vue"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output 'FULL-PAGE-VISUAL-CONTRACT PASS'
|
||||
@@ -0,0 +1,31 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
$componentPath = Join-Path $root 'components/ModulePage.vue'
|
||||
|
||||
if (-not (Test-Path -LiteralPath $componentPath)) {
|
||||
throw 'Missing shared module page component'
|
||||
}
|
||||
|
||||
$component = Get-Content -LiteralPath $componentPath -Raw -Encoding utf8
|
||||
|
||||
foreach ($anchor in @(
|
||||
'a01-primary-button.png',
|
||||
'background-image:',
|
||||
'background-size: 100% 100%',
|
||||
'.primary-action--compact'
|
||||
)) {
|
||||
if ($component -notmatch [regex]::Escape($anchor)) {
|
||||
throw "ModulePage is missing primary-action visual anchor: $anchor"
|
||||
}
|
||||
}
|
||||
|
||||
if ($component -match '(?s)\.primary-action\s*\{[^}]*\bborder\s*:') {
|
||||
throw 'ModulePage primary actions must not redraw the final button border with CSS'
|
||||
}
|
||||
|
||||
if ($component -match '(?s)\.primary-action\s*\{[^}]*\bbackground\s*:\s*\$brand-red') {
|
||||
throw 'ModulePage primary actions must not redraw the final button surface with a plain CSS color'
|
||||
}
|
||||
|
||||
Write-Output 'MODULE-PAGE-VISUAL-CONTRACT PASS'
|
||||
@@ -25,7 +25,6 @@ $requiredFiles = @(
|
||||
'pages/tree/t03-member-profile.vue',
|
||||
'pages/family/f01-family-feed.vue',
|
||||
'pages/family/f02-publish-feed.vue',
|
||||
'pages/skeleton/content-list-skeleton.vue',
|
||||
'pages/profile/m01-profile-home.vue',
|
||||
'pages/notification/n01-message-center.vue',
|
||||
'static/icons/tab-genealogy.png',
|
||||
@@ -53,7 +52,6 @@ $requiredRoutes = @(
|
||||
'pages/tree/t03-member-profile',
|
||||
'pages/family/f01-family-feed',
|
||||
'pages/family/f02-publish-feed',
|
||||
'pages/skeleton/content-list-skeleton',
|
||||
'pages/profile/m01-profile-home',
|
||||
'pages/notification/n01-message-center'
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user