feat: 版本号按分支轨道分开管理,版本号配置收归管理员

master/develop 共用 release 轨(App_Store 打包构建号自增),feature 分支走
feature 轨(构建号永不自增,始终使用手填值),两条轨道的 App_Ver 各自独立。

- versions 改为 tracks 结构,旧的单轨/单值配置在加载时自动迁移进 release 轨
- 新增 config["branch_track"] 记录分支归属,缺省按分支名推断,可在分支管理页改
- 新增 PUT /api/config/branches/track;分支名含 / 时走请求体而非路径参数
- 修复 DELETE /api/config/branches/{name} 无法删除含 / 的分支(405)
- 版本号接口不再对普通用户开放,打包设置页整页改为管理员可见

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-07-31 17:56:10 +09:00
co-authored by Claude Opus 5
parent 3cb4053ab5
commit 21542c88d7
10 changed files with 511 additions and 115 deletions
+80 -26
View File
@@ -15,7 +15,7 @@
<li v-if="isAdmin" :class="{ active: tab === 'schemes' }" @click="tab = 'schemes'">Schemes 配置</li>
<li v-if="isAdmin" :class="{ active: tab === 'branches' }" @click="tab = 'branches'">分支管理</li>
<li v-if="isAdmin" :class="{ active: tab === 'upload' }" @click="tab = 'upload'">上传配置</li>
<li :class="{ active: tab === 'build' }" @click="tab = 'build'">打包设置</li>
<li v-if="isAdmin" :class="{ active: tab === 'build' }" @click="tab = 'build'">打包设置</li>
<li v-if="isAdmin" :class="{ active: tab === 'json' }" @click="tab = 'json'">JSON 编辑</li>
</ul>
</div>
@@ -151,7 +151,10 @@
<!-- 分支管理 -->
<div v-if="tab === 'branches'">
<h2>分支管理</h2>
<p style="color: #666; margin-bottom: 16px; font-size: 14px;">管理可打包的代码分支打包时从对应分支目录获取源码</p>
<p style="color: #666; margin-bottom: 16px; font-size: 14px;">
管理可打包的代码分支打包时从对应分支目录获取源码
版本轨道决定该分支使用哪一套版本号上架master/develop 共用App_Store 打包构建号自增自测feature 构建号不自增
</p>
<div style="display: flex; gap: 12px; margin-bottom: 16px;">
<input v-model="newBranch" type="text" placeholder="输入分支名称" style="flex: 1; padding: 10px 12px; border: 1px solid #d9d9d9; border-radius: 6px; font-size: 14px;">
<button class="btn btn-primary" style="width: auto; padding: 10px 24px;" @click="addBranch">添加分支</button>
@@ -160,18 +163,25 @@
<thead>
<tr>
<th>分支名称</th>
<th>版本轨道</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="b in branches" :key="b">
<td><strong>{{ b }}</strong></td>
<td>
<select :value="branchTrack[b] || 'feature'" style="width: auto; padding: 4px 8px;" @change="setBranchTrack(b, $event.target.value)">
<option value="release">上架master/develop</option>
<option value="feature">自测feature</option>
</select>
</td>
<td class="action-btns">
<button class="action-btn delete" @click="deleteBranch(b)">删除</button>
</td>
</tr>
<tr v-if="!branches.length">
<td colspan="2" style="text-align: center; color: #999;">暂无分支配置</td>
<td colspan="3" style="text-align: center; color: #999;">暂无分支配置</td>
</tr>
</tbody>
</table>
@@ -269,21 +279,36 @@
<div v-if="tab === 'build'">
<h2>打包设置</h2>
<div style="max-width: 500px;">
<h4 class="section-title">应用版本号</h4>
<h4 class="section-title">上架版本号master / develop</h4>
<div class="form-group">
<label>App_Ver应用版本</label>
<input type="text" v-model="versions.app_ver" placeholder="2.180.0" @input="onAppVerInput">
<div style="font-size: 12px; color: #999; margin-top: 4px;">格式主版本.次版本.修订号</div>
<input type="text" v-model="versions.release.app_ver" placeholder="2.180.0" @input="onAppVerInput('release')">
<div style="font-size: 12px; color: #999; margin-top: 4px;">格式主版本.次版本.修订号master develop 共用这一套版本号</div>
</div>
<div class="form-group">
<label>Build_Ver构建号</label>
<input type="text" v-model="versions.build_ver" placeholder="2.180.0.0">
<input type="text" v-model="versions.release.build_ver" placeholder="2.180.0.0">
<div style="font-size: 12px; color: #999; margin-top: 4px;">
四段数字前三位须与 App_Ver 一致修改 App_Ver 会自动切换为该版本号已记录的构建号
每个版本号的构建号独立递增App_Store 打包时自动 +1一般无需手动修改
</div>
</div>
<button class="btn btn-primary" style="width: auto; padding: 10px 32px; margin-bottom: 24px;" @click="saveVersions">保存版本号</button>
<button class="btn btn-primary" style="width: auto; padding: 10px 32px; margin-bottom: 24px;" @click="saveVersions('release')">保存上架版本号</button>
<h4 class="section-title">自测版本号feature 分支</h4>
<div class="form-group">
<label>App_Ver应用版本</label>
<input type="text" v-model="versions.feature.app_ver" placeholder="2.180.0" @input="onAppVerInput('feature')">
<div style="font-size: 12px; color: #999; margin-top: 4px;">与上架版本号各自独立互不影响</div>
</div>
<div class="form-group">
<label>Build_Ver构建号</label>
<input type="text" v-model="versions.feature.build_ver" placeholder="2.180.0.0">
<div style="font-size: 12px; color: #999; margin-top: 4px;">
仅供自测打包时<strong>不会自动递增</strong>始终使用这里填写的值默认 .0需要区分时请手动修改
</div>
</div>
<button class="btn btn-primary" style="width: auto; padding: 10px 32px; margin-bottom: 24px;" @click="saveVersions('feature')">保存自测版本号</button>
<template v-if="isAdmin">
<h4 class="section-title">打包参数</h4>
@@ -613,20 +638,30 @@ const servers = ref({})
const branches = ref([])
const newBranch = ref('')
const buildSettings = ref({})
const versions = ref({ app_ver: '', build_ver: '' })
const buildMap = ref({})
// 两条版本轨道:releasemaster/develop,上架用)与 feature(自测用),各自独立
const versions = ref({
release: { app_ver: '', build_ver: '' },
feature: { app_ver: '', build_ver: '' },
})
const buildMap = ref({ release: {}, feature: {} })
const branchTrack = ref({})
const applyVersions = (data) => {
buildMap.value = data.build_map || {}
versions.value = { app_ver: data.app_ver || '', build_ver: data.build_ver || '' }
const tracks = data.tracks || {}
for (const track of ['release', 'feature']) {
const item = tracks[track] || {}
buildMap.value[track] = item.build_map || {}
versions.value[track] = { app_ver: item.app_ver || '', build_ver: item.build_ver || '' }
}
branchTrack.value = data.branch_track || {}
}
// 修改 App_Ver 时,自动把 Build_Ver 切换为该版本号已记录的构建号(新版本号则为 .0)
const onAppVerInput = () => {
const appVer = (versions.value.app_ver || '').trim()
const onAppVerInput = (track) => {
const appVer = (versions.value[track].app_ver || '').trim()
if (/^\d+\.\d+\.\d+$/.test(appVer)) {
const n = Number(buildMap.value[appVer] || 0)
versions.value.build_ver = `${appVer}.${n}`
const n = Number((buildMap.value[track] || {})[appVer] || 0)
versions.value[track].build_ver = `${appVer}.${n}`
}
}
const uploadConfig = ref({ mode: 'oss', oss: {}, webdav: {}, dingtalk: {} })
@@ -659,12 +694,9 @@ onMounted(async () => {
const loadData = async () => {
if (!isAdmin.value) {
const [appsRes, versionsRes] = await Promise.all([
authFetch('/api/config/apps'),
authFetch('/api/config/versions'),
])
// 普通用户只能管理 Apps,版本号等配置均为管理员专属
const appsRes = await authFetch('/api/config/apps')
if (appsRes.ok) apps.value = await appsRes.json()
if (versionsRes.ok) applyVersions(await versionsRes.json())
return
}
@@ -1111,6 +1143,24 @@ const addBranch = async () => {
}
}
const setBranchTrack = async (name, track) => {
try {
const res = await authFetch('/api/config/branches/track', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ branch: name, track }),
})
if (!res.ok) {
const err = await res.json()
throw new Error(err.detail || '修改失败')
}
branchTrack.value = { ...branchTrack.value, [name]: track }
} catch (e) {
alert('修改失败: ' + e.message)
await loadData()
}
}
const deleteBranch = async (name) => {
if (!confirm(`确定删除分支「${name}」?`)) return
try {
@@ -1132,21 +1182,25 @@ const saveBuildSettings = async () => {
alert('设置已保存')
}
const saveVersions = async () => {
const saveVersions = async (track = 'release') => {
try {
const res = await authFetch('/api/config/versions', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
app_ver: versions.value.app_ver,
build_ver: versions.value.build_ver,
track,
app_ver: versions.value[track].app_ver,
build_ver: versions.value[track].build_ver,
}),
})
if (res.ok) {
const data = await res.json()
// 回写该版本号最新的构建号,保持本地 build_map 与服务端一致
buildMap.value = { ...buildMap.value, [data.app_ver]: Number((data.build_ver.split('.')[3]) || 0) }
versions.value = { app_ver: data.app_ver, build_ver: data.build_ver }
buildMap.value[track] = {
...(buildMap.value[track] || {}),
[data.app_ver]: Number((data.build_ver.split('.')[3]) || 0),
}
versions.value[track] = { app_ver: data.app_ver, build_ver: data.build_ver }
alert('版本号已保存')
} else {
const err = await res.json()