Refactor frontend to Vue 3 + Vite + Pinia + Cypress E2E

- Replace single-file 8441-line HTML with Vue 3 SPA
- Pinia stores: auth, oils, recipes, diary, ui
- Composables: useApi, useDialog, useSmartPaste, useOilTranslation
- 6 shared components: RecipeCard, RecipeDetailOverlay, TagPicker, etc.
- 9 page views: RecipeSearch, RecipeManager, Inventory, OilReference, etc.
- 14 Cypress E2E test specs (113 tests), all passing
- Multi-stage Dockerfile (Node build + Python runtime)
- Demo video generation scripts (TTS + subtitles + screen recording)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-06 18:35:00 +00:00
parent 0368e85abe
commit ee8ec23dc7
62 changed files with 15035 additions and 8448 deletions

View File

@@ -0,0 +1,69 @@
describe('Navigation & Routing', () => {
const ADMIN_TOKEN = 'c86ae7afbe10fabe3c1d5e1a7fee74feaadfd5dc7be2ab62'
it('direct URL /oils loads oil reference page', () => {
cy.visit('/oils')
cy.contains('精油价目').should('be.visible')
})
it('direct URL / loads search page', () => {
cy.visit('/')
cy.get('input[placeholder*="搜索"]', { timeout: 8000 }).should('be.visible')
})
it('unknown route still renders the app', () => {
cy.visit('/nonexistent-page')
cy.get('.app-header').should('be.visible')
cy.get('.nav-tabs').should('be.visible')
})
it('back button works between tabs', () => {
cy.visit('/')
cy.get('.nav-tab').contains('精油价目').click()
cy.url().should('include', '/oils')
cy.go('back')
cy.url().should('not.include', '/oils')
})
it('tab active state tracks after click', () => {
cy.visit('/')
cy.get('.nav-tab').contains('精油价目').click()
cy.get('.nav-tab').contains('精油价目').should('have.class', 'active')
cy.get('.nav-tab').contains('配方查询').should('not.have.class', 'active')
})
it('admin tabs only visible when authenticated', () => {
cy.visit('/')
cy.get('.nav-tab').contains('操作日志').should('not.exist')
cy.get('.nav-tab').contains('用户管理').should('not.exist')
})
it('admin tabs appear after login', () => {
cy.visit('/', {
onBeforeLoad(win) {
win.localStorage.setItem('oil_auth_token', ADMIN_TOKEN)
}
})
cy.get('.nav-tab', { timeout: 10000 }).contains('操作日志').should('be.visible')
cy.get('.nav-tab').contains('用户管理').should('be.visible')
})
it('all admin pages are navigable', () => {
cy.visit('/', {
onBeforeLoad(win) {
win.localStorage.setItem('oil_auth_token', ADMIN_TOKEN)
}
})
const pages = [
{ tab: '管理配方', url: '/manage' },
{ tab: '个人库存', url: '/inventory' },
{ tab: '精油价目', url: '/oils' },
{ tab: '操作日志', url: '/audit' },
{ tab: '用户管理', url: '/users' },
]
pages.forEach(({ tab, url }) => {
cy.get('.nav-tab').contains(tab).click()
cy.url().should('include', url)
})
})
})