chore: 清理旧测试基础设施与废弃文件
- 移除 everything-is-suitable-test/ 旧测试框架 - 移除根目录 test-automation/、test-data-manager/、test-monitoring/ - 移除根目录 scripts/ 旧部署/测试脚本 - 移除 everything-is-suitable-uniapp/src/utils/httpClient.ts、tokenManager.ts - 移除根目录旧配置文件(jest.config.js、package.json、tsconfig.json 等) - 移除临时测试文件(test-*.ts、test-*.html、playwright-diagnose-*.js)
This commit is contained in:
@@ -1,195 +0,0 @@
|
||||
import appConfig from '../../config'
|
||||
import type { ApiResult } from '../types/api'
|
||||
|
||||
export enum ErrorType {
|
||||
NETWORK_ERROR = 'NETWORK_ERROR',
|
||||
TIMEOUT_ERROR = 'TIMEOUT_ERROR',
|
||||
BUSINESS_ERROR = 'BUSINESS_ERROR',
|
||||
AUTH_ERROR = 'AUTH_ERROR',
|
||||
TOKEN_EXPIRED = 'TOKEN_EXPIRED',
|
||||
UNKNOWN_ERROR = 'UNKNOWN_ERROR'
|
||||
}
|
||||
|
||||
export interface ApiError {
|
||||
type: ErrorType
|
||||
code: number
|
||||
message: string
|
||||
detail?: any
|
||||
}
|
||||
|
||||
export interface RequestConfig {
|
||||
url: string
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE'
|
||||
data?: any
|
||||
params?: any
|
||||
headers?: Record<string, string>
|
||||
timeout?: number
|
||||
needAuth?: boolean
|
||||
}
|
||||
|
||||
class HttpClient {
|
||||
private baseURL: string
|
||||
private defaultTimeout: number = 10000
|
||||
private tokenKey: string = 'auth_token'
|
||||
|
||||
constructor() {
|
||||
this.baseURL = appConfig.baseURL
|
||||
}
|
||||
|
||||
async request<T>(config: RequestConfig): Promise<ApiResult<T>> {
|
||||
const { url, method, data, params, headers = {}, timeout = this.defaultTimeout, needAuth = true } = config
|
||||
|
||||
const requestUrl = this.buildUrl(url, params)
|
||||
const requestHeaders = this.buildHeaders(headers, needAuth)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: requestUrl,
|
||||
method,
|
||||
data,
|
||||
header: requestHeaders,
|
||||
timeout,
|
||||
success: (response: any) => {
|
||||
try {
|
||||
const result = this.handleResponse<T>(response)
|
||||
resolve(result)
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
},
|
||||
fail: (error: any) => {
|
||||
reject(this.handleError(error))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async get<T>(url: string, params?: any, options?: { needAuth?: boolean }): Promise<ApiResult<T>> {
|
||||
return this.request<T>({ url, method: 'GET', params, ...options })
|
||||
}
|
||||
|
||||
async post<T>(url: string, data?: any, options?: { needAuth?: boolean }): Promise<ApiResult<T>> {
|
||||
return this.request<T>({ url, method: 'POST', data, ...options })
|
||||
}
|
||||
|
||||
async put<T>(url: string, data?: any, options?: { needAuth?: boolean }): Promise<ApiResult<T>> {
|
||||
return this.request<T>({ url, method: 'PUT', data, ...options })
|
||||
}
|
||||
|
||||
async delete<T>(url: string, params?: any, options?: { needAuth?: boolean }): Promise<ApiResult<T>> {
|
||||
return this.request<T>({ url, method: 'DELETE', params, ...options })
|
||||
}
|
||||
|
||||
private buildUrl(url: string, params?: any): string {
|
||||
let fullUrl = url.startsWith('http') ? url : `${this.baseURL}${url}`
|
||||
|
||||
if (params) {
|
||||
const queryString = Object.keys(params)
|
||||
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
|
||||
.join('&')
|
||||
fullUrl += `?${queryString}`
|
||||
}
|
||||
|
||||
return fullUrl
|
||||
}
|
||||
|
||||
private buildHeaders(headers: Record<string, string>, needAuth: boolean): Record<string, string> {
|
||||
const requestHeaders: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
...headers
|
||||
}
|
||||
|
||||
if (needAuth) {
|
||||
const token = this.getToken()
|
||||
if (token) {
|
||||
requestHeaders['Authorization'] = `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
|
||||
return requestHeaders
|
||||
}
|
||||
|
||||
private handleResponse<T>(response: any): ApiResult<T> {
|
||||
const { statusCode, data } = response
|
||||
|
||||
if (statusCode === 200) {
|
||||
return data as ApiResult<T>
|
||||
} else if (statusCode === 401) {
|
||||
throw {
|
||||
type: ErrorType.AUTH_ERROR,
|
||||
code: 401,
|
||||
message: '认证失败,请重新登录'
|
||||
} as ApiError
|
||||
} else if (statusCode === 400 || statusCode === 500) {
|
||||
const errorMessage = data?.message || '请求失败'
|
||||
throw {
|
||||
type: ErrorType.BUSINESS_ERROR,
|
||||
code: statusCode,
|
||||
message: errorMessage
|
||||
} as ApiError
|
||||
} else {
|
||||
throw {
|
||||
type: ErrorType.UNKNOWN_ERROR,
|
||||
code: statusCode,
|
||||
message: '未知错误'
|
||||
} as ApiError
|
||||
}
|
||||
}
|
||||
|
||||
private handleError(error: any): ApiError {
|
||||
if (error.errMsg) {
|
||||
if (error.errMsg.includes('timeout')) {
|
||||
return {
|
||||
type: ErrorType.TIMEOUT_ERROR,
|
||||
code: 0,
|
||||
message: '请求超时,请稍后重试'
|
||||
}
|
||||
} else if (error.errMsg.includes('fail')) {
|
||||
return {
|
||||
type: ErrorType.NETWORK_ERROR,
|
||||
code: 0,
|
||||
message: '网络连接失败,请检查网络设置'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (error.type) {
|
||||
return error as ApiError
|
||||
}
|
||||
|
||||
return {
|
||||
type: ErrorType.UNKNOWN_ERROR,
|
||||
code: 0,
|
||||
message: error.message || '未知错误'
|
||||
}
|
||||
}
|
||||
|
||||
private getToken(): string | null {
|
||||
try {
|
||||
return uni.getStorageSync(this.tokenKey) || null
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
setToken(token: string): void {
|
||||
try {
|
||||
uni.setStorageSync(this.tokenKey, token)
|
||||
} catch (error) {
|
||||
console.error('设置Token失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
removeToken(): void {
|
||||
try {
|
||||
uni.removeStorageSync(this.tokenKey)
|
||||
} catch (error) {
|
||||
console.error('删除Token失败:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const httpClient = new HttpClient()
|
||||
|
||||
export default httpClient
|
||||
export { HttpClient }
|
||||
@@ -1,93 +0,0 @@
|
||||
import type { ApiError, ErrorType } from './httpClient'
|
||||
|
||||
const TOKEN_KEY = 'auth_token'
|
||||
const REFRESH_TOKEN_KEY = 'refresh_token'
|
||||
const TOKEN_EXPIRY_KEY = 'token_expiry'
|
||||
|
||||
class TokenManager {
|
||||
setToken(token: string, expiresIn?: number): void {
|
||||
try {
|
||||
uni.setStorageSync(TOKEN_KEY, token)
|
||||
if (expiresIn !== undefined) {
|
||||
const expiryTime = Date.now() + expiresIn * 1000
|
||||
uni.setStorageSync(TOKEN_EXPIRY_KEY, expiryTime)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('设置Token失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
getToken(): string | null {
|
||||
try {
|
||||
const token = uni.getStorageSync(TOKEN_KEY)
|
||||
if (token === null || token === undefined) {
|
||||
return null
|
||||
}
|
||||
return token
|
||||
} catch (error) {
|
||||
console.error('获取Token失败:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
removeToken(): void {
|
||||
try {
|
||||
uni.removeStorageSync(TOKEN_KEY)
|
||||
uni.removeStorageSync(TOKEN_EXPIRY_KEY)
|
||||
} catch (error) {
|
||||
console.error('删除Token失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
setRefreshToken(refreshToken: string): void {
|
||||
try {
|
||||
uni.setStorageSync(REFRESH_TOKEN_KEY, refreshToken)
|
||||
} catch (error) {
|
||||
console.error('设置刷新Token失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
getRefreshToken(): string | null {
|
||||
try {
|
||||
const refreshToken = uni.getStorageSync(REFRESH_TOKEN_KEY)
|
||||
if (refreshToken === null || refreshToken === undefined) {
|
||||
return null
|
||||
}
|
||||
return refreshToken
|
||||
} catch (error) {
|
||||
console.error('获取刷新Token失败:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
removeRefreshToken(): void {
|
||||
try {
|
||||
uni.removeStorageSync(REFRESH_TOKEN_KEY)
|
||||
} catch (error) {
|
||||
console.error('删除刷新Token失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
isTokenExpired(): boolean {
|
||||
try {
|
||||
const expiryTime = uni.getStorageSync(TOKEN_EXPIRY_KEY)
|
||||
if (expiryTime === null || expiryTime === undefined) {
|
||||
return false
|
||||
}
|
||||
return Date.now() >= expiryTime
|
||||
} catch (error) {
|
||||
console.error('检查Token过期失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
clearAll(): void {
|
||||
this.removeToken()
|
||||
this.removeRefreshToken()
|
||||
}
|
||||
}
|
||||
|
||||
const tokenManager = new TokenManager()
|
||||
|
||||
export default tokenManager
|
||||
export { TokenManager }
|
||||
@@ -1,14 +0,0 @@
|
||||
import lunarUtils from './src/utils/lunarUtils'
|
||||
|
||||
const testDates = [
|
||||
{ year: 2025, month: 1, day: 18 },
|
||||
{ year: 2025, month: 1, day: 19 },
|
||||
{ year: 2025, month: 1, day: 20 },
|
||||
{ year: 2025, month: 1, day: 21 },
|
||||
{ year: 2025, month: 1, day: 22 }
|
||||
]
|
||||
|
||||
testDates.forEach(date => {
|
||||
const lunarDate = lunarUtils.solarToLunar(date.year, date.month, date.day)
|
||||
console.log(`${date.year}-${date.month}-${date.day} => ${lunarDate.yearStr} ${lunarDate.monthStr}${lunarDate.dayStr}`)
|
||||
})
|
||||
@@ -1,13 +0,0 @@
|
||||
import lunarUtils from './src/utils/lunarUtils'
|
||||
|
||||
const today = new Date(2026, 0, 20)
|
||||
const lunarDate = lunarUtils.solarToLunar(
|
||||
today.getFullYear(),
|
||||
today.getMonth() + 1,
|
||||
today.getDate()
|
||||
)
|
||||
|
||||
console.log('公历日期:', today.toISOString().split('T')[0])
|
||||
console.log('农历日期:', lunarDate.yearStr, lunarDate.monthStr, lunarDate.dayStr)
|
||||
console.log('生肖:', lunarDate.zodiac.name)
|
||||
console.log('闰月:', lunarDate.isLeapMonth ? '是' : '否')
|
||||
@@ -1,14 +0,0 @@
|
||||
import lunarUtils from './src/utils/lunarUtils'
|
||||
|
||||
const testDates = [
|
||||
{ year: 2026, month: 1, day: 18 },
|
||||
{ year: 2026, month: 1, day: 19 },
|
||||
{ year: 2026, month: 1, day: 20 },
|
||||
{ year: 2026, month: 1, day: 21 },
|
||||
{ year: 2026, month: 1, day: 22 }
|
||||
]
|
||||
|
||||
testDates.forEach(date => {
|
||||
const lunarDate = lunarUtils.solarToLunar(date.year, date.month, date.day)
|
||||
console.log(`${date.year}-${date.month}-${date.day} => ${lunarDate.yearStr} ${lunarDate.monthStr}${lunarDate.dayStr}`)
|
||||
})
|
||||
@@ -1,529 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AIGC 小程序环境验证</title>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', 'Microsoft YaHei', sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background-color: white;
|
||||
padding: 40px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #2C180C;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 2px solid #C41E3A;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #2C180C;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.checklist {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.checklist-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #E6E1DC;
|
||||
}
|
||||
|
||||
.checklist-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #C41E3A;
|
||||
border-radius: 4px;
|
||||
margin-right: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.checkbox.checked {
|
||||
background-color: #C41E3A;
|
||||
}
|
||||
|
||||
.checkbox.checked::after {
|
||||
content: '✓';
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.checklist-text {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #2C180C;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 4px 12px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status.pass {
|
||||
background-color: #16A38A;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.status.fail {
|
||||
background-color: #EF4444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: #C41E3A;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
margin-right: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #A81A30;
|
||||
}
|
||||
|
||||
.button:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.button.secondary {
|
||||
background-color: #717B7A;
|
||||
}
|
||||
|
||||
.button.secondary:hover {
|
||||
background-color: #5A6362;
|
||||
}
|
||||
|
||||
.preview-frame {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
border: 2px solid #E6E1DC;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
background-color: #FAF9F6;
|
||||
}
|
||||
|
||||
.device-simulator {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.device-item {
|
||||
border: 1px solid #E6E1DC;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.device-item:hover {
|
||||
border-color: #C41E3A;
|
||||
background-color: rgba(196, 30, 58, 0.05);
|
||||
}
|
||||
|
||||
.device-item.active {
|
||||
border-color: #C41E3A;
|
||||
background-color: rgba(196, 30, 58, 0.1);
|
||||
}
|
||||
|
||||
.device-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin: 0 auto 12px;
|
||||
background-color: #C41E3A;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.device-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #2C180C;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.device-spec {
|
||||
font-size: 12px;
|
||||
color: #717B7A;
|
||||
}
|
||||
|
||||
.summary {
|
||||
background-color: #FAF9F6;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.summary-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #2C180C;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.summary-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.summary-stat {
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.summary-stat-value {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
color: #C41E3A;
|
||||
}
|
||||
|
||||
.summary-stat-label {
|
||||
font-size: 12px;
|
||||
color: #717B7A;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background-color: #E6E1DC;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background-color: #16A38A;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>AIGC 小程序环境验证</h1>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">设备模拟器</h2>
|
||||
<div class="device-simulator">
|
||||
<div class="device-item active" onclick="selectDevice('iphone-se')">
|
||||
<div class="device-icon">📱</div>
|
||||
<div class="device-name">iPhone SE</div>
|
||||
<div class="device-spec">375 × 667</div>
|
||||
</div>
|
||||
<div class="device-item" onclick="selectDevice('iphone-12')">
|
||||
<div class="device-icon">📱</div>
|
||||
<div class="device-name">iPhone 12</div>
|
||||
<div class="device-spec">390 × 844</div>
|
||||
</div>
|
||||
<div class="device-item" onclick="selectDevice('iphone-14')">
|
||||
<div class="device-icon">📱</div>
|
||||
<div class="device-name">iPhone 14</div>
|
||||
<div class="device-spec">393 × 852</div>
|
||||
</div>
|
||||
<div class="device-item" onclick="selectDevice('ipad')">
|
||||
<div class="device-icon">📱</div>
|
||||
<div class="device-name">iPad</div>
|
||||
<div class="device-spec">768 × 1024</div>
|
||||
</div>
|
||||
<div class="device-item" onclick="selectDevice('desktop')">
|
||||
<div class="device-icon">🖥️</div>
|
||||
<div class="device-name">桌面端</div>
|
||||
<div class="device-spec">1920 × 1080</div>
|
||||
</div>
|
||||
</div>
|
||||
<iframe class="preview-frame" id="previewFrame" src="http://localhost:8081/#/pages/aigc/index"></iframe>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">小程序环境检查清单</h2>
|
||||
<ul class="checklist" id="miniprogramChecklist">
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">页面在H5环境正常加载</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">所有样式在小程序中正确渲染</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">rpx单位正确转换为像素</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">flex布局在小程序中正常工作</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">grid布局在小程序中正常工作</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">所有颜色值精确显示</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">字体在小程序中正确加载</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">图标在小程序中正确显示</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">交互效果检查清单</h2>
|
||||
<ul class="checklist" id="interactionChecklist">
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">点击事件在小程序中正常触发</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">hover效果在小程序中正常显示</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">active效果在小程序中正常显示</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">过渡动画在小程序中流畅播放</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">状态切换在小程序中正常工作</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">所有交互反馈及时准确</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">响应式适配检查清单</h2>
|
||||
<ul class="checklist" id="responsiveChecklist">
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">移动端布局完整显示</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">平板端布局完整显示</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">桌面端布局完整显示</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">断点切换流畅无闪烁</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">字体大小自适应准确</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">间距比例保持一致</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">验证操作</h2>
|
||||
<button class="button" onclick="runFullVerification()">运行完整验证</button>
|
||||
<button class="button secondary" onclick="exportReport()">导出验证报告</button>
|
||||
<button class="button secondary" onclick="openPixsoDesign()">打开设计稿</button>
|
||||
<button class="button secondary" onclick="refreshPreview()">刷新预览</button>
|
||||
</div>
|
||||
|
||||
<div class="summary">
|
||||
<h2 class="summary-title">验证总结</h2>
|
||||
<div class="summary-stats">
|
||||
<div class="summary-stat">
|
||||
<div class="summary-stat-value" id="totalChecks">20</div>
|
||||
<div class="summary-stat-label">总检查项</div>
|
||||
</div>
|
||||
<div class="summary-stat">
|
||||
<div class="summary-stat-value" id="passedChecks">20</div>
|
||||
<div class="summary-stat-label">通过项</div>
|
||||
</div>
|
||||
<div class="summary-stat">
|
||||
<div class="summary-stat-value" id="failedChecks">0</div>
|
||||
<div class="summary-stat-label">失败项</div>
|
||||
</div>
|
||||
<div class="summary-stat">
|
||||
<div class="summary-stat-value" id="passRate">100%</div>
|
||||
<div class="summary-stat-label">通过率</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" style="width: 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let currentDevice = 'iphone-se';
|
||||
|
||||
function selectDevice(device) {
|
||||
currentDevice = device;
|
||||
|
||||
document.querySelectorAll('.device-item').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
|
||||
event.currentTarget.classList.add('active');
|
||||
|
||||
const frame = document.getElementById('previewFrame');
|
||||
|
||||
const deviceSizes = {
|
||||
'iphone-se': { width: '375px', height: '667px' },
|
||||
'iphone-12': { width: '390px', height: '844px' },
|
||||
'iphone-14': { width: '393px', height: '852px' },
|
||||
'ipad': { width: '768px', height: '1024px' },
|
||||
'desktop': { width: '100%', height: '800px' }
|
||||
};
|
||||
|
||||
const size = deviceSizes[device];
|
||||
frame.style.width = size.width;
|
||||
frame.style.height = size.height;
|
||||
}
|
||||
|
||||
function runFullVerification() {
|
||||
alert('完整验证已启动!\n\n正在检查:\n- 小程序环境兼容性\n- 视觉效果还原度\n- 交互功能完整性\n- 响应式布局适配\n\n验证完成: 100% 匹配');
|
||||
}
|
||||
|
||||
function exportReport() {
|
||||
const report = {
|
||||
timestamp: new Date().toISOString(),
|
||||
device: currentDevice,
|
||||
miniprogramChecks: {
|
||||
total: 8,
|
||||
passed: 8,
|
||||
failed: 0,
|
||||
passRate: '100%'
|
||||
},
|
||||
interactionChecks: {
|
||||
total: 6,
|
||||
passed: 6,
|
||||
failed: 0,
|
||||
passRate: '100%'
|
||||
},
|
||||
responsiveChecks: {
|
||||
total: 6,
|
||||
passed: 6,
|
||||
failed: 0,
|
||||
passRate: '100%'
|
||||
},
|
||||
overall: {
|
||||
total: 20,
|
||||
passed: 20,
|
||||
failed: 0,
|
||||
passRate: '100%'
|
||||
},
|
||||
conclusion: '所有检查项均通过,AIGC页面在小程序环境中完美呈现,与设计稿达到100%匹配度。'
|
||||
};
|
||||
|
||||
const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'aigc-miniprogram-verification-report.json';
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function openPixsoDesign() {
|
||||
window.open('https://pixso.cn/app/design/8teaTOeN2QkeggcAJdfjMw?item-id=2:582', '_blank');
|
||||
}
|
||||
|
||||
function refreshPreview() {
|
||||
const frame = document.getElementById('previewFrame');
|
||||
frame.src = frame.src;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
selectDevice('iphone-se');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,603 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AIGC 页面像素级对比验证</title>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', 'Microsoft YaHei', sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background-color: white;
|
||||
padding: 40px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #2C180C;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 2px solid #C41E3A;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #2C180C;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.checklist {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.checklist-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #E6E1DC;
|
||||
}
|
||||
|
||||
.checklist-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #C41E3A;
|
||||
border-radius: 4px;
|
||||
margin-right: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.checkbox.checked {
|
||||
background-color: #C41E3A;
|
||||
}
|
||||
|
||||
.checkbox.checked::after {
|
||||
content: '✓';
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.checklist-text {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #2C180C;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 4px 12px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status.pass {
|
||||
background-color: #16A38A;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.status.fail {
|
||||
background-color: #EF4444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.color-palette {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.color-item {
|
||||
border: 1px solid #E6E1DC;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.color-preview {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 8px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.color-info {
|
||||
font-size: 12px;
|
||||
color: #717B7A;
|
||||
}
|
||||
|
||||
.color-name {
|
||||
font-weight: 500;
|
||||
color: #2C180C;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.spacing-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.spacing-item {
|
||||
border: 1px solid #E6E1DC;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.spacing-visual {
|
||||
background-color: #C41E3A;
|
||||
height: 20px;
|
||||
border-radius: 2px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.spacing-info {
|
||||
font-size: 12px;
|
||||
color: #717B7A;
|
||||
}
|
||||
|
||||
.spacing-name {
|
||||
font-weight: 500;
|
||||
color: #2C180C;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.typography-item {
|
||||
border: 1px solid #E6E1DC;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.typography-preview {
|
||||
margin-bottom: 12px;
|
||||
color: #2C180C;
|
||||
}
|
||||
|
||||
.typography-info {
|
||||
font-size: 12px;
|
||||
color: #717B7A;
|
||||
}
|
||||
|
||||
.typography-spec {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.typography-spec-item {
|
||||
background-color: #FAF9F6;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
color: #2C180C;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: #C41E3A;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
margin-right: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #A81A30;
|
||||
}
|
||||
|
||||
.button:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.button.secondary {
|
||||
background-color: #717B7A;
|
||||
}
|
||||
|
||||
.button.secondary:hover {
|
||||
background-color: #5A6362;
|
||||
}
|
||||
|
||||
.summary {
|
||||
background-color: #FAF9F6;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.summary-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #2C180C;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.summary-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.summary-stat {
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.summary-stat-value {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
color: #C41E3A;
|
||||
}
|
||||
|
||||
.summary-stat-label {
|
||||
font-size: 12px;
|
||||
color: #717B7A;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background-color: #E6E1DC;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background-color: #16A38A;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>AIGC 页面像素级对比验证</h1>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">视觉元素还原检查清单</h2>
|
||||
<ul class="checklist" id="visualChecklist">
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">布局结构与设计稿完全一致</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">颜色值精确匹配(误差<1px)</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">字体样式完全一致</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">间距尺寸精确匹配</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">阴影效果一致</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">边框样式精确</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">圆角半径准确</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">图标样式精确还原</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">交互状态还原检查清单</h2>
|
||||
<ul class="checklist" id="interactionChecklist">
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">正常状态显示正确</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">悬停状态效果一致</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">点击状态反馈准确</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">禁用状态样式正确</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">选中状态高亮准确</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">状态切换动画流畅</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">响应式设计检查清单</h2>
|
||||
<ul class="checklist" id="responsiveChecklist">
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">移动端布局完整</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">平板端布局适配</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">桌面端布局完整</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">断点切换流畅</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">字体大小自适应</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
<li class="checklist-item">
|
||||
<div class="checkbox checked"></div>
|
||||
<span class="checklist-text">间距比例保持</span>
|
||||
<span class="status pass">通过</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">颜色规范验证</h2>
|
||||
<div class="color-palette">
|
||||
<div class="color-item">
|
||||
<div class="color-preview" style="background-color: rgba(250, 249, 246, 255);"></div>
|
||||
<div class="color-name">背景色</div>
|
||||
<div class="color-info">rgba(250, 249, 246, 255)</div>
|
||||
</div>
|
||||
<div class="color-item">
|
||||
<div class="color-preview" style="background-color: rgba(255, 255, 255, 255);"></div>
|
||||
<div class="color-name">表面色</div>
|
||||
<div class="color-info">rgba(255, 255, 255, 255)</div>
|
||||
</div>
|
||||
<div class="color-item">
|
||||
<div class="color-preview" style="background-color: rgba(44, 24, 16, 255);"></div>
|
||||
<div class="color-name">主文本色</div>
|
||||
<div class="color-info">rgba(44, 24, 16, 255)</div>
|
||||
</div>
|
||||
<div class="color-item">
|
||||
<div class="color-preview" style="background-color: rgba(113, 113, 122, 255);"></div>
|
||||
<div class="color-name">次要文本色</div>
|
||||
<div class="color-info">rgba(113, 113, 122, 255)</div>
|
||||
</div>
|
||||
<div class="color-item">
|
||||
<div class="color-preview" style="background-color: rgba(196, 30, 58, 255);"></div>
|
||||
<div class="color-name">主题色</div>
|
||||
<div class="color-info">rgba(196, 30, 58, 255)</div>
|
||||
</div>
|
||||
<div class="color-item">
|
||||
<div class="color-preview" style="background-color: rgba(2, 9, 16, 0.13);"></div>
|
||||
<div class="color-name">边框色</div>
|
||||
<div class="color-info">rgba(2, 9, 16, 0.13)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">间距规范验证</h2>
|
||||
<div class="spacing-grid">
|
||||
<div class="spacing-item">
|
||||
<div class="spacing-visual" style="width: 8px;"></div>
|
||||
<div class="spacing-name">极小间距</div>
|
||||
<div class="spacing-info">8px (4rpx)</div>
|
||||
</div>
|
||||
<div class="spacing-item">
|
||||
<div class="spacing-visual" style="width: 16px;"></div>
|
||||
<div class="spacing-name">小间距</div>
|
||||
<div class="spacing-info">16px (8rpx)</div>
|
||||
</div>
|
||||
<div class="spacing-item">
|
||||
<div class="spacing-visual" style="width: 32px;"></div>
|
||||
<div class="spacing-name">中间距</div>
|
||||
<div class="spacing-info">32px (16rpx)</div>
|
||||
</div>
|
||||
<div class="spacing-item">
|
||||
<div class="spacing-visual" style="width: 48px;"></div>
|
||||
<div class="spacing-name">大间距</div>
|
||||
<div class="spacing-info">48px (24rpx)</div>
|
||||
</div>
|
||||
<div class="spacing-item">
|
||||
<div class="spacing-visual" style="width: 64px;"></div>
|
||||
<div class="spacing-name">超大间距</div>
|
||||
<div class="spacing-info">64px (32rpx)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">字体规范验证</h2>
|
||||
<div class="typography-item">
|
||||
<div class="typography-preview" style="font-size: 32px; font-weight: 600;">日历数字</div>
|
||||
<div class="typography-info">
|
||||
<div class="typography-spec">
|
||||
<span class="typography-spec-item">32px</span>
|
||||
<span class="typography-spec-item">600 weight</span>
|
||||
<span class="typography-spec-item">Inter</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="typography-item">
|
||||
<div class="typography-preview" style="font-size: 28px; font-weight: 500;">2026年1月</div>
|
||||
<div class="typography-info">
|
||||
<div class="typography-spec">
|
||||
<span class="typography-spec-item">28px</span>
|
||||
<span class="typography-spec-item">500 weight</span>
|
||||
<span class="typography-spec-item">Inter</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="typography-item">
|
||||
<div class="typography-preview" style="font-size: 24px; font-weight: 500;">日一二三四五六</div>
|
||||
<div class="typography-info">
|
||||
<div class="typography-spec">
|
||||
<span class="typography-spec-item">24px</span>
|
||||
<span class="typography-spec-item">500 weight</span>
|
||||
<span class="typography-spec-item">Inter</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="typography-item">
|
||||
<div class="typography-preview" style="font-size: 20px; font-weight: 400;">农历日期</div>
|
||||
<div class="typography-info">
|
||||
<div class="typography-spec">
|
||||
<span class="typography-spec-item">20px</span>
|
||||
<span class="typography-spec-item">400 weight</span>
|
||||
<span class="typography-spec-item">Microsoft YaHei</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">验证操作</h2>
|
||||
<button class="button" onclick="runFullVerification()">运行完整验证</button>
|
||||
<button class="button secondary" onclick="exportReport()">导出验证报告</button>
|
||||
<button class="button secondary" onclick="openPixsoDesign()">打开设计稿</button>
|
||||
</div>
|
||||
|
||||
<div class="summary">
|
||||
<h2 class="summary-title">验证总结</h2>
|
||||
<div class="summary-stats">
|
||||
<div class="summary-stat">
|
||||
<div class="summary-stat-value" id="totalChecks">20</div>
|
||||
<div class="summary-stat-label">总检查项</div>
|
||||
</div>
|
||||
<div class="summary-stat">
|
||||
<div class="summary-stat-value" id="passedChecks">20</div>
|
||||
<div class="summary-stat-label">通过项</div>
|
||||
</div>
|
||||
<div class="summary-stat">
|
||||
<div class="summary-stat-value" id="failedChecks">0</div>
|
||||
<div class="summary-stat-label">失败项</div>
|
||||
</div>
|
||||
<div class="summary-stat">
|
||||
<div class="summary-stat-value" id="passRate">100%</div>
|
||||
<div class="summary-stat-label">通过率</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" style="width: 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function runFullVerification() {
|
||||
alert('完整验证已启动!\n\n正在检查:\n- 视觉元素还原\n- 交互状态实现\n- 响应式布局适配\n- 像素级精度\n\n验证完成: 100% 匹配');
|
||||
}
|
||||
|
||||
function exportReport() {
|
||||
const report = {
|
||||
timestamp: new Date().toISOString(),
|
||||
visualChecks: {
|
||||
total: 8,
|
||||
passed: 8,
|
||||
failed: 0,
|
||||
passRate: '100%'
|
||||
},
|
||||
interactionChecks: {
|
||||
total: 6,
|
||||
passed: 6,
|
||||
failed: 0,
|
||||
passRate: '100%'
|
||||
},
|
||||
responsiveChecks: {
|
||||
total: 6,
|
||||
passed: 6,
|
||||
failed: 0,
|
||||
passRate: '100%'
|
||||
},
|
||||
overall: {
|
||||
total: 20,
|
||||
passed: 20,
|
||||
failed: 0,
|
||||
passRate: '100%'
|
||||
},
|
||||
conclusion: '所有检查项均通过,实现效果与设计稿达到100%匹配度。'
|
||||
};
|
||||
|
||||
const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'aigc-verification-report.json';
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function openPixsoDesign() {
|
||||
window.open('https://pixso.cn/app/design/8teaTOeN2QkeggcAJdfjMw?item-id=2:582', '_blank');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,307 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>暗黑模式切换测试</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
}
|
||||
|
||||
.light-mode {
|
||||
background-color: #F9FAFB;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.dark-mode {
|
||||
background-color: #1E1E1E;
|
||||
color: #E0E0E0;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #C7A27C 0%, #E8D5B5 100%);
|
||||
border-radius: 12px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.dark-mode .header {
|
||||
background: linear-gradient(135deg, #5B9BD5 0%, #2D2D2D 100%);
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid #E5E7EB;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-bottom: 16px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.dark-mode .card {
|
||||
background-color: #2D2D2D;
|
||||
border-color: #404040;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.dark-mode .card:hover {
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #C7A27C;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.dark-mode .btn-primary {
|
||||
background-color: #5B9BD5;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: scale(1.02);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-primary:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.icon:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.icon:active {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-success {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
|
||||
.dark-mode .status-success {
|
||||
background-color: #1e4620;
|
||||
color: #d4edda;
|
||||
border-color: #2d5a30;
|
||||
}
|
||||
|
||||
.status-error {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
|
||||
.dark-mode .status-error {
|
||||
background-color: #5a1e24;
|
||||
color: #f8d7da;
|
||||
border-color: #7a2a32;
|
||||
}
|
||||
|
||||
.test-section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.test-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid #E5E7EB;
|
||||
}
|
||||
|
||||
.dark-mode .test-item {
|
||||
border-bottom-color: #404040;
|
||||
}
|
||||
|
||||
.test-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.color-swatch {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
border: 2px solid #E5E7EB;
|
||||
}
|
||||
|
||||
.dark-mode .color-swatch {
|
||||
border-color: #404040;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="light-mode">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1 style="margin: 0;">暗黑模式切换测试</h1>
|
||||
<svg class="icon" @click="toggleTheme" id="theme-icon" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-2.98 0-5.4-2.42-5.4-5.4 0-1.81.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div id="status" class="status status-success" style="display: none;">
|
||||
主题切换成功!
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>当前主题状态</h2>
|
||||
<div class="test-section">
|
||||
<div class="test-item">
|
||||
<span>主题模式</span>
|
||||
<strong id="current-theme">浅色模式</strong>
|
||||
</div>
|
||||
<div class="test-item">
|
||||
<span>背景颜色</span>
|
||||
<div class="color-swatch" id="bg-color"></div>
|
||||
</div>
|
||||
<div class="test-item">
|
||||
<span>文字颜色</span>
|
||||
<div class="color-swatch" id="text-color"></div>
|
||||
</div>
|
||||
<div class="test-item">
|
||||
<span>卡片背景</span>
|
||||
<div class="color-swatch" id="card-bg"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>测试说明</h2>
|
||||
<p>点击右上角的月亮图标可以切换暗黑模式。</p>
|
||||
<p>切换后,页面的所有元素应该立即更新为暗黑主题。</p>
|
||||
<p>主题设置会自动保存,下次打开时会恢复上次选择的主题。</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>功能测试</h2>
|
||||
<div class="test-section">
|
||||
<button class="btn btn-primary" onclick="testThemeSwitch()">测试主题切换</button>
|
||||
<button class="btn btn-primary" onclick="testLocalStorage()">测试本地存储</button>
|
||||
<button class="btn btn-primary" onclick="testCSSVariables()">测试CSS变量</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let currentTheme = 'light';
|
||||
|
||||
function toggleTheme() {
|
||||
currentTheme = currentTheme === 'light' ? 'dark' : 'light';
|
||||
applyTheme(currentTheme);
|
||||
showStatus('success', `已切换到${currentTheme === 'dark' ? '深色' : '浅色'}模式`);
|
||||
}
|
||||
|
||||
function applyTheme(theme) {
|
||||
const body = document.body;
|
||||
const themeIcon = document.getElementById('theme-icon');
|
||||
const currentThemeText = document.getElementById('current-theme');
|
||||
const bgColor = document.getElementById('bg-color');
|
||||
const textColor = document.getElementById('text-color');
|
||||
const cardBg = document.getElementById('card-bg');
|
||||
|
||||
if (theme === 'dark') {
|
||||
body.classList.remove('light-mode');
|
||||
body.classList.add('dark-mode');
|
||||
currentThemeText.textContent = '深色模式';
|
||||
bgColor.style.backgroundColor = '#1E1E1E';
|
||||
textColor.style.backgroundColor = '#E0E0E0';
|
||||
cardBg.style.backgroundColor = '#2D2D2D';
|
||||
} else {
|
||||
body.classList.remove('dark-mode');
|
||||
body.classList.add('light-mode');
|
||||
currentThemeText.textContent = '浅色模式';
|
||||
bgColor.style.backgroundColor = '#F9FAFB';
|
||||
textColor.style.backgroundColor = '#333333';
|
||||
cardBg.style.backgroundColor = '#FFFFFF';
|
||||
}
|
||||
|
||||
localStorage.setItem('theme-mode', theme);
|
||||
}
|
||||
|
||||
function showStatus(type, message) {
|
||||
const status = document.getElementById('status');
|
||||
status.className = `status status-${type}`;
|
||||
status.textContent = message;
|
||||
status.style.display = 'block';
|
||||
|
||||
setTimeout(() => {
|
||||
status.style.display = 'none';
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
function testThemeSwitch() {
|
||||
toggleTheme();
|
||||
showStatus('success', '主题切换测试通过!');
|
||||
}
|
||||
|
||||
function testLocalStorage() {
|
||||
const savedTheme = localStorage.getItem('theme-mode');
|
||||
if (savedTheme === currentTheme) {
|
||||
showStatus('success', '本地存储测试通过!');
|
||||
} else {
|
||||
showStatus('error', '本地存储测试失败!');
|
||||
}
|
||||
}
|
||||
|
||||
function testCSSVariables() {
|
||||
const computedStyle = getComputedStyle(document.body);
|
||||
const bgColor = computedStyle.backgroundColor;
|
||||
|
||||
if (currentTheme === 'dark' && bgColor.includes('30') || currentTheme === 'light' && bgColor.includes('250')) {
|
||||
showStatus('success', 'CSS变量测试通过!');
|
||||
} else {
|
||||
showStatus('error', 'CSS变量测试失败!');
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('theme-icon').addEventListener('click', toggleTheme);
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const savedTheme = localStorage.getItem('theme-mode') || 'light';
|
||||
currentTheme = savedTheme;
|
||||
applyTheme(savedTheme);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
import lunarUtils from './src/utils/lunarUtils'
|
||||
|
||||
const testDates = [
|
||||
{ year: 2025, month: 1, day: 28 },
|
||||
{ year: 2025, month: 1, day: 29 },
|
||||
{ year: 2025, month: 1, day: 30 },
|
||||
{ year: 2026, month: 2, day: 16 },
|
||||
{ year: 2026, month: 2, day: 17 },
|
||||
{ year: 2026, month: 2, day: 18 }
|
||||
]
|
||||
|
||||
console.log('=== 农历年切换测试 ===')
|
||||
testDates.forEach(date => {
|
||||
const lunarDate = lunarUtils.solarToLunar(date.year, date.month, date.day)
|
||||
console.log(`${date.year}-${date.month}-${date.day} => ${lunarDate.yearStr} ${lunarDate.monthStr}${lunarDate.dayStr}`)
|
||||
})
|
||||
Reference in New Issue
Block a user