Files
form-center-front/vite.config.ts
T
chenxing e5555efb79
build / build (push) Successful in 1m47s
fix(p93): 附件/图片回显 URL 用错挂载点——/static/* 404,应为 {ctx}/upload/*
- 160 演练实测(honor_student 证书图片上传后):/static/form/** 404(146B)、
  /form-center/upload/form/** 200 且字节数一致——/static 是平台主题通道保留前缀,
  附件实际挂载在 {ctx}/upload/(Caddy 静态路由 + 后端 upload 映射)。
- 修复两处 URL 构建:fields/image/pc.vue 回显 img src → `${serviceHost}/upload/{key}`;
  AnswerValueView.vue fileLinks 前缀 → `${serviceHost}/upload/`(serviceHost 空=dev
  同源相对,vite 代理补 /upload → localhost:8086)。
- 附带发现记 P92:字段级 maxSize 仅前端 beforeUpload 拦截,/api/form/upload 对
  5.7MB 文件返回成功(上传端点不感知表单 schema 的字段约束)——裁决口径见报告。

验证:vue-tsc --noEmit 干净;vite build 通过。160 端回显修复随下次部署生效。
2026-10-16 00:04:02 +08:00

147 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { ConfigEnv, UserConfig, defineConfig } from "vite";
import { createHtmlPlugin } from "vite-plugin-html";
import vue from "@vitejs/plugin-vue";
import path, { resolve } from "path";
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import { viteMockServe } from "vite-plugin-mock";
import ViteAutoImport from "unplugin-auto-import/vite";
import UnoCSS from "@unocss/vite";
import presetUno from "@unocss/preset-uno";
import presetRemToPx from "@unocss/preset-rem-to-px";
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
// 构建产物直出 dist/(notice / flow-center 同口径):projman 前端发布要求 zip 根含 index.html,
// 不得再套 dist/base 中间层;dev(serve) 不写产物,两者共用该值无冲突
const outDir = "dist";
return {
plugins: [
vue(),
createSvgIconsPlugin({
iconDirs: [resolve(process.cwd(), "src/assets/icons")],
symbolId: "icon-[dir]-[name]",
inject: "body-last",
customDomId: "__svg__icons__dom__",
}),
viteMockServe({
mockPath: "./src/assets/mock",
// 仅 --mode mock 时启用 mock 中间件;dev/联调/正式均走真实后端
localEnabled: mode === "mock",
}),
ViteAutoImport({
imports: [
"vue",
"vue-router",
{
"view-ui-plus": ["Button", "Table", "Modal", "Message", "Form", "FormItem", "Input", "Select"],
},
{
from: "alova/client",
imports: ["usePagination", "useRequest", "useWatcher", "useForm"],
},
],
dts: "./src/auto-imports.d.ts",
dirs: ["src/api"], // 自动导入 api hooks
}),
createHtmlPlugin({
minify: true,
pages: [
{
entry: "src/main.ts",
template: "index.html",
filename: "index.html",
injectOptions: {
data: {
title: "表单中心",
injectScript: ``,
},
},
},
],
}),
UnoCSS({
presets: [presetUno(), presetRemToPx({ baseFontSize: 4 })],
shortcuts: {
"flex-center": "flex items-center justify-center",
btn: "px-16px py-8px rounded-4px cursor-pointer transition-all duration-200",
card: "bg-white rounded-8px shadow-md p-16px",
"text-ellipsis": "truncate",
"text-multi-ellipsis": "line-clamp-2",
},
theme: {
fontSize: {
xs: "12px",
sm: "14px",
base: "16px",
lg: "18px",
xl: "20px",
"2xl": "24px",
},
},
}),
// 主题接入已免插件化(A 类,规范 §2.1):唯一接入点 = index.html 的 loader;
// 下面的 /static 代理只是 dev 联调便利(prod 同源用不到),删除不影响线上主题
],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
server: {
host: "localhost",
// 本地 dev 端口(执行手册 §5 P6:service=8086 / front=8089)
port: 8089,
proxy: {
// dev 正式模式:serviceHost 为空(同源相对路径 /api/...),经此代理转发到本地后端
// (后端 CAS 过滤器对 /api/* 不回 CORS 头,浏览器直连 8086 会被拦,必须同源代理)
"/api": {
target: "http://localhost:8086",
changeOrigin: true,
},
// 附件/图片回显同源代理(P93:URL={serviceHost}/upload/{key},dev 下 serviceHost 空)
"/upload": {
target: "http://localhost:8086",
changeOrigin: true,
},
// 主题通道本地联调:dev 下 loader 走同源相对,manifest/CSS 经此代理到中央通道。
// 联调本地通道源:YschTheme 侧 npm run serve:static(默认 9527),再用
// YH_THEME_PROXY_TARGET=http://localhost:9527 覆盖本 target
"/static": {
target: process.env.YH_THEME_PROXY_TARGET || "http://base.yunhaixingtong.com",
changeOrigin: true,
},
},
},
base: "./",
build: {
target: "modules",
outDir,
assetsDir: "assets",
sourcemap: false,
minify: "esbuild",
chunkSizeWarningLimit: 1000,
cssTarget: "chrome61", // 安卓微信 webview 不支持 #RGBA 十六进制颜色
rollupOptions: {
input: {
main: resolve("index.html"),
},
output: {
chunkFileNames: "static/js/[name]-[hash].js",
entryFileNames: "static/js/[name]-[hash].js",
assetFileNames: (assetInfo: any) => {
if (assetInfo.type === "asset" && /\.(jpe?g|png|gif|svg)$/i.test(assetInfo.name)) {
return "static/img/[name].[hash][ext]";
}
if (assetInfo.type === "asset" && /\.(ttf|woff|woff2|eot)$/i.test(assetInfo.name)) {
return "static/fonts/[name].[hash][ext]";
}
return "static/[ext]/[name]-[hash].[ext]";
},
},
manualEntrypoint: "index.html",
},
},
};
});