first commit

This commit is contained in:
2026-09-16 15:04:08 +08:00
commit 43d095ea0f
117 changed files with 31982 additions and 0 deletions
+191
View File
@@ -0,0 +1,191 @@
<!--
* 平台软件
-->
<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>