chore(merge): 合并 App 版本管理页面
This commit is contained in:
commit
9fc57e5c1a
2 files changed
+388
No files matched your search
@@ -0,0 +1,65 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import request from '@/axios'
|
||||
|
||||
export interface AppVersionItem {
|
||||
id: string
|
||||
platform: 'ANDROID' | 'IOS'
|
||||
platformText: string
|
||||
versionCode: number
|
||||
versionName: string
|
||||
downloadUrl: string
|
||||
updateContent?: string
|
||||
forceUpdate: number
|
||||
minSupportedCode: number
|
||||
status: number
|
||||
statusText: string
|
||||
releasedAt?: string
|
||||
createdAt?: string
|
||||
}
|
||||
|
||||
export interface AppVersionSaveData {
|
||||
platform: 'ANDROID' | 'IOS'
|
||||
versionCode: number
|
||||
versionName: string
|
||||
downloadUrl: string
|
||||
updateContent?: string
|
||||
forceUpdate?: 0 | 1
|
||||
minSupportedCode?: number
|
||||
}
|
||||
|
||||
export const listAppVersionsApi = (params: {
|
||||
pageIndex?: number
|
||||
pageSize?: number
|
||||
platform?: string
|
||||
status?: number
|
||||
keyword?: string
|
||||
}) => {
|
||||
return request.get<{ total: number; list: AppVersionItem[] }>({
|
||||
url: '/admin/app-versions/page',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const getAppVersionApi = (id: string | number) => {
|
||||
return request.get<AppVersionItem>({ url: `/admin/app-versions/${id}` })
|
||||
}
|
||||
|
||||
export const createAppVersionApi = (data: AppVersionSaveData) => {
|
||||
return request.post<string>({ url: '/admin/app-versions', data })
|
||||
}
|
||||
|
||||
export const updateAppVersionApi = (id: string | number, data: AppVersionSaveData) => {
|
||||
return request.post<void>({ url: `/admin/app-versions/${id}`, data })
|
||||
}
|
||||
|
||||
export const updateAppVersionStatusApi = (id: string | number, status: 0 | 1) => {
|
||||
return request.post<void>({
|
||||
url: `/admin/app-versions/${id}/status`,
|
||||
params: { status }
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteAppVersionApi = (id: string | number) => {
|
||||
return request.delete<void>({ url: `/admin/app-versions/${id}` })
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
<!-- Author: fruit.com.cn -->
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { computed, h, ref } from 'vue'
|
||||
import PageHeading from '@/views/components/PageHeading.vue'
|
||||
import { AppIcons } from '@/constants/icons'
|
||||
import { dateCell } from '@/utils/format'
|
||||
import {
|
||||
createAppVersionApi,
|
||||
deleteAppVersionApi,
|
||||
listAppVersionsApi,
|
||||
updateAppVersionApi,
|
||||
updateAppVersionStatusApi
|
||||
} from '@/api/admin/appVersion'
|
||||
import type { AppVersionItem, AppVersionSaveData } from '@/api/admin/appVersion'
|
||||
|
||||
const platformOptions = [
|
||||
{ label: 'Android', value: 'ANDROID' },
|
||||
{ label: 'iOS', value: 'IOS' }
|
||||
]
|
||||
const statusOptions = [
|
||||
{ label: '已发布', value: 1 },
|
||||
{ label: '已下架', value: 0 }
|
||||
]
|
||||
|
||||
interface FilterState {
|
||||
platform: string
|
||||
status: number | undefined
|
||||
keyword: string
|
||||
}
|
||||
|
||||
const loading = ref(false)
|
||||
const list = ref<AppVersionItem[]>([])
|
||||
const total = ref(0)
|
||||
const pageIndex = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const filter = ref<FilterState>({ platform: '', status: undefined, keyword: '' })
|
||||
|
||||
const dialogOpen = ref(false)
|
||||
const formLoading = ref(false)
|
||||
const editingId = ref<string | null>(null)
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
const emptyForm: AppVersionSaveData = {
|
||||
platform: 'ANDROID',
|
||||
versionCode: 0,
|
||||
versionName: '',
|
||||
downloadUrl: '',
|
||||
updateContent: '',
|
||||
forceUpdate: 0,
|
||||
minSupportedCode: 0
|
||||
}
|
||||
const form = ref<AppVersionSaveData>({ ...emptyForm })
|
||||
|
||||
const rules: FormRules = {
|
||||
platform: [{ required: true, message: '请选择平台', trigger: 'change' }],
|
||||
versionCode: [
|
||||
{ required: true, message: '请填写整数版本号', trigger: 'blur' },
|
||||
{ type: 'number', min: 1, max: 999999, message: '范围 1~999999', trigger: 'blur' }
|
||||
],
|
||||
versionName: [
|
||||
{ required: true, message: '请填写展示版本号', trigger: 'blur' },
|
||||
{ max: 32, message: '最长 32 字符', trigger: 'blur' }
|
||||
],
|
||||
downloadUrl: [
|
||||
{ required: true, message: '请填写下载地址', trigger: 'blur' },
|
||||
{ max: 512, message: '最长 512 字符', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
async function fetchList() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await listAppVersionsApi({
|
||||
pageIndex: pageIndex.value,
|
||||
pageSize: pageSize.value,
|
||||
platform: filter.value.platform || undefined,
|
||||
status: filter.value.status,
|
||||
keyword: filter.value.keyword.trim() || undefined
|
||||
})
|
||||
list.value = res.data?.list ?? []
|
||||
total.value = res.data?.total ?? 0
|
||||
} catch {
|
||||
/* axios interceptor 已弹消息 */
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
editingId.value = null
|
||||
form.value = { ...emptyForm }
|
||||
dialogOpen.value = true
|
||||
}
|
||||
|
||||
function openEdit(row: AppVersionItem) {
|
||||
editingId.value = row.id
|
||||
form.value = {
|
||||
platform: row.platform,
|
||||
versionCode: row.versionCode,
|
||||
versionName: row.versionName,
|
||||
downloadUrl: row.downloadUrl,
|
||||
updateContent: row.updateContent ?? '',
|
||||
forceUpdate: row.forceUpdate,
|
||||
minSupportedCode: row.minSupportedCode
|
||||
}
|
||||
dialogOpen.value = true
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
if (!formRef.value) return
|
||||
await formRef.value.validate(async (valid) => {
|
||||
if (!valid) return
|
||||
formLoading.value = true
|
||||
try {
|
||||
if (editingId.value) {
|
||||
await updateAppVersionApi(editingId.value, form.value)
|
||||
ElMessage.success('已更新')
|
||||
} else {
|
||||
await createAppVersionApi(form.value)
|
||||
ElMessage.success('已创建')
|
||||
}
|
||||
dialogOpen.value = false
|
||||
await fetchList()
|
||||
} catch {
|
||||
/* axios interceptor 已弹消息 */
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function toggleStatus(row: AppVersionItem) {
|
||||
const next = row.status === 1 ? 0 : 1
|
||||
await updateAppVersionStatusApi(row.id, next as 0 | 1)
|
||||
ElMessage.success(next === 1 ? '已发布' : '已下架')
|
||||
await fetchList()
|
||||
}
|
||||
|
||||
async function remove(row: AppVersionItem) {
|
||||
await ElMessageBox.confirm(
|
||||
`确认删除 ${row.platformText} ${row.versionName}(${row.versionCode})?`,
|
||||
'删除版本',
|
||||
{ type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消' }
|
||||
)
|
||||
await deleteAppVersionApi(row.id)
|
||||
ElMessage.success('已删除')
|
||||
await fetchList()
|
||||
}
|
||||
|
||||
const forceText = computed(() => (v: number) => (v === 1 ? '强制' : '可选升级'))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PageHeading eyebrow="后台管理" title="版本管理" description="App 版本发布、上下架与删除。" />
|
||||
|
||||
<section class="toolbar">
|
||||
<el-form :inline="true" :model="filter" @submit.prevent="fetchList">
|
||||
<el-form-item label="平台">
|
||||
<el-select v-model="filter.platform" clearable placeholder="全部" style="width: 120px">
|
||||
<el-option
|
||||
v-for="o in platformOptions"
|
||||
:key="o.value"
|
||||
:label="o.label"
|
||||
:value="o.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="filter.status" clearable placeholder="全部" style="width: 120px">
|
||||
<el-option v-for="o in statusOptions" :key="o.value" :label="o.label" :value="o.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="关键字">
|
||||
<el-input
|
||||
v-model="filter.keyword"
|
||||
placeholder="匹配 versionName"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :icon="AppIcons.search" @click="fetchList">查询</el-button>
|
||||
<el-button plain @click="openCreate" :icon="AppIcons.plus">新建版本</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</section>
|
||||
|
||||
<el-table :data="list" v-loading="loading" border stripe>
|
||||
<el-table-column prop="platformText" label="平台" width="100" />
|
||||
<el-table-column prop="versionCode" label="版本码" width="90" />
|
||||
<el-table-column prop="versionName" label="展示版本" width="120" />
|
||||
<el-table-column prop="downloadUrl" label="下载地址" show-overflow-tooltip min-width="220">
|
||||
<template #default="{ row }">
|
||||
<el-link :href="row.downloadUrl" target="_blank" type="primary" :underline="false">
|
||||
{{ row.downloadUrl }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="forceUpdate" label="升级" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.forceUpdate === 1 ? 'danger' : 'info'" size="small">
|
||||
{{ forceText(row.forceUpdate) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="minSupportedCode" label="兜底最低" width="100" />
|
||||
<el-table-column prop="status" label="状态" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'info'" size="small">
|
||||
{{ row.statusText }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="releasedAt" label="发布时间" width="160" :formatter="dateCell" />
|
||||
<el-table-column prop="createdAt" label="创建时间" width="160" :formatter="dateCell" />
|
||||
<el-table-column label="操作" width="240" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" link type="primary" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button size="small" link type="warning" @click="toggleStatus(row)">
|
||||
{{ row.status === 1 ? '下架' : '发布' }}
|
||||
</el-button>
|
||||
<el-button size="small" link type="danger" @click="remove(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
class="pagination"
|
||||
v-model:current-page="pageIndex"
|
||||
v-model:page-size="pageSize"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
@current-change="fetchList"
|
||||
@size-change="fetchList"
|
||||
/>
|
||||
|
||||
<el-dialog
|
||||
v-model="dialogOpen"
|
||||
:title="editingId ? '编辑版本' : '新建版本'"
|
||||
width="640px"
|
||||
@close="() => formRef?.resetFields()"
|
||||
>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px" v-loading="formLoading">
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="平台" prop="platform">
|
||||
<el-radio-group v-model="form.platform">
|
||||
<el-radio-button value="ANDROID">Android</el-radio-button>
|
||||
<el-radio-button value="IOS">iOS</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="整数版本" prop="versionCode">
|
||||
<el-input-number
|
||||
v-model="form.versionCode"
|
||||
:min="1"
|
||||
:max="999999"
|
||||
controls-position="right"
|
||||
class="w-full"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="展示版本" prop="versionName">
|
||||
<el-input v-model="form.versionName" placeholder="如 1.2.0" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="下载地址" prop="downloadUrl">
|
||||
<el-input v-model="form.downloadUrl" placeholder="Android apk / iOS App Store 链接" />
|
||||
</el-form-item>
|
||||
<el-form-item label="强制升级">
|
||||
<el-switch
|
||||
v-model="form.forceUpdate"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
active-text="强制"
|
||||
inactive-text="可选"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="兜底最低版本">
|
||||
<el-input-number v-model="form.minSupportedCode" :min="0" controls-position="right" />
|
||||
<span class="hint">低于此版本的客户端一律强制升级,0=不开启</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="更新说明">
|
||||
<el-input
|
||||
v-model="form.updateContent"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="换行分隔多条更新内容"
|
||||
maxlength="2000"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogOpen = false">取消</el-button>
|
||||
<el-button type="primary" :loading="formLoading" @click="submit">
|
||||
{{ editingId ? '保存' : '创建并发布' }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.toolbar {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.pagination {
|
||||
margin-top: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.hint {
|
||||
margin-left: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user