Browse Source

feat: add download version management

Pchen. 1 week ago
parent
commit
b0b0504b13
3 changed files with 302 additions and 1 deletions
  1. 15 1
      src/api/download.js
  2. 278 0
      src/pages/admin/downloadVersion/index.vue
  3. 9 0
      src/router/index.js

+ 15 - 1
src/api/download.js

@@ -3,7 +3,9 @@ import request from '../utils/request'
 const api = {
   DownloadCenter: '/Public/GetDownloadCenter',
   AdminDownloadList: '/Admin/Download/List',
-  AdminDownload: '/Admin/Download'
+  AdminDownload: '/Admin/Download',
+  AdminDownloadVersionList: '/Admin/DownloadVersion/List',
+  AdminDownloadVersion: '/Admin/DownloadVersion'
 }
 
 export function getDownloadCenter() {
@@ -21,3 +23,15 @@ export function saveAdminDownload(data) {
 export function deleteAdminDownload(data) {
   return request({ url: api.AdminDownload, method: 'delete', data })
 }
+
+export function getAdminDownloadVersionList(params) {
+  return request({ url: api.AdminDownloadVersionList, method: 'get', params })
+}
+
+export function saveAdminDownloadVersion(data) {
+  return request({ url: api.AdminDownloadVersion, method: 'post', data })
+}
+
+export function deleteAdminDownloadVersion(data) {
+  return request({ url: api.AdminDownloadVersion, method: 'delete', data })
+}

+ 278 - 0
src/pages/admin/downloadVersion/index.vue

@@ -0,0 +1,278 @@
+<template>
+  <div class="store-page">
+    <Breadcrumb />
+    <a-card title="下载版本管理">
+      <div class="toolbar">
+        <a-space>
+          <a-select v-model="filters.client_type" allow-clear placeholder="客户端类型" style="width: 180px" @change="getList">
+            <a-option value="windows">Windows 客户端</a-option>
+            <a-option value="android">Android App</a-option>
+            <a-option value="ios">iOS App</a-option>
+            <a-option value="macos">macOS 客户端</a-option>
+          </a-select>
+          <a-button @click="getList">刷新</a-button>
+        </a-space>
+        <a-button type="primary" @click="openCreate">新增版本</a-button>
+      </div>
+
+      <a-table :bordered="false" :data="data" :loading="loading" :pagination="false" row-key="id">
+        <template #columns>
+          <a-table-column title="客户端" data-index="client_type" :width="110" />
+          <a-table-column title="版本" :width="150">
+            <template #cell="{ record }">
+              <div class="version-cell">
+                <strong>{{ record.version_name }}</strong>
+                <span>code {{ record.version_code || 0 }}</span>
+              </div>
+            </template>
+          </a-table-column>
+          <a-table-column title="标题" data-index="title" :width="180" ellipsis tooltip />
+          <a-table-column title="下载地址" data-index="download_url" ellipsis tooltip />
+          <a-table-column title="SHA256" data-index="sha256" :width="160" ellipsis tooltip />
+          <a-table-column title="策略" :width="150">
+            <template #cell="{ record }">
+              <a-space wrap>
+                <a-tag :color="record.force_update ? 'red' : 'gray'">{{ record.force_update ? '强制' : '可选' }}</a-tag>
+                <a-tag :color="record.silent_update ? 'blue' : 'gray'">{{ record.silent_update ? '静默' : '提示' }}</a-tag>
+              </a-space>
+            </template>
+          </a-table-column>
+          <a-table-column title="状态" :width="90">
+            <template #cell="{ record }">
+              <a-tag :color="record.is_active ? 'green' : 'gray'">{{ record.is_active ? '发布' : '停用' }}</a-tag>
+            </template>
+          </a-table-column>
+          <a-table-column title="操作" :width="160" fixed="right">
+            <template #cell="{ record }">
+              <a-space>
+                <a-button size="mini" @click="openEdit(record)">编辑</a-button>
+                <a-button status="danger" size="mini" @click="remove(record)">删除</a-button>
+              </a-space>
+            </template>
+          </a-table-column>
+        </template>
+      </a-table>
+    </a-card>
+
+    <a-modal v-model:visible="editorVisible" :title="form.id ? '编辑版本' : '新增版本'" width="760px" @before-ok="submitEditor">
+      <a-form :model="form" layout="vertical">
+        <a-row :gutter="16">
+          <a-col :span="12">
+            <a-form-item label="客户端类型" required>
+              <a-select v-model="form.client_type" placeholder="选择客户端类型">
+                <a-option value="windows">Windows 客户端</a-option>
+                <a-option value="android">Android App</a-option>
+                <a-option value="ios">iOS App</a-option>
+                <a-option value="macos">macOS 客户端</a-option>
+              </a-select>
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="标题">
+              <a-input v-model="form.title" allow-clear placeholder="例如:Windows 客户端 1.0.1" />
+            </a-form-item>
+          </a-col>
+        </a-row>
+
+        <a-row :gutter="16">
+          <a-col :span="12">
+            <a-form-item label="版本名称" required>
+              <a-input v-model="form.version_name" allow-clear placeholder="1.0.1" />
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="版本码">
+              <a-input-number v-model="form.version_code" mode="button" :min="0" />
+            </a-form-item>
+          </a-col>
+        </a-row>
+
+        <a-form-item label="下载地址" required>
+          <a-input v-model="form.download_url" allow-clear />
+        </a-form-item>
+
+        <a-row :gutter="16">
+          <a-col :span="12">
+            <a-form-item label="SHA256">
+              <a-input v-model="form.sha256" allow-clear placeholder="建议填写,用于客户端校验安装包完整性" />
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="文件大小(字节)">
+              <a-input-number v-model="form.file_size" mode="button" :min="0" />
+            </a-form-item>
+          </a-col>
+        </a-row>
+
+        <a-row :gutter="16">
+          <a-col :span="8">
+            <a-form-item label="整包/热更新">
+              <a-select v-model="form.package_type">
+                <a-option :value="0">整包</a-option>
+                <a-option :value="1">热更新</a-option>
+              </a-select>
+            </a-form-item>
+          </a-col>
+          <a-col :span="8">
+            <a-form-item label="排序">
+              <a-input-number v-model="form.sort_order" mode="button" />
+            </a-form-item>
+          </a-col>
+          <a-col :span="8">
+            <a-form-item label="最低支持版本">
+              <a-input v-model="form.min_supported_version" allow-clear placeholder="可选" />
+            </a-form-item>
+          </a-col>
+        </a-row>
+
+        <a-space class="switch-row">
+          <span>发布</span>
+          <a-switch v-model="form.is_active" :checked-value="1" :unchecked-value="0" />
+          <span>强制更新</span>
+          <a-switch v-model="form.force_update" :checked-value="1" :unchecked-value="0" />
+          <span>静默下载</span>
+          <a-switch v-model="form.silent_update" :checked-value="1" :unchecked-value="0" />
+        </a-space>
+
+        <a-form-item label="更新日志">
+          <a-textarea v-model="form.release_notes" :auto-size="{ minRows: 3 }" />
+        </a-form-item>
+        <a-form-item label="更新日志 HTML">
+          <a-textarea v-model="form.release_notes_html" :auto-size="{ minRows: 3 }" placeholder="可选,兼容 uni-app 更新弹窗" />
+        </a-form-item>
+      </a-form>
+    </a-modal>
+  </div>
+</template>
+
+<script setup>
+import { reactive, ref, onMounted } from 'vue'
+import { Modal, Notification, Message } from '@arco-design/web-vue'
+import {
+  getAdminDownloadVersionList,
+  saveAdminDownloadVersion,
+  deleteAdminDownloadVersion
+} from '@/api/download'
+
+const loading = ref(false)
+const data = ref([])
+const editorVisible = ref(false)
+const filters = reactive({ client_type: '' })
+
+const defaultForm = () => ({
+  id: null,
+  client_type: 'windows',
+  version_name: '',
+  version_code: 0,
+  title: '',
+  download_url: '',
+  sha256: '',
+  file_size: 0,
+  package_type: 0,
+  force_update: 0,
+  silent_update: 0,
+  is_active: 1,
+  release_notes: '',
+  release_notes_html: '',
+  min_supported_version: '',
+  sort_order: 0
+})
+
+const form = reactive(defaultForm())
+
+const resetForm = (values = {}) => {
+  Object.assign(form, defaultForm(), values)
+}
+
+const getList = async () => {
+  loading.value = true
+  try {
+    const res = await getAdminDownloadVersionList({
+      client_type: filters.client_type || ''
+    })
+    if (!res || res.code !== 0) {
+      Notification.error({ title: '获取列表失败', content: res?.msg ?? '请稍后再试' })
+      return
+    }
+    data.value = res.data || []
+  } finally {
+    loading.value = false
+  }
+}
+
+const openCreate = () => {
+  resetForm({ client_type: filters.client_type || 'windows' })
+  editorVisible.value = true
+}
+
+const openEdit = (record) => {
+  resetForm({
+    ...record,
+    version_code: Number(record.version_code) || 0,
+    file_size: Number(record.file_size) || 0,
+    package_type: Number(record.package_type) || 0,
+    force_update: Number(record.force_update) || 0,
+    silent_update: Number(record.silent_update) || 0,
+    is_active: Number(record.is_active) === 0 ? 0 : 1,
+    sort_order: Number(record.sort_order) || 0
+  })
+  editorVisible.value = true
+}
+
+const submitEditor = async () => {
+  if (!form.client_type || !form.version_name || !form.download_url) {
+    Message.error('请填写客户端类型、版本名称和下载地址')
+    return false
+  }
+  const res = await saveAdminDownloadVersion({ ...form })
+  if (!res || res.code !== 0) {
+    Notification.error({ title: '保存失败', content: res?.msg ?? '请稍后再试' })
+    return false
+  }
+  Message.success('保存成功')
+  getList()
+  return true
+}
+
+const remove = (record) => {
+  Modal.confirm({
+    title: '删除下载版本',
+    content: `确定删除 ${record.client_type} ${record.version_name} 吗?`,
+    onOk: async () => {
+      const res = await deleteAdminDownloadVersion({ id: record.id })
+      if (!res || res.code !== 0) {
+        Notification.error({ title: '删除失败', content: res?.msg ?? '请稍后再试' })
+        return
+      }
+      Message.success('删除成功')
+      getList()
+    }
+  })
+}
+
+onMounted(getList)
+</script>
+
+<style scoped>
+.toolbar {
+  display: flex;
+  justify-content: space-between;
+  gap: 16px;
+  margin-bottom: 12px;
+}
+
+.version-cell {
+  display: flex;
+  flex-direction: column;
+  gap: 2px;
+}
+
+.version-cell span {
+  color: var(--color-text-3);
+  font-size: 12px;
+}
+
+.switch-row {
+  margin: 4px 0 18px;
+}
+</style>

+ 9 - 0
src/router/index.js

@@ -484,6 +484,15 @@ const routes = [
                     title: '下载项管理',
                     permission: ['admin', 'service']
                 }
+            },
+            {
+                path: 'downloadVersion',
+                name: 'noticeManage.downloadVersion',
+                component: () => import('../pages/admin/downloadVersion/index.vue'),
+                meta: {
+                    title: '下载版本管理',
+                    permission: ['admin', 'service']
+                }
             }
         ]
     },