更新:游客模式、我的湖分、我的证书

This commit is contained in:
2025-12-09 14:28:02 +08:00
parent 79aa8e3fe6
commit 66b004d6bf
18 changed files with 702 additions and 60 deletions

View File

@@ -0,0 +1,134 @@
<template>
<view class="certificate-page">
<nav-bar :title="$t('user.certificate')"></nav-bar>
<view v-if="certificateList.length > 0">
<view style="margin: 10rpx;" >{{certificateList.length}}个证书</view>
<view class="certificate-list" v-for="(item,index) in certificateList" :key="index">
<view class="certificate-list-row">
<h3>证书编号{{item.bh}}</h3>
<text style="font-size: 26rpx; color: #999;">获得时间{{item.time}}</text>
</view>
<view class="certificate-certificate">
<view class="img" v-for="(i,index) in item.certificateUrl" :key="index">
<image @click="preveImg(i.url)" :src="i.url" mode="heightFix"></image>
</view>
<view class="certificate-detailed" @click="detailed(item)">详细信息</view>
</view>
</view>
</view>
<view v-else><wd-divider>您还未获得证书</wd-divider></view>
</view>
<wd-popup v-model="detailedState" position="bottom" :closeable="true">
<view class="detailed">
<view class="detailed-text">
证书详情
</view>
<view class="detailed-row">证书类型<text class="text">{{detailedData.a}}</text></view>
<view class="detailed-row">获得时间<text class="text">{{detailedData.time}}</text></view>
<view class="detailed-row">获得途径<text class="text">{{detailedData.b}}</text></view>
</view>
</wd-popup>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const detailedState = ref(false)
const detailedData = ref({})
// 模拟的 certificateList 数据
const certificateList = ref([
{ a: 'ZH', b: '吴门', bh: 1, time: '2025-6-20', certificateUrl: "https://ehh-private-01.oss-cn-beijing.aliyuncs.com/certificate/ca2140c3-d212-4d4e-9203-ddc161d50470.jpg,https://ehh-private-01.oss-cn-beijing.aliyuncs.com/certificate/18a7ea22-b75a-4ef6-9109-f448f45e424f.jpg" },
{ bh: 2, time: '2025-6-22', certificateUrl: "https://ehh-private-01.oss-cn-beijing.aliyuncs.com/certificate/ca2140c3-d212-4d4e-9203-ddc161d50470.jpg,https://ehh-private-01.oss-cn-beijing.aliyuncs.com/certificate/18a7ea22-b75a-4ef6-9109-f448f45e424f.jpg" }
]);
// 重新获取数据
certificateList.value = certificateList.value.map(item => {
return { ...item, certificateUrl: item.certificateUrl.split(',').map(url => ({ url })) };
});
/**
* 查看证书详情信息
*/
const detailed = (item) => {
detailedState.value = true
detailedData.value = item
}
/**
* 查看照片
*/
const preveImg = (url : any) => {
uni.previewImage({
urls: [url],
current: 0
});
}
</script>
<style lang="scss" scoped>
.certificate-page {
min-height: 100vh;
background-color: #f7faf9;
}
.certificate-list {
background: #fff;
border-radius: 15rpx;
overflow: hidden;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
margin: 20rpx;
padding: 20rpx;
}
.certificate-list-row {
display: flex;
justify-content: space-between;
}
.certificate-certificate {
display: flex;
align-items: center;
justify-content: space-between;
padding-top: 20rpx;
.img {
width: 36%;
overflow: hidden;
height: 300rpx;
image {
width: 100%;
height: 100%;
}
}
.certificate-detailed {
color: #55aaff;
border: #55aaff 1px solid;
padding: 10rpx 20rpx;
border-radius: 15rpx;
}
}
.detailed {
padding: 20rpx;
.detailed-text {
text-align: center;
margin-bottom: 40rpx;
}
.detailed-row {
color: #999;
margin: 20rpx 0;
font-size: 26rpx;
.text {
color: #000;
}
}
}
</style>

View File

@@ -0,0 +1,113 @@
<template>
<z-paging ref="paging" v-model="bookList" auto-show-back-to-top class="my-book-page" @query="hufenList"
:default-page-size="10">
<template #top>
<!-- 自定义导航栏 -->
<nav-bar :title="$t('user.hufenRecord')"></nav-bar>
</template>
<view class="recharge-record" v-if="(bookList && bookList.length > 0)">
<view class="go-gecharge">{{hufenData.nameValue}} {{$t('user.hufenRecord')}}</view>
<view class="recharge-record-block" v-for="(item, index) in bookList" :key="index">
<view class="recharge-record-block-row">{{item.createTime}}<text class="text">{{item.score}}</text>
</view>
<view class="time">{{item.detail}}</view>
</view>
</view>
</z-paging>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import { useUserStore } from '@/stores/user'
import { getUserContributionByTypeList } from '@/api/modules/user'
import { copyToClipboard } from '@/utils/index'
const { t } = useI18n()
const paging = ref<any>()
const userStore = useUserStore()
// 数据状态
const bookList = ref([])
const loading = ref(false)
const firstLoad = ref(true)
const hufenData = ref('')
// 湖分记录
async function hufenList(pageNo : number, pageSize : number) {
loading.value = true
try {
const res = await getUserContributionByTypeList(pageNo, pageSize, hufenData.value.type)
console.log(res, 'res');
paging.value.complete(res.list.records)
} catch (error) {
paging.value.complete(false)
console.error('Failed to load book list:', error)
} finally {
firstLoad.value = false
loading.value = false
}
}
onLoad((options) => {
hufenData.value = options
console.log(hufenData);
});
</script>
<style lang="scss" scoped>
.my-book-page {
background: #f7faf9;
min-height: 100vh;
.recharge-record {
background: #fff;
border-radius: 15rpx;
overflow: hidden;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
// padding: 20rpx;
margin: 20rpx;
.go-gecharge {
text-align: center;
background: linear-gradient(to right, #007bff, #17a2b8);
font-size: 30rpx;
font-weight: bold;
color: #fff;
padding: 20rpx;
margin-bottom: 20rpx;
}
.title {
font-size: 30rpx;
padding-left: 20rpx;
margin-bottom: 30rpx;
color: #007bff;
font-weight: bold;
}
.recharge-record-block {
border-bottom: 1px solid #e0e0e0;
padding: 20rpx;
.time {
font-size: 24rpx;
margin-bottom: 20rpx;
color: #343434
}
.recharge-record-block-row {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
// font-weight: 700;
color: #909090;
.text {
color: #007bff;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,69 @@
<template>
<view class="recharge-page">
<nav-bar :title="$t('user.iHufen')"></nav-bar>
<view class="menu-section" v-if="hufenList.list.length > 0">
<wd-cell-group border class="menu-list">
<wd-cell v-for="item in hufenList.list" :key="item.type" :title="item.dict_value" is-link
@click="handleMenuClick(item)">
<text class="menu-list-hufen">{{item.score}}</text><text class="menu-list-hufen-text">{{$t('user.hufen')}}</text>
</wd-cell>
</wd-cell-group>
</view>
<view v-else><wd-divider>您还未获得湖分</wd-divider></view>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { getUserContributionData } from '@/api/modules/user'
const { t } = useI18n()
const hufenList = ref([])
/**
* 获取用户湖分
*/
const getHufen = async () => {
hufenList.value = await getUserContributionData()
console.log(hufenList.value.list)
}
const handleMenuClick = (item) => {
uni.navigateTo({
url: `/pages/user/hufen/forDetails?type=${item.type}&nameValue=${item.dict_value}`
})
}
onMounted(() => {
getHufen()
})
</script>
<style lang="scss" scoped>
.recharge-page {
min-height: 100vh;
background-color: #f7faf9;
}
.menu-section {
padding: 20rpx 20rpx;
}
.menu-list {
background: #fff;
border-radius: 15rpx;
overflow: hidden;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
.menu-list-hufen {
font-size: 36rpx;
color: #007bff;
margin-right: 6rpx;
}
.menu-list-hufen-text {
color: #007bff;
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<view class="user-page" :style="{ paddingTop: getNotchHeight() + 30 + 'px' }">
<view class="user-page" :style="{ paddingTop: getNotchHeight() + 30 + 'px' }" v-if="userStore.token">
<!-- 设置图标 -->
<view class="settings-icon" :style="{ top: getNotchHeight() + 30 + 'px' }" @click="goSettings">
<wd-icon name="setting1" size="24px" color="#666" />
@@ -23,18 +23,23 @@
<view class="vip-card-title">{{ $t('user.vip') }}</view>
<view class="vip-card-content">
<view class="vip-item-list">
<view v-if="vipInfo?.length > 0" v-for="vip in vipInfo">{{ vipTypeDict[vip.type] }}{{ parseTime(vip.endTime, '{y}-{m}-{d}') }} 截止</view>
<view v-if="vipInfo?.length > 0" v-for="vip in vipInfo">
{{ vipTypeDict[vip.type] }}{{ parseTime(vip.endTime, '{y}-{m}-{d}') }} 截止</view>
<view v-else>办理课程VIP畅享更多权益</view>
</view>
<wd-button v-if="vipInfo?.length > 0" plain type="primary" size="small" @click="goCourseVipSub">{{ $t('vip.renewal') }}</wd-button>
<wd-button v-else plain type="primary" size="small" @click="goCourseVipSub">{{ $t('vip.openVip') }}</wd-button>
<wd-button v-if="vipInfo?.length > 0" plain type="primary" size="small"
@click="goCourseVipSub">{{ $t('vip.renewal') }}</wd-button>
<wd-button v-else plain type="primary" size="small"
@click="goCourseVipSub">{{ $t('vip.openVip') }}</wd-button>
</view>
<view class="vip-card-content">
<view class="vip-item-list">
<view v-if="vipInfoEbook?.length > 0" v-for="vip in vipInfoEbook">电子书VIP{{ vipTypeDict[vip.type] }}{{ parseTime(vip.endTime, '{y}-{m}-{d}') }} 截止</view>
<view v-if="vipInfoEbook?.length > 0" v-for="vip in vipInfoEbook">
电子书VIP{{ vipTypeDict[vip.type] }}{{ parseTime(vip.endTime, '{y}-{m}-{d}') }} 截止</view>
<view v-else>办理电子书VIP畅享更多权益</view>
</view>
<wd-button v-if="!vipInfoEbook?.length" plain type="primary" size="small" @click="goSubscribe">{{ $t('vip.openVip') }}</wd-button>
<wd-button v-if="!vipInfoEbook?.length" plain type="primary" size="small"
@click="goSubscribe">{{ $t('vip.openVip') }}</wd-button>
</view>
</view>
</view>
@@ -65,22 +70,28 @@
<!-- 功能菜单列表 -->
<view class="menu-section">
<wd-cell-group border class="menu-list">
<wd-cell v-for="item in menuItems" :key="item.id" :title="item.name" :label="item.desc" is-link @click="handleMenuClick(item)" />
<wd-cell v-for="item in menuItems" :key="item.id" :title="item.name" :label="item.desc" is-link
@click="handleMenuClick(item)">
<text v-if="item.hufenState" class="menu-list-hufen">{{hufenData.total ?? 0}}<text
style="margin-left: 6rpx;">湖分</text></text>
</wd-cell>
</wd-cell-group>
</view>
</view>
<visitor v-else></visitor>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useUserStore } from '@/stores/user'
import { useSysStore } from '@/stores/sys'
import { getUserInfo, getVipInfo } from '@/api/modules/user'
import { getUserInfo, getVipInfo, getUserContributionData } from '@/api/modules/user'
import type { IVipInfo } from '@/types/user'
import { getNotchHeight } from '@/utils/system'
import { parseTime } from '@/utils/index'
import { t } from '@/utils/i18n'
import { onShow } from '@dcloudio/uni-app'
import visitor from '@/pages/visitor/index.vue';
const userStore = useUserStore()
const sysStore = useSysStore()
@@ -140,7 +151,22 @@
// desc: t('user.migrateSubtitle'),
// type: 'pageJump'
// }
// {
// id: 7,
// name: t('user.certificate'),
// url: '/pages/user/certificate/index',
// type: 'pageJump'
// },
{
id: 8,
name: t('user.iHufen'),
url: '/pages/user/hufen/index',
type: 'pageJump',
hufenState: true
},
])
// 湖分
const hufenData = ref('')
/**
* 获取平台信息
@@ -161,6 +187,13 @@
}
}
/**
* 获取用户湖分
*/
const getHufen = async () => {
hufenData.value = await getUserContributionData()
}
/**
* 跳转到设置页面
*/
@@ -243,11 +276,20 @@
}
onShow(() => {
getData()
console.log(userInfo, 'userInfo');
if (userStore.token) {
getData()
}
})
onMounted(() => {
getPlatform()
console.log(userInfo, 'userInfo');
if (userStore.token) {
getPlatform()
getHufen()
}
})
</script>
@@ -394,6 +436,16 @@
border-radius: 15rpx;
overflow: hidden;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
.menu-list-hufen {
font-size: 36rpx;
color: #007bff;
text {
font-size: 26rpx;
}
}
}
.chong_btn {