65b0bb04f8
把 CarLog v2.8 全套源码 + 配置导入到 i 仓库作为 baseline: - server/src/ (13 个路由 + middleware + services + config) - server/migrations/ (0001~0018 共 18 个迁移 + mysql) - server/test/ (12 文件 101 测试) - client/src/ (20 个 view + components + stores + api + composables) - client/public/ + client/scripts/ - 全部配置文件 (.editorconfig, .eslintrc.json, .prettierrc.json, vitest.config.js, lighthouserc.json, .pa11yci.json, package.json, carlog-init.sql) - .husky/pre-commit (git hooks) - docs/install/ (宝塔部署文档) 不含: - node_modules/ (本地 npm install) - .env (敏感, 走 .env.example) - *.zip / *.log / *.sqlite / .DS_Store 新增文档 docs/DEV-PLAN.md: - Phase 1: 平台基座 (019 migration + 3 个 platform 路由 + 3 个 view) - Phase 2: CarLog 子系统化 (后端 routes/ → subsystems/carlog/ + 前端 views/ → views/subsystems/carlog/ + 元数据驱动菜单) - Phase 3: 验证 (测试 + E2E + DB 完整性) - 交付清单 + commit 模板 + 给 Mavis review 的材料 后续 Trae 实施, 提交后我 code review + 跑测试。
159 lines
6.2 KiB
JavaScript
159 lines
6.2 KiB
JavaScript
import { defineConfig } from 'vite';
|
|
import vue from '@vitejs/plugin-vue';
|
|
import { VitePWA } from 'vite-plugin-pwa';
|
|
import path from 'node:path';
|
|
|
|
export default defineConfig({
|
|
plugins: [
|
|
vue(),
|
|
VitePWA({
|
|
registerType: 'autoUpdate',
|
|
injectRegister: 'auto', // 自动注入 register 脚本
|
|
includeAssets: ['favicon-16x16.png', 'favicon-32x32.png'],
|
|
manifest: {
|
|
id: '/',
|
|
name: 'CarLog 洗车管理系统',
|
|
short_name: 'CarLog',
|
|
description: '记录洗车/加油/充电/保养/保险/车品的全能车辆账本',
|
|
lang: 'zh-CN',
|
|
dir: 'ltr',
|
|
start_url: '/',
|
|
scope: '/',
|
|
display: 'standalone',
|
|
orientation: 'portrait',
|
|
background_color: '#F5F8FC',
|
|
theme_color: '#1B6EF3',
|
|
categories: ['productivity', 'lifestyle', 'utilities'],
|
|
icons: [
|
|
{ src: '/pwa/pwa-192x192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
|
|
{ src: '/pwa/pwa-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
|
|
{
|
|
src: '/pwa/pwa-maskable-512x512.png',
|
|
sizes: '512x512',
|
|
type: 'image/png',
|
|
purpose: 'maskable',
|
|
},
|
|
{
|
|
src: '/pwa/apple-touch-icon.png',
|
|
sizes: '180x180',
|
|
type: 'image/png',
|
|
purpose: 'any',
|
|
},
|
|
],
|
|
shortcuts: [
|
|
{
|
|
name: '拍照录入',
|
|
short_name: '拍照',
|
|
url: '/washes/new?capture=1',
|
|
description: '打开相机快速拍照记录',
|
|
},
|
|
{
|
|
name: '新建洗车',
|
|
short_name: '洗车',
|
|
url: '/washes/new',
|
|
description: '快速记录一次洗车',
|
|
},
|
|
{
|
|
name: '新建加油',
|
|
short_name: '加油',
|
|
url: '/refuel/new',
|
|
description: '快速记录一次加油',
|
|
},
|
|
{
|
|
name: '新建保养',
|
|
short_name: '保养',
|
|
url: '/maintenance/new',
|
|
description: '快速记录一次保养',
|
|
},
|
|
],
|
|
// 拍照快捷方式行为
|
|
capture_links: ['/washes/new?capture=1'],
|
|
launch_handler: { client_mode: 'auto' },
|
|
},
|
|
workbox: {
|
|
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2,webmanifest}'],
|
|
navigateFallback: '/index.html',
|
|
navigateFallbackDenylist: [/^\/api/, /^\/uploads/],
|
|
runtimeCaching: [
|
|
{
|
|
urlPattern: ({ url }) => url.pathname.startsWith('/api/') && url.pathname.includes('/static'),
|
|
handler: 'CacheFirst',
|
|
options: {
|
|
cacheName: 'api-static',
|
|
expiration: { maxEntries: 200, maxAgeSeconds: 60 * 60 * 24 * 30 },
|
|
},
|
|
},
|
|
{
|
|
urlPattern: ({ url }) => url.pathname.startsWith('/uploads/'),
|
|
handler: 'CacheFirst',
|
|
options: {
|
|
cacheName: 'uploads',
|
|
expiration: { maxEntries: 200, maxAgeSeconds: 60 * 60 * 24 * 30 },
|
|
},
|
|
},
|
|
{
|
|
urlPattern: ({ request }) => request.destination === 'image',
|
|
handler: 'StaleWhileRevalidate',
|
|
options: { cacheName: 'images' },
|
|
},
|
|
{
|
|
urlPattern: ({ request }) => request.destination === 'font',
|
|
handler: 'CacheFirst',
|
|
options: { cacheName: 'fonts' },
|
|
},
|
|
],
|
|
},
|
|
devOptions: {
|
|
enabled: false, // dev 不开 SW(避免 HMR 干扰)
|
|
},
|
|
}),
|
|
],
|
|
resolve: {
|
|
alias: {
|
|
'@': path.resolve(__dirname, './src'),
|
|
},
|
|
},
|
|
server: {
|
|
port: 5173,
|
|
proxy: {
|
|
'/api': {
|
|
target: 'http://127.0.0.1:8787',
|
|
changeOrigin: true,
|
|
},
|
|
},
|
|
},
|
|
optimizeDeps: {
|
|
include: ['vue', 'pinia', 'axios', 'chart.js/auto'],
|
|
},
|
|
build: {
|
|
outDir: 'dist',
|
|
emptyOutDir: true,
|
|
cssCodeSplit: true,
|
|
target: 'es2018',
|
|
rollupOptions: {
|
|
output: {
|
|
manualChunks(id) {
|
|
if (id.includes('node_modules')) {
|
|
if (id.includes('chart.js') || id.includes('vue-chartjs')) return 'chart';
|
|
if (id.includes('echarts') || id.includes('zrender')) return 'chart';
|
|
if (id.includes('workbox') || id.includes('vite-plugin-pwa')) return 'pwa';
|
|
if (id.includes('vue') || id.includes('pinia') || id.includes('@vue')) return 'vue';
|
|
if (
|
|
id.includes('axios') ||
|
|
id.includes('dayjs') ||
|
|
id.includes('dompurify')
|
|
)
|
|
return 'utils';
|
|
return 'vendor';
|
|
}
|
|
},
|
|
// 文件名带 hash,长期缓存友好
|
|
entryFileNames: 'assets/[name]-[hash].js',
|
|
chunkFileNames: 'assets/[name]-[hash].js',
|
|
assetFileNames: 'assets/[name]-[hash][extname]',
|
|
},
|
|
},
|
|
chunkSizeWarningLimit: 600,
|
|
},
|
|
});
|