接口开始5%
This commit is contained in:
@@ -1,65 +0,0 @@
|
||||
param(
|
||||
[string]$ManifestPath = 'docs/design/assets/a01-vnext/source/a01-psd-manifest.json',
|
||||
[string]$OutputPath = 'docs/design/assets/a01-vnext/source/A01-layered-source-v1.psd'
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
$rootFullPath = [IO.Path]::GetFullPath($root).TrimEnd([IO.Path]::DirectorySeparatorChar) + [IO.Path]::DirectorySeparatorChar
|
||||
|
||||
function Resolve-WorkspacePath {
|
||||
param(
|
||||
[string]$Path,
|
||||
[bool]$MustExist
|
||||
)
|
||||
|
||||
$candidate = if ([IO.Path]::IsPathRooted($Path)) { $Path } else { Join-Path $root $Path }
|
||||
$fullPath = [IO.Path]::GetFullPath($candidate)
|
||||
if (-not $fullPath.StartsWith($rootFullPath, [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "Path must remain inside the workspace: $Path"
|
||||
}
|
||||
if ($MustExist -and -not (Test-Path -LiteralPath $fullPath -PathType Leaf)) {
|
||||
throw "Required file does not exist: $fullPath"
|
||||
}
|
||||
return $fullPath
|
||||
}
|
||||
|
||||
function ConvertTo-JavaScriptString {
|
||||
param([string]$Value)
|
||||
$escaped = $Value.Replace('\', '\\').Replace("'", "\'").Replace("`r", '\r').Replace("`n", '\n')
|
||||
return "'$escaped'"
|
||||
}
|
||||
|
||||
$resolvedManifest = Resolve-WorkspacePath -Path $ManifestPath -MustExist $true
|
||||
$resolvedOutput = Resolve-WorkspacePath -Path $OutputPath -MustExist $false
|
||||
$outputName = [IO.Path]::GetFileName($resolvedOutput)
|
||||
if ($outputName -notmatch '^A01-layered-source-v[0-9]+\.psd$') {
|
||||
throw "PSD output must use a versioned A01 source name: $outputName"
|
||||
}
|
||||
if (Test-Path -LiteralPath $resolvedOutput) {
|
||||
throw "Refusing to overwrite an existing PSD: $resolvedOutput"
|
||||
}
|
||||
if (-not (Get-Process -Name Photoshop -ErrorAction SilentlyContinue)) {
|
||||
throw 'Adobe Photoshop must already be running.'
|
||||
}
|
||||
|
||||
$outputDirectory = Split-Path -Parent $resolvedOutput
|
||||
if (-not (Test-Path -LiteralPath $outputDirectory -PathType Container)) {
|
||||
New-Item -ItemType Directory -Path $outputDirectory | Out-Null
|
||||
}
|
||||
|
||||
$jsxPath = Resolve-WorkspacePath -Path 'scripts/photoshop/a01-build-layered-psd.jsx' -MustExist $true
|
||||
$app = New-Object -ComObject 'Photoshop.Application.150'
|
||||
$bootstrap = @"
|
||||
var JIAPU_A01_ROOT = $(ConvertTo-JavaScriptString $rootFullPath.TrimEnd([IO.Path]::DirectorySeparatorChar));
|
||||
var JIAPU_A01_MANIFEST = $(ConvertTo-JavaScriptString $resolvedManifest);
|
||||
var JIAPU_A01_PSD = $(ConvertTo-JavaScriptString $resolvedOutput);
|
||||
$.evalFile(new File($(ConvertTo-JavaScriptString $jsxPath)));
|
||||
"@
|
||||
$app.DoJavaScript($bootstrap, @(), 1)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $resolvedOutput -PathType Leaf)) {
|
||||
throw "Photoshop did not create the expected PSD: $resolvedOutput"
|
||||
}
|
||||
|
||||
Write-Output "A01-LAYERED-PSD BUILD PASS: $resolvedOutput"
|
||||
@@ -1,553 +0,0 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const [url, selector, output, width = '360', height = '800', action] = process.argv.slice(2)
|
||||
const renderSettleMilliseconds = 900
|
||||
const chromeDebuggingPort = process.env.CHROME_DEBUGGING_PORT || '9222'
|
||||
|
||||
if (!url || !selector || !output) {
|
||||
throw new Error('Usage: node scripts/capture-chrome-page.js <url> <selector> <output> [width] [height] [password-tab|sms-tab|invalid-login|verification-dialog|g06-results|g06-empty|g06-invite|g03-*|fresh-navigation]')
|
||||
}
|
||||
|
||||
const sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds))
|
||||
|
||||
const connect = async () => {
|
||||
const pages = await (await fetch(`http://127.0.0.1:${chromeDebuggingPort}/json/list`)).json()
|
||||
const page = pages.find((item) => item.type === 'page' && item.url.startsWith('http://localhost:5173'))
|
||||
if (!page) throw new Error('Chrome debugging has no localhost:5173 page')
|
||||
|
||||
const socket = new WebSocket(page.webSocketDebuggerUrl)
|
||||
await new Promise((resolve, reject) => {
|
||||
socket.addEventListener('open', resolve, { once: true })
|
||||
socket.addEventListener('error', reject, { once: true })
|
||||
})
|
||||
|
||||
let id = 0
|
||||
const pending = new Map()
|
||||
socket.addEventListener('message', (event) => {
|
||||
const message = JSON.parse(event.data)
|
||||
const request = pending.get(message.id)
|
||||
if (!request) return
|
||||
pending.delete(message.id)
|
||||
if (message.error) request.reject(new Error(message.error.message))
|
||||
else request.resolve(message.result)
|
||||
})
|
||||
|
||||
const send = (method, params = {}) => new Promise((resolve, reject) => {
|
||||
id += 1
|
||||
pending.set(id, { resolve, reject })
|
||||
socket.send(JSON.stringify({ id, method, params }))
|
||||
})
|
||||
|
||||
return { socket, send }
|
||||
}
|
||||
|
||||
const waitForLoadedImages = async (send) => {
|
||||
for (let attempt = 0; attempt < 30; attempt += 1) {
|
||||
const result = await send('Runtime.evaluate', {
|
||||
expression: 'Array.from(document.images).every((image) => image.complete && image.naturalWidth > 0)',
|
||||
returnByValue: true
|
||||
})
|
||||
if (result.result?.value) return
|
||||
if (attempt === 29) throw new Error('Page images did not finish loading before screenshot')
|
||||
await sleep(100)
|
||||
}
|
||||
}
|
||||
|
||||
const prepareA01LoginState = async (send, action) => {
|
||||
if (action !== 'password-tab' && action !== 'sms-tab' && action !== 'invalid-login' && action !== 'verification-dialog') return
|
||||
const tabIndex = action === 'sms-tab' ? 1 : 0
|
||||
|
||||
for (let attempt = 0; attempt < 30; attempt += 1) {
|
||||
const prepared = await send('Runtime.evaluate', {
|
||||
expression: `(() => {
|
||||
const tabs = document.querySelectorAll('.login-tab')
|
||||
if (tabs.length < 2) return false
|
||||
tabs[${tabIndex}].click()
|
||||
return true
|
||||
})()`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (prepared.result?.value) break
|
||||
if (attempt === 29) throw new Error(`A01 login tabs did not render for ${action}`)
|
||||
await sleep(100)
|
||||
}
|
||||
|
||||
for (let attempt = 0; attempt < 30; attempt += 1) {
|
||||
const active = await send('Runtime.evaluate', {
|
||||
expression: `document.querySelectorAll('.login-tab')[${tabIndex}]?.classList.contains('active') === true`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (active.result?.value) break
|
||||
if (attempt === 29) throw new Error(`A01 did not enter the requested ${action} state`)
|
||||
await sleep(100)
|
||||
}
|
||||
|
||||
if (action === 'invalid-login') {
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.login-submit').click()" })
|
||||
for (let attempt = 0; attempt < 30; attempt += 1) {
|
||||
const visible = await send('Runtime.evaluate', {
|
||||
expression: "Boolean(document.querySelector('.feedback-toast'))",
|
||||
returnByValue: true
|
||||
})
|
||||
if (visible.result?.value) return
|
||||
if (attempt === 29) throw new Error('A01 invalid-login Toast did not render')
|
||||
await sleep(100)
|
||||
}
|
||||
}
|
||||
|
||||
if (action === 'verification-dialog') {
|
||||
const prepared = await send('Runtime.evaluate', {
|
||||
expression: `(() => {
|
||||
const inputs = document.querySelectorAll('.auth-input input')
|
||||
if (inputs.length < 2) return false
|
||||
inputs[0].value = '13800138000'
|
||||
inputs[0].dispatchEvent(new Event('input', { bubbles: true }))
|
||||
inputs[1].value = 'demo-password'
|
||||
inputs[1].dispatchEvent(new Event('input', { bubbles: true }))
|
||||
document.querySelector('.agreement-row').click()
|
||||
document.querySelector('.login-submit').click()
|
||||
return true
|
||||
})()`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (!prepared.result?.value) throw new Error('Could not prepare A01 verification dialog state')
|
||||
for (let attempt = 0; attempt < 30; attempt += 1) {
|
||||
const visible = await send('Runtime.evaluate', {
|
||||
expression: "Boolean(document.querySelector('.verification-dialog'))",
|
||||
returnByValue: true
|
||||
})
|
||||
if (visible.result?.value) return
|
||||
if (attempt === 29) throw new Error('A01 verification dialog did not render')
|
||||
await sleep(100)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const prepareG01State = async (send, action) => {
|
||||
if (action === 'g01-add-dialog') {
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.create-action')?.click()" })
|
||||
await waitForSelector(send, '.add-dialog-layer', 'G01 add dialog did not render')
|
||||
return
|
||||
}
|
||||
if (action !== 'g01-switcher') return
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.current-slip')?.click()" })
|
||||
await waitForSelector(send, '.genealogy-switcher-layer', 'G01 switcher did not render')
|
||||
}
|
||||
|
||||
const waitForRequestedUrl = async (send, url) => {
|
||||
for (let attempt = 0; attempt < 30; attempt += 1) {
|
||||
const result = await send('Runtime.evaluate', {
|
||||
expression: `location.href === ${JSON.stringify(url)}`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (result.result?.value) return
|
||||
if (attempt === 29) throw new Error(`Browser did not navigate to the requested URL: ${url}`)
|
||||
await sleep(100)
|
||||
}
|
||||
}
|
||||
|
||||
const waitForFreshDocument = async (send, documentTimeOrigin) => {
|
||||
for (let attempt = 0; attempt < 30; attempt += 1) {
|
||||
const result = await send('Runtime.evaluate', {
|
||||
expression: 'performance.timeOrigin',
|
||||
returnByValue: true
|
||||
})
|
||||
if (result.result?.value !== documentTimeOrigin) return
|
||||
if (attempt === 29) throw new Error('Page did not reload into a fresh document before screenshot')
|
||||
await sleep(100)
|
||||
}
|
||||
}
|
||||
|
||||
const prepareG06State = async (send, action) => {
|
||||
if (action !== 'g06-results' && action !== 'g06-empty' && action !== 'g06-error' && action !== 'g06-invite' && action !== 'g06-invite-invalid') return
|
||||
|
||||
if (action === 'g06-invite' || action === 'g06-invite-invalid') {
|
||||
const switched = await send('Runtime.evaluate', {
|
||||
expression: `(() => {
|
||||
const tab = document.querySelectorAll('.mode-tab')[1]
|
||||
if (!tab) return false
|
||||
tab.click()
|
||||
return true
|
||||
})()`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (!switched.result?.value) throw new Error('Could not switch G06 to invite mode')
|
||||
await sleep(100)
|
||||
const prepared = await send('Runtime.evaluate', {
|
||||
expression: `(() => {
|
||||
const input = document.querySelector('.invite-input input')
|
||||
const button = document.querySelector('.invite-controls .search-action')
|
||||
if (!input || !button) return false
|
||||
input.value = ${JSON.stringify(action === 'g06-invite' ? 'JP2026' : 'INVALID')}
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }))
|
||||
return true
|
||||
})()`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (!prepared.result?.value) throw new Error('Could not prepare G06 invite code')
|
||||
await sleep(100)
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.invite-controls .search-action').click()" })
|
||||
} else {
|
||||
|
||||
const keyword = action === 'g06-results' ? '汤' : action === 'g06-error' ? '失败' : '不存在的家谱'
|
||||
const prepared = await send('Runtime.evaluate', {
|
||||
expression: `(() => {
|
||||
const input = document.querySelector('.search-input input')
|
||||
if (!input) return false
|
||||
input.value = ${JSON.stringify(keyword)}
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }))
|
||||
return true
|
||||
})()`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (!prepared.result?.value) throw new Error(`Could not prepare ${action}`)
|
||||
await sleep(100)
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.search-action').click()" })
|
||||
}
|
||||
|
||||
const stateSelector = action === 'g06-results' ? '.search-results .genealogy-card' : action === 'g06-empty' ? '.search-empty' : action === 'g06-error' ? '.search-error' : action === 'g06-invite-invalid' ? '.invite-invalid' : '.invite-result .genealogy-card'
|
||||
for (let attempt = 0; attempt < 30; attempt += 1) {
|
||||
const result = await send('Runtime.evaluate', {
|
||||
expression: `Boolean(document.querySelector(${JSON.stringify(stateSelector)}))`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (result.result?.value) return
|
||||
if (attempt === 29) throw new Error(`Expected G06 state did not render: ${action}`)
|
||||
await sleep(100)
|
||||
}
|
||||
}
|
||||
|
||||
const waitForPageLoad = async (socket, trigger) => new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
socket.removeEventListener('message', onMessage)
|
||||
reject(new Error('Page reload did not emit Page.loadEventFired'))
|
||||
}, 4000)
|
||||
const onMessage = (event) => {
|
||||
const message = JSON.parse(event.data)
|
||||
if (message.method !== 'Page.loadEventFired') return
|
||||
clearTimeout(timeout)
|
||||
socket.removeEventListener('message', onMessage)
|
||||
resolve()
|
||||
}
|
||||
socket.addEventListener('message', onMessage)
|
||||
Promise.resolve().then(trigger).catch((error) => {
|
||||
clearTimeout(timeout)
|
||||
socket.removeEventListener('message', onMessage)
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
|
||||
const waitForSelector = async (send, selector, message) => {
|
||||
for (let attempt = 0; attempt < 30; attempt += 1) {
|
||||
const result = await send('Runtime.evaluate', {
|
||||
expression: `Boolean(document.querySelector(${JSON.stringify(selector)}))`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (result.result?.value) return
|
||||
if (attempt === 29) throw new Error(message)
|
||||
await sleep(100)
|
||||
}
|
||||
}
|
||||
|
||||
const waitForText = async (send, selector, text, message) => {
|
||||
for (let attempt = 0; attempt < 30; attempt += 1) {
|
||||
const result = await send('Runtime.evaluate', {
|
||||
expression: `document.querySelector(${JSON.stringify(selector)})?.textContent.includes(${JSON.stringify(text)}) === true`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (result.result?.value) return
|
||||
if (attempt === 29) throw new Error(message)
|
||||
await sleep(50)
|
||||
}
|
||||
}
|
||||
|
||||
const prepareG03State = async (send, action) => {
|
||||
const actions = new Set([
|
||||
'g03-create-validation',
|
||||
'g03-duplicate-reminder',
|
||||
'g03-create-submitting',
|
||||
'g03-create-error',
|
||||
'g03-ancestor-validation',
|
||||
'g03-ancestor-submitting',
|
||||
'g03-ancestor-error',
|
||||
'g03-ancestor-success'
|
||||
])
|
||||
if (!actions.has(action)) return
|
||||
|
||||
if (action === 'g03-create-validation') {
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.flow-primary-action').click()" })
|
||||
await waitForSelector(send, '.field-error', 'G03 create validation did not render')
|
||||
return
|
||||
}
|
||||
|
||||
if (action.startsWith('g03-create') || action === 'g03-duplicate-reminder') {
|
||||
const name = action === 'g03-create-error' ? '失败' : '汤氏家谱'
|
||||
const prepared = await send('Runtime.evaluate', {
|
||||
expression: `(() => {
|
||||
const inputs = document.querySelectorAll('.field-row input')
|
||||
if (inputs.length < 4) return false
|
||||
const values = ['汤', ${JSON.stringify(name)}, '', '河南·洛阳']
|
||||
inputs.forEach((input, index) => {
|
||||
input.value = values[index]
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }))
|
||||
})
|
||||
document.querySelector('.flow-primary-action').click()
|
||||
return true
|
||||
})()`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (!prepared.result?.value) throw new Error(`Could not prepare ${action}`)
|
||||
await waitForSelector(send, '.duplicate-reminder', `G03 duplicate reminder did not render for ${action}`)
|
||||
if (action === 'g03-duplicate-reminder') return
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.duplicate-reminder__confirm').click()" })
|
||||
if (action === 'g03-create-submitting') {
|
||||
await waitForText(send, '.flow-primary-action__copy', '正在创建', 'G03 create submitting state did not render')
|
||||
return
|
||||
}
|
||||
await waitForSelector(send, '.flow-error', 'G03 create error state did not render')
|
||||
return
|
||||
}
|
||||
|
||||
if (action === 'g03-ancestor-validation') {
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.flow-primary-action').click()" })
|
||||
await waitForSelector(send, '.field-error', 'G03 ancestor validation did not render')
|
||||
return
|
||||
}
|
||||
|
||||
const personName = action === 'g03-ancestor-error' ? '失败' : '汤远'
|
||||
const prepared = await send('Runtime.evaluate', {
|
||||
expression: `(() => {
|
||||
const input = document.querySelector('.field-row input')
|
||||
if (!input) return false
|
||||
input.value = ${JSON.stringify(personName)}
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }))
|
||||
document.querySelector('.flow-primary-action').click()
|
||||
return true
|
||||
})()`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (!prepared.result?.value) throw new Error(`Could not prepare ${action}`)
|
||||
if (action === 'g03-ancestor-submitting') {
|
||||
await waitForText(send, '.flow-primary-action__copy', '正在保存', 'G03 ancestor submitting state did not render')
|
||||
} else if (action === 'g03-ancestor-error') {
|
||||
await waitForSelector(send, '.flow-error', 'G03 ancestor error state did not render')
|
||||
} else {
|
||||
await waitForSelector(send, '.flow-success-layer', 'G03 ancestor success state did not render')
|
||||
}
|
||||
}
|
||||
|
||||
const prepareG08State = async (send, action) => {
|
||||
if (!['g08-validation', 'g08-submitting', 'g08-error', 'g08-success'].includes(action)) return
|
||||
if (action === 'g08-validation') {
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.join-action').click()" })
|
||||
await waitForSelector(send, '.join-field-error', 'G08 validation errors did not render')
|
||||
return
|
||||
}
|
||||
|
||||
const realName = action === 'g08-error' ? '失败' : '汤明'
|
||||
const prepared = await send('Runtime.evaluate', {
|
||||
expression: `(() => {
|
||||
const inputs = document.querySelectorAll('.join-field input')
|
||||
if (inputs.length < 2) return false
|
||||
const values = [${JSON.stringify(realName)}, '堂侄']
|
||||
inputs.forEach((input, index) => {
|
||||
input.value = values[index]
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }))
|
||||
})
|
||||
return true
|
||||
})()`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (!prepared.result?.value) throw new Error(`Could not prepare ${action}`)
|
||||
await sleep(50)
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.join-action').click()" })
|
||||
if (action === 'g08-submitting') {
|
||||
await waitForText(send, '.join-action', '正在', 'G08 submitting state did not render')
|
||||
} else if (action === 'g08-error') {
|
||||
await waitForSelector(send, '.join-state--error', 'G08 error state did not render')
|
||||
} else {
|
||||
await waitForSelector(send, '.join-state--success', 'G08 success state did not render')
|
||||
}
|
||||
}
|
||||
|
||||
const prepareG09State = async (send, action) => {
|
||||
if (!['g09-withdraw-dialog', 'g09-withdrawn'].includes(action)) return
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.application-card__action')?.click()" })
|
||||
await waitForSelector(send, '.app-dialog-layer', 'G09 withdrawal dialog did not render')
|
||||
if (action === 'g09-withdrawn') {
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.app-dialog__actions .app-button:last-child')?.click()" })
|
||||
await waitForText(send, '.application-card__status', '已撤回', 'G09 withdrawn state did not render')
|
||||
}
|
||||
}
|
||||
|
||||
const prepareG10State = async (send, action) => {
|
||||
const actions = ['g10-help', 'g10-approve-dialog', 'g10-approved', 'g10-reject-dialog', 'g10-rejected']
|
||||
if (!actions.includes(action)) return
|
||||
if (action === 'g10-help') {
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.header-action')?.click()" })
|
||||
await waitForText(send, '.app-dialog__title', '审核说明', 'G10 help dialog did not render')
|
||||
return
|
||||
}
|
||||
const isApprove = action === 'g10-approve-dialog' || action === 'g10-approved'
|
||||
const selector = isApprove ? '.review-action:last-child' : '.review-action:first-child'
|
||||
await send('Runtime.evaluate', { expression: `document.querySelector(${JSON.stringify(selector)})?.click()` })
|
||||
await waitForSelector(send, '.app-dialog-layer', 'G10 audit confirmation did not render')
|
||||
if (action === 'g10-approved' || action === 'g10-rejected') {
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.app-dialog__actions .app-button:last-child')?.click()" })
|
||||
await waitForText(send, '.application-card__status', isApprove ? '已通过' : '已拒绝', 'G10 audit result did not render')
|
||||
}
|
||||
}
|
||||
|
||||
const prepareG11State = async (send, action) => {
|
||||
if (!['g11-validation', 'g11-saved'].includes(action)) return
|
||||
if (action === 'g11-validation') {
|
||||
await send('Runtime.evaluate', {
|
||||
expression: `(() => {
|
||||
const input = document.querySelector('.settings-field input')
|
||||
if (!input) return false
|
||||
input.value = ''
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }))
|
||||
document.querySelector('.settings-action')?.click()
|
||||
return true
|
||||
})()`,
|
||||
returnByValue: true
|
||||
})
|
||||
await waitForSelector(send, '.settings-field-error', 'G11 validation state did not render')
|
||||
return
|
||||
}
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.settings-action')?.click()" })
|
||||
await waitForSelector(send, '.settings-state--success', 'G11 saved state did not render')
|
||||
await waitForSelector(send, '.settings-feedback', 'G11 saved feedback did not render')
|
||||
}
|
||||
|
||||
const prepareG12State = async (send, action) => {
|
||||
if (!['g12-validation', 'g12-save-error', 'g12-saved'].includes(action)) return
|
||||
const value = action === 'g12-validation' ? '' : action === 'g12-save-error' ? '失败' : '启宗敦本继世传芳'
|
||||
await send('Runtime.evaluate', {
|
||||
expression: `(() => {
|
||||
const textarea = document.querySelector('.poem-field textarea')
|
||||
if (!textarea) return false
|
||||
textarea.value = ${JSON.stringify(value)}
|
||||
textarea.dispatchEvent(new Event('input', { bubbles: true }))
|
||||
document.querySelector('.poem-editor__actions .poem-action:last-child')?.click()
|
||||
return true
|
||||
})()`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (action === 'g12-saved') {
|
||||
await waitForSelector(send, '.poem-state--list', 'G12 saved list did not render')
|
||||
await waitForSelector(send, '.poem-feedback', 'G12 saved feedback did not render')
|
||||
} else {
|
||||
await waitForSelector(send, '.poem-field-error', 'G12 validation or save error did not render')
|
||||
}
|
||||
}
|
||||
|
||||
const prepareT03T06State = async (send, action) => {
|
||||
if (action !== 't06-conflict-help') return
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.conflict-actions .member-form-action:last-child')?.click()" })
|
||||
await waitForSelector(send, '.app-dialog-layer', 'T06 conflict help dialog did not render')
|
||||
}
|
||||
|
||||
const prepareFSeriesState = async (send, action) => {
|
||||
if (action === 'f02-validation') {
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.publish-form .app-button')?.click()" })
|
||||
await waitForSelector(send, '.app-toast', 'F02 empty-content toast did not render')
|
||||
return
|
||||
}
|
||||
if (action !== 'module-preview-toast') return
|
||||
await send('Runtime.evaluate', { expression: "(document.querySelector('.list-card') || document.querySelector('.settings-row'))?.click()" })
|
||||
await waitForSelector(send, '.app-toast', 'ModulePage preview toast did not render')
|
||||
}
|
||||
|
||||
const prepareNSeriesState = async (send, action) => {
|
||||
if (action === 'n01-read-first') {
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.notice-card')?.click()" })
|
||||
for (let attempt = 0; attempt < 30; attempt += 1) {
|
||||
const result = await send('Runtime.evaluate', {
|
||||
expression: "document.querySelector('.notice-card')?.textContent.includes('未读提醒') === false",
|
||||
returnByValue: true
|
||||
})
|
||||
if (result.result?.value) return
|
||||
if (attempt === 29) throw new Error('N01 first notice did not enter the read state')
|
||||
await sleep(50)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (action !== 'n01-mark-all') return
|
||||
await send('Runtime.evaluate', { expression: "document.querySelector('.header-action')?.click()" })
|
||||
await waitForSelector(send, '.app-toast', 'N01 mark-all-read toast did not render')
|
||||
}
|
||||
|
||||
const capture = async () => {
|
||||
const { socket, send } = await connect()
|
||||
const viewportWidth = Number(width)
|
||||
const viewportHeight = Number(height)
|
||||
|
||||
try {
|
||||
await send('Page.enable')
|
||||
await send('Runtime.enable')
|
||||
await send('Emulation.setDeviceMetricsOverride', {
|
||||
width: viewportWidth,
|
||||
height: viewportHeight,
|
||||
deviceScaleFactor: 1,
|
||||
mobile: true,
|
||||
screenWidth: viewportWidth,
|
||||
screenHeight: viewportHeight
|
||||
})
|
||||
const timeOriginResult = await send('Runtime.evaluate', {
|
||||
expression: 'performance.timeOrigin',
|
||||
returnByValue: true
|
||||
})
|
||||
const documentTimeOrigin = timeOriginResult.result?.value
|
||||
await send('Page.navigate', { url })
|
||||
await waitForRequestedUrl(send, url)
|
||||
if (action !== 'fresh-navigation') {
|
||||
await waitForPageLoad(socket, () => send('Page.reload'))
|
||||
await waitForFreshDocument(send, documentTimeOrigin)
|
||||
}
|
||||
|
||||
for (let attempt = 0; attempt < 30; attempt += 1) {
|
||||
const result = await send('Runtime.evaluate', {
|
||||
expression: `Boolean(document.querySelector(${JSON.stringify(selector)}))`,
|
||||
returnByValue: true
|
||||
})
|
||||
if (result.result?.value) break
|
||||
if (attempt === 29) throw new Error(`Expected page selector did not render: ${selector}`)
|
||||
await sleep(100)
|
||||
}
|
||||
|
||||
await send('Runtime.evaluate', { expression: 'scrollTo(0, 0)' })
|
||||
|
||||
await prepareA01LoginState(send, action)
|
||||
await prepareG01State(send, action)
|
||||
await prepareG06State(send, action)
|
||||
await prepareG03State(send, action)
|
||||
await prepareG08State(send, action)
|
||||
await prepareG09State(send, action)
|
||||
await prepareG10State(send, action)
|
||||
await prepareG11State(send, action)
|
||||
await prepareG12State(send, action)
|
||||
await prepareT03T06State(send, action)
|
||||
await prepareFSeriesState(send, action)
|
||||
await prepareNSeriesState(send, action)
|
||||
|
||||
await waitForLoadedImages(send)
|
||||
await sleep(action?.endsWith('-submitting') ? 80 : renderSettleMilliseconds)
|
||||
await send('Page.bringToFront')
|
||||
const screenshot = await send('Page.captureScreenshot', { format: 'png', fromSurface: action !== 'from-view', captureBeyondViewport: false })
|
||||
fs.mkdirSync(path.dirname(output), { recursive: true })
|
||||
fs.writeFileSync(output, Buffer.from(screenshot.data, 'base64'))
|
||||
process.stdout.write(`CAPTURED ${output}\n`)
|
||||
} finally {
|
||||
if (action !== 'keep-view') {
|
||||
try { await send('Emulation.clearDeviceMetricsOverride') } catch (_) {}
|
||||
}
|
||||
socket.close()
|
||||
}
|
||||
}
|
||||
|
||||
capture().catch((error) => {
|
||||
process.stderr.write(`${error.stack || error.message}\n`)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -1,43 +0,0 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Output,
|
||||
[Parameter(Mandatory = $true)][string[]]$InputPaths
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
|
||||
if ($InputPaths.Count -lt 2) { throw 'Contact sheet requires at least two images.' }
|
||||
|
||||
$images = @()
|
||||
try {
|
||||
foreach ($inputPath in $InputPaths) {
|
||||
$resolved = (Resolve-Path -LiteralPath $inputPath).Path
|
||||
$images += [System.Drawing.Bitmap]::FromFile($resolved)
|
||||
}
|
||||
|
||||
$width = ($images | Measure-Object -Property Width -Sum).Sum
|
||||
$height = ($images | Measure-Object -Property Height -Maximum).Maximum
|
||||
$canvas = [System.Drawing.Bitmap]::new($width, $height, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb)
|
||||
try {
|
||||
$graphics = [System.Drawing.Graphics]::FromImage($canvas)
|
||||
try {
|
||||
$graphics.Clear([System.Drawing.Color]::FromArgb(247, 240, 228))
|
||||
$offsetX = 0
|
||||
foreach ($image in $images) {
|
||||
$graphics.DrawImageUnscaled($image, $offsetX, 0)
|
||||
$offsetX += $image.Width
|
||||
}
|
||||
} finally {
|
||||
$graphics.Dispose()
|
||||
}
|
||||
|
||||
$outputPath = [System.IO.Path]::GetFullPath($Output)
|
||||
[System.IO.Directory]::CreateDirectory([System.IO.Path]::GetDirectoryName($outputPath)) | Out-Null
|
||||
$canvas.Save($outputPath, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
Write-Output "CONTACT-SHEET $outputPath"
|
||||
} finally {
|
||||
$canvas.Dispose()
|
||||
}
|
||||
} finally {
|
||||
foreach ($image in $images) { $image.Dispose() }
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
param(
|
||||
[string]$ManifestPath = 'docs/design/assets/a01-vnext/source/a01-psd-manifest.json',
|
||||
[string]$PsdPath = 'docs/design/assets/a01-vnext/source/A01-layered-source-v1.psd'
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
$rootFullPath = [IO.Path]::GetFullPath($root).TrimEnd([IO.Path]::DirectorySeparatorChar) + [IO.Path]::DirectorySeparatorChar
|
||||
|
||||
function Resolve-WorkspaceFile {
|
||||
param([string]$Path)
|
||||
$candidate = if ([IO.Path]::IsPathRooted($Path)) { $Path } else { Join-Path $root $Path }
|
||||
$fullPath = [IO.Path]::GetFullPath($candidate)
|
||||
if (-not $fullPath.StartsWith($rootFullPath, [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "Path must remain inside the workspace: $Path"
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $fullPath -PathType Leaf)) {
|
||||
throw "Required file does not exist: $fullPath"
|
||||
}
|
||||
return $fullPath
|
||||
}
|
||||
|
||||
function ConvertTo-JavaScriptString {
|
||||
param([string]$Value)
|
||||
$escaped = $Value.Replace('\', '\\').Replace("'", "\'").Replace("`r", '\r').Replace("`n", '\n')
|
||||
return "'$escaped'"
|
||||
}
|
||||
|
||||
$resolvedManifest = Resolve-WorkspaceFile -Path $ManifestPath
|
||||
$resolvedPsd = Resolve-WorkspaceFile -Path $PsdPath
|
||||
$jsxPath = Resolve-WorkspaceFile -Path 'scripts/photoshop/a01-export-layered-assets.jsx'
|
||||
if (-not (Get-Process -Name Photoshop -ErrorAction SilentlyContinue)) {
|
||||
throw 'Adobe Photoshop must already be running.'
|
||||
}
|
||||
|
||||
$app = New-Object -ComObject 'Photoshop.Application.150'
|
||||
$bootstrap = @"
|
||||
var JIAPU_A01_ROOT = $(ConvertTo-JavaScriptString $rootFullPath.TrimEnd([IO.Path]::DirectorySeparatorChar));
|
||||
var JIAPU_A01_MANIFEST = $(ConvertTo-JavaScriptString $resolvedManifest);
|
||||
var JIAPU_A01_PSD = $(ConvertTo-JavaScriptString $resolvedPsd);
|
||||
$.evalFile(new File($(ConvertTo-JavaScriptString $jsxPath)));
|
||||
"@
|
||||
$app.DoJavaScript($bootstrap, @(), 1)
|
||||
|
||||
Write-Output 'A01-LAYERED-ASSETS EXPORT PASS'
|
||||
@@ -1,63 +0,0 @@
|
||||
#target photoshop
|
||||
|
||||
app.displayDialogs = DialogModes.NO;
|
||||
|
||||
var createdDocument = null;
|
||||
var previousRulerUnits = app.preferences.rulerUnits;
|
||||
|
||||
function readJson(filePath) {
|
||||
var file = File(filePath);
|
||||
if (!file.exists) {
|
||||
throw new Error('Manifest does not exist: ' + filePath);
|
||||
}
|
||||
file.encoding = 'UTF8';
|
||||
if (!file.open('r')) {
|
||||
throw new Error('Unable to open manifest: ' + filePath);
|
||||
}
|
||||
var content = file.read();
|
||||
file.close();
|
||||
return eval('(' + content + ')');
|
||||
}
|
||||
|
||||
try {
|
||||
app.preferences.rulerUnits = Units.PIXELS;
|
||||
var manifest = readJson(JIAPU_A01_MANIFEST);
|
||||
var psdFile = File(JIAPU_A01_PSD);
|
||||
|
||||
if (psdFile.exists) {
|
||||
throw new Error('Refusing to overwrite existing PSD: ' + psdFile.fsName);
|
||||
}
|
||||
|
||||
createdDocument = app.documents.add(
|
||||
manifest.canvas.width,
|
||||
manifest.canvas.height,
|
||||
manifest.canvas.resolution,
|
||||
'JIAPU_A01_SCRIPT_SOURCE',
|
||||
NewDocumentMode.RGB,
|
||||
DocumentFill.TRANSPARENT
|
||||
);
|
||||
|
||||
for (var groupIndex = manifest.groups.length - 1; groupIndex >= 0; groupIndex--) {
|
||||
var groupSpec = manifest.groups[groupIndex];
|
||||
var group = createdDocument.layerSets.add();
|
||||
group.name = groupSpec.name;
|
||||
group.visible = groupSpec.visible;
|
||||
}
|
||||
|
||||
if (createdDocument.artLayers.length > 0) {
|
||||
createdDocument.artLayers[0].remove();
|
||||
}
|
||||
|
||||
var saveOptions = new PhotoshopSaveOptions();
|
||||
saveOptions.layers = true;
|
||||
createdDocument.saveAs(psdFile, saveOptions, true, Extension.LOWERCASE);
|
||||
createdDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||
createdDocument = null;
|
||||
} catch (error) {
|
||||
if (createdDocument !== null) {
|
||||
createdDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
app.preferences.rulerUnits = previousRulerUnits;
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
#target photoshop
|
||||
|
||||
app.displayDialogs = DialogModes.NO;
|
||||
app.preferences.rulerUnits = Units.PIXELS;
|
||||
|
||||
var ROOT = new Folder(JIAPU_A01_ROOT).fsName;
|
||||
var OUTPUT = new Folder(JIAPU_A01_OUTPUT_DIR);
|
||||
var PREVIEW_VERSION = typeof JIAPU_A01_PREVIEW_VERSION === 'undefined' ? 'v2' : JIAPU_A01_PREVIEW_VERSION;
|
||||
if (!OUTPUT.exists) {
|
||||
OUTPUT.create();
|
||||
}
|
||||
|
||||
function assetPath(relativePath) {
|
||||
return ROOT + '/' + relativePath;
|
||||
}
|
||||
|
||||
function color(hex) {
|
||||
var value = hex.replace('#', '');
|
||||
var result = new SolidColor();
|
||||
result.rgb.red = parseInt(value.substring(0, 2), 16);
|
||||
result.rgb.green = parseInt(value.substring(2, 4), 16);
|
||||
result.rgb.blue = parseInt(value.substring(4, 6), 16);
|
||||
return result;
|
||||
}
|
||||
|
||||
function placeImage(document, sourcePath, layerName, x, y, width, height, group) {
|
||||
var sourceDocument = app.open(File(sourcePath));
|
||||
var layer = sourceDocument.activeLayer.duplicate(document, ElementPlacement.PLACEATBEGINNING);
|
||||
sourceDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||
app.activeDocument = document;
|
||||
layer.name = layerName;
|
||||
var bounds = layer.bounds;
|
||||
var currentWidth = bounds[2].as('px') - bounds[0].as('px');
|
||||
var currentHeight = bounds[3].as('px') - bounds[1].as('px');
|
||||
layer.resize(width / currentWidth * 100, height / currentHeight * 100, AnchorPosition.TOPLEFT);
|
||||
bounds = layer.bounds;
|
||||
layer.translate(x - bounds[0].as('px'), y - bounds[1].as('px'));
|
||||
if (group) {
|
||||
layer.move(group, ElementPlacement.PLACEATBEGINNING);
|
||||
}
|
||||
return layer;
|
||||
}
|
||||
|
||||
function addText(document, group, layerName, contents, x, y, size, hex, alignment) {
|
||||
app.activeDocument = document;
|
||||
var layer = document.artLayers.add();
|
||||
layer.kind = LayerKind.TEXT;
|
||||
layer.name = layerName;
|
||||
layer.textItem.contents = contents;
|
||||
layer.textItem.font = 'KaiTi';
|
||||
layer.textItem.size = UnitValue(size, 'px');
|
||||
layer.textItem.color = color(hex);
|
||||
layer.textItem.position = [UnitValue(x, 'px'), UnitValue(y, 'px')];
|
||||
layer.textItem.justification = alignment === 'left' ? Justification.LEFT : Justification.CENTER;
|
||||
layer.textItem.antiAliasMethod = AntiAlias.SMOOTH;
|
||||
layer.move(group, ElementPlacement.PLACEATBEGINNING);
|
||||
return layer;
|
||||
}
|
||||
|
||||
function addRect(document, group, layerName, x, y, width, height, hex) {
|
||||
app.activeDocument = document;
|
||||
var layer = document.artLayers.add();
|
||||
layer.name = layerName;
|
||||
document.selection.select([
|
||||
[x, y],
|
||||
[x + width, y],
|
||||
[x + width, y + height],
|
||||
[x, y + height]
|
||||
]);
|
||||
document.selection.fill(color(hex));
|
||||
document.selection.deselect();
|
||||
layer.move(group, ElementPlacement.PLACEATBEGINNING);
|
||||
return layer;
|
||||
}
|
||||
|
||||
function savePsd(document, filePath) {
|
||||
var options = new PhotoshopSaveOptions();
|
||||
options.layers = true;
|
||||
document.saveAs(File(filePath), options, true, Extension.LOWERCASE);
|
||||
}
|
||||
|
||||
function savePng(document, filePath) {
|
||||
var options = new PNGSaveOptions();
|
||||
options.interlaced = false;
|
||||
document.saveAs(File(filePath), options, true, Extension.LOWERCASE);
|
||||
}
|
||||
|
||||
function compose(stateName) {
|
||||
var document = app.documents.add(1236, 2745, 72, 'A01-' + stateName, NewDocumentMode.RGB, DocumentFill.WHITE);
|
||||
var common = document.layerSets.add();
|
||||
common.name = '00-common';
|
||||
var state = document.layerSets.add();
|
||||
state.name = stateName === 'password' ? '10-password' : '10-sms';
|
||||
|
||||
addRect(document, common, 'page-background', 0, 0, 1236, 2745, '#f4eadc');
|
||||
placeImage(document, assetPath('docs/design/assets/a01-vnext/candidates/a01-header-background-candidate-v3.png'), 'ancestral-header', 0, 0, 1236, 510, common);
|
||||
// 设计预览使用完整母图,避免拆片尚未定稿时产生重叠接缝;运行时切片另行验收。
|
||||
placeImage(document, assetPath('docs/design/assets/a01-vnext/candidates/a01-scroll-master-transparent-candidate-v4-1236x2745.png'), 'scroll-master', 29, 430, 1178, 2315, common);
|
||||
placeImage(document, assetPath('static/assets/foundation/transparent/brand-seal.png'), 'brand-seal', 510, 105, 216, 259, common);
|
||||
|
||||
placeImage(document, assetPath('docs/design/assets/a01-vnext/candidates/a01-title-ornament-candidate-v2.png'), 'title-ornament', 430, 700, 376, 130, common);
|
||||
addText(document, common, 'page-title', '\u767b\u5f55\u5bb6\u8c31', 618, 960, 126, '#721417', 'center');
|
||||
placeImage(document, assetPath('docs/design/assets/a01-vnext/candidates/a01-divider-ornament-candidate-v2.png'), 'title-divider', 348, 995, 540, 90, common);
|
||||
|
||||
addText(document, common, 'password-tab', '\u5bc6\u7801\u767b\u5f55', 390, 1165, 70, stateName === 'password' ? '#b10f18' : '#aa8240', 'center');
|
||||
addText(document, common, 'sms-tab', '\u9a8c\u8bc1\u7801\u767b\u5f55', 846, 1165, 70, stateName === 'sms' ? '#b10f18' : '#aa8240', 'center');
|
||||
addRect(document, common, 'tab-rule', 180, 1210, 876, 3, '#d7b37a');
|
||||
addRect(document, common, 'active-tab-rule', stateName === 'password' ? 300 : 756, 1200, 180, 12, '#c3151d');
|
||||
|
||||
placeImage(document, assetPath('docs/design/assets/a01-vnext/candidates/a01-icon-phone-candidate-v1.png'), 'phone-icon', 190, 1270, 100, 100, state);
|
||||
addText(document, state, 'phone-placeholder', '\u624b\u673a\u53f7', 330, 1355, 60, '#8b8580', 'left');
|
||||
addRect(document, state, 'phone-line', 180, 1440, 876, 3, '#d7b37a');
|
||||
|
||||
if (stateName === 'password') {
|
||||
placeImage(document, assetPath('static/assets/modules/auth/transparent/a01-icon-lock-v1.png'), 'lock-icon', 196, 1518, 82, 98, state);
|
||||
addText(document, state, 'password-placeholder', '\u5bc6\u7801', 330, 1590, 60, '#8b8580', 'left');
|
||||
placeImage(document, assetPath('docs/design/assets/a01-vnext/candidates/a01-icon-eye-closed-pupil-candidate-v1.png'), 'eye-closed-pupil', 952, 1530, 84, 70, state);
|
||||
addRect(document, state, 'password-line', 180, 1665, 876, 3, '#d7b37a');
|
||||
addText(document, state, 'forgot-password', '\u5fd8\u8bb0\u5bc6\u7801', 1020, 1745, 48, '#b10f18', 'center');
|
||||
} else {
|
||||
placeImage(document, assetPath('docs/design/assets/a01-vnext/candidates/a01-icon-sms-three-dots-candidate-v1.png'), 'sms-three-dots', 190, 1510, 100, 100, state);
|
||||
addText(document, state, 'code-placeholder', '\u9a8c\u8bc1\u7801', 330, 1590, 60, '#8b8580', 'left');
|
||||
addText(document, state, 'get-code', '\u83b7\u53d6\u9a8c\u8bc1\u7801', 930, 1590, 48, '#b10f18', 'center');
|
||||
addRect(document, state, 'code-line', 180, 1665, 876, 3, '#d7b37a');
|
||||
}
|
||||
|
||||
placeImage(document, assetPath('docs/design/assets/a01-vnext/candidates/a01-primary-button-master-candidate-v2.png'), 'primary-button-skin', 180, 1790, 876, 195, common);
|
||||
addText(document, common, 'login-label', '\u767b\u5f55', 618, 1925, 82, '#ffffff', 'center');
|
||||
|
||||
addRect(document, common, 'other-left-line', 190, 2072, 260, 3, '#d7b37a');
|
||||
addRect(document, common, 'other-right-line', 786, 2072, 260, 3, '#d7b37a');
|
||||
addText(document, common, 'other-methods', '\u5176\u4ed6\u767b\u5f55\u65b9\u5f0f', 618, 2100, 50, '#aa8240', 'center');
|
||||
|
||||
placeImage(document, assetPath('docs/design/assets/a01-vnext/candidates/a01-secondary-button-master-candidate-v2.png'), 'secondary-button-skin', 180, 2160, 876, 180, common);
|
||||
placeImage(document, assetPath('static/assets/foundation/transparent/auth-wechat.png'), 'wechat-icon', 365, 2190, 105, 105, common);
|
||||
addText(document, common, 'wechat-label', '\u5fae\u4fe1\u767b\u5f55', 690, 2290, 66, '#30241d', 'center');
|
||||
|
||||
addText(document, common, 'register-copy', '\u8fd8\u6ca1\u6709\u8d26\u53f7\uff1f \u6ce8\u518c\u8d26\u53f7', 618, 2425, 46, '#4c3b30', 'center');
|
||||
placeImage(document, assetPath('static/assets/modules/auth/transparent/a02-agreement-unchecked.png'), 'agreement-unchecked', 180, 2460, 66, 66, common);
|
||||
addText(document, common, 'agreement-copy', '\u6211\u5df2\u9605\u8bfb\u5e76\u540c\u610f\u300a\u7528\u6237\u534f\u8bae\u300b\u4e0e\u300a\u9690\u79c1\u653f\u7b56\u300b', 270, 2515, 36, '#4c3b30', 'left');
|
||||
|
||||
var baseName = stateName === 'password' ? 'A01-page-password-' + PREVIEW_VERSION : 'A01-page-sms-' + PREVIEW_VERSION;
|
||||
savePsd(document, OUTPUT.fsName + '/' + baseName + '.psd');
|
||||
savePng(document, OUTPUT.fsName + '/' + baseName + '-1236x2745.png');
|
||||
document.close(SaveOptions.DONOTSAVECHANGES);
|
||||
}
|
||||
|
||||
function makeContactSheet() {
|
||||
var contact = null;
|
||||
var step = 'create';
|
||||
try {
|
||||
contact = app.documents.add(2472, 2745, 72, 'A01-contact-sheet', NewDocumentMode.RGB, DocumentFill.WHITE);
|
||||
step = 'place-password';
|
||||
placeImage(contact, OUTPUT.fsName + '/A01-page-password-' + PREVIEW_VERSION + '-1236x2745.png', 'password', 0, 0, 1236, 2745, null);
|
||||
step = 'place-sms';
|
||||
placeImage(contact, OUTPUT.fsName + '/A01-page-sms-' + PREVIEW_VERSION + '-1236x2745.png', 'sms', 1236, 0, 1236, 2745, null);
|
||||
step = 'resize';
|
||||
contact.resizeImage(UnitValue(824, 'px'), UnitValue(915, 'px'), null, ResampleMethod.BICUBICSHARPER);
|
||||
step = 'save';
|
||||
savePng(contact, OUTPUT.fsName + '/A01-page-password-sms-contact-' + PREVIEW_VERSION + '-824x915.png');
|
||||
contact.close(SaveOptions.DONOTSAVECHANGES);
|
||||
contact = null;
|
||||
} catch (error) {
|
||||
if (contact) {
|
||||
contact.close(SaveOptions.DONOTSAVECHANGES);
|
||||
}
|
||||
throw new Error('CONTACT_' + step + ': ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof JIAPU_A01_CONTACT_ONLY === 'undefined' || !JIAPU_A01_CONTACT_ONLY) {
|
||||
compose('password');
|
||||
compose('sms');
|
||||
}
|
||||
if (typeof JIAPU_A01_SKIP_CONTACT === 'undefined' || !JIAPU_A01_SKIP_CONTACT) {
|
||||
makeContactSheet();
|
||||
}
|
||||
'A01_PAGE_PREVIEWS_OK';
|
||||
@@ -1,38 +0,0 @@
|
||||
#target photoshop
|
||||
|
||||
app.displayDialogs = DialogModes.NO;
|
||||
|
||||
var openedDocument = null;
|
||||
|
||||
function readJson(filePath) {
|
||||
var file = File(filePath);
|
||||
if (!file.exists) {
|
||||
throw new Error('Manifest does not exist: ' + filePath);
|
||||
}
|
||||
file.encoding = 'UTF8';
|
||||
if (!file.open('r')) {
|
||||
throw new Error('Unable to open manifest: ' + filePath);
|
||||
}
|
||||
var content = file.read();
|
||||
file.close();
|
||||
return eval('(' + content + ')');
|
||||
}
|
||||
|
||||
try {
|
||||
var manifest = readJson(JIAPU_A01_MANIFEST);
|
||||
var psdFile = File(JIAPU_A01_PSD);
|
||||
if (!psdFile.exists) {
|
||||
throw new Error('PSD does not exist: ' + psdFile.fsName);
|
||||
}
|
||||
openedDocument = app.open(psdFile);
|
||||
if (manifest.exports.length !== 0) {
|
||||
throw new Error('Layer export is not implemented until assets are approved.');
|
||||
}
|
||||
openedDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||
openedDocument = null;
|
||||
} catch (error) {
|
||||
if (openedDocument !== null) {
|
||||
openedDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#target photoshop
|
||||
|
||||
var runnerFile = new File($.fileName);
|
||||
var root = runnerFile.parent.parent.parent.fsName;
|
||||
|
||||
var JIAPU_A01_ROOT = root;
|
||||
var JIAPU_A01_OUTPUT_DIR = root + '/docs/design/assets/a01-vnext/review/page-v2';
|
||||
var JIAPU_A01_PREVIEW_VERSION = 'v2';
|
||||
var JIAPU_A01_SKIP_CONTACT = true;
|
||||
|
||||
try {
|
||||
$.evalFile(new File(root + '/scripts/photoshop/a01-compose-page-preview.jsx'));
|
||||
} catch (error) {
|
||||
var log = new File(root + '/docs/design/assets/a01-vnext/review/page-v2/photoshop-error.log');
|
||||
log.encoding = 'UTF8';
|
||||
log.open('w');
|
||||
log.write('number=' + error.number + '\nline=' + error.line + '\nmessage=' + error.message + '\nsource=' + error.source);
|
||||
log.close();
|
||||
throw error;
|
||||
}
|
||||
Reference in New Issue
Block a user