feat(file): 上传预检/秒传/批量处理/预览 + 缩略图(P2-7)与 H5 独立 dev server(P2-9)

缩略图:StorageService.thumbUrl(path, expire, maxLongSide),七牛在**签名之前**拼
imageView2/2/w/h(拼在签名之后必 401),本地存储回退原图,让调用方不必判断当前是什么存储。
实测 1454KB → 36KB(2.4%)、1600×1200 → 400×300。

F-73:秒传原来排在份数检查之后 → 网络重传得到「你超了」而不是「你已经传过了」。

H5 dev server:vite 加 /api 代理 + .env.development 把 VITE_API_BASE 设空串,
改一行不用再 build:h5;三端行为不同(dev→代理 / 构建产物→location.origin / 小程序→写死)。

验证:新增 e2e-m9-thumb.py 12/12(打到真实七牛)、e2e-m8-orgfile 54/54;
H5 与小程序两端构建都验过。
This commit is contained in:
2026-09-30 09:02:56 +08:00
parent 9216bd66d2
commit c8837ee602
10 changed files with 813 additions and 12 deletions
+15
View File
@@ -0,0 +1,15 @@
# H5 开发时的环境变量(`pnpm dev:h5` 用)
#
# ⚠️ 这个文件**只在 dev server 下生效**(vite 的 .env.development 约定)。
# 它是给"独立 dev server"用的,不是给构建产物用的。
#
# 为什么是**空串**:
# `src/config/index.ts` 的逻辑是"优先取 VITE_API_BASE,没有才用 location.origin"。
# dev server 下 location.origin 是 dev server 自己(≠ 后端),
# 所以这里显式设成空 → 走相对路径 → 由 `vite.config.ts` 的 server.proxy
# 把 /api 转发到 http://localhost:8080。
#
# ⚠️ 构建产物(`pnpm build:h5`,由后端托管)**不要**带这个变量 ——
# 那种场景下 location.origin 就是对的(页面与接口同源)。
# 所以它写在这里而不是 .env(后者对所有模式生效)。
VITE_API_BASE = ''
+18 -8
View File
@@ -19,17 +19,27 @@
*/
// #ifdef H5
/**
* H5:用**绝对的同源地址**(`location.origin`),不要用相对路径。
* H5:优先取 `VITE_API_BASE`,没有才用**同源的** `location.origin`。
*
* ⚠️ 实测:`uni.request` 传相对路径(`/api/v1/...`)会以 `request:fail` 失败,
* 而**同样地址用原生 XHR / fetch 是成功的**(都是 200)。也就是说这是 uni-app
* 自身的处理差异,不是服务端或跨域问题 —— 但排查时它长得像"网络不通",
* 很容易往 CORS / 域名 / 后端去查,白绕一大圈。
* ⚠️ 为什么要有 `VITE_API_BASE` 这一档:
*
* 用 `location.origin` 而不是 `''`:既绕开这个差异,又保持**同源**
* (不经过 CORS),换域名也不用改配置。
* `location.origin` 是"与页面同源",这在**后端托管构建产物**时是对的
* (`http://localhost:8080` 上页面与接口同源)。
* 但 `pnpm dev:h5` 起的是一个**独立的 vite dev server**(另一个端口)——
* 那时 `location.origin` 指的是 dev server 自己,接口就打到 dev server 上去了,
* 于是只能"build 一次再看",改一行要等一次完整构建。
*
* 所以 dev server 下把这个值设成**空串**(见 `.env.development`),
* 让请求走相对路径 `/api/...`,再由 vite 的 `server.proxy` 转发到后端。
*
* ⚠️ 另外:`uni.request` **不能**传相对路径(实测会 `request:fail`,
* 而同样地址用原生 XHR/fetch 是 200)。所以空串时补上 `location.origin`,
* 保持"绝对地址"这个前提不变 —— 只是那个 origin 由代理兜住。
*/
export const API_BASE_URL = typeof location !== 'undefined' ? location.origin : ''
const VITE_API_BASE = (import.meta.env?.VITE_API_BASE ?? '') as string
export const API_BASE_URL =
VITE_API_BASE !== '' ? VITE_API_BASE
: (typeof location !== 'undefined' ? location.origin : '')
// #endif
// #ifndef H5
+35 -1
View File
@@ -1,7 +1,41 @@
import { defineConfig } from "vite";
import uni from "@dcloudio/vite-plugin-uni";
// https://vitejs.dev/config/
/**
* 学生端(uni-app)的 vite 配置。
*
* ## ⚠️ 这里加了一个 dev proxy,解决一个真实的开发痛点
*
* H5 **没有独立 dev server** 是之前的状态:`src/config/index.ts` 里
* `API_BASE_URL = location.origin`,而 vite dev server 不代理 `/api`,
* 于是 `pnpm dev:h5`(另一个端口)打不到后端 8080 上的接口 ——
* 只能 `pnpm build:h5` 再访问**后端托管的产物**(http://localhost:8080)。
*
* 后果不只是"麻烦一点":**改一行要等一次完整构建**,而学生端正是改得最多的地方
* (表单、上传、核验页),反馈慢会直接改变开发方式 ——
* 人会倾向"一次改一堆再构建",而那时候出问题已经分不清是哪一处引起的。
*
* 所以这里补两件事(**缺一不可**):
* ① 本文件的 `server.proxy`:把 `/api` 转发到后端;
* ② `src/config/index.ts` 里让 `API_BASE_URL` 优先取 `VITE_API_BASE` ——
* 否则它仍然是 `location.origin`(= dev server 自己),代理配得再对也没用。
*
* ## ⚠️ 微信小程序端不受影响
*
* 小程序的请求不走 vite(它在微信开发者工具里跑),而且微信的
* **request 合法域名是固定白名单**,配不了 localhost —— 所以那边照旧。
* 由 `#ifdef H5` 条件编译保证。
*/
export default defineConfig({
plugins: [uni()],
server: {
port: 5174,
proxy: {
// 与 frontend-pc 同一做法:dev server 上的 /api 转发到本机后端
"/api": {
target: "http://localhost:8080",
changeOrigin: true,
},
},
},
});