|
|
@@ -12,6 +12,7 @@
|
|
|
<div class="versions-section">
|
|
|
<div class="toolbar">
|
|
|
<el-button type="primary" :icon="Plus" @click="openCreateVersion">新建版本</el-button>
|
|
|
+ <el-button type="default" @click="openUpdateFilesDialog">管理自动更新软件</el-button>
|
|
|
<el-select
|
|
|
v-model="versionPlatformFilter"
|
|
|
placeholder="平台"
|
|
|
@@ -128,6 +129,12 @@
|
|
|
</template>
|
|
|
</el-upload>
|
|
|
<span v-if="versionForm.fileName" class="file-name">{{ versionForm.fileName }}</span>
|
|
|
+ <el-progress
|
|
|
+ v-if="versionUploadPercent >= 0"
|
|
|
+ :percentage="versionUploadPercent"
|
|
|
+ style="margin-top: 8px;"
|
|
|
+ :status="versionUploadPercent === 100 ? 'success' : undefined"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="显示名称">
|
|
|
<el-input v-model="versionForm.version_name" placeholder="选填,默认同版本号" />
|
|
|
@@ -179,6 +186,75 @@
|
|
|
</template>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+ <!-- 管理自动更新软件(update 桶) -->
|
|
|
+ <el-dialog v-model="updateFilesDialogVisible" title="管理自动更新软件" width="680px" @open="onUpdateFilesDialogOpen">
|
|
|
+ <div class="update-files-dialog">
|
|
|
+ <el-form-item label="操作系统" required>
|
|
|
+ <el-select
|
|
|
+ v-model="updateFilesPlatform"
|
|
|
+ placeholder="请先选择操作系统"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="fetchUpdateFilesList"
|
|
|
+ >
|
|
|
+ <el-option label="Android" value="android" />
|
|
|
+ <el-option label="安卓手机" value="android_phone" />
|
|
|
+ <el-option label="安卓平板" value="android_tablet" />
|
|
|
+ <el-option label="iOS" value="ios" />
|
|
|
+ <el-option label="iPadOS" value="ipados" />
|
|
|
+ <el-option label="Windows" value="windows" />
|
|
|
+ <el-option label="macOS" value="macos" />
|
|
|
+ <el-option label="鸿蒙电脑" value="harmonyos_pc" />
|
|
|
+ <el-option label="鸿蒙平板" value="harmonyos_pad" />
|
|
|
+ <el-option label="鸿蒙手机" value="harmonyos_phone" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <template v-if="updateFilesPlatform">
|
|
|
+ <div v-if="updateFilesBaseUrl" class="update-base-url-wrap">
|
|
|
+ <span class="label">更新目录 URL(Electron generic 可填):</span>
|
|
|
+ <el-input v-model="updateFilesBaseUrl" readonly>
|
|
|
+ <template #append>
|
|
|
+ <el-button @click="copyUpdateUrl(updateFilesBaseUrl)">复制</el-button>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </div>
|
|
|
+ <div class="update-files-upload">
|
|
|
+ <el-upload
|
|
|
+ :auto-upload="false"
|
|
|
+ :show-file-list="false"
|
|
|
+ :on-change="onUpdateFileSelect"
|
|
|
+ :disabled="updateFileUploadPercent >= 0"
|
|
|
+ accept="*"
|
|
|
+ >
|
|
|
+ <el-button type="primary" :loading="updateFileUploadPercent >= 0">上传文件</el-button>
|
|
|
+ </el-upload>
|
|
|
+ <el-progress
|
|
|
+ v-if="updateFileUploadPercent >= 0"
|
|
|
+ :percentage="updateFileUploadPercent"
|
|
|
+ style="margin-top: 8px;"
|
|
|
+ :status="updateFileUploadPercent === 100 ? 'success' : undefined"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <el-table v-loading="updateFilesLoading" :data="updateFilesList" border stripe style="width: 100%; margin-top: 12px;">
|
|
|
+ <el-table-column prop="name" label="文件名" min-width="180" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="size" label="大小" width="100">
|
|
|
+ <template #default="scope">{{ formatSize(scope.row.size) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="URL" width="100">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button link type="primary" @click="copyUpdateUrl(scope.row.url)">复制</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="80">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button link type="danger" @click="handleDeleteUpdateFile(scope.row)">删除</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <el-empty v-if="!updateFilesLoading && updateFilesList.length === 0" description="暂无文件,请上传" style="margin-top: 16px;" />
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -194,8 +270,12 @@ import {
|
|
|
createVersion,
|
|
|
updateVersion,
|
|
|
deleteVersion,
|
|
|
+ listUpdateFiles,
|
|
|
+ uploadUpdateFile,
|
|
|
+ deleteUpdateFile,
|
|
|
ClientDistribution,
|
|
|
- ClientVersion
|
|
|
+ ClientVersion,
|
|
|
+ type UpdateFileItem
|
|
|
} from '../../api/clientDistributions'
|
|
|
|
|
|
const route = useRoute()
|
|
|
@@ -218,6 +298,18 @@ const createStep = ref(1)
|
|
|
/** 用于提示“当前该平台最新版本”,打开新建弹窗时拉取 */
|
|
|
const allVersionsForHint = ref<ClientVersion[]>([])
|
|
|
|
|
|
+/** 新建版本上传进度:-1 不显示,0-100 显示百分比 */
|
|
|
+const versionUploadPercent = ref(-1)
|
|
|
+/** 管理自动更新文件上传进度:-1 不显示,0-100 显示百分比 */
|
|
|
+const updateFileUploadPercent = ref(-1)
|
|
|
+
|
|
|
+/** 管理自动更新软件弹窗 */
|
|
|
+const updateFilesDialogVisible = ref(false)
|
|
|
+const updateFilesPlatform = ref('')
|
|
|
+const updateFilesList = ref<UpdateFileItem[]>([])
|
|
|
+const updateFilesBaseUrl = ref('')
|
|
|
+const updateFilesLoading = ref(false)
|
|
|
+
|
|
|
const versionForm = ref({
|
|
|
version_code: '',
|
|
|
version_name: '',
|
|
|
@@ -296,6 +388,7 @@ const onVersionSearch = () => {
|
|
|
const openCreateVersion = async () => {
|
|
|
versionEditId.value = null
|
|
|
createStep.value = 1
|
|
|
+ versionUploadPercent.value = -1
|
|
|
versionForm.value = { version_code: '', version_name: '', release_notes: '', platform: '', fileName: '' }
|
|
|
selectedFile.value = null
|
|
|
uploadRef.value?.clearFiles()
|
|
|
@@ -376,8 +469,11 @@ const handleVersionSubmit = async () => {
|
|
|
if (versionForm.value.version_name) formData.append('version_name', versionForm.value.version_name)
|
|
|
if (versionForm.value.release_notes) formData.append('release_notes', versionForm.value.release_notes)
|
|
|
versionSubmitting.value = true
|
|
|
+ versionUploadPercent.value = 0
|
|
|
try {
|
|
|
- await createVersion(distId.value, formData)
|
|
|
+ await createVersion(distId.value, formData, {
|
|
|
+ onUploadProgress: (p) => { versionUploadPercent.value = p }
|
|
|
+ })
|
|
|
ElMessage.success('创建成功')
|
|
|
versionDialogVisible.value = false
|
|
|
fetchVersions()
|
|
|
@@ -386,6 +482,7 @@ const handleVersionSubmit = async () => {
|
|
|
ElMessage.error(typeof msg === 'string' ? msg : JSON.stringify(msg))
|
|
|
} finally {
|
|
|
versionSubmitting.value = false
|
|
|
+ versionUploadPercent.value = -1
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -399,6 +496,66 @@ const handleDeleteVersion = async (row: ClientVersion) => {
|
|
|
} catch {}
|
|
|
}
|
|
|
|
|
|
+const openUpdateFilesDialog = () => {
|
|
|
+ updateFilesPlatform.value = ''
|
|
|
+ updateFilesList.value = []
|
|
|
+ updateFilesBaseUrl.value = ''
|
|
|
+ updateFilesDialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const onUpdateFilesDialogOpen = () => {
|
|
|
+ updateFilesPlatform.value = ''
|
|
|
+ updateFilesList.value = []
|
|
|
+ updateFilesBaseUrl.value = ''
|
|
|
+}
|
|
|
+
|
|
|
+const fetchUpdateFilesList = async () => {
|
|
|
+ if (!distId.value || !updateFilesPlatform.value) return
|
|
|
+ updateFilesLoading.value = true
|
|
|
+ try {
|
|
|
+ const res = await listUpdateFiles(distId.value, updateFilesPlatform.value)
|
|
|
+ updateFilesList.value = res.data.files || []
|
|
|
+ updateFilesBaseUrl.value = res.data.base_url || ''
|
|
|
+ } catch {
|
|
|
+ updateFilesList.value = []
|
|
|
+ updateFilesBaseUrl.value = ''
|
|
|
+ } finally {
|
|
|
+ updateFilesLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const onUpdateFileSelect = async (file: { raw?: File }) => {
|
|
|
+ if (!file.raw || !distId.value || !updateFilesPlatform.value) return
|
|
|
+ updateFileUploadPercent.value = 0
|
|
|
+ try {
|
|
|
+ await uploadUpdateFile(distId.value, updateFilesPlatform.value, file.raw, {
|
|
|
+ onUploadProgress: (p) => { updateFileUploadPercent.value = p }
|
|
|
+ })
|
|
|
+ ElMessage.success('上传成功')
|
|
|
+ await fetchUpdateFilesList()
|
|
|
+ } catch (err: any) {
|
|
|
+ const msg = err?.response?.data?.detail || err?.message || '上传失败'
|
|
|
+ ElMessage.error(typeof msg === 'string' ? msg : JSON.stringify(msg))
|
|
|
+ } finally {
|
|
|
+ updateFileUploadPercent.value = -1
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleDeleteUpdateFile = async (row: UpdateFileItem) => {
|
|
|
+ await ElMessageBox.confirm(`确定删除 ${row.name}?`, '确认删除', { type: 'warning' })
|
|
|
+ if (!distId.value || !updateFilesPlatform.value) return
|
|
|
+ try {
|
|
|
+ await deleteUpdateFile(distId.value, updateFilesPlatform.value, row.key)
|
|
|
+ ElMessage.success('已删除')
|
|
|
+ await fetchUpdateFilesList()
|
|
|
+ } catch {}
|
|
|
+}
|
|
|
+
|
|
|
+const copyUpdateUrl = (url: string) => {
|
|
|
+ if (!url) return
|
|
|
+ navigator.clipboard.writeText(url).then(() => ElMessage.success('已复制到剪贴板')).catch(() => ElMessage.error('复制失败'))
|
|
|
+}
|
|
|
+
|
|
|
onMounted(() => {
|
|
|
fetchDetail()
|
|
|
fetchVersions()
|
|
|
@@ -458,4 +615,19 @@ onMounted(() => {
|
|
|
margin-top: 4px;
|
|
|
color: #909399;
|
|
|
}
|
|
|
+
|
|
|
+.update-files-dialog .label {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #606266;
|
|
|
+}
|
|
|
+.update-base-url-wrap {
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+.update-base-url-wrap .label {
|
|
|
+ display: block;
|
|
|
+ margin-bottom: 6px;
|
|
|
+}
|
|
|
+.update-files-upload {
|
|
|
+ margin-bottom: 8px;
|
|
|
+}
|
|
|
</style>
|