fix: FilePreview fileType case + Tailwind v4 gradient transparent bug

- FilePreview.vue: add normalizedFileType computed to handle backend
  returning uppercase HTML/MD/PPTX (fixes preview/download buttons)
- FilePreview.vue: bg-gradient-to-r from-orange-500 -> bg-orange-500
  (Tailwind v4 gradient + CSS variable = transparent)
- ReportCard.vue: bg-gradient-to-r -> bg-orange-600 for selected state
- Add .opencode/, node_modules/, dist/ to .gitignore
- Initial git setup for publish project
This commit is contained in:
2026-05-24 20:09:42 +08:00
commit b9137204a0
78 changed files with 12950 additions and 0 deletions
+119
View File
@@ -0,0 +1,119 @@
import { test, expect } from '@playwright/test'
/**
* Responsive Layout E2E Tests
* Tests responsive design for PC (1920px) and Mobile (375px) viewports
* Note: Sidebar (<aside>) exists only on ProjectDetail.vue, not ProjectList.vue
*/
test.describe('Responsive Layout', () => {
test('should display main content on PC width (1920px)', async ({ page }) => {
// Set PC viewport
await page.setViewportSize({ width: 1920, height: 1080 })
await page.goto('/')
await page.waitForLoadState('networkidle')
await page.waitForTimeout(1000)
// Main content should be visible
const mainContent = page.locator('main')
await expect(mainContent).toBeVisible({ timeout: 15000 })
// Page title should be visible
await expect(page.locator('text=选择项目')).toBeVisible({ timeout: 5000 })
// Projects should be displayed
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 5000 })
})
test('should display stats cards on mobile width (375px)', async ({ page }) => {
// Set mobile viewport
await page.setViewportSize({ width: 375, height: 667 })
await page.goto('/')
await page.waitForLoadState('networkidle')
await page.waitForTimeout(1000)
// Main content should be visible
const mainContent = page.locator('main')
await expect(mainContent).toBeVisible({ timeout: 10000 })
// Stats cards should be visible
await expect(page.locator('text=个项目')).toBeVisible({ timeout: 5000 })
})
test('should display project cards in single column on mobile', async ({ page }) => {
// Set mobile viewport
await page.setViewportSize({ width: 375, height: 667 })
await page.goto('/')
await page.waitForLoadState('networkidle')
await page.waitForTimeout(1000)
// Project cards should still be visible
await expect(page.locator('text=项目一').first()).toBeVisible({ timeout: 15000 })
})
test('should navigate to project detail on mobile', async ({ page }) => {
// Set mobile viewport
await page.setViewportSize({ width: 375, height: 667 })
await page.goto('/')
await page.waitForLoadState('networkidle')
await page.waitForTimeout(1000)
// Click on a project to navigate to detail (use force to bypass overlay)
await page.locator('text=项目一').first().click({ force: true })
// Should navigate to project detail
await expect(page).toHaveURL(/\/project\/\d+/, { timeout: 10000 })
})
test('should display sidebar in project detail on PC width', async ({ page }) => {
// Set PC viewport
await page.setViewportSize({ width: 1920, height: 1080 })
// First go to home page to load mock data
await page.goto('/')
await page.waitForLoadState('networkidle')
await page.waitForTimeout(1000)
// Navigate to project detail via click
await page.locator('text=项目一').first().click({ force: true })
await page.waitForURL(/\/project\/\d+/, { timeout: 10000 })
// Wait for the sidebar to appear
await expect(page.locator('text=返回项目列表')).toBeVisible({ timeout: 15000 })
})
test('should display reports list in project detail on mobile width', async ({ page }) => {
// Set mobile viewport
await page.setViewportSize({ width: 375, height: 667 })
// First go to home page to load mock data
await page.goto('/')
await page.waitForLoadState('networkidle')
await page.waitForTimeout(1000)
// Navigate to project detail via click
await page.locator('text=项目一').first().click({ force: true })
await page.waitForURL(/\/project\/\d+/, { timeout: 10000 })
// Reports should be visible (from mock data)
await expect(page.locator('text=2026-05-22 日报.html')).toBeVisible({ timeout: 5000 })
})
test('should close sidebar after navigation on mobile', async ({ page }) => {
// Set mobile viewport
await page.setViewportSize({ width: 375, height: 667 })
await page.goto('/project/1')
await page.waitForLoadState('networkidle')
await page.waitForTimeout(1000)
// Navigate back to project list
await page.locator('text=返回项目列表').click()
// Should be back on project list page
await expect(page).toHaveURL(/\/$/, { timeout: 10000 })
})
})