192 lines
5.4 KiB
Vue
192 lines
5.4 KiB
Vue
<!--
|
|
* 平台软件
|
|
-->
|
|
<template>
|
|
<view class="sub-page">
|
|
<nav-bar title="平台软件" :show-back="true" @back="closeSub()" />
|
|
|
|
<scroll-view scroll-y class="page-content">
|
|
<!-- Clients -->
|
|
<text class="section-title">客户端下载</text>
|
|
|
|
<view class="client-card" v-for="(item, i) in downloads" :key="i">
|
|
<view class="cc-left">
|
|
<view class="cc-icon-wrap" :style="{ background: item.platform === 'windows' ? '#d1fae5' : '#dbeafe' }">
|
|
<text class="cc-icon">{{ item.platform === 'windows' ? '🪟' : '🐧' }}</text>
|
|
</view>
|
|
<view class="cc-info">
|
|
<text class="cc-name">{{ item.name }}</text>
|
|
<text class="cc-version">v{{ item.version }}</text>
|
|
<text class="cc-downloads">{{ formatThousands(item.download_count) }} 次下载</text>
|
|
</view>
|
|
</view>
|
|
<view class="cc-btn" @click="downloadFile(item)">下载</view>
|
|
</view>
|
|
|
|
<view v-if="downloads.length === 0" class="client-card" style="justify-content:center;">
|
|
<text style="color:#999;font-size:26rpx;">暂无可用客户端</text>
|
|
</view>
|
|
<view class="load-more" v-if="hasMore" @click="loadMore">
|
|
<text>{{ loadingMore ? '加载中...' : '加载更多' }}</text>
|
|
</view>
|
|
|
|
<!-- Changelog -->
|
|
<view class="changelog-card" v-if="allChangelogs.length">
|
|
<text class="section-title">更新日志</text>
|
|
|
|
<view v-for="(log, i) in allChangelogs" :key="i" class="cl-item">
|
|
<view class="cl-header">
|
|
<text class="cl-version">{{ log.version }}</text>
|
|
<text class="cl-date">{{ log.date }}</text>
|
|
</view>
|
|
<text class="cl-change">{{ log.changelog }}</text>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import NavBar from '@/components/NavBar.vue'
|
|
import { ref, computed } from 'vue'
|
|
import { onLoad } from '@dcloudio/uni-app'
|
|
import { closeSub, parseSubParams } from '@/composables/useNavigation'
|
|
import { formatThousands } from '@/utils/util'
|
|
import request from '@/utils/request'
|
|
|
|
const downloads = ref([])
|
|
const page = ref(1)
|
|
const hasMore = ref(false)
|
|
const loadingMore = ref(false)
|
|
|
|
onLoad(async (options) => {
|
|
const params = parseSubParams(options)
|
|
await fetchDownloads(1)
|
|
})
|
|
|
|
const fetchDownloads = async (pageNum = 1) => {
|
|
try {
|
|
const res = await request.get('/c/downloads', { page: pageNum, showLoading: false })
|
|
const items = res.data?.items || []
|
|
downloads.value = pageNum === 1 ? items : downloads.value.concat(items)
|
|
page.value = res.data?.current_page || pageNum
|
|
hasMore.value = page.value < (res.data?.last_page || 1)
|
|
} catch (e) { /* 忽略 */ }
|
|
}
|
|
|
|
const loadMore = async () => {
|
|
if (!hasMore.value || loadingMore.value) return
|
|
loadingMore.value = true
|
|
try { await fetchDownloads(page.value + 1) } finally { loadingMore.value = false }
|
|
}
|
|
|
|
const goBack = () => closeSub()
|
|
|
|
const downloadFile = (item) => {
|
|
if (item.file_path) {
|
|
uni.showToast({ title: '开始下载 ' + item.name, icon: 'none' })
|
|
} else {
|
|
uni.showToast({ title: '暂无下载链接', icon: 'none' })
|
|
}
|
|
}
|
|
|
|
// 合并所有软件的更新日志,按日期倒序
|
|
const allChangelogs = computed(() => {
|
|
const logs = []
|
|
downloads.value.forEach(d => {
|
|
if (d.changelogs && d.changelogs.length) {
|
|
d.changelogs.forEach(v => logs.push({ ...v, software: d.name }))
|
|
}
|
|
})
|
|
logs.sort((a, b) => (b.date || '').localeCompare(a.date || ''))
|
|
return logs
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.sub-page {
|
|
min-height: 100vh;
|
|
background: #F2F4F8;
|
|
}
|
|
.load-more { text-align: center; padding: 24rpx 0 40rpx; font-size: 24rpx; color: #1677FF; }
|
|
.page-content {
|
|
height: calc(100vh - 44px);
|
|
padding: 20rpx 30rpx 40rpx;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 30rpx;
|
|
font-weight: 600;
|
|
color: #333;
|
|
display: block;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.client-card {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
background: #fff;
|
|
border-radius: 16rpx;
|
|
padding: 30rpx;
|
|
margin-bottom: 16rpx;
|
|
.cc-left { display: flex; align-items: center; gap: 20rpx; flex: 1; }
|
|
.cc-icon-wrap {
|
|
width: 72rpx; height: 72rpx; border-radius: 36rpx;
|
|
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
|
|
}
|
|
.cc-icon { font-size: 32rpx; }
|
|
.cc-info { flex: 1; }
|
|
.cc-name { font-size: 28rpx; font-weight: 500; color: #333; display: block; }
|
|
.cc-version { font-size: 22rpx; color: #999; display: block; margin: 4rpx 0; }
|
|
.cc-downloads { font-size: 22rpx; color: #999; }
|
|
.cc-btn {
|
|
padding: 12rpx 36rpx;
|
|
background: linear-gradient(135deg, #3b82f6, #2563eb);
|
|
color: #fff;
|
|
font-size: 26rpx;
|
|
border-radius: 32rpx;
|
|
flex-shrink: 0;
|
|
}
|
|
}
|
|
|
|
.changelog-card {
|
|
background: #fff;
|
|
border-radius: 16rpx;
|
|
padding: 30rpx;
|
|
margin-top: 20rpx;
|
|
.cl-item {
|
|
margin-bottom: 24rpx;
|
|
&:last-child { margin-bottom: 0; }
|
|
}
|
|
.cl-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16rpx;
|
|
margin-bottom: 12rpx;
|
|
}
|
|
.cl-version {
|
|
font-size: 28rpx; font-weight: 600; color: #3b82f6;
|
|
}
|
|
.cl-date { font-size: 22rpx; color: #999; }
|
|
.cl-change {
|
|
display: block;
|
|
font-size: 26rpx;
|
|
color: #666;
|
|
padding: 6rpx 0;
|
|
padding-left: 20rpx;
|
|
position: relative;
|
|
&::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 0;
|
|
top: 16rpx;
|
|
width: 8rpx;
|
|
height: 8rpx;
|
|
background: #d1d5db;
|
|
border-radius: 4rpx;
|
|
}
|
|
}
|
|
}
|
|
</style>
|