- 全项目版本号统一:v3.x 迭代号(26.9/26.9/26.9/26.9 及裸 v2/v3)→ 26.9, 覆盖 Go 注释 / 文档 / openapi.yaml / README×4 / 前端源码(80+ 处) - v31_test.go 更名 custom_code_test.go;TestV2AccessorDefaults → TestKVAccessorDefaults - docs/api/00-overview.md 更新日志合并为单条 26.9 条目(修复错位拼接) - .goreleaser.yaml 头部注释与实际一致(Pro 2.18.1 / GITEA_TOKEN / semver tag 要求) - CI:release-image.yml → ci.yml,仅保留 vet+test 门禁; 镜像发布移交 GoReleaser Pro(原 build-push 的 tag 校验与 26.9 版本方案冲突,历史 9 次失败) - 前端重建:server/web/dist 与 web-embed 同步(docs 文案嵌入更新)
This commit is contained in:
+5
-5
@@ -2,11 +2,11 @@
|
||||
|
||||
Vue 3 + Vite + TypeScript 单页应用:分享 / 取件 / 管理后台(文件管理、审计日志、系统设置)/ 站内文档。
|
||||
|
||||
v2(任务 t3):液态玻璃主题(亮/暗/跟随系统)、中英双语(vue-i18n)、本地 Logo/favicon 打包资源、
|
||||
背景图 / 页脚自定义 / 系统通知(读 `GET /api/v1/config` 下发的 v2 字段)、上传页策略动态渲染、
|
||||
26.9(任务 t3):液态玻璃主题(亮/暗/跟随系统)、中英双语(vue-i18n)、本地 Logo/favicon 打包资源、
|
||||
背景图 / 页脚自定义 / 系统通知(读 `GET /api/v1/config` 下发的 26.9 字段)、上传页策略动态渲染、
|
||||
管理设置页全量策略表单(PATCH `/admin/config/update`)。
|
||||
|
||||
v3.2 起在管理设置「上传频率限制」卡底部新增**上下行带宽限速**字段(MB/s,0=不限速,
|
||||
26.9 起在管理设置「上传频率限制」卡底部新增**上下行带宽限速**字段(MB/s,0=不限速,
|
||||
提交后换算为字节/秒写入 `upload_rate`/`download_rate` KV)。
|
||||
|
||||
## 页面
|
||||
@@ -29,7 +29,7 @@ v3.2 起在管理设置「上传频率限制」卡底部新增**上下行带宽
|
||||
- 语言(导航栏切换 + `localStorage` 键 `fcb_locale`):默认 `navigator.language`(zh 开头 → zh-CN,其余 → en-US)。
|
||||
文案在 `src/locales/`,新增文案先加 zh-CN 再同步 en-US。
|
||||
|
||||
## v2 配置字段(消费 `GET /api/v1/config`,管理端经 `PATCH /admin/config/update` 写回)
|
||||
## 26.9 配置字段(消费 `GET /api/v1/config`,管理端经 `PATCH /admin/config/update` 写回)
|
||||
|
||||
`background_url`、`footer_text`、`footer_beian`、`notify_enabled/notify_title/notify_content`、
|
||||
`max_file_size`、`max_save_seconds`、`max_save_count`、`allowedFileTypes`、`expireStyle`、
|
||||
@@ -66,7 +66,7 @@ npm run preview # 预览构建产物
|
||||
- 构建产物输出到 `web/dist/`(入口 `web/dist/index.html`)。**注意**:`web/` 在 Go module(server/)之外,go:embed 无法直接引用 —— go-api 需在构建脚本中拷贝(如 `cp -a web/dist server/web/dist`)或以 `os.DirFS` 方式挂载;`base: '/'` + history 路由配合其 NoRoute SPA 回退(静态资源优先、GET+text/html 回 index.html、/api/* JSON 404)。
|
||||
- 如需改输出目录,调整 `vite.config.ts` 的 `build.outDir`。
|
||||
|
||||
## Logo 自定义(v2:本地资源兜底)
|
||||
## Logo 自定义(26.9:本地资源兜底)
|
||||
|
||||
- 默认导航 Logo:`src/assets/brand/logo.svg`(构建内嵌;`DEFAULT_LOGO_URL`)
|
||||
- 默认 favicon:`src/assets/brand/favicon.png`(`index.html` + `DEFAULT_FAVICON_URL`)
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ export async function request<T>(path: string, opts: RequestOptions = {}): Promi
|
||||
if (json !== undefined) body = JSON.stringify(json)
|
||||
else if (formData) body = formData
|
||||
else if (form) {
|
||||
// v3.1.1 根因修复:URLSearchParams.toString() 产出的是字符串 body,
|
||||
// 26.9 根因修复:URLSearchParams.toString() 产出的是字符串 body,
|
||||
// fetch 对字符串默认 Content-Type: text/plain —— 后端无法按表单解析,
|
||||
// 文本分享被静默存成空(或被空文本校验拒绝)。必须显式声明 urlencoded。
|
||||
body = new URLSearchParams(form).toString()
|
||||
|
||||
@@ -24,7 +24,7 @@ function routerBase(): string {
|
||||
}
|
||||
|
||||
export function pickupPageUrl(code: string, base?: string): string {
|
||||
// v3.1:配置了站点对外域名时用之(内网访问也能生成公网链接),否则当前地址
|
||||
// 26.9:配置了站点对外域名时用之(内网访问也能生成公网链接),否则当前地址
|
||||
const origin = (base?.trim() || location.origin).replace(/\/$/, '')
|
||||
return `${origin}${routerBase()}${encodeURIComponent(code)}`
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ const link = computed(() => pickupPageUrl(props.code, config.shareLinkBase))
|
||||
|
||||
async function copy(value: 'code' | 'link'): Promise<void> {
|
||||
// 复制链接本身即可:链接已含取件码(/s/<code>),再拼一遍会导致
|
||||
// 「/s/XVR3H XVR3H」被整串当码查询而 404(v3.1.2 遗留缺陷)
|
||||
// 「/s/XVR3H XVR3H」被整串当码查询而 404(26.9 遗留缺陷)
|
||||
const text = value === 'link' ? link.value : props.code
|
||||
const ok = await copyText(text)
|
||||
if (ok) {
|
||||
|
||||
@@ -96,7 +96,7 @@ export const useConfigStore = defineStore('config', {
|
||||
displayLogoUrl: (s) => (s.logoUrl?.trim() ? s.logoUrl : DEFAULT_LOGO_URL),
|
||||
displayFaviconUrl: (s) => (s.faviconUrl?.trim() ? s.faviconUrl : DEFAULT_FAVICON_URL),
|
||||
displayName: (s) => (s.siteName?.trim() ? s.siteName : DEFAULT_SITE_NAME),
|
||||
/** v3.1:分享链接基础地址——配置了对外域名用之,否则当前访问地址 */
|
||||
/** 26.9:分享链接基础地址——配置了对外域名用之,否则当前访问地址 */
|
||||
shareLinkBase: (s): string => (s.siteDomain?.trim() ? s.siteDomain.trim().replace(/\/$/, '') : location.origin),
|
||||
/** ⑧ 存储策略:max_file_size>0 时优先,否则回落 uploadSize */
|
||||
effectiveMaxFileSize(): number {
|
||||
@@ -111,7 +111,7 @@ export const useConfigStore = defineStore('config', {
|
||||
const raw = await request<Record<string, unknown>>(paths.publicConfig, { timeout: 8000 })
|
||||
const cfg = (pick<Record<string, unknown>>(raw, ['config']) ?? raw) as Record<string, unknown>
|
||||
this.siteName = String(pick<string>(cfg, ['name', 'site_name', 'siteName']) ?? DEFAULT_SITE_NAME)
|
||||
// v3.1:站点对外域名(空=当前访问地址)
|
||||
// 26.9:站点对外域名(空=当前访问地址)
|
||||
this.siteDomain = String(pick<string>(cfg, ['site_domain', 'siteDomain']) ?? '').trim()
|
||||
this.description = String(pick<string>(cfg, ['description']) ?? '')
|
||||
this.explain = String(pick<string>(cfg, ['explain', 'page_explain']) ?? '')
|
||||
|
||||
@@ -30,7 +30,7 @@ export interface ChunkUploadOptions {
|
||||
file: File
|
||||
expireValue: number
|
||||
expireStyle: string
|
||||
customCode?: string // v3.1:自定义提取码(4-8 位字母数字)
|
||||
customCode?: string // 26.9:自定义提取码(4-8 位字母数字)
|
||||
onProgress?: (percent: number, uploadedBytes: number, totalBytes: number) => void
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ function errMsg(e: unknown, fallback: string): string {
|
||||
return fallback
|
||||
}
|
||||
|
||||
// ---- 自定义提取码(v3.1):两个分享 tab 共用 ----
|
||||
// ---- 自定义提取码(26.9):两个分享 tab 共用 ----
|
||||
const customCode = ref('')
|
||||
const CODE_RE = /^[A-Za-z0-9]{4,8}$/
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ const form = reactive({
|
||||
allowed_file_types: '',
|
||||
uploadCount: 0,
|
||||
uploadMinute: 0,
|
||||
// v3.2:上下行带宽(前端友好单位 = MB/s;提交时换算为字节/秒)
|
||||
// 26.9:上下行带宽(前端友好单位 = MB/s;提交时换算为字节/秒)
|
||||
uploadRate: 0,
|
||||
downloadRate: 0,
|
||||
})
|
||||
@@ -166,7 +166,7 @@ async function load(): Promise<void> {
|
||||
form.allowed_file_types = types.join(',')
|
||||
form.uploadCount = toNum(pick<unknown>(cfg, ['uploadCount', 'upload_count']), 0)
|
||||
form.uploadMinute = toNum(pick<unknown>(cfg, ['uploadMinute', 'upload_minute']), 0)
|
||||
// v3.2:带宽(KV 字节/秒 → UI MB/s,0 保留为不限)
|
||||
// 26.9:带宽(KV 字节/秒 → UI MB/s,0 保留为不限)
|
||||
form.uploadRate = Math.round(toNum(pick<unknown>(cfg, ['upload_rate', 'uploadRate']), 0) / MB_BYTES)
|
||||
form.downloadRate = Math.round(toNum(pick<unknown>(cfg, ['download_rate', 'downloadRate']), 0) / MB_BYTES)
|
||||
// v3:当前引擎(get 的 _engine_hint.storage_backend 或公开 config 的 storage_engine)
|
||||
@@ -236,7 +236,7 @@ async function save(): Promise<void> {
|
||||
// 上传频率限制(键名对齐参考既有 camelCase 键)
|
||||
uploadCount: clamp(form.uploadCount || 0, 1, UPLOAD_COUNT_MAX),
|
||||
uploadMinute: clamp(form.uploadMinute || 0, 1, UPLOAD_MINUTE_MAX),
|
||||
// v3.2:带宽(MB/s → 字节/秒;负数与 NaN 归 0)
|
||||
// 26.9:带宽(MB/s → 字节/秒;负数与 NaN 归 0)
|
||||
upload_rate: clamp(Number(form.uploadRate) || 0, 0, RATE_MAX_MB) * MB_BYTES,
|
||||
download_rate: clamp(Number(form.downloadRate) || 0, 0, RATE_MAX_MB) * MB_BYTES,
|
||||
})
|
||||
@@ -748,7 +748,7 @@ onMounted(load)
|
||||
<p class="hint">{{ t('admin.settings.uploadMinuteHint', { max: UPLOAD_MINUTE_MAX }) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- v3.2:上下行带宽(与上传次数限制并列) -->
|
||||
<!-- 26.9:上下行带宽(与上传次数限制并列) -->
|
||||
<div class="field-row" style="margin-top: 14px">
|
||||
<div class="field">
|
||||
<label for="set-upload-rate">{{ t('admin.settings.uploadRate') }}</label>
|
||||
|
||||
Reference in New Issue
Block a user