Merge remote-tracking branch 'origin/master'
| | |
| | | { |
| | | "sassImplementationName" : "node-sass", |
| | | "name" : "海诊康道", |
| | | "appid" : "__UNI__1A90541", |
| | | "description" : "", |
| | | "versionName" : "1.0.0", |
| | | "versionCode" : "100", |
| | | "transformPx" : false, |
| | | /* 5+App特有相关 */ |
| | | "app-plus" : { |
| | | "usingComponents" : true, |
| | | "nvueStyleCompiler" : "uni-app", |
| | | "compilerVersion" : 3, |
| | | "splashscreen" : { |
| | | "alwaysShowBeforeRender" : true, |
| | | "waiting" : true, |
| | | "autoclose" : true, |
| | | "delay" : 0 |
| | | }, |
| | | /* 模块配置 */ |
| | | "modules" : { |
| | | "Barcode" : {}, |
| | | "VideoPlayer" : {} |
| | | }, |
| | | /* 应用发布信息 */ |
| | | "distribute" : { |
| | | /* android打包配置 */ |
| | | "android" : { |
| | | "permissions" : [ |
| | | "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", |
| | | "<uses-permission android:name=\"android.permission.VIBRATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.READ_LOGS\"/>", |
| | | "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", |
| | | "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", |
| | | "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.CAMERA\"/>", |
| | | "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", |
| | | "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", |
| | | "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", |
| | | "<uses-feature android:name=\"android.hardware.camera\"/>", |
| | | "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" |
| | | ] |
| | | }, |
| | | /* ios打包配置 */ |
| | | "ios" : { |
| | | "dSYMs" : false |
| | | }, |
| | | /* SDK配置 */ |
| | | "sdkConfigs" : { |
| | | "push" : {} |
| | | }, |
| | | "icons" : { |
| | | "android" : { |
| | | "hdpi" : "unpackage/res/icons/72x72.png", |
| | | "xhdpi" : "unpackage/res/icons/96x96.png", |
| | | "xxhdpi" : "unpackage/res/icons/144x144.png", |
| | | "xxxhdpi" : "unpackage/res/icons/192x192.png" |
| | | }, |
| | | "ios" : { |
| | | "appstore" : "unpackage/res/icons/1024x1024.png", |
| | | "ipad" : { |
| | | "app" : "unpackage/res/icons/76x76.png", |
| | | "app@2x" : "unpackage/res/icons/152x152.png", |
| | | "notification" : "unpackage/res/icons/20x20.png", |
| | | "notification@2x" : "unpackage/res/icons/40x40.png", |
| | | "proapp@2x" : "unpackage/res/icons/167x167.png", |
| | | "settings" : "unpackage/res/icons/29x29.png", |
| | | "settings@2x" : "unpackage/res/icons/58x58.png", |
| | | "spotlight" : "unpackage/res/icons/40x40.png", |
| | | "spotlight@2x" : "unpackage/res/icons/80x80.png" |
| | | }, |
| | | "iphone" : { |
| | | "app@2x" : "unpackage/res/icons/120x120.png", |
| | | "app@3x" : "unpackage/res/icons/180x180.png", |
| | | "notification@2x" : "unpackage/res/icons/40x40.png", |
| | | "notification@3x" : "unpackage/res/icons/60x60.png", |
| | | "settings@2x" : "unpackage/res/icons/58x58.png", |
| | | "settings@3x" : "unpackage/res/icons/87x87.png", |
| | | "spotlight@2x" : "unpackage/res/icons/80x80.png", |
| | | "spotlight@3x" : "unpackage/res/icons/120x120.png" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | /* 快应用特有相关 */ |
| | | "quickapp" : {}, |
| | | /* 小程序特有相关 */ |
| | | "mp-weixin" : { |
| | | "appid" : "wx424c015a245af04d", |
| | | "setting" : { |
| | | "urlCheck" : false, |
| | | "minified" : true |
| | | }, |
| | | "usingComponents" : true |
| | | }, |
| | | "mp-alipay" : { |
| | | "usingComponents" : true |
| | | }, |
| | | "mp-baidu" : { |
| | | "usingComponents" : true |
| | | }, |
| | | "mp-toutiao" : { |
| | | "usingComponents" : true |
| | | }, |
| | | "uniStatistics" : { |
| | | "enable" : false |
| | | }, |
| | | "vueVersion" : "3", |
| | | "permission" : { |
| | | "scope.userLocation" : { |
| | | "desc" : "下载文件" |
| | | } |
| | | }, |
| | | "h5" : { |
| | | "unipush" : { |
| | | "enable" : false |
| | | } |
| | | } |
| | | } |
| | | "sassImplementationName": "node-sass", |
| | | "name": "海诊康道", |
| | | "appid": "__UNI__1A90541", |
| | | "description": "", |
| | | "versionName": "1.0.1", |
| | | "versionCode": 101, |
| | | "transformPx": false, |
| | | /* 5+App特有相关 */ |
| | | "app-plus": { |
| | | "usingComponents": true, |
| | | "nvueStyleCompiler": "uni-app", |
| | | "compilerVersion": 3, |
| | | "splashscreen": { |
| | | "alwaysShowBeforeRender": true, |
| | | "waiting": true, |
| | | "autoclose": true, |
| | | "delay": 0 |
| | | }, |
| | | /* 模块配置 */ |
| | | "modules": { |
| | | "Barcode": {}, |
| | | "VideoPlayer": {}, |
| | | "Share": {}, |
| | | "OAuth": {} |
| | | }, |
| | | /* 应用发布信息 */ |
| | | "distribute": { |
| | | /* android打包配置 */ |
| | | "android": { |
| | | "permissions": [ |
| | | "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", |
| | | "<uses-permission android:name=\"android.permission.VIBRATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.READ_LOGS\"/>", |
| | | "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", |
| | | "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", |
| | | "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.CAMERA\"/>", |
| | | "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", |
| | | "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", |
| | | "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", |
| | | "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", |
| | | "<uses-feature android:name=\"android.hardware.camera\"/>", |
| | | "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" |
| | | ], |
| | | "minSdkVersion": 30, |
| | | "targetSdkVersion": 29 |
| | | }, |
| | | /* ios打包配置 */ |
| | | "ios": { |
| | | "dSYMs": false, |
| | | "capabilities": { |
| | | "entitlements": { |
| | | "com.apple.developer.associated-domains": [ |
| | | "applinks:static-mp-dd6d96de-bdf2-4284-a975-638af4cc8ec8.next.bspapp.com" |
| | | ] |
| | | } |
| | | } |
| | | }, |
| | | /* SDK配置 */ |
| | | "sdkConfigs": { |
| | | "push": {}, |
| | | "share": { |
| | | "weixin": { |
| | | "appid": "wx074c1f0cf5a0ecf4", |
| | | "UniversalLinks": "https://static-mp-dd6d96de-bdf2-4284-a975-638af4cc8ec8.next.bspapp.com/uni-universallinks/__UNI__1A90541/" |
| | | } |
| | | }, |
| | | "oauth": { |
| | | "weixin": { |
| | | "appid": "wx074c1f0cf5a0ecf4", |
| | | "UniversalLinks": "https://static-mp-dd6d96de-bdf2-4284-a975-638af4cc8ec8.next.bspapp.com/uni-universallinks/__UNI__1A90541/" |
| | | } |
| | | } |
| | | }, |
| | | "icons": { |
| | | "android": { |
| | | "hdpi": "unpackage/res/icons/72x72.png", |
| | | "xhdpi": "unpackage/res/icons/96x96.png", |
| | | "xxhdpi": "unpackage/res/icons/144x144.png", |
| | | "xxxhdpi": "unpackage/res/icons/192x192.png" |
| | | }, |
| | | "ios": { |
| | | "appstore": "unpackage/res/icons/1024x1024.png", |
| | | "ipad": { |
| | | "app": "unpackage/res/icons/76x76.png", |
| | | "app@2x": "unpackage/res/icons/152x152.png", |
| | | "notification": "unpackage/res/icons/20x20.png", |
| | | "notification@2x": "unpackage/res/icons/40x40.png", |
| | | "proapp@2x": "unpackage/res/icons/167x167.png", |
| | | "settings": "unpackage/res/icons/29x29.png", |
| | | "settings@2x": "unpackage/res/icons/58x58.png", |
| | | "spotlight": "unpackage/res/icons/40x40.png", |
| | | "spotlight@2x": "unpackage/res/icons/80x80.png" |
| | | }, |
| | | "iphone": { |
| | | "app@2x": "unpackage/res/icons/120x120.png", |
| | | "app@3x": "unpackage/res/icons/180x180.png", |
| | | "notification@2x": "unpackage/res/icons/40x40.png", |
| | | "notification@3x": "unpackage/res/icons/60x60.png", |
| | | "settings@2x": "unpackage/res/icons/58x58.png", |
| | | "settings@3x": "unpackage/res/icons/87x87.png", |
| | | "spotlight@2x": "unpackage/res/icons/80x80.png", |
| | | "spotlight@3x": "unpackage/res/icons/120x120.png" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | /* 快应用特有相关 */ |
| | | "quickapp": {}, |
| | | /* 小程序特有相关 */ |
| | | "mp-weixin": { |
| | | "appid": "wxa17e8d1331e50934", |
| | | "setting": { |
| | | "urlCheck": false, |
| | | "minified": true |
| | | }, |
| | | "usingComponents": true |
| | | }, |
| | | "mp-alipay": { |
| | | "usingComponents": true |
| | | }, |
| | | "mp-baidu": { |
| | | "usingComponents": true |
| | | }, |
| | | "mp-toutiao": { |
| | | "usingComponents": true |
| | | }, |
| | | "uniStatistics": { |
| | | "enable": false |
| | | }, |
| | | "vueVersion": "3", |
| | | "permission": { |
| | | "scope.userLocation": { |
| | | "desc": "下载文件" |
| | | } |
| | | }, |
| | | "h5": { |
| | | "unipush": { |
| | | "enable": false |
| | | } |
| | | }, |
| | | "_spaceID": "mp-dd6d96de-bdf2-4284-a975-638af4cc8ec8" |
| | | } |
| | |
| | | "iconPath": "static/image/tabBar/home.png", |
| | | "selectedIconPath": "static/image/tabBar/Yhome.png", |
| | | "text": "首页" |
| | | }, |
| | | }, |
| | | { |
| | | "pagePath": "pages/healthCourses/healthCourses", |
| | | "iconPath": "static/image/tabBar/book.png", |
| | |
| | | "backgroundColor": "#fff" |
| | | }, |
| | | "uniIdRouter": {} |
| | | } |
| | | } |
| | |
| | | </view> |
| | | </view> |
| | | |
| | | <scroll-view |
| | | scroll-y |
| | | class="list-scroll" |
| | | @scrolltolower="loadMore" |
| | | > |
| | | <scroll-view scroll-y class="list-scroll" @scrolltolower="loadMore"> |
| | | <view v-if="noticeList.length === 0 && !loading" class="empty-box"> |
| | | <text class="empty-text">暂无活动通知</text> |
| | | </view> |
| | | <view |
| | | v-for="item in noticeList" |
| | | :key="item.id" |
| | | class="notice-card" |
| | | @tap="goDetail(item)" |
| | | > |
| | | <view v-for="item in noticeList" :key="item.id" class="notice-card" @tap="goDetail(item)"> |
| | | <view class="card-top"> |
| | | <view class="title-wrap"> |
| | | <text class="title">{{ item.title }}</text> |
| | |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { onMounted, ref } from 'vue' |
| | | import { onShow } from '@dcloudio/uni-app' |
| | | import { getActivityNoticePage, markActivityNoticeRead } from './service' |
| | | import { onMounted, ref } from 'vue' |
| | | import { onShow } from '@dcloudio/uni-app' |
| | | import { getActivityNoticePage, markActivityNoticeRead } from './service' |
| | | |
| | | interface ActivityNoticeItem { |
| | | id: number |
| | | title: string |
| | | createdAt: string |
| | | read: boolean |
| | | } |
| | | |
| | | const statusBarHeight = ref(20) |
| | | const noticeList = ref<ActivityNoticeItem[]>([]) |
| | | const loading = ref(false) |
| | | const page = ref(1) |
| | | const pageSize = ref(10) |
| | | const finished = ref(false) |
| | | |
| | | /** |
| | | * 页面初始化时记录状态栏高度。 |
| | | */ |
| | | onMounted(() => { |
| | | const systemInfo = uni.getSystemInfoSync() |
| | | statusBarHeight.value = systemInfo.statusBarHeight || 20 |
| | | }) |
| | | |
| | | /** |
| | | * 页面展示时刷新活动通知列表。 |
| | | */ |
| | | onShow(() => { |
| | | resetAndLoad() |
| | | }) |
| | | |
| | | /** |
| | | * 返回上一页。 |
| | | */ |
| | | function goBack() { |
| | | uni.navigateBack() |
| | | } |
| | | |
| | | /** |
| | | * 重置分页状态并重新拉取列表。 |
| | | */ |
| | | function resetAndLoad() { |
| | | page.value = 1 |
| | | finished.value = false |
| | | noticeList.value = [] |
| | | loadList() |
| | | } |
| | | |
| | | /** |
| | | * 拉取活动通知分页列表。 |
| | | */ |
| | | async function loadList() { |
| | | if (loading.value || finished.value) { |
| | | return |
| | | interface ActivityNoticeItem { |
| | | id : number |
| | | title : string |
| | | createdAt : string |
| | | read : boolean |
| | | } |
| | | loading.value = true |
| | | try { |
| | | const response = await getActivityNoticePage({ |
| | | page: page.value, |
| | | pageSize: pageSize.value, |
| | | }) |
| | | if (response?.code !== 0 || !response.data) { |
| | | |
| | | const statusBarHeight = ref(20) |
| | | const noticeList = ref<ActivityNoticeItem[]>([]) |
| | | const loading = ref(false) |
| | | const page = ref(1) |
| | | const pageSize = ref(10) |
| | | const finished = ref(false) |
| | | |
| | | /** |
| | | * 页面初始化时记录状态栏高度。 |
| | | */ |
| | | onMounted(() => { |
| | | const systemInfo = uni.getSystemInfoSync() |
| | | statusBarHeight.value = systemInfo.statusBarHeight || 20 |
| | | }) |
| | | |
| | | /** |
| | | * 页面展示时刷新活动通知列表。 |
| | | */ |
| | | onShow(() => { |
| | | resetAndLoad() |
| | | }) |
| | | |
| | | /** |
| | | * 返回上一页。 |
| | | */ |
| | | function goBack() { |
| | | uni.navigateBack() |
| | | } |
| | | |
| | | /** |
| | | * 重置分页状态并重新拉取列表。 |
| | | */ |
| | | function resetAndLoad() { |
| | | page.value = 1 |
| | | finished.value = false |
| | | noticeList.value = [] |
| | | loadList() |
| | | } |
| | | |
| | | /** |
| | | * 拉取活动通知分页列表。 |
| | | */ |
| | | async function loadList() { |
| | | if (loading.value || finished.value) { |
| | | return |
| | | } |
| | | const nextList = Array.isArray(response.data.list) ? response.data.list.map(normalizeNoticeItem) : [] |
| | | if (page.value === 1) { |
| | | noticeList.value = nextList |
| | | } else { |
| | | noticeList.value = noticeList.value.concat(nextList) |
| | | } |
| | | const total = Number(response.data.total || 0) |
| | | const currentCount = noticeList.value.length |
| | | finished.value = currentCount >= total || nextList.length < pageSize.value |
| | | if (!finished.value) { |
| | | page.value += 1 |
| | | } |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 将活动通知数据归一化为页面结构。 |
| | | */ |
| | | function normalizeNoticeItem(source: any): ActivityNoticeItem { |
| | | return { |
| | | id: Number(source?.id || 0), |
| | | title: typeof source?.title === 'string' ? source.title.trim() : '', |
| | | createdAt: typeof source?.createdAt === 'string' ? source.createdAt.trim() : '', |
| | | read: Boolean(source?.read), |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 触底后加载下一页活动通知。 |
| | | */ |
| | | function loadMore() { |
| | | loadList() |
| | | } |
| | | |
| | | /** |
| | | * 点击活动通知后先标记已读,再跳转详情页。 |
| | | */ |
| | | async function goDetail(item: ActivityNoticeItem) { |
| | | if (!item.id) { |
| | | return |
| | | } |
| | | if (!item.read) { |
| | | await markActivityNoticeRead(item.id) |
| | | noticeList.value = noticeList.value.map((current) => { |
| | | if (current.id !== item.id) { |
| | | return current |
| | | loading.value = true |
| | | try { |
| | | const response = await getActivityNoticePage({ |
| | | page: page.value, |
| | | pageSize: pageSize.value, |
| | | }) |
| | | if (response?.code !== 0 || !response.data) { |
| | | return |
| | | } |
| | | return { |
| | | ...current, |
| | | read: true, |
| | | const nextList = Array.isArray(response.data.list) ? response.data.list.map(normalizeNoticeItem) : [] |
| | | if (page.value === 1) { |
| | | noticeList.value = nextList |
| | | } else { |
| | | noticeList.value = noticeList.value.concat(nextList) |
| | | } |
| | | const total = Number(response.data.total || 0) |
| | | const currentCount = noticeList.value.length |
| | | finished.value = currentCount >= total || nextList.length < pageSize.value |
| | | if (!finished.value) { |
| | | page.value += 1 |
| | | } |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 将活动通知数据归一化为页面结构。 |
| | | */ |
| | | function normalizeNoticeItem(source : any) : ActivityNoticeItem { |
| | | return { |
| | | id: Number(source?.id || 0), |
| | | title: typeof source?.title === 'string' ? source.title.trim() : '', |
| | | createdAt: typeof source?.createdAt === 'string' ? source.createdAt.trim() : '', |
| | | read: Boolean(source?.read), |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 触底后加载下一页活动通知。 |
| | | */ |
| | | function loadMore() { |
| | | loadList() |
| | | } |
| | | |
| | | /** |
| | | * 点击活动通知后先标记已读,再跳转详情页。 |
| | | */ |
| | | async function goDetail(item : ActivityNoticeItem) { |
| | | if (!item.id) { |
| | | return |
| | | } |
| | | if (!item.read) { |
| | | await markActivityNoticeRead(item.id) |
| | | noticeList.value = noticeList.value.map((current) => { |
| | | if (current.id !== item.id) { |
| | | return current |
| | | } |
| | | return { |
| | | ...current, |
| | | read: true, |
| | | } |
| | | }) |
| | | } |
| | | uni.navigateTo({ |
| | | url: `/pages/activityNotice/activityNoticeDetail?id=${item.id}`, |
| | | }) |
| | | } |
| | | uni.navigateTo({ |
| | | url: `/pages/activityNotice/activityNoticeDetail?id=${item.id}`, |
| | | }) |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .container { |
| | | min-height: 100vh; |
| | | background: #f6f7fb; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | .container { |
| | | min-height: 100vh; |
| | | background: #f6f7fb; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .custom-navbar { |
| | | background: #ffffff; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 20; |
| | | } |
| | | .custom-navbar { |
| | | background: #ffffff; |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 20; |
| | | } |
| | | |
| | | .nav-content { |
| | | height: 88rpx; |
| | | padding: 0 32rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | .nav-content { |
| | | height: 88rpx; |
| | | padding: 0 32rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .back-icon { |
| | | width: 40rpx; |
| | | height: 40rpx; |
| | | } |
| | | .back-icon { |
| | | width: 40rpx; |
| | | height: 40rpx; |
| | | } |
| | | |
| | | .nav-title { |
| | | font-size: 36rpx; |
| | | line-height: 50rpx; |
| | | font-weight: 600; |
| | | color: #1a1a1a; |
| | | } |
| | | .nav-title { |
| | | font-size: 36rpx; |
| | | line-height: 50rpx; |
| | | font-weight: 600; |
| | | color: #1a1a1a; |
| | | } |
| | | |
| | | .nav-placeholder { |
| | | width: 40rpx; |
| | | height: 40rpx; |
| | | } |
| | | .nav-placeholder { |
| | | width: 40rpx; |
| | | height: 40rpx; |
| | | } |
| | | |
| | | .list-scroll { |
| | | flex: 1; |
| | | padding: 24rpx 24rpx 40rpx; |
| | | box-sizing: border-box; |
| | | } |
| | | .list-scroll { |
| | | flex: 1; |
| | | padding: 24rpx 24rpx 40rpx; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .notice-card { |
| | | background: #ffffff; |
| | | border-radius: 24rpx; |
| | | padding: 28rpx 24rpx; |
| | | margin-bottom: 24rpx; |
| | | } |
| | | .notice-card { |
| | | background: #ffffff; |
| | | border-radius: 24rpx; |
| | | padding: 28rpx 24rpx; |
| | | margin-bottom: 24rpx; |
| | | } |
| | | |
| | | .card-top { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | column-gap: 24rpx; |
| | | } |
| | | .card-top { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | column-gap: 24rpx; |
| | | } |
| | | |
| | | .title-wrap { |
| | | display: flex; |
| | | align-items: center; |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | .title-wrap { |
| | | display: flex; |
| | | align-items: center; |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .title { |
| | | font-size: 32rpx; |
| | | line-height: 44rpx; |
| | | color: #1f2329; |
| | | font-weight: 500; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | .title { |
| | | font-size: 32rpx; |
| | | line-height: 44rpx; |
| | | color: #1f2329; |
| | | font-weight: 500; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .red-dot { |
| | | width: 14rpx; |
| | | height: 14rpx; |
| | | border-radius: 50%; |
| | | background: #d93026; |
| | | margin-left: 12rpx; |
| | | flex-shrink: 0; |
| | | } |
| | | .red-dot { |
| | | width: 14rpx; |
| | | height: 14rpx; |
| | | border-radius: 50%; |
| | | background: #d93026; |
| | | margin-left: 12rpx; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .time { |
| | | font-size: 24rpx; |
| | | line-height: 34rpx; |
| | | color: #86909c; |
| | | flex-shrink: 0; |
| | | } |
| | | .time { |
| | | font-size: 24rpx; |
| | | line-height: 34rpx; |
| | | color: #86909c; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .desc { |
| | | margin-top: 16rpx; |
| | | font-size: 26rpx; |
| | | line-height: 38rpx; |
| | | color: #86909c; |
| | | } |
| | | .desc { |
| | | margin-top: 16rpx; |
| | | font-size: 26rpx; |
| | | line-height: 38rpx; |
| | | color: #86909c; |
| | | } |
| | | |
| | | .empty-box { |
| | | padding-top: 200rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | .empty-box { |
| | | padding-top: 200rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .empty-text, |
| | | .footer-text { |
| | | font-size: 26rpx; |
| | | line-height: 36rpx; |
| | | color: #86909c; |
| | | text-align: center; |
| | | } |
| | | .empty-text, |
| | | .footer-text { |
| | | font-size: 26rpx; |
| | | line-height: 36rpx; |
| | | color: #86909c; |
| | | text-align: center; |
| | | } |
| | | |
| | | .footer-text { |
| | | padding: 12rpx 0 24rpx; |
| | | } |
| | | </style> |
| | | .footer-text { |
| | | padding: 12rpx 0 24rpx; |
| | | } |
| | | </style> |
| | |
| | | <view class="search-btn">搜索</view> |
| | | </view> |
| | | |
| | | |
| | | <!-- Banners --> |
| | | <view v-if="bannerList.length" class="banner-section"> |
| | | <u-swiper :list="bannerList" :height="280" mode="none" name="imageUrl" bgColor="#fff" border-radius="20" |
| | |
| | | :class="{ active: bannerActiveIndex === index }"></view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="AI" style="width: 702rpx;height: 160rpx;margin: 0 auto;margin-top: 26rpx;" |
| | | @tap="goMiniProgram"> |
| | | <image src="/static/image/home/AI.png" mode="aspectFill" class="w100 h100"></image> |
| | | </view> |
| | | |
| | | |
| | | <!-- Live Section --> |
| | | <view v-if="liveCourseList.length" class="section"> |
| | |
| | | <view v-for="item in liveCourseList" :key="item.courseId" class="live-card"> |
| | | <image :src="item.displayImage || defaultLiveCover" mode="aspectFill" class="live-bg" |
| | | @tap="handleLiveImageClick(item)"></image> |
| | | <image v-if="item.displayStatus === 'LIVING'" src="/static/image/home/zhibo.png" class="zhibo-icon" |
| | | mode="aspectFill"></image> |
| | | <image v-if="item.displayStatus === 'LIVING'" src="/static/image/home/zhibo.png" |
| | | class="zhibo-icon" mode="aspectFill"></image> |
| | | <view v-else class="live-bottom-bar"> |
| | | <view class="time-tag" :class="{ ended: item.displayStatus === 'ENDED' }"> |
| | | {{ item.displayStatusText }} |
| | |
| | | mode="aspectFill"></image> |
| | | </view> |
| | | <view v-if="featuredHealthCourseList.length" class="course-list"> |
| | | <view v-for="item in featuredHealthCourseList" :key="item.id" class="course-item" @tap="goCourseDetail(item.id)"> |
| | | <image :src="item.coverImage || defaultCourseCover" mode="aspectFill" class="course-img"></image> |
| | | <view v-for="item in featuredHealthCourseList" :key="item.id" class="course-item" |
| | | @tap="goCourseDetail(item.id)"> |
| | | <image :src="item.coverImage || defaultCourseCover" mode="aspectFill" class="course-img"> |
| | | </image> |
| | | <view class="course-info"> |
| | | <view class="course-top"> |
| | | <text v-if="resolveCourseTag(item)" class="tag-yangsheng">{{ resolveCourseTag(item) }}</text> |
| | | <text v-if="resolveCourseTag(item)" |
| | | class="tag-yangsheng">{{ resolveCourseTag(item) }}</text> |
| | | <text class="course-name">{{ item.title }}</text> |
| | | </view> |
| | | <view class="course-bottom"> |
| | | <text class="price"><text class="price-symbol">¥</text>{{ formatPrice(item.salePrice) }}</text> |
| | | <text class="price"><text |
| | | class="price-symbol">¥</text>{{ formatPrice(item.salePrice) }}</text> |
| | | <view class="people"> |
| | | <image src="/static/image/home/person.png" class="person-icon"></image> |
| | | <text>{{ formatLearningCount(item.learnUserCount) }}人正在学</text> |
| | |
| | | <view v-if="featuredPopularizationList.length" class="article-grid"> |
| | | <view v-for="item in featuredPopularizationList" :key="item.id" class="article-card" |
| | | @tap="goPopularizationDetail(item.id)"> |
| | | <image :src="item.coverImage || defaultPopularizationCover" mode="aspectFill" class="article-bg"></image> |
| | | <image :src="item.coverImage || defaultPopularizationCover" mode="aspectFill" |
| | | class="article-bg"></image> |
| | | <view class="article-title">{{ item.title }}</view> |
| | | </view> |
| | | </view> |
| | |
| | | import { getBannerList, getHomeFeaturedContent, getHomeLiveCourseList } from "./service"; |
| | | |
| | | interface HomeBannerItem { |
| | | id: number | string; |
| | | imageUrl: string; |
| | | title?: string; |
| | | jumpType?: string; |
| | | targetId?: number | string | null; |
| | | targetUrl?: string; |
| | | id : number | string; |
| | | imageUrl : string; |
| | | title ?: string; |
| | | jumpType ?: string; |
| | | targetId ?: number | string | null; |
| | | targetUrl ?: string; |
| | | } |
| | | |
| | | interface HomeLiveCourseItem { |
| | | courseId: number | string; |
| | | title: string; |
| | | coverImage?: string; |
| | | liveImage?: string; |
| | | displayImage?: string; |
| | | displayStatus?: 'NOT_STARTED' | 'LIVING' | 'ENDED'; |
| | | displayStatusText?: string; |
| | | nextStartTime?: string; |
| | | courseId : number | string; |
| | | title : string; |
| | | coverImage ?: string; |
| | | liveImage ?: string; |
| | | displayImage ?: string; |
| | | displayStatus ?: 'NOT_STARTED' | 'LIVING' | 'ENDED'; |
| | | displayStatusText ?: string; |
| | | nextStartTime ?: string; |
| | | } |
| | | |
| | | interface HomeFeaturedHealthCourseItem { |
| | | id: number | string; |
| | | title: string; |
| | | coverImage?: string; |
| | | levelName?: string; |
| | | difficultyName?: string; |
| | | salePrice?: number | string; |
| | | learnUserCount?: number; |
| | | id : number | string; |
| | | title : string; |
| | | coverImage ?: string; |
| | | levelName ?: string; |
| | | difficultyName ?: string; |
| | | salePrice ?: number | string; |
| | | learnUserCount ?: number; |
| | | } |
| | | |
| | | interface HomeFeaturedPopularizationItem { |
| | | id: number | string; |
| | | title: string; |
| | | coverImage?: string; |
| | | contentType?: number; |
| | | clickCount?: number; |
| | | id : number | string; |
| | | title : string; |
| | | coverImage ?: string; |
| | | contentType ?: number; |
| | | clickCount ?: number; |
| | | } |
| | | |
| | | interface HomeFeaturedContentResponse { |
| | | healthCourses?: HomeFeaturedHealthCourseItem[]; |
| | | healthPopularizations?: HomeFeaturedPopularizationItem[]; |
| | | healthCourses ?: HomeFeaturedHealthCourseItem[]; |
| | | healthPopularizations ?: HomeFeaturedPopularizationItem[]; |
| | | } |
| | | |
| | | const statusBarHeight = ref(0); |
| | |
| | | bannerActiveIndex.value = 0; |
| | | return; |
| | | } |
| | | bannerList.value = res.data.filter((item: HomeBannerItem) => !!item?.imageUrl); |
| | | bannerList.value = res.data.filter((item : HomeBannerItem) => !!item?.imageUrl); |
| | | bannerActiveIndex.value = 0; |
| | | } catch { |
| | | bannerList.value = []; |
| | | bannerActiveIndex.value = 0; |
| | | } |
| | | }; |
| | | |
| | | //跳转海诊通小程序 |
| | | const goMiniProgram = () => { |
| | | plus.share.getServices((services) => { |
| | | const weixinService = services.find((service) => service.id === 'weixin'); |
| | | if (!weixinService) { |
| | | uni.showToast({ |
| | | title: '当前环境不支持打开微信小程序', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | if (!weixinService.nativeClient) { |
| | | uni.showToast({ |
| | | title: '请先安装微信客户端', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | weixinService.launchMiniProgram({ |
| | | // App 端拉起微信小程序需使用原始 ID,若当前值不是原始 ID 请替换为 gh_ 开头的值。 |
| | | id: 'gh_e3c3d19c3c71', |
| | | path: 'pages/home/home', |
| | | type: 0 |
| | | }, () => { |
| | | console.log('跳转成功'); |
| | | }, (err) => { |
| | | console.log('跳转失败', err); |
| | | uni.showToast({ |
| | | title: err?.message === 'badprogram' ? '请检查小程序原始ID及关联配置' : '打开小程序失败', |
| | | icon: 'none' |
| | | }); |
| | | }); |
| | | }, (err) => { |
| | | console.log('获取微信服务失败', err); |
| | | uni.showToast({ |
| | | title: '微信服务不可用', |
| | | icon: 'none' |
| | | }); |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | /** |
| | | * 请求首页养生直播模块列表。 |
| | |
| | | liveCourseList.value = []; |
| | | return; |
| | | } |
| | | liveCourseList.value = res.data.filter((item: HomeLiveCourseItem) => !!item?.courseId); |
| | | liveCourseList.value = res.data.filter((item : HomeLiveCourseItem) => !!item?.courseId); |
| | | } catch { |
| | | liveCourseList.value = []; |
| | | } |
| | |
| | | } |
| | | const responseData = res.data as HomeFeaturedContentResponse; |
| | | featuredHealthCourseList.value = Array.isArray(responseData.healthCourses) |
| | | ? responseData.healthCourses.filter((item: HomeFeaturedHealthCourseItem) => !!item?.id) |
| | | ? responseData.healthCourses.filter((item : HomeFeaturedHealthCourseItem) => !!item?.id) |
| | | : []; |
| | | featuredPopularizationList.value = Array.isArray(responseData.healthPopularizations) |
| | | ? responseData.healthPopularizations.filter((item: HomeFeaturedPopularizationItem) => !!item?.id) |
| | | ? responseData.healthPopularizations.filter((item : HomeFeaturedPopularizationItem) => !!item?.id) |
| | | : []; |
| | | } catch { |
| | | featuredHealthCourseList.value = []; |
| | |
| | | /** |
| | | * 将课程价格格式化为首页展示文案。 |
| | | */ |
| | | const formatPrice = (price?: number | string) => { |
| | | const formatPrice = (price ?: number | string) => { |
| | | const numericPrice = Number(price || 0); |
| | | if (!Number.isFinite(numericPrice)) { |
| | | return '0.00'; |
| | |
| | | /** |
| | | * 将学习人数格式化为首页展示文案。 |
| | | */ |
| | | const formatLearningCount = (count?: number) => { |
| | | const formatLearningCount = (count ?: number) => { |
| | | const safeCount = typeof count === 'number' && count > 0 ? count : 0; |
| | | if (safeCount >= 10000) { |
| | | return `${(Math.round(safeCount / 100) / 100).toFixed(2)}W`; |
| | |
| | | /** |
| | | * 解析首页养生课程卡片标签,优先展示等级,其次展示难度。 |
| | | */ |
| | | const resolveCourseTag = (item: HomeFeaturedHealthCourseItem) => { |
| | | const resolveCourseTag = (item : HomeFeaturedHealthCourseItem) => { |
| | | if (item.levelName) { |
| | | return item.levelName; |
| | | } |
| | |
| | | /** |
| | | * 同步当前轮播索引,用于自定义指示器展示。 |
| | | */ |
| | | const handleBannerChange = (index: number) => { |
| | | const handleBannerChange = (index : number) => { |
| | | bannerActiveIndex.value = index; |
| | | }; |
| | | |
| | | /** |
| | | * 根据 banner 跳转类型分发到现有页面。 |
| | | */ |
| | | const handleBannerClick = (index: number) => { |
| | | const handleBannerClick = (index : number) => { |
| | | const currentBanner = bannerList.value[index]; |
| | | if (!currentBanner) { |
| | | return; |
| | |
| | | /** |
| | | * 点击直播图片时统一进入直播详情页。 |
| | | */ |
| | | const handleLiveImageClick = (item: HomeLiveCourseItem) => { |
| | | const handleLiveImageClick = (item : HomeLiveCourseItem) => { |
| | | goLiveDetail(item.courseId); |
| | | }; |
| | | |
| | | /** |
| | | * 点击直播名称时统一跳转直播介绍页。 |
| | | */ |
| | | const handleLiveTitleClick = (item: HomeLiveCourseItem) => { |
| | | const handleLiveTitleClick = (item : HomeLiveCourseItem) => { |
| | | goLiveDetail(item.courseId); |
| | | }; |
| | | |
| | | /** |
| | | * 打开第三方链接。 |
| | | */ |
| | | const openThirdPartyUrl = (targetUrl?: string) => { |
| | | const openThirdPartyUrl = (targetUrl ?: string) => { |
| | | if (!targetUrl) { |
| | | return; |
| | | } |
| | |
| | | /** |
| | | * 跳转直播详情页。 |
| | | */ |
| | | const goLiveDetail = (courseId?: number | string | null) => { |
| | | const goLiveDetail = (courseId ?: number | string | null) => { |
| | | uni.navigateTo({ |
| | | url: courseId ? `/pages/liveDetail/liveDetail?id=${courseId}` : '/pages/liveDetail/liveDetail' |
| | | }); |
| | |
| | | /** |
| | | * 跳转养生课程详情页。 |
| | | */ |
| | | const goCourseDetail = (courseId?: number | string | null) => { |
| | | const goCourseDetail = (courseId ?: number | string | null) => { |
| | | uni.navigateTo({ |
| | | url: courseId ? `/pages/courseDetail/courseDetail?id=${courseId}` : '/pages/courseDetail/courseDetail' |
| | | }); |
| | |
| | | /** |
| | | * 跳转健康科普详情页。 |
| | | */ |
| | | const goPopularizationDetail = (popularizationId?: number | string | null) => { |
| | | const goPopularizationDetail = (popularizationId ?: number | string | null) => { |
| | | uni.navigateTo({ |
| | | url: popularizationId |
| | | ? `/pages/articleDetail/articleDetail?id=${popularizationId}` |
| | |
| | | opacity: 1; |
| | | } |
| | | } |
| | | </style> |
| | | </style> |