From 0142ef5a8abf3558cb429a79761ee50b482f6189 Mon Sep 17 00:00:00 2001
From: hejianhao <15708179461@qq.com>
Date: 星期二, 18 八月 2026 09:15:40 +0800
Subject: [PATCH] feat0806: 解压APP端静态页面
---
traditionalApp/pages/home/home.vue | 128 ++++++++++++++-
traditionalApp/pages/mine/mine.vue | 23 ++
traditionalApp/pages.json | 47 +++++
traditionalApp/pages/articleDetail/articleDetail.vue | 20 ++
traditionalApp/pages/good/good.vue | 168 +++++++++++++++++++-
traditionalApp/pages/healthCourses/healthCourses.vue | 37 +++
traditionalApp/pages/publish/publish.vue | 38 ++++
7 files changed, 426 insertions(+), 35 deletions(-)
diff --git a/traditionalApp/pages.json b/traditionalApp/pages.json
index 364cd94..8506151 100644
--- a/traditionalApp/pages.json
+++ b/traditionalApp/pages.json
@@ -30,6 +30,18 @@
}
},
{
+ "path": "pages/good/projectList",
+ "style": {
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/good/projectDetail",
+ "style": {
+ "navigationStyle": "custom"
+ }
+ },
+ {
"path": "pages/mineInfo/mineInfo",
"style": {
"navigationBarTitleText": "个人中心"
@@ -307,6 +319,12 @@
}
},
{
+ "path": "pages/bankCard/bankCard",
+ "style": {
+ "navigationStyle": "custom"
+ }
+ },
+ {
"path": "pages/myPatients/myPatients",
"style": {
"navigationStyle": "custom"
@@ -335,6 +353,25 @@
"style": {
"navigationStyle": "custom"
}
+ },
+ {
+ "path": "pages/passwordInput/passwordInput",
+ "style": {
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/healthCoursesHub/healthCoursesHub",
+ "style": {
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/healthPopularization/healthPopularization",
+ "style": {
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": true
+ }
}
],
"tabBar": {
@@ -349,16 +386,16 @@
"text": "首页"
},
{
- "pagePath": "pages/healthCourses/healthCourses",
+ "pagePath": "pages/healthCoursesHub/healthCoursesHub",
"iconPath": "static/image/tabBar/book.png",
"selectedIconPath": "static/image/tabBar/YBook.png",
"text": "养生课程"
},
{
- "pagePath": "pages/publish/publish",
- "iconPath": "static/image/tabBar/study.png",
- "selectedIconPath": "static/image/tabBar/YStudy.png",
- "text": "科普"
+ "pagePath": "pages/good/good",
+ "iconPath": "static/image/tabBar/project.png",
+ "selectedIconPath": "static/image/tabBar/Yproject.png",
+ "text": "项目中心"
},
{
"pagePath": "pages/mine/mine",
diff --git a/traditionalApp/pages/articleDetail/articleDetail.vue b/traditionalApp/pages/articleDetail/articleDetail.vue
index 8e9860e..cbbb0f5 100644
--- a/traditionalApp/pages/articleDetail/articleDetail.vue
+++ b/traditionalApp/pages/articleDetail/articleDetail.vue
@@ -22,6 +22,7 @@
<!-- 内容区域 -->
<view class="content-area">
<text class="content-title">{{ detail.title || '健康科普详情' }}</text>
+ <text v-if="isVideoType && videoSubtitle" class="content-subtitle">{{ videoSubtitle }}</text>
<rich-text v-if="!isVideoType" :nodes="contentNodes" class="rich-content"></rich-text>
<view v-if="loading" class="status-text">加载中...</view>
<view v-else-if="!detail.id" class="status-text">暂无详情内容</view>
@@ -37,6 +38,7 @@
interface PopularizationDetail {
id ?: number | null
title : string
+ subtitle : string
coverImage : string
contentType ?: number | null
contentTypeText : string
@@ -57,6 +59,7 @@
return {
id: null,
title: '',
+ subtitle: '',
coverImage: '',
contentType: null,
contentTypeText: '',
@@ -70,6 +73,14 @@
* 根据内容类型判断当前详情是否属于视频详情页。
*/
const isVideoType = computed(() => Number(detail.value.contentType) === 2)
+
+ /**
+ * 视频详情副标题,兼容后台 subtitle / summary / description 字段。
+ */
+ const videoSubtitle = computed(() => {
+ const source = detail.value as PopularizationDetail & { summary ?: string; description ?: string; subTitle ?: string }
+ return String(source.subtitle || source.subTitle || source.summary || source.description || '').trim()
+ })
/**
* 将富文本正文转换为 `rich-text` 可直接消费的节点字符串。
@@ -251,6 +262,15 @@
line-height: 48rpx;
}
+ .content-subtitle {
+ display: block;
+ margin-top: 12rpx;
+ font-size: 12px;
+ font-family: 'PingFang SC', 'PingFang-SC', sans-serif;
+ color: #5B6475;
+ line-height: 1.5;
+ }
+
.rich-content {
margin-top: 24rpx;
width: 100%;
diff --git a/traditionalApp/pages/good/good.vue b/traditionalApp/pages/good/good.vue
index da8aaa5..8f4c5e4 100644
--- a/traditionalApp/pages/good/good.vue
+++ b/traditionalApp/pages/good/good.vue
@@ -1,24 +1,174 @@
<template>
<view class="container">
+ <view class="status-bar-placeholder" :style="{ height: statusBarHeight + 'px' }"></view>
+ <view class="nav-bar">
+ <text class="nav-title">项目中心</text>
+ </view>
+ <view v-if="!unlocked" class="password-content">
+ <view class="input-wrap">
+ <input
+ class="password-input"
+ password
+ v-model="password"
+ placeholder="请输入密码"
+ placeholder-class="input-placeholder"
+ :focus="autoFocus"
+ @confirm="handleConfirm"
+ />
+ </view>
+ <view class="confirm-btn" :class="{ disabled: !password }" @tap="handleConfirm">确认</view>
+ </view>
+ <view v-else class="entry-content">
+ <image class="entry-image" src="/static/image/project/jieshao.png" mode="widthFix"
+ hover-class="entry-image--pressed" hover-stay-time="80" @tap="goList('platform', '平台介绍')"></image>
+ <image class="entry-image" src="/static/image/project/moshi.png" mode="widthFix"
+ hover-class="entry-image--pressed" hover-stay-time="80" @tap="goList('mode', '营销模式')"></image>
+ <image class="entry-image" src="/static/image/project/dongzuo.png" mode="widthFix"
+ hover-class="entry-image--pressed" hover-stay-time="80" @tap="goList('action', '营销动作')"></image>
+ </view>
</view>
</template>
<script setup lang="ts">
- import { onLoad } from "@dcloudio/uni-app";
- import { ref } from "vue";
+import { onMounted, ref } from 'vue'
- onLoad((params) => {
+/** 项目中心访问密码,后续改为接口读取后台配置。 */
+const PROJECT_CENTER_PASSWORD = '123456'
- });
+const statusBarHeight = ref(0)
+const password = ref('')
+const autoFocus = ref(true)
+const unlocked = ref(false)
+
+onMounted(() => {
+ uni.getSystemInfo({
+ success: (res) => {
+ statusBarHeight.value = res.statusBarHeight || 0
+ }
+ })
+})
+
+/**
+ * 校验项目中心密码,通过后展示三个入口。
+ */
+function handleConfirm() {
+ if (!password.value) {
+ return
+ }
+ if (password.value !== PROJECT_CENTER_PASSWORD) {
+ uni.showToast({
+ title: '密码错误,请重新输入',
+ icon: 'none'
+ })
+ return
+ }
+ unlocked.value = true
+ password.value = ''
+ autoFocus.value = false
+}
+
+/**
+ * 进入对应入口的静态列表页。
+ */
+function goList(type: string, title: string) {
+ uni.navigateTo({
+ url: `/pages/good/projectList?type=${encodeURIComponent(type)}&title=${encodeURIComponent(title)}`
+ })
+}
</script>
<style>
- page {
- background-color: #fff;
- }
+page, html {
+ background-color: #ffffff;
+}
</style>
+
<style scoped lang="scss">
- .container {}
-</style>
\ No newline at end of file
+.container {
+ width: 750rpx;
+ min-height: 100vh;
+ background-color: #ffffff;
+}
+
+.status-bar-placeholder {
+ width: 100%;
+}
+
+.nav-bar {
+ width: 100%;
+ height: 88rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ .nav-title {
+ font-size: 36rpx;
+ font-weight: 600;
+ color: #1a1a1a;
+ }
+}
+
+.password-content {
+ padding: 80rpx 40rpx 0;
+ box-sizing: border-box;
+}
+
+.input-wrap {
+ width: 670rpx;
+ height: 108rpx;
+ border: 1rpx solid #e5e5e5;
+ border-radius: 16rpx;
+ display: flex;
+ align-items: center;
+ padding: 0 32rpx;
+ box-sizing: border-box;
+ background: #ffffff;
+ margin-bottom: 48rpx;
+
+ .password-input {
+ width: 100%;
+ height: 100%;
+ font-size: 32rpx;
+ color: #333;
+ }
+
+ .input-placeholder {
+ color: #999;
+ font-size: 32rpx;
+ }
+}
+
+.confirm-btn {
+ width: 670rpx;
+ height: 104rpx;
+ background: #c63520;
+ border-radius: 16rpx;
+ color: #ffffff;
+ font-size: 34rpx;
+ font-weight: 500;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &.disabled {
+ opacity: 0.5;
+ }
+}
+
+.entry-content {
+ padding: 32rpx 40rpx 0;
+ box-sizing: border-box;
+
+ .entry-image {
+ display: block;
+ width: 670rpx;
+ margin-bottom: 0;
+
+ &--pressed {
+ opacity: 0.92;
+ }
+ }
+}
+</style>
diff --git a/traditionalApp/pages/healthCourses/healthCourses.vue b/traditionalApp/pages/healthCourses/healthCourses.vue
index 93b4ef2..d5880d4 100644
--- a/traditionalApp/pages/healthCourses/healthCourses.vue
+++ b/traditionalApp/pages/healthCourses/healthCourses.vue
@@ -5,7 +5,8 @@
<!-- 导航栏 -->
<view class="nav-bar">
- <view class="nav-title">养生课程</view>
+ <image class="back-icon" src="/static/image/home/left.png" mode="aspectFit" @tap="goBack"></image>
+ <view class="nav-title">养生功法</view>
<image class="search-icon" src="/static/image/healthCourses/search.png" mode="aspectFit" @tap="goSearch"></image>
</view>
@@ -153,6 +154,20 @@
function goSearch() {
uni.navigateTo({
url: '/pages/search/search'
+ })
+}
+
+/**
+ * 返回上一页;没有历史栈时回到养生课程中转页。
+ */
+function goBack() {
+ const currentPages = getCurrentPages()
+ if (currentPages.length > 1) {
+ uni.navigateBack()
+ return
+ }
+ uni.switchTab({
+ url: '/pages/healthCoursesHub/healthCoursesHub'
})
}
@@ -327,20 +342,30 @@
height: 88rpx;
display: flex;
align-items: center;
- justify-content: center;
+ justify-content: space-between;
+ padding-left: 32rpx;
+ padding-right: 32rpx;
position: relative;
background-color: #fff;
+ box-sizing: border-box;
+
+ .back-icon {
+ flex-shrink: 0;
+ width: 40rpx;
+ height: 40rpx;
+ }
}
.nav-title {
- font-size: 36rpx;
+ flex: 1;
+ text-align: center;
+ font-size: 32rpx;
font-weight: 600;
- color: #333;
+ color: #1A1A1A;
}
.search-icon {
- position: absolute;
- right: 48rpx;
+ flex-shrink: 0;
width: 28rpx;
height: 28rpx;
}
diff --git a/traditionalApp/pages/home/home.vue b/traditionalApp/pages/home/home.vue
index 0deee8b..6c72549 100644
--- a/traditionalApp/pages/home/home.vue
+++ b/traditionalApp/pages/home/home.vue
@@ -39,16 +39,32 @@
</view>
</view>
+ <!-- Quick Entry: 微赞直播 + 经营者培训 -->
+ <view class="quick-entry-row">
+ <view class="quick-entry-card" hover-class="quick-entry-card--pressed" hover-stay-time="80"
+ @tap="goWeiZanLive">
+ <image src="/static/image/home/weizan-live.png" mode="aspectFill" class="quick-entry-card__img"></image>
+ </view>
+ <view class="quick-entry-card" hover-class="quick-entry-card--pressed" hover-stay-time="80"
+ @tap="goOperatorTraining">
+ <template v-if="operatorTrainingLiveCourse && operatorTrainingLiveCourse.displayStatus === 'LIVING'">
+ <image :src="operatorTrainingLiveCourse.displayImage || defaultLiveCover" mode="aspectFill"
+ class="quick-entry-card__img"></image>
+ <image src="/static/image/home/zhibo.png" class="quick-entry-card__living-tag"
+ mode="aspectFill"></image>
+ </template>
+ <template v-else>
+ <image :src="operatorTrainingLiveCourse?.displayImage || defaultLiveCover" mode="aspectFill"
+ class="quick-entry-card__img"></image>
+ </template>
+ </view>
+ </view>
+
<!-- Live Section -->
- <view v-if="liveCourseList.length" class="section">
- <view class="section-header">
- <text class="section-title">养生直播</text>
- <image src="/static/image/home/conentHead.png" mode="aspectFill" class="header-deco"></image>
- <image src="/static/image/home/circle.png" class="header-circle" mode="aspectFill"></image>
- </view>
+ <view v-if="advancedLiveCourseList.length" class="section">
<view class="live-list">
- <view v-for="item in liveCourseList" :key="item.courseId" class="live-card">
+ <view v-for="item in advancedLiveCourseList" :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"
@@ -66,9 +82,9 @@
</view>
<!-- Course Section -->
- <view v-if="featuredHealthCourseList.length || featuredPopularizationList.length" class="section">
+ <view v-if="featuredHealthCourseList.length" class="section">
<view class="section-header">
- <text class="section-title">精选课程介绍</text>
+ <text class="section-title">精选课程</text>
<image src="/static/image/home/conentHead.png" class="header-deco"></image>
<image src="/static/image/home/circle.png" style="left: 238rpx !important;" class="header-circle"
mode="aspectFill"></image>
@@ -99,8 +115,16 @@
</view>
</view>
</view>
+ </view>
- <!-- Articles Section -->
+ <!-- Popularization Section -->
+ <view v-if="featuredPopularizationList.length" class="section">
+ <view class="section-header">
+ <text class="section-title">养生科普</text>
+ <image src="/static/image/home/conentHead.png" class="header-deco"></image>
+ <image src="/static/image/home/circle.png" style="left: 238rpx !important;" class="header-circle"
+ mode="aspectFill"></image>
+ </view>
<view v-if="featuredPopularizationList.length" class="article-grid">
<view v-for="item in featuredPopularizationList" :key="item.id" class="article-card"
@tap="goPopularizationDetail(item.id)">
@@ -175,6 +199,21 @@
const shouldUsePlainIosPlayIcon = computed(() => {
const systemInfo = uni.getSystemInfoSync();
return String(systemInfo.platform || '').toLowerCase() === 'ios';
+ });
+ const weiZanLiveH5Url = 'https://www.vzan.com';
+ const advancedLiveCourseList = computed(() => {
+ if (!Array.isArray(liveCourseList.value) || liveCourseList.value.length === 0) {
+ return [];
+ }
+ return liveCourseList.value.filter((item) => !String(item.title || '').includes('筑基'));
+ });
+ const operatorTrainingLiveCourse = computed(() => {
+ const list = advancedLiveCourseList.value;
+ if (!Array.isArray(list) || list.length === 0) {
+ return null;
+ }
+ const livingCourse = list.find((item) => item.displayStatus === 'LIVING');
+ return livingCourse || list[0];
});
/**
@@ -271,6 +310,36 @@
});
return;
}
+
+ /**
+ * 跳转微赞直播 H5 页面(后端接口暂未提供,先使用静态链接)。
+ */
+ const goWeiZanLive = () => {
+ uni.navigateTo({
+ url: `/pages/webview/webview?url=${encodeURIComponent(weiZanLiveH5Url)}&title=${encodeURIComponent('微赞直播')}`
+ });
+ };
+
+ /**
+ * 经营者培训入口跳转:
+ * - 先进入密码验证页,验证成功后:
+ * - 有正在直播的场次(LIVING)→ 跳转直播视频播放页(watchLive)
+ * - 无正在直播 → 跳转课程介绍页(liveDetail)
+ */
+ const goOperatorTraining = () => {
+ const targetCourse = operatorTrainingLiveCourse.value;
+ let targetPageUrl = '';
+ if (!targetCourse || !targetCourse.courseId) {
+ targetPageUrl = '/pages/liveDetail/liveDetail';
+ } else if (targetCourse.displayStatus === 'LIVING') {
+ targetPageUrl = `/pages/watchLive/watchLive?courseId=${targetCourse.courseId}`;
+ } else {
+ targetPageUrl = `/pages/liveDetail/liveDetail?id=${targetCourse.courseId}`;
+ }
+ uni.navigateTo({
+ url: `/pages/passwordInput/passwordInput?targetUrl=${encodeURIComponent(targetPageUrl)}`
+ });
+ };
/**
* 请求首页养生直播模块列表。
@@ -689,6 +758,45 @@
transform: translateY(-1rpx);
}
+ /* Quick Entry Row */
+ .quick-entry-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 702rpx;
+ margin: 26rpx auto 0;
+
+ .quick-entry-card {
+ position: relative;
+ width: 340rpx;
+ height: 160rpx;
+ border-radius: 16rpx;
+ overflow: hidden;
+ background: #fff;
+ box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.06);
+ transition: transform 0.12s ease, box-shadow 0.12s ease;
+
+ .quick-entry-card__img {
+ width: 100%;
+ height: 100%;
+ display: block;
+ }
+
+ .quick-entry-card__living-tag {
+ width: 72rpx;
+ height: 24rpx;
+ position: absolute;
+ right: 10rpx;
+ top: 10rpx;
+ }
+ }
+
+ .quick-entry-card--pressed {
+ transform: scale(0.97);
+ box-shadow: 0 3rpx 10rpx rgba(0, 0, 0, 0.05);
+ }
+ }
+
/* Sections */
.section {
diff --git a/traditionalApp/pages/mine/mine.vue b/traditionalApp/pages/mine/mine.vue
index d0d8ac3..55c8127 100644
--- a/traditionalApp/pages/mine/mine.vue
+++ b/traditionalApp/pages/mine/mine.vue
@@ -137,6 +137,7 @@
{ title: '我的下载', icon: '/static/image/mine/downLoad.png', url: '/pages/downloads/downloads', requiresLogin: true },
{ title: '我的患者', icon: '/static/image/mine/person.png', url: '/pages/myPatients/myPatients', requiresLogin: true },
{ title: '诊所信息', icon: '/static/image/mine/hosptial.png', url: '/pages/clinicInfo/clinicInfo', requiresLogin: true },
+ { title: '银行卡设置', icon: '/static/image/mine/yinghangka.png', url: '/pages/bankCard/bankCard', requiresLogin: true },
{ title: '二维码', icon: '/static/image/mine/code.png', url: '/pages/inviteUser/inviteUser', requiresLogin: true },
]
}
@@ -149,11 +150,19 @@
]
})
+const WEIZAN_LIVE_STATS_URL = 'https://www.vzan.com'
+const WEIZAN_LIVE_ORDER_URL = 'https://www.vzan.com'
+
const bottomMenuList = computed<MenuItem[]>(() => {
const baseList = [
{ title: '我的课程', icon: '/static/image/mine/myClass.png', url: '/pages/myCourses/myCourses', iconClass: 'icon-myclass', requiresLogin: true },
{ title: '帮助中心', icon: '/static/image/mine/help.png', url: '/pages/helpCenter/helpCenter', iconClass: 'icon-help' },
{ title: '意见反馈', icon: '/static/image/mine/message.png', url: '/pages/feedback/feedback', iconClass: 'icon-feedback' },
+ ]
+ const liveOrderItem = { title: '直播订单', icon: '/static/image/mine/dingdan.png', url: buildWebviewUrl(WEIZAN_LIVE_ORDER_URL, '直播订单'), iconClass: 'icon-live-order', requiresLogin: true }
+ const liveMenuList = [
+ { title: '直播统计', icon: '/static/image/mine/tongji.png', url: buildWebviewUrl(WEIZAN_LIVE_STATS_URL, '直播统计'), iconClass: 'icon-live-stats', requiresLogin: true },
+ liveOrderItem,
]
const agreementList = [
{ title: '用户协议', icon: '/static/image/mine/coustom.png', url: '/pages/protocol/protocol?type=user', iconClass: 'icon-user-agree' },
@@ -161,15 +170,27 @@
]
if (userRole.value === 'doctor') {
return [
+ ...liveMenuList,
...baseList,
{ title: '平台资质', icon: '/static/image/mine/chimaping.png', url: '/pages/qualification/qualification', iconClass: 'icon-platform' },
...agreementList,
]
}
- return [...baseList, ...agreementList]
+ return [
+ liveOrderItem,
+ ...baseList,
+ ...agreementList,
+ ]
})
/**
+ * 生成微赞 H5 内嵌页地址。
+ */
+function buildWebviewUrl(url: string, title: string) {
+ return `/pages/webview/webview?url=${encodeURIComponent(url)}&title=${encodeURIComponent(title)}`
+}
+
+/**
* 创建个人中心默认数据,便于首屏兜底。
*/
function createDefaultUserCenter(): UserCenterData {
diff --git a/traditionalApp/pages/publish/publish.vue b/traditionalApp/pages/publish/publish.vue
index b28f452..9f48c75 100644
--- a/traditionalApp/pages/publish/publish.vue
+++ b/traditionalApp/pages/publish/publish.vue
@@ -5,7 +5,9 @@
<!-- 搜索头部区域/导航栏 -->
<view class="nav-bar">
- <text class="nav-title">健康科普</text>
+ <image class="back-icon" src="/static/image/home/left.png" mode="aspectFit" @tap="goBack"></image>
+ <text class="nav-title">健康知识</text>
+ <view class="nav-right-placeholder"></view>
</view>
<!-- 科普内容区域 -->
@@ -19,7 +21,7 @@
</view>
</view>
<view v-else-if="loading" class="status-text">加载中...</view>
- <view v-else class="status-text">暂无健康科普内容</view>
+ <view v-else class="status-text">暂无健康知识内容</view>
</view>
</view>
</template>
@@ -43,7 +45,7 @@
const defaultCoverImage = '/static/image/home/zixun.png'
/**
- * 页面加载时初始化状态栏高度并拉取健康科普列表。
+ * 页面加载时初始化状态栏高度并拉取健康知识列表。
*/
onMounted(() => {
uni.getSystemInfo({
@@ -53,6 +55,20 @@
});
loadPopularizationList()
});
+
+/**
+ * 返回上一页;没有历史栈时回到养生课程中转页。
+ */
+function goBack() {
+ const currentPages = getCurrentPages()
+ if (currentPages.length > 1) {
+ uni.navigateBack()
+ return
+ }
+ uni.switchTab({
+ url: '/pages/healthCoursesHub/healthCoursesHub'
+ })
+}
/**
* 下拉刷新健康科普列表,并在结束后关闭原生刷新状态。
@@ -112,15 +128,29 @@
height: 88rpx;
display: flex;
align-items: center;
- justify-content: center;
+ justify-content: space-between;
+ padding: 0 32rpx;
+}
+
+.back-icon {
+ width: 40rpx;
+ height: 40rpx;
+ flex-shrink: 0;
}
.nav-title {
+ flex: 1;
+ text-align: center;
font-size: 36rpx;
font-weight: 600;
color: #333;
}
+.nav-right-placeholder {
+ width: 40rpx;
+ flex-shrink: 0;
+}
+
.content {
margin-top: 32rpx;
padding: 0 32rpx;
--
Gitblit v1.7.1