Browse Source

feat: page-product;

fox
Fox-33 1 month ago
parent
commit
ad2db76ed0
43 changed files with 1505 additions and 121 deletions
  1. +7
    -3
      components/base/navbar.vue
  2. +11
    -2
      components/base/tabbar.vue
  3. +144
    -0
      components/product/courseLiveCard.vue
  4. +120
    -0
      components/product/courseRecommendView.vue
  5. +77
    -0
      components/product/detectCustomView.vue
  6. +154
    -0
      components/product/detectPackageView.vue
  7. +94
    -0
      components/product/detectRecommendSwiper.vue
  8. +159
    -0
      components/product/nutrientCustomView.vue
  9. +92
    -0
      components/product/nutrientRecommendSwiper.vue
  10. +136
    -0
      components/product/productCard.vue
  11. +56
    -0
      components/product/sectionHeader.vue
  12. +86
    -0
      components/product/styles/tab.scss
  13. +85
    -0
      components/product/tabCourse.vue
  14. +95
    -0
      components/product/tabDetect.vue
  15. +89
    -0
      components/product/tabNutrient.vue
  16. +6
    -111
      components/report/recommendTest.vue
  17. +1
    -1
      pages.json
  18. +89
    -0
      pages/index/product.vue
  19. +1
    -1
      pages_order/report/compare/result.vue
  20. +1
    -1
      pages_order/report/compare/select.vue
  21. +1
    -1
      pages_order/report/detail/index.vue
  22. +1
    -1
      pages_order/report/nutritionProgram/index.vue
  23. BIN
      pages_order/static/product/course-1.png
  24. BIN
      pages_order/static/product/course-2.png
  25. BIN
      pages_order/static/product/course-3.png
  26. BIN
      pages_order/static/product/course-4.png
  27. BIN
      pages_order/static/product/detect-1.png
  28. BIN
      pages_order/static/product/detect-10.png
  29. BIN
      pages_order/static/product/detect-11.png
  30. BIN
      pages_order/static/product/detect-12.png
  31. BIN
      pages_order/static/product/detect-13.png
  32. BIN
      pages_order/static/product/detect-14.png
  33. BIN
      pages_order/static/product/detect-15.png
  34. BIN
      pages_order/static/product/detect-2.png
  35. BIN
      pages_order/static/product/detect-3.png
  36. BIN
      pages_order/static/product/detect-4.png
  37. BIN
      pages_order/static/product/detect-5.png
  38. BIN
      pages_order/static/product/detect-6.png
  39. BIN
      pages_order/static/product/detect-7.png
  40. BIN
      pages_order/static/product/detect-8.png
  41. BIN
      pages_order/static/product/detect-9.png
  42. BIN
      pages_order/static/product/intro-bg.png
  43. BIN
      pages_order/static/product/live.png

+ 7
- 3
components/base/navbar.vue View File

@ -1,7 +1,7 @@
<template>
<!-- <view class="navbar"
:style="{backgroundColor : bgColor}"> -->
<view class="title navbar__view"
<view class="title nav-bar__view"
:style="{backgroundColor : bgColor,color}">
<view class="left">
@ -15,7 +15,11 @@
@click="$emit('leftClick')"
:color="color" size="46rpx"></uv-icon>
</view>
<view>{{ title }}</view>
<view>
<slot>
{{ title }}
</slot>
</view>
<view class="icon">
<uv-icon name="search"
@ -110,7 +114,7 @@
left: 0;
padding-top: calc(var(--status-bar-height) + 20rpx);
width: 100%;
height: 100rpx;
height: $navbar-height;
background-color: #fff;
display: flex;
justify-content: center;


+ 11
- 2
components/base/tabbar.vue View File

@ -26,19 +26,28 @@
props: ['select'],
data() {
return {
list: [{
list: [
{
"selectedIconPath": "/static/image/tabbar/home-active.png",
"iconPath": "/static/image/tabbar/home.png",
"pagePath": "/pages/index/index",
"title": "首页",
key: 'home',
},{
},
{
"selectedIconPath": "/static/image/tabbar/report-active.png",
"iconPath": "/static/image/tabbar/report.png",
"pagePath": "/pages/index/report",
"title": "报告",
key: 'report',
},
{
"selectedIconPath": "/static/image/tabbar/product-active.png",
"iconPath": "/static/image/tabbar/product.png",
"pagePath": "/pages/index/product",
"title": "产品",
key: 'product',
},
]
};
},


+ 144
- 0
components/product/courseLiveCard.vue View File

@ -0,0 +1,144 @@
<template>
<view class="card">
<image class="card-bg" :src="data.url" mode="scaleToFill"></image>
<view class="flex card-bar">
<view class="flex countdown" v-if="countdown">
距开始<text class="count">{{ countdown.day }}</text>
<text class="count">{{ countdown.hour }}</text>:<text class="count">{{ countdown.minute }}</text>:<text class="count">{{ countdown.second }}</text>
</view>
<button class="flex btn">进入直播间</button>
</view>
</view>
</template>
<script>
import dayjs from "dayjs";
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
dayjs.extend(isSameOrBefore);
export default {
props: {
data: {
type: Object,
default() {
return {}
}
}
},
data() {
return {
timer: null,
countdown: null,
}
},
watch: {
data: {
handler() {
this.updateCountdown()
},
immediate: true,
deep: true,
}
},
methods: {
updateCountdown() {
this.timer && clearTimeout(this.timer)
let current = dayjs()
let startTime = dayjs(this.data.startTime)
if (startTime.isSameOrBefore(current)) {
this.countdown = null
return
}
let countdown = {
day: 0,
hour: 0,
minute: 0,
second: 0,
}
let day = Math.floor(startTime.diff(current, 'day', true))
countdown.day = day
let hour = Math.floor(startTime.diff(current, 'hour', true))
countdown.hour = hour - day * 24
countdown.hour = countdown.hour < 10 ? `0${countdown.hour}` : countdown.hour
let minute = Math.floor(startTime.diff(current, 'minute', true))
countdown.minute = minute - hour * 60
countdown.minute = countdown.minute < 10 ? `0${countdown.minute}` : countdown.minute
let second = startTime.diff(current, 'second')
countdown.second = second - minute * 60
countdown.second = countdown.second < 10 ? `0${countdown.second}` : countdown.second
this.countdown = countdown
this.timer = setTimeout(() => {
this.updateCountdown()
}, 1000)
},
},
}
</script>
<style scoped lang="scss">
.card {
position: relative;
width: 100%;
height: 316rpx;
border-radius: 40rpx;
overflow: hidden;
box-shadow: -5rpx -5rpx 10rpx 0 #FFFFFF,
10rpx 10rpx 20rpx 0 #AAAACC80,
4rpx 4rpx 10rpx 0 #AAAACC40,
-2rpx -2rpx 5rpx 0 #FFFFFF;
&-bg {
width: 100%;
height: 100%;
}
&-bar {
justify-content: space-between;
position: absolute;
left: 0;
bottom: 0;
width: 100%;
padding: 13rpx 32rpx;
background: #FFFFFF69;
box-sizing: border-box;
.countdown {
font-family: PingFang SC;
font-weight: 400;
font-size: 28rpx;
line-height: 1.4;
color: #252545;
.count {
margin: 0 8rpx;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 40rpx;
height: 40rpx;
background: #0000002B;
border-radius: 8rpx;
}
}
.btn {
font-family: PingFang SC;
font-weight: 600;
font-size: 24rpx;
line-height: 1.5;
color: #252545;
padding: 7rpx 16rpx;
border-radius: 24rpx;
background: #FFFFFF;
}
}
}
</style>

+ 120
- 0
components/product/courseRecommendView.vue View File

@ -0,0 +1,120 @@
<template>
<view>
<view class="tabs">
<uv-tabs
:list="tabs"
:activeStyle="{
'font-family': 'PingFang SC',
'font-weight': 600,
'font-size': '28rpx',
'line-height': 1.5,
'color': '#FFFFFF',
'background-color': '#252545',
'border-radius': '32rpx',
'padding': '9rpx 40rpx',
}"
:inactiveStyle="{
'font-family': 'PingFang SC',
'font-weight': 400,
'font-size': '28rpx',
'line-height': 1.5,
'color': '#252545',
'background-color': '#E5E4EB',
'border-radius': '32rpx',
'padding': '9rpx 40rpx',
}"
lineWidth="0"
lineHeight="0"
@change="onChange"
></uv-tabs>
</view>
<view class="content">
<view v-for="item in list" :key="item.id">
<productCard
:data="item"
cardStyle="width: 100%; height: 210px;"
imgStyle="width: 100%; height: 110px;"
></productCard>
</view>
</view>
</view>
</template>
<script>
import productCard from '@/components/product/productCard.vue'
export default {
components: {
productCard,
},
data() {
return {
tabs: [],
current: 0,
list: [],
}
},
mounted() {
this.tabs = [
{ name: '生理' },
{ name: '心理' },
]
this.list = [
{
id: '001',
url: '/pages_order/static/product/course-1.png',
productName: '情绪管理课程',
sales: 24770,
productPriceVal: 688.00,
productPriceBef: 1664,
},
{
id: '002',
url: '/pages_order/static/product/course-2.png',
productName: '我认知与成长课程',
sales: 24770,
productPriceVal: 688.00,
productPriceBef: 1664,
},
{
id: '003',
url: '/pages_order/static/product/course-3.png',
productName: '情绪管理课程',
sales: 24770,
productPriceVal: 688.00,
productPriceBef: 1664,
},
{
id: '004',
url: '/pages_order/static/product/course-4.png',
productName: '我认知与成长课程',
sales: 24770,
productPriceVal: 688.00,
productPriceBef: 1664,
},
]
},
methods: {
onChange(e) {
console.log('current', e.index)
this.current = e.index
// todo
},
},
}
</script>
<style scoped lang="scss">
.tabs {
margin: 0 5px;
}
.content {
padding: 16rpx 32rpx 24rpx 32rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32rpx;
}
</style>

+ 77
- 0
components/product/detectCustomView.vue View File

@ -0,0 +1,77 @@
<template>
<view class="view">
<view v-for="item in list" :key="item.id">
<productCard
:data="item"
cardStyle="width: 100%; height: 210px;"
imgStyle="width: 100%; height: 110px;"
></productCard>
</view>
</view>
</template>
<script>
import productCard from '@/components/product/productCard.vue'
export default {
components: {
productCard,
},
data() {
return {
list: [],
}
},
mounted() {
this.list = [
{
id: '001',
url: '/pages_order/static/product/detect-3.png',
productName: '腹部超声',
sales: 24770,
productPriceVal: 688.00,
productPriceBef: 1664,
},
{
id: '002',
url: '/pages_order/static/product/detect-4.png',
productName: '血常规',
sales: 24770,
productPriceVal: 688.00,
productPriceBef: 1664,
},
{
id: '003',
url: '/pages_order/static/product/detect-3.png',
productName: '腹部超声',
sales: 24770,
productPriceVal: 688.00,
productPriceBef: 1664,
},
]
},
methods: {
onLevel1TabChange(e) {
console.log('level1', e.index)
this.level1 = e.index
this.level2 = this.tabs[this.level1].children?.length ? 0 : null
// todo
},
onLevel2TabChange(e) {
console.log('level2', e.index)
this.level2 = e.index
// todo
},
},
}
</script>
<style scoped lang="scss">
.view {
padding: 0 32rpx 24rpx 32rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32rpx;
}
</style>

+ 154
- 0
components/product/detectPackageView.vue View File

@ -0,0 +1,154 @@
<template>
<view class="package__view">
<view class="flex package" v-for="packageItem in list" :key="packageItem.id">
<view class="flex flex-column package-info">
<view class="package-info-title">{{ packageItem.title }}</view>
<view class="package-info-desc">{{ packageItem.desc }}</view>
</view>
<view class="package-detail">
<view class="package-detail-item" v-for="item in packageItem.children" :key="item.id">
<view class="package-detail-item-img">
<image class="img" :src="item.url" mode="aspectFill"></image>
</view>
<view class="package-detail-item-label">{{ item.label }}</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
list: [],
}
},
mounted() {
this.list = [
{
id: '001',
title: '儿童体检套餐',
desc: '适合3-12岁的儿童',
children: [
{ id: '0011', label: '腹部超声', url: '/pages_order/static/product/detect-3.png', },
{ id: '0012', label: '视力检查', url: '/pages_order/static/product/detect-5.png', },
{ id: '0013', label: '骨龄检测', url: '/pages_order/static/product/detect-6.png', },
{ id: '0014', label: '生长激素水平检测', url: '/pages_order/static/product/detect-7.png', },
],
},
{
id: '002',
title: '青少年体检套餐',
desc: '适用于13-18岁的青少年',
children: [
{ id: '0021', label: '身高', url: '/pages_order/static/product/detect-8.png', },
{ id: '0022', label: '血常规', url: '/pages_order/static/product/detect-9.png', },
{ id: '0023', label: '胸部 X 光', url: '/pages_order/static/product/detect-10.png', },
{ id: '0024', label: '心电图检查', url: '/pages_order/static/product/detect-11.png', },
],
},
{
id: '003',
title: '孕产妇体检套餐',
desc: '适用于孕期女性',
children: [
{ id: '0031', label: '胎儿超声检查', url: '/pages_order/static/product/detect-12.png', },
{ id: '0032', label: '唐氏筛查', url: '/pages_order/static/product/detect-13.png', },
{ id: '0033', label: '糖筛查试验', url: '/pages_order/static/product/detect-14.png', },
{ id: '0034', label: '血压', url: '/pages_order/static/product/detect-15.png', },
],
},
]
},
}
</script>
<style scoped lang="scss">
.package__view {
width: 100%;
padding: 0 32rpx;
box-sizing: border-box;
}
.package {
width: 100%;
padding: 8rpx 8rpx 8rpx 16rpx;
box-sizing: border-box;
background: #FAFAFF;
border: 2rpx solid #FFFFFF;
border-radius: 24rpx;
& + & {
margin-top: 32rpx;
}
&-info {
flex: none;
display: inline-flex;
align-items: flex-start;
width: 200rpx;
font-family: PingFang SC;
line-height: 1.4;
&-title {
font-weight: 600;
font-size: 28rpx;
color: #000000;
}
&-desc {
margin-top: 8rpx;
font-weight: 400;
font-size: 24rpx;
color: #8B8B8B;
}
}
&-detail {
flex: 1;
white-space: nowrap;
overflow-x: auto;
padding: 16rpx;
background-image: linear-gradient(#FAFAFF, #F3F3F3);
border-radius: 16rpx;
&-item {
$size: 120rpx;
& + & {
margin-left: 16rpx;
}
display: inline-block;
width: $size;
&-img {
width: $size;
height: $size;
border: 2rpx solid #E6E6E6;
border-radius: 8rpx;
overflow: hidden;
.img {
width: 100%;
height: 100%;
}
}
&-label {
margin-top: 8rpx;
font-family: PingFang SC;
font-weight: 400;
font-size: 24rpx;
line-height: 1;
color: #8B8B8B;
width: 100%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
}
}
}
</style>

+ 94
- 0
components/product/detectRecommendSwiper.vue View File

@ -0,0 +1,94 @@
<template>
<view style="padding: 0 32rpx;">
<swiper
class="swiper"
:current="current"
:autoplay="false"
:display-multiple-items="2.09"
@change="onChange"
>
<swiper-item v-for="item in list" :key="item.id" style="display: inline-block;">
<view class="swiper-item">
<productCard
:data="item"
cardStyle="width: 100%; height: 192px;"
imgStyle="width: 100%; height: 110px;"
></productCard>
</view>
</swiper-item>
<swiper-item style="display: inline-block;">
<view class="swiper-item"></view>
</swiper-item>
<swiper-item style="display: inline-block;">
<view class="swiper-item"></view>
</swiper-item>
</swiper>
<indicator :current="current" :length="list.length" @click="current = $event"></indicator>
</view>
</template>
<script>
import productCard from '@/components/product/productCard.vue'
import indicator from '@/components/home/indicator.vue'
export default {
components: {
productCard,
indicator,
},
data() {
return {
current: 0,
list: [],
}
},
mounted() {
this.list = [
{
id: '001',
url: '/pages_order/static/product/detect-1.png',
productName: '心电图检查',
productPriceVal: 688.00,
productPriceBef: 1664,
},
{
id: '002',
url: '/pages_order/static/product/detect-2.png',
productName: '甲状腺功能检查',
productPriceVal: 688.00,
productPriceBef: 1664,
},
{
id: '003',
url: '/pages_order/static/product/detect-1.png',
productName: '心电图检查',
productPriceVal: 688.00,
productPriceBef: 1664,
},
]
},
methods: {
onChange(e) {
this.current = e.detail.current
}
},
}
</script>
<style scoped lang="scss">
.swiper {
width: 100vw;
height: 194px;
margin-bottom: 24rpx;
&-item {
width: 100%;
height: 460rpx;
padding-right: 32rpx;
box-sizing: border-box;
}
}
</style>

+ 159
- 0
components/product/nutrientCustomView.vue View File

@ -0,0 +1,159 @@
<template>
<view>
<view class="tabs">
<uv-tabs
:list="tabs"
:activeStyle="{
'font-family': 'PingFang SC',
'font-weight': 600,
'font-size': '28rpx',
'line-height': 1.5,
'color': '#FFFFFF',
'background-color': '#252545',
'border-radius': '32rpx',
'padding': '9rpx 40rpx',
}"
:inactiveStyle="{
'font-family': 'PingFang SC',
'font-weight': 400,
'font-size': '28rpx',
'line-height': 1.5,
'color': '#252545',
'background-color': '#E5E4EB',
'border-radius': '32rpx',
'padding': '9rpx 40rpx',
}"
lineWidth="0"
lineHeight="0"
@change="onLevel1TabChange"
></uv-tabs>
<view v-if="tabs[level1] && tabs[level1].children && tabs[level1].children.length"
class="tabs-second"
>
<uv-tabs
:list="tabs[level1].children"
:activeStyle="{
'font-family': 'PingFang SC',
'font-weight': 600,
'font-size': '28rpx',
'line-height': 1.5,
'color': '#7451DE',
}"
:inactiveStyle="{
'font-family': 'PingFang SC',
'font-weight': 400,
'font-size': '28rpx',
'line-height': 1.5,
'color': '#252545',
}"
lineWidth="0"
lineHeight="0"
@change="onLevel2TabChange"
></uv-tabs>
</view>
</view>
<view class="content">
<view v-for="item in list" :key="item.id">
<productCard
:data="item"
cardStyle="width: 100%; height: 210px;"
imgStyle="width: 100%; height: 110px;"
></productCard>
</view>
</view>
</view>
</template>
<script>
import productCard from '@/components/product/productCard.vue'
export default {
components: {
productCard,
},
data() {
return {
tabs: [],
level1: 0,
level2: null,
list: [],
}
},
mounted() {
let children = [
{ name: '草本类' },
{ name: '蔬菜类' },
{ name: '维生素类' },
{ name: '草本类' },
{ name: '蔬菜类' },
]
this.tabs = [
{ name: '全部' },
{ name: '皮肤', children, },
{ name: '身材管理', children, },
{ name: '精力', children, },
]
this.list = [
{
id: '001',
url: '/pages_order/static/index/recommend-pic.png',
productName: '月度装定制营养包',
sales: 24770,
productPriceVal: 688.00,
productPriceBef: 1664,
},
{
id: '002',
url: '/pages_order/static/index/recommend-pic.png',
productName: '月度装定制营养包',
sales: 24770,
productPriceVal: 688.00,
productPriceBef: 1664,
},
{
id: '003',
url: '/pages_order/static/index/recommend-pic.png',
productName: '月度装定制营养包',
sales: 24770,
productPriceVal: 688.00,
productPriceBef: 1664,
},
]
},
methods: {
onLevel1TabChange(e) {
console.log('level1', e.index)
this.level1 = e.index
this.level2 = this.tabs[this.level1].children?.length ? 0 : null
// todo
},
onLevel2TabChange(e) {
console.log('level2', e.index)
this.level2 = e.index
// todo
},
},
}
</script>
<style scoped lang="scss">
.tabs {
margin: 0 5px;
&-second {
margin: 0 11px;
padding-left: 9px;
background: #E5E4EB;
border-radius: 16rpx;
}
}
.content {
padding: 24rpx 32rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32rpx;
}
</style>

+ 92
- 0
components/product/nutrientRecommendSwiper.vue View File

@ -0,0 +1,92 @@
<template>
<view>
<swiper
class="swiper"
:current="current"
:autoplay="false"
:display-multiple-items="1.8"
@change="onChange"
>
<swiper-item v-for="item in list" :key="item.id" style="display: inline-block;">
<view class="swiper-item">
<productCard
:data="item"
cardStyle="width: 100%; height: 228px;"
imgStyle="width: 100%; height: 130px;"
></productCard>
</view>
</swiper-item>
<swiper-item style="display: inline-block;">
<view class="swiper-item"></view>
</swiper-item>
</swiper>
<indicator :current="current" :length="list.length" @click="current = $event"></indicator>
</view>
</template>
<script>
import productCard from '@/components/product/productCard.vue'
import indicator from '@/components/home/indicator.vue'
export default {
components: {
productCard,
indicator,
},
data() {
return {
current: 0,
list: [],
}
},
mounted() {
this.list = [
{
id: '001',
url: '/pages_order/static/index/recommend-pic.png',
productName: '月度装定制营养包',
sales: 24770,
productPriceVal: 688.00,
productPriceBef: 1664,
},
{
id: '002',
url: '/pages_order/static/index/recommend-pic.png',
productName: '月度装定制营养包',
sales: 24770,
productPriceVal: 688.00,
productPriceBef: 1664,
},
{
id: '003',
url: '/pages_order/static/index/recommend-pic.png',
productName: '月度装定制营养包',
sales: 24770,
productPriceVal: 688.00,
productPriceBef: 1664,
},
]
},
methods: {
onChange(e) {
this.current = e.detail.current
}
},
}
</script>
<style scoped lang="scss">
.swiper {
width: 100vw;
height: 230px;
margin-bottom: 24rpx;
&-item {
height: 460rpx;
padding-left: 32rpx;
}
}
</style>

+ 136
- 0
components/product/productCard.vue View File

@ -0,0 +1,136 @@
<template>
<view class="card product-card__view" :style="cardStyle">
<view class="card-img flex" :style="imgStyle">
<image class="img" :src="data.url" mode="scaleToFill"></image>
</view>
<view class="card-detail">
<view class="product-name">{{ data.productName }}</view>
<view class="product-sales" v-if="data.sales">{{ `已售出${data.sales}+` }}</view>
<view class="flex product-price">
<view>
<view class="product-price-val">
<text>¥</text>
<text class="highlight">{{ data.productPriceVal }}</text>
<text>/</text>
</view>
<view class="product-price-bef">
{{ `¥${data.productPriceBef}/份` }}
</view>
</view>
<button class="btn flex" @click="addToCart(data.id)">
<image class="btn-img" src="@/pages_order/static/report/plus.png" mode="widthFix"></image>
</button>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
data: {
type: Object,
default() {
return {}
},
},
cardStyle: {
type: String,
default: '',
},
imgStyle: {
type: String,
default: '',
}
},
methods: {
addToCart(id) {
// todo
},
},
}
</script>
<style scoped lang="scss">
.card {
font-size: 0;
height: 420rpx;
background-image: linear-gradient(#FAFAFF, #F3F3F3);
border: 2rpx solid #FFFFFF;
border-radius: 32rpx;
overflow: hidden;
&-img {
width: 100%;
height: 220rpx;
.img {
width: 100%;
height: 100%;
}
}
&-detail {
padding: 16rpx 24rpx 0 24rpx;
.product {
&-name {
font-family: PingFang SC;
font-weight: 600;
font-size: 28rpx;
line-height: 1.4;
color: #000000;
}
&-sales {
margin-top: 8rpx;
font-family: PingFang SC;
font-weight: 400;
font-size: 24rpx;
line-height: 1;
color: #8B8B8B;
}
&-price {
margin-top: 16rpx;
justify-content: space-between;
&-val {
font-family: PingFang SC;
font-weight: 600;
font-size: 24rpx;
line-height: 1.4;
color: $uni-color;
.highlight {
font-size: 32rpx;
margin: 0 8rpx;
}
}
&-bef {
margin-top: 4rpx;
text-decoration: line-through;
font-family: PingFang SC;
font-weight: 400;
font-size: 24rpx;
line-height: 1;
color: #8B8B8B;
}
.btn {
width: 56rpx;
height: 56rpx;
border-radius: 50%;
background: $uni-color;
&-img {
width: 20rpx;
height: 20rpx;
}
}
}
}
}
}
</style>

+ 56
- 0
components/product/sectionHeader.vue View File

@ -0,0 +1,56 @@
<template>
<view class="header-box" :style="style">
<view class="flex header">
<view class="title">{{ title }}</view>
<button class="flex btn" @click="$emit('showAll')">
<text class="btn-text">查看全部</text>
<uv-icon name="arrow-right" color="#C6C6C6" size="24rpx"></uv-icon>
</button>
</view>
</view>
</template>
<script>
export default {
props: {
title: {
type: String,
default: '',
},
style: {
type: String,
default: '',
}
},
}
</script>
<style scoped lang="scss">
.header {
&-box {
padding: 0 32rpx 24rpx 32rpx;
}
justify-content: space-between;
.title {
font-family: PingFang SC;
font-size: 36rpx;
font-weight: 600;
line-height: 1.2;
color: #252545;
}
.btn {
font-family: PingFang SC;
font-size: 24rpx;
font-weight: 400;
line-height: 1.4;
color: #A8A8A8;
&-text {
margin-right: 4rpx;
}
}
}
</style>

+ 86
- 0
components/product/styles/tab.scss View File

@ -0,0 +1,86 @@
.search {
padding: 30rpx 32rpx 0 32rpx;
/deep/ .uv-search__content__icon {
margin-top: 2rpx;
}
}
.intro {
position: relative;
margin: 38rpx 32rpx 0 32rpx;
width: calc(100% - 32rpx * 2);
height: 316rpx;
background: #E5E5E5;
border-radius: 40rpx;
box-shadow: -5rpx -5rpx 10rpx 0 #FFFFFF,
10rpx 10rpx 20rpx 0 #AAAACC80,
4rpx 4rpx 10rpx 0 #AAAACC40,
-2rpx -2rpx 5rpx 0 #FFFFFF;
.bg {
float: right;
width: 100%;
height: 100%;
}
.content {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
padding: 40rpx;
box-sizing: border-box;
.font1 {
font-size: 28rpx;
font-weight: 600;
line-height: 1.4;
font-family: PingFang SC;
color: #252545;
}
.font2 {
font-size: 36rpx;
font-weight: 600;
line-height: 1.4;
font-family: PingFang SC;
color: transparent;
background-image: linear-gradient(to right, #4B348F, #845CFA);
background-clip: text;
display: inline-block;
}
.font3 {
font-size: 24rpx;
font-weight: 400;
line-height: 1.4;
font-family: PingFang SC;
color: #252545;
}
.btn {
margin-top: 56rpx;
display: inline-flex;
padding: 9rpx 22rpx;
font-size: 24rpx;
font-weight: 400;
font-family: PingFang SC;
line-height: 1.4;
color: #252545;
border-radius: 28rpx;
border: 2rpx solid #252545;
&-icon {
width: 28rpx;
height: 28rpx;
margin-right: 8rpx;
}
}
}
}
.section {
margin-top: 48rpx;
}

+ 85
- 0
components/product/tabCourse.vue View File

@ -0,0 +1,85 @@
<template>
<view>
<!-- 搜索栏 -->
<view class="search">
<uv-search
v-model="keyword"
placeholder="请输入要查询的内容"
placeholderColor="#C6C6C6"
searchIconColor="#8B8B8B"
:searchIconSize="40"
:inputStyle="{
'font-family': 'PingFang SC',
'font-weight': 400,
'font-size': '28rpx',
'line-height': 1.4,
'padding': '12rpx 0',
}"
bgColor="#fff"
:showAction="false"
@search="search"
></uv-search>
</view>
<view class="section">
<!-- todo -->
<sectionHeader :title="`直播 · 05月11日 12:00`" @showAll="jumpToLive"></sectionHeader>
<view style="padding: 0 32rpx;">
<courseLiveCard v-for="item in liveList" :key="item.id" :data="item"></courseLiveCard>
</view>
</view>
<view class="section">
<sectionHeader style="padding-bottom: 16rpx;" title="推荐课程" @showAll="jumpToRecommendCourse"></sectionHeader>
<courseRecommendView></courseRecommendView>
</view>
</view>
</template>
<script>
import sectionHeader from './sectionHeader.vue'
import courseLiveCard from './courseLiveCard.vue'
import courseRecommendView from './courseRecommendView.vue'
export default {
components: {
sectionHeader,
courseLiveCard,
courseRecommendView,
},
data() {
return {
keyword: '',
liveList: [],
}
},
mounted() {
this.liveList = [
{
id: '001',
url: '/pages_order/static/product/live.png',
startTime: '2025-07-25 19:30:00',
},
]
},
methods: {
//
search() {
// todo
uni.navigateTo({
url: '/pages/index/category?search=' + this.keyword
})
this.keyword = ''
},
jumpToLive() {
// todo
},
jumpToRecommendCourse() {
// todo
},
},
}
</script>
<style scoped lang="scss">
@import './styles/tab.scss';
</style>

+ 95
- 0
components/product/tabDetect.vue View File

@ -0,0 +1,95 @@
<template>
<view>
<!-- 搜索栏 -->
<view class="search">
<uv-search
v-model="keyword"
placeholder="请输入要查询的内容"
placeholderColor="#C6C6C6"
searchIconColor="#8B8B8B"
:searchIconSize="40"
:inputStyle="{
'font-family': 'PingFang SC',
'font-weight': 400,
'font-size': '28rpx',
'line-height': 1.4,
'padding': '12rpx 0',
}"
bgColor="#fff"
:showAction="false"
@search="search"
></uv-search>
</view>
<view class="intro">
<image class="bg" src="@/pages_order/static/product/intro-bg.png" mode="heightFix"></image>
<view class="content">
<view class="font1">你是独一无二的</view>
<view class="font2">你的健康数据也是</view>
<view class="font3">普兆即精准</view>
<button class="flex btn">
<image class="btn-icon" src="@/pages_order/static/index/btn-icon.png" mode="widthFix"></image>
<text>定制健康档案</text>
</button>
</view>
</view>
<view class="section">
<sectionHeader title="推荐检测" @showAll="jumpToRecommendDetect"></sectionHeader>
<detectRecommendSwiper></detectRecommendSwiper>
</view>
<view class="section">
<sectionHeader title="个性化检测包" @showAll="jumpToPersonalDetect"></sectionHeader>
<detectPackageViewVue></detectPackageViewVue>
</view>
<view class="section">
<sectionHeader title="自选检测" @showAll="jumpToCustomDetect"></sectionHeader>
<detectCustomView></detectCustomView>
</view>
</view>
</template>
<script>
import sectionHeader from './sectionHeader.vue'
import detectRecommendSwiper from './detectRecommendSwiper.vue'
import detectPackageViewVue from './detectPackageView.vue'
import detectCustomView from './detectCustomView.vue'
export default {
components: {
sectionHeader,
detectRecommendSwiper,
detectPackageViewVue,
detectCustomView,
},
data() {
return {
keyword: '',
}
},
methods: {
//
search() {
// todo
uni.navigateTo({
url: '/pages/index/category?search=' + this.keyword
})
this.keyword = ''
},
jumpToRecommendDetect() {
// todo
},
jumpToPersonalDetect() {
// todo
},
jumpToCustomDetect() {
// todo
},
},
}
</script>
<style scoped lang="scss">
@import './styles/tab.scss';
</style>

+ 89
- 0
components/product/tabNutrient.vue View File

@ -0,0 +1,89 @@
<template>
<view>
<!-- 搜索栏 -->
<view class="search">
<uv-search
v-model="keyword"
placeholder="请输入要查询的内容"
placeholderColor="#C6C6C6"
searchIconColor="#8B8B8B"
:searchIconSize="40"
:inputStyle="{
'font-family': 'PingFang SC',
'font-weight': 400,
'font-size': '28rpx',
'line-height': 1.4,
'padding': '12rpx 0',
}"
bgColor="#fff"
:showAction="false"
@search="search"
></uv-search>
</view>
<view class="intro">
<image class="bg" src="@/pages_order/static/product/intro-bg.png" mode="heightFix"></image>
<view class="content">
<view class="font1">你是独一无二的</view>
<view class="font2">你的健康数据也是</view>
<view class="font3">普兆即精准</view>
<button class="flex btn">
<image class="btn-icon" src="@/pages_order/static/index/btn-icon.png" mode="widthFix"></image>
<text>定制健康档案</text>
</button>
</view>
</view>
<view class="section">
<sectionHeader title="推荐产品" @showAll="jumpToRecommendNutrient"></sectionHeader>
<nutrientRecommendSwiper></nutrientRecommendSwiper>
</view>
<view class="section section-custom">
<sectionHeader style="padding-bottom: 16rpx;" title="自选补剂" @showAll="jumpToCustomNutrient"></sectionHeader>
<nutrientCustomView></nutrientCustomView>
</view>
</view>
</template>
<script>
import sectionHeader from './sectionHeader.vue'
import nutrientRecommendSwiper from './nutrientRecommendSwiper.vue'
import nutrientCustomView from './nutrientCustomView.vue'
export default {
components: {
sectionHeader,
nutrientRecommendSwiper,
nutrientCustomView,
},
data() {
return {
keyword: '',
}
},
mounted() {
},
methods: {
//
search() {
// todo
uni.navigateTo({
url: '/pages/index/category?search=' + this.keyword
})
this.keyword = ''
},
jumpToRecommendNutrient() {
// todo
},
jumpToCustomNutrient() {
// todo
},
},
}
</script>
<style scoped lang="scss">
@import './styles/tab.scss';
</style>

+ 6
- 111
components/report/recommendTest.vue View File

@ -5,36 +5,18 @@
<button class="btn flex"><text style="margin-right: 4rpx;">查看全部</text><uv-icon name="arrow-right" color="#C6C6C6" size="24rpx"></uv-icon></button>
</view>
<view class="content">
<view class="card" v-for="item in list" :key="item.id">
<view class="card-img flex">
<image class="img" :src="item.url" mode="aspectFit"></image>
</view>
<view class="card-detail">
<view class="product-name">{{ item.productName }}</view>
<view class="product-sales">{{ `已售出${item.sales}+单` }}</view>
<view class="flex product-price">
<view>
<view class="product-price-val">
<text>¥</text>
<text class="highlight">{{ item.productPriceVal }}</text>
<text>/</text>
</view>
<view class="product-price-bef">
{{ `¥${item.productPriceBef}/份` }}
</view>
</view>
<button class="btn flex" @click="addToCart(item.id)">
<image class="btn-img" src="@/pages_order/static/report/plus.png" mode="widthFix"></image>
</button>
</view>
</view>
</view>
<productCard v-for="item in list" :key="item.id" :data="item"></productCard>
</view>
</view>
</template>
<script>
import productCard from '@/components/product/productCard.vue'
export default {
components: {
productCard,
},
data() {
return {
list: [],
@ -60,11 +42,6 @@
},
]
},
methods: {
addToCart(id) {
// todo
},
},
}
</script>
@ -96,86 +73,4 @@
grid-column-gap: 32rpx;
}
.card {
font-size: 0;
height: 420rpx;
background-image: linear-gradient(#FAFAFF, #F3F3F3);
border: 2rpx solid #FFFFFF;
border-radius: 32rpx;
&-img {
width: 100%;
height: 220rpx;
padding: 0 20rpx;
box-sizing: border-box;
.img {
width: 100%;
height: 100%;
}
}
&-detail {
padding: 16rpx 24rpx 0 24rpx;
.product {
&-name {
font-family: PingFang SC;
font-weight: 600;
font-size: 28rpx;
line-height: 1.4;
color: #000000;
}
&-sales {
margin-top: 8rpx;
font-family: PingFang SC;
font-weight: 400;
font-size: 24rpx;
line-height: 1;
color: #8B8B8B;
}
&-price {
margin-top: 16rpx;
justify-content: space-between;
&-val {
font-family: PingFang SC;
font-weight: 600;
font-size: 24rpx;
line-height: 1.4;
color: $uni-color;
.highlight {
font-size: 32rpx;
margin: 0 8rpx;
}
}
&-bef {
margin-top: 4rpx;
text-decoration: line-through;
font-family: PingFang SC;
font-weight: 400;
font-size: 24rpx;
line-height: 1;
color: #8B8B8B;
}
.btn {
width: 56rpx;
height: 56rpx;
border-radius: 50%;
background: $uni-color;
&-img {
width: 20rpx;
height: 20rpx;
}
}
}
}
}
}
</style>

+ 1
- 1
pages.json View File

@ -13,7 +13,7 @@
}
},
{
"path": "pages/index/category",
"path": "pages/index/product",
"style": {
"navigationBarTitleText": ""
}


+ 89
- 0
pages/index/product.vue View File

@ -0,0 +1,89 @@
<template>
<view class="page__view">
<navbar bgColor="#FFFFFF" >
<view>
<uv-tabs
:current="current"
:list="tabs"
:activeStyle="{
'font-family': 'PingFang SC',
'font-weight': 600,
'font-size': '40rpx',
'line-height': 1.2,
'color': '#252545',
}"
:inactiveStyle="{
'font-family': 'PingFang SC',
'font-weight': 400,
'font-size': '32rpx',
'line-height': 1.5,
'color': '#A8A8A8',
}"
lineWidth="22rpx"
lineHeight="4rpx"
lineColor="#252545"
@change="onTabChange"
></uv-tabs>
</view>
</navbar>
<!-- 营养素 -->
<tabNutrient v-if="current == 0"></tabNutrient>
<!-- 检测 -->
<tabDetect v-else-if="current == 1"></tabDetect>
<!-- 课程 -->
<tabCourse v-else-if="current == 2"></tabCourse>
<tabber select="product" />
</view>
</template>
<script>
import tabber from '@/components/base/tabbar.vue'
import tabNutrient from '@/components/product/tabNutrient.vue'
import tabDetect from '@/components/product/tabDetect.vue'
import tabCourse from '@/components/product/tabCourse.vue'
export default {
components: {
tabNutrient,
tabDetect,
tabCourse,
tabber,
},
data() {
return {
tabs: [
{ name: '营养素' },
{ name: '检测' },
{ name: '课程' },
],
current: 0,
}
},
methods: {
onTabChange(e) {
console.log('current', e.index)
this.current = e.index
// todo
},
},
}
</script>
<style scoped lang="scss">
.page__view {
width: 100vw;
min-height: 100vh;
background-image: linear-gradient(#EAE5FF, #F3F2F7, #F3F2F7);
position: relative;
/deep/ .uv-tabs__wrapper__nav__line {
border-radius: 2rpx;
}
}
</style>

+ 1
- 1
pages_order/report/compare/result.vue View File

@ -243,7 +243,7 @@
background-color: $uni-bg-color;
position: relative;
/deep/ .navbar__view {
/deep/ .nav-bar__view {
position: fixed;
top: 0;
left: 0;


+ 1
- 1
pages_order/report/compare/select.vue View File

@ -79,7 +79,7 @@
background-color: $uni-bg-color;
position: relative;
/deep/ .navbar__view {
/deep/ .nav-bar__view {
position: fixed;
top: 0;
left: 0;


+ 1
- 1
pages_order/report/detail/index.vue View File

@ -187,7 +187,7 @@
background-color: $uni-bg-color;
position: relative;
/deep/ .navbar__view {
/deep/ .nav-bar__view {
position: fixed;
top: 0;
left: 0;


+ 1
- 1
pages_order/report/nutritionProgram/index.vue View File

@ -203,7 +203,7 @@
background-color: $uni-bg-color;
position: relative;
/deep/ .navbar__view {
/deep/ .nav-bar__view {
position: fixed;
top: 0;
left: 0;


BIN
pages_order/static/product/course-1.png View File

Before After
Width: 82  |  Height: 55  |  Size: 7.3 KiB

BIN
pages_order/static/product/course-2.png View File

Before After
Width: 83  |  Height: 55  |  Size: 5.9 KiB

BIN
pages_order/static/product/course-3.png View File

Before After
Width: 82  |  Height: 55  |  Size: 7.0 KiB

BIN
pages_order/static/product/course-4.png View File

Before After
Width: 83  |  Height: 55  |  Size: 7.0 KiB

BIN
pages_order/static/product/detect-1.png View File

Before After
Width: 82  |  Height: 55  |  Size: 11 KiB

BIN
pages_order/static/product/detect-10.png View File

Before After
Width: 480  |  Height: 361  |  Size: 207 KiB

BIN
pages_order/static/product/detect-11.png View File

Before After
Width: 480  |  Height: 320  |  Size: 170 KiB

BIN
pages_order/static/product/detect-12.png View File

Before After
Width: 480  |  Height: 720  |  Size: 282 KiB

BIN
pages_order/static/product/detect-13.png View File

Before After
Width: 768  |  Height: 1024  |  Size: 751 KiB

BIN
pages_order/static/product/detect-14.png View File

Before After
Width: 924  |  Height: 2000  |  Size: 2.2 MiB

BIN
pages_order/static/product/detect-15.png View File

Before After
Width: 355  |  Height: 2200  |  Size: 533 KiB

BIN
pages_order/static/product/detect-2.png View File

Before After
Width: 83  |  Height: 55  |  Size: 9.4 KiB

BIN
pages_order/static/product/detect-3.png View File

Before After
Width: 82  |  Height: 55  |  Size: 9.3 KiB

BIN
pages_order/static/product/detect-4.png View File

Before After
Width: 83  |  Height: 55  |  Size: 10 KiB

BIN
pages_order/static/product/detect-5.png View File

Before After
Width: 480  |  Height: 320  |  Size: 152 KiB

BIN
pages_order/static/product/detect-6.png View File

Before After
Width: 480  |  Height: 320  |  Size: 160 KiB

BIN
pages_order/static/product/detect-7.png View File

Before After
Width: 480  |  Height: 576  |  Size: 482 KiB

BIN
pages_order/static/product/detect-8.png View File

Before After
Width: 480  |  Height: 1075  |  Size: 302 KiB

BIN
pages_order/static/product/detect-9.png View File

Before After
Width: 480  |  Height: 315  |  Size: 142 KiB

BIN
pages_order/static/product/intro-bg.png View File

Before After
Width: 162  |  Height: 158  |  Size: 24 KiB

BIN
pages_order/static/product/live.png View File

Before After
Width: 172  |  Height: 79  |  Size: 19 KiB

Loading…
Cancel
Save