fe17886ac4
- 车辆 / 洗车 / 加油 / 充电 / 保养 / 保险 完整 CRUD + 软删 - AI 截图识别(5 类型 OCR schema):OpenAI 兼容 + MiniMax M3 - 化学品 / Grocy 实例对接 + 库存镜像同步 - 仪表盘:30 天频次 + 健康度 + 同比环比 + 油价趋势 + 年均养护 - 月度报表:Excel 6 sheet + PDF - PWA:manifest / SW / 离线缓存 / iOS 引导 - 安全:bcrypt + CSRF + 登录锁定(IP/用户/全局三级)+ 401 自动跳登录 + 表单草稿 - 高 ROI 8 功能:里程/提醒/成本/搜索/标签/通知/同比/成就 - 3 个新 migration(0016/0017/0018)+ 18 个迁移全幂等 - 101/101 测试通过(含 ipRateLimit / CSRF / retry / stats / tags / notifications) - 部署:宝塔面板文档 + PM2 + Nginx
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,
|
|
},
|
|
});
|