Browse Source

feat: page-checkup;

fox
Fox-33 3 weeks ago
parent
commit
e420f3e035
27 changed files with 3106 additions and 26 deletions
  1. +13
    -1
      pages.json
  2. +26
    -8
      pages/index/center.vue
  3. +0
    -0
      pages_order/applyService/index.vue
  4. +0
    -0
      pages_order/applyService/productCard.vue
  5. +1
    -1
      pages_order/applyService/serviceSelectPopup.vue
  6. +533
    -0
      pages_order/checkup/checkupBook/apply.vue
  7. +286
    -0
      pages_order/checkup/checkupBook/detail.vue
  8. +126
    -0
      pages_order/checkup/checkupBook/style.scss
  9. +241
    -0
      pages_order/checkup/checkupCard.vue
  10. +196
    -0
      pages_order/checkup/checkupRecords.vue
  11. +331
    -0
      pages_order/checkup/checkupReport/index.vue
  12. +336
    -0
      pages_order/checkup/checkupReport/progressCircleLine.vue
  13. +86
    -0
      pages_order/checkup/checkupReport/progressLine.vue
  14. +80
    -0
      pages_order/checkup/checkupReport/reportAbnormalView.vue
  15. +239
    -0
      pages_order/checkup/checkupReport/reportCommonView.vue
  16. +86
    -0
      pages_order/checkup/checkupReport/reportMainIndexView.vue
  17. +241
    -0
      pages_order/checkup/checkupReport/reportSummaryView.vue
  18. +17
    -0
      pages_order/checkup/checkupReport/style.scss
  19. +67
    -0
      pages_order/checkup/checkupServicePopup.vue
  20. +184
    -0
      pages_order/checkup/checkupTrackingNoPopup.vue
  21. +7
    -6
      pages_order/order/orderDetail/index.vue
  22. +1
    -1
      pages_order/order/orderList/index.vue
  23. +8
    -8
      pages_order/order/orderList/orderCard.vue
  24. +1
    -1
      pages_order/report/nutritionProgram/index.vue
  25. BIN
      pages_order/static/checkup/icon-error.png
  26. BIN
      pages_order/static/checkup/icon-success.png
  27. BIN
      pages_order/static/checkup/qrcode.png

+ 13
- 1
pages.json View File

@ -98,7 +98,19 @@
"path": "comment/commentWrite"
},
{
"path": "service/index"
"path": "applyService/index"
},
{
"path": "checkup/checkupRecords"
},
{
"path": "checkup/checkupBook/apply"
},
{
"path": "checkup/checkupBook/detail"
},
{
"path": "checkup/checkupReport/index"
},


+ 26
- 8
pages/index/center.vue View File

@ -27,7 +27,7 @@
<view class="card">
<view v-for="item in list1" :key="item.id">
<!-- todo: key -> custom -->
<view class="flex row" @click="$utils.navigateTo(item.path)">
<view class="flex row" @click="onClick(item)">
<view class="flex label">
<image class="icon" :src="item.icon" mode="scaleToFill"></image>
<view>{{ item.label }}</view>
@ -39,8 +39,7 @@
<view class="card">
<view v-for="item in list2" :key="item.id">
<!-- todo: key -> custom -->
<view class="flex row" @click="$utils.navigateTo(item.path)">
<view class="flex row" @click="onClick(item)">
<view class="flex label">
<image class="icon" :src="item.icon" mode="scaleToFill"></image>
<view>{{ item.label }}</view>
@ -60,6 +59,8 @@
</template>
<script>
import { mapState } from 'vuex'
import tabber from '@/components/base/tabbar.vue'
import reportCard from '@/components/center/reportCard.vue'
import orderCard from '@/components/center/orderCard.vue'
@ -75,8 +76,7 @@
data() {
return {
list1: [
// todo
{ id: '001', label: '检测预约', icon: '/pages_order/static/center/icon-detectBook.png', path: '/pages_order/mine/detectBook' },
{ id: '001', label: '检测预约', icon: '/pages_order/static/center/icon-detectBook.png', path: '/pages_order/checkup/checkupRecords' },
// todo
{ id: '002', label: '联系客服', icon: '/pages_order/static/center/icon-service.png', key: 'service' },
// todo
@ -85,17 +85,35 @@
{ id: '004', label: '用户须知', icon: '/pages_order/static/center/icon-userAgreement.png', path: '/pages_order/mine/userAgreement' },
],
list2: [
// todo
{ id: '005', label: '我的评价', icon: '/pages_order/static/center/icon-comment.png', path: '/pages_order/mine/detectBook' },
{ id: '005', label: '我的评价', icon: '/pages_order/static/center/icon-comment.png', key: 'comment' },
// todo
{ id: '006', label: '关于我们', icon: '/pages_order/static/center/icon-aboutUs.png', path: '/pages_order/mine/service' },
// todo
{ id: '007', label: '修改信息', icon: '/pages_order/static/center/icon-modifyInfo.png', path: '/pages_order/mine/instruc' },
// todo
{ id: '008', label: '退出登录', icon: '/pages_order/static/center/icon-logout.png', key: 'logout' },
],
}
},
computed: {
...mapState(['userInfo']),
},
methods: {
onClick(target) {
const { key, path } = target
switch(key) {
case 'comment':
this.$utils.navigateTo(`/pages_order/comment/commentRecords?creator=${this.userInfo.id}`)
break
case 'logout':
this.$store.commit('logout')
break
default:
path && this.$utils.navigateTo(path)
break
}
},
},
}
</script>


pages_order/service/index.vue → pages_order/applyService/index.vue View File


pages_order/service/productCard.vue → pages_order/applyService/productCard.vue View File


pages_order/service/serviceSelectPopup.vue → pages_order/applyService/serviceSelectPopup.vue View File

@ -140,7 +140,7 @@
// todo: submit
this.$store.commit('setApplyServiceProduct', productList)
this.$utils.navigateTo(`/pages_order/service/index?id=${this.id}&type=${type}`)
this.$utils.navigateTo(`/pages_order/applyService/index?id=${this.id}&type=${type}`)
this.close()

+ 533
- 0
pages_order/checkup/checkupBook/apply.vue View File

@ -0,0 +1,533 @@
<template>
<view class="page__view">
<navbar title="检测预约" leftClick @leftClick="$utils.navigateBack" color="#191919" bgColor="#F3F2F7" />
<view class="main">
<view class="card">
<view class="card-header">{{ typeDesc }}</view>
<view class="flex row">
<view class="row-label">联系电话</view>
<view class="row-content">{{ info.phone || '-' }}</view>
</view>
</view>
<view class="card">
<view class="card-header">预约信息</view>
<view class="form">
<uv-form
ref="form"
:model="form"
errorType="toast"
>
<!-- 自采 -->
<template v-if="type == 1">
<view class="form-item">
<uv-form-item prop="addressData" :customStyle="formItemStyle">
<view class="form-item-label">寄送地址</view>
<view class="form-item-content">
<view class="flex row" @click="jumpToSelectAddress">
<view v-if="form.addressData" class="text">{{ getAddressDesc(form.addressData) }}</view>
<view v-else class="text placeholder">请选择内容</view>
<uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
</view>
</view>
</uv-form-item>
</view>
</template>
<!-- 上门 -->
<template v-else-if="type == 2">
<view class="form-item">
<uv-form-item prop="addressData" :customStyle="formItemStyle">
<view class="form-item-label">体检地址</view>
<view class="form-item-content">
<view class="flex row" @click="jumpToSelectAddress">
<view v-if="form.addressData" class="text">{{ getAddressDesc(form.addressData) }}</view>
<view v-else class="text placeholder">请选择内容</view>
<uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
</view>
</view>
</uv-form-item>
</view>
<view class="form-item">
<uv-form-item prop="date" :customStyle="formItemStyle">
<view class="form-item-label">预约日期</view>
<view class="form-item-content">
<view class="flex row" @click="openDatePicker">
<view v-if="form.date" class="text">{{ $dayjs(form.date).format('YYYY-MM-DD') }}</view>
<view v-else class="text placeholder">请选择内容</view>
<uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
</view>
<uv-datetime-picker
ref="datetimePicker"
v-model="form.date"
mode="date"
title="预约日期"
confirmColor="#7451DE"
cancelColor="#8B8B8B"
round="32rpx"
:minDate="minTime"
@confirm="onDateChange"
></uv-datetime-picker>
</view>
</uv-form-item>
</view>
<view class="form-item">
<uv-form-item prop="timeRange" :customStyle="formItemStyle">
<view class="form-item-label">预约时段</view>
<view class="form-item-content">
<view class="flex row" @click="openTimePicker">
<view v-if="form.timeRange" class="text">{{ form.timeRange.join('') }}</view>
<view v-else class="text placeholder">请选择内容</view>
<uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
</view>
<uv-picker
ref="timeRangePicker"
:columns="timeColumns"
:defaultIndex="[0, 0]"
title="预约时段"
confirmColor="#7451DE"
cancelColor="#8B8B8B"
round="32rpx"
@change="onTimeRangeColChange"
@confirm="onTimeRangeChange"
></uv-picker>
</view>
</uv-form-item>
</view>
</template>
<!-- 到店 -->
<template v-else-if="type == 3">
<view class="form-item">
<uv-form-item prop="addressData" :customStyle="formItemStyle">
<view class="form-item-label">预约日期</view>
<view class="form-item-content">
<view class="flex row" @click="openDatePicker">
<view v-if="form.date" class="text">{{ $dayjs(form.date).format('YYYY-MM-DD') }}</view>
<view v-else class="text placeholder">请选择内容</view>
<uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
</view>
<uv-datetime-picker
ref="datetimePicker"
v-model="form.date"
mode="date"
title="预约日期"
confirmColor="#7451DE"
cancelColor="#8B8B8B"
round="32rpx"
:minDate="minTime"
@confirm="onDateChange"
></uv-datetime-picker>
</view>
</uv-form-item>
</view>
<view class="form-item">
<uv-form-item prop="timeRange" :customStyle="formItemStyle">
<view class="form-item-label">预约时段</view>
<view class="form-item-content">
<view class="flex row" @click="openTimePicker">
<view v-if="form.timeRange" class="text">{{ form.timeRange.join('~') }}</view>
<view v-else class="text placeholder">请选择内容</view>
<uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
</view>
<uv-picker
ref="timeRangePicker"
:columns="timeColumns"
:defaultIndex="[0, 0]"
title="预约时段"
confirmColor="#7451DE"
cancelColor="#8B8B8B"
round="32rpx"
@change="onTimeRangeColChange"
@confirm="onTimeRangeChange"
></uv-picker>
</view>
</uv-form-item>
</view>
<view class="form-item">
<uv-form-item prop="hospital" :customStyle="formItemStyle">
<view class="form-item-label">预约医院</view>
<view class="form-item-content">
<view class="flex row" @click="openHospitalPicker">
<view v-if="form.hospital" class="text">{{ getHospitalDesc(form.hospital) }}</view>
<view v-else class="text placeholder">请选择内容</view>
<uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
</view>
<uv-picker
ref="hospitalPicker"
:columns="[hospitalOptions]"
keyName="label"
title="预约医院"
confirmColor="#7451DE"
cancelColor="#8B8B8B"
round="32rpx"
@confirm="onHospitalChange"
></uv-picker>
</view>
</uv-form-item>
</view>
<view class="form-item">
<uv-form-item prop="name" :customStyle="formItemStyle">
<view class="form-item-label">姓名</view>
<view class="form-item-content input">
<formInput v-model="form.name" placeholder="请输入内容"></formInput>
</view>
</uv-form-item>
</view>
<view class="form-item">
<uv-form-item prop="phone" :customStyle="formItemStyle">
<view class="form-item-label">联系方式</view>
<view class="form-item-content input">
<formInput v-model="form.phone" placeholder="请输入内容"></formInput>
</view>
</uv-form-item>
</view>
</template>
</uv-form>
</view>
</view>
</view>
<view class="bottom">
<button class="btn" @click="onSubmit">提交预约</button>
</view>
</view>
</template>
<script>
import { mapState } from 'vuex'
import formInput from '@/pages_order/components/formInput.vue'
const TYPE_AND_DESC_MAPPING = {
1: '自采',
2: '上门',
3: '到店',
}
export default {
components: {
formInput,
},
data() {
return {
id: null,
type: null,
info: {},
form: {
addressData: null,
date: null,
timeRange: null,
hospital: null,
name: null,
phone: null,
},
formItemStyle: { padding: 0 },
minTime: new Date().getTime(),
hours: [],
timeColumns: [],
hospitalOptions: []
}
},
computed: {
...mapState(['configList', 'userInfo', 'payOrderProduct', 'addressInfo']),
typeDesc() {
const desc = TYPE_AND_DESC_MAPPING[this.type] || ''
return `${desc}检测`
},
},
onShow() {
console.log('onShow')
console.log('address', this.addressInfo)
this.form.addressData = this.addressInfo || null
},
onLoad(arg) {
const { id, type } = arg
this.id = id
this.type = parseInt(type)
// todo: fetch data
this.fetchCheckUpPackageInfo()
this.fetchCheckUpBookInfo()
},
onReady() {
this.setRules()
},
onUnload() {
this.$store.commit('setAddressInfo', null)
},
methods: {
setRules() {
let rules = {}
switch(this.type) {
case 1: //
rules = {
'addressData': {
type: 'object',
required: true,
message: '请选择寄送地址',
},
}
break
case 2: //
rules = {
'addressData': {
type: 'object',
required: true,
message: '请选择体检地址',
},
'date': {
type: 'string',
required: true,
message: '请选择预约日期',
},
'timeRange': {
type: 'array',
required: true,
message: '请选择预约时段',
},
}
this.setTimeColumns()
break
case 3: //
rules = {
'date': {
type: 'string',
required: true,
message: '请选择预约日期',
},
'timeRange': {
type: 'array',
required: true,
message: '请选择预约时段',
},
'hospital': {
type: 'string',
required: true,
message: '请选择预约医院',
},
'name': {
type: 'string',
required: true,
message: '请输入姓名',
},
'phone': {
type: 'string',
required: true,
message: '请输入联系方式',
},
}
this.setTimeColumns()
this.hospitalOptions = [
{ id: '001', label: '湖南省长沙市湘雅第一医院' },
{ id: '002', label: '湖南省长沙市湘雅第二医院' },
{ id: '003', label: '湖南省长沙市湘雅第三医院' },
{ id: '004', label: '湖南省长沙市湘雅第四医院' },
]
break
default:
break
}
this.$refs.form.setRules(rules);
},
getAddressDesc(data) {
if (!data) {
return ''
}
const { area, address } = data
return `${area.join('')}${address}`
},
getHospitalDesc() {
},
fixedZero(num) {
return `${num < 10 ? '0' + num : num}`
},
setTimeColumns(date) {
let currentTime = this.$dayjs()
let startHour = 8
let endHour = 22
if (date && this.$dayjs(date).isSame(currentTime, 'day')) {
let currentHour = currentTime.hour()
console.log('currentHour', currentHour)
console.log('startHour', startHour)
console.log('endHour', endHour)
console.log('date', this.$dayjs(date))
console.log('currentTime', currentTime)
console.log('isToday', this.$dayjs(date).isSame(currentTime, 'day'))
if (currentHour > endHour) {
this.timeColumns = []
return
}
if (currentHour > startHour) {
// todo: check
startHour = currentHour
}
if (startHour == endHour) {
this.timeColumns = []
return
}
}
this.hours = new Array(endHour - startHour + 1).fill(startHour).map((val, idx) => val + idx)
let startCols = this.hours.slice(0, this.hours.length - 1).map(hour => `${this.fixedZero(hour)}:00`)
let endCols = this.hours.slice(1).map(hour => `${this.fixedZero(hour)}:00`)
this.timeColumns = [startCols, endCols]
this.$refs.timeRangePicker.setColumnValues(0, startCols)
this.$refs.timeRangePicker.setColumnValues(1, endCols)
},
openTimePicker() {
this.$refs.timeRangePicker.open();
},
onTimeRangeColChange(e) {
console.log('onTimeRangeColChange', e)
if (e.columnIndex == 0) {
let startIdx = e.indexs[0]
let endCols = startIdx == this.hours.length - 1 ? [] : this.hours.slice(startIdx + 1).map(hour => `${this.fixedZero(hour)}:00`)
console.log('endCols', endCols)
this.timeColumns[1] = endCols
this.$refs.timeRangePicker.setColumnValues(1, endCols)
}
},
onTimeRangeChange(e) {
console.log('onTimeRangeChange', e)
this.form.timeRange = e.value
const [startTime, endTime] = this.form.timeRange
let startIdx = startTime ? this.timeColumns[0].findIndex(item => item === startTime) : 0
let endIdx = endTime ? this.timeColumns[1].findIndex(item => item === endTime) : 0
console.log('setIndexs', [startIdx, endIdx])
this.$refs.timeRangePicker.setIndexs([startIdx, endIdx], true);
},
openDatePicker() {
this.$refs.datetimePicker.open();
},
onDateChange(e) {
const date = e.value
this.setTimeColumns(date)
const [startTime] = this.form.timeRange || []
let dateStr = this.$dayjs(date).format('YYYY-MM-DD')
if (startTime && this.$dayjs(`${dateStr} ${startTime}`).isBefore(this.$dayjs())) {
this.form.timeRange = null
this.$refs.datetimePicker.setIndexs([0, 0]);
}
},
openHospitalPicker() {
this.$refs.hospitalPicker.open();
},
onHospitalChange(e) {
console.log('onHospitalChange', e)
this.form.hospital = e.value[0].id
console.log('form.hospital', this.form.hospital)
},
getHospitalDesc(id) {
console.log('getHospitalDesc', id)
return this.hospitalOptions.find(item => item.id === id)?.label
},
fetchCheckUpPackageInfo() {
// todo: fetch
this.info = {
phone: '0745-5982433',
}
},
fetchCheckUpBookInfo() {
// todo: fetch
const detail = {
id: '003',
url: '/pages_order/static/product/detect-8.png',
title: '孕产妇体检套餐',
userName: '周小艺',
phone: '15558661691',
amount: 688,
area: ['海南省', '海口市', '秀英区'],
address: '秀英街道5单元183室',
hospital: '001',
createTime: '2025-04-28 08:14',
appointmentDate: '2025-04-28',
appointmentTime: ['08:00', '09:00'],
type: 2,
status: 2,
}
const {
userName,
phone,
area,
address,
appointmentDate,
appointmentTime,
hospital,
} = detail
this.form = {
addressData: {
id: '001',
name: userName,
phone,
area,
address,
},
date: appointmentDate,
timeRange: appointmentTime,
hospital,
name: userName,
phone,
}
},
jumpToSelectAddress() {
this.$utils.navigateTo('/pages_order/address/addressList')
},
async onSubmit() {
try {
const res = await this.$refs.form.validate()
console.log('onSubmit res', res)
// todo
setTimeout(() => {
// todo: check
// this.$utils.navigateBack()
let id = '001'
this.$utils.redirectTo(`/pages_order/checkup/checkupBook/detail?id=${id}`)
}, 800)
} catch (err) {
console.log('onSubmit err', err)
}
},
},
}
</script>
<style scoped lang="scss">
@import './style.scss';
</style>

+ 286
- 0
pages_order/checkup/checkupBook/detail.vue View File

@ -0,0 +1,286 @@
<template>
<view class="page__view">
<navbar title="我的预约" leftClick @leftClick="$utils.navigateBack" color="#191919" bgColor="#F3F2F7" />
<template v-if="detail">
<view class="main">
<view class="flex flex-column status">
<template v-if="orderSuccess">
<image class="icon" src="@/pages_order/static/checkup/icon-success.png" mode="widthFix"></image>
</template>
<template v-else>
<image class="icon" src="@/pages_order/static/checkup/icon-error.png" mode="widthFix"></image>
</template>
<view>{{ statusDesc }}</view>
</view>
<!-- 自采 -->
<template v-if="detail.type == 1">
<view class="card">
<view class="card-header">自采检测预约信息</view>
<view class="flex row">
<view class="row-label">姓名</view>
<view class="row-content">{{ detail.userName }}</view>
</view>
<view class="flex row">
<view class="row-label">寄送地址</view>
<view class="row-content">{{ detail.addressDesc }}</view>
</view>
<view class="flex row">
<view class="row-label">电话</view>
<view class="row-content">{{ detail.phone }}</view>
</view>
</view>
</template>
<!-- 上门 -->
<template v-else-if="detail.type == 2">
<view class="card">
<view class="card-header">上门检测预约信息</view>
<view class="flex row">
<view class="row-label">姓名</view>
<view class="row-content">{{ detail.userName }}</view>
</view>
<view class="flex row">
<view class="row-label">体检地址</view>
<view class="row-content">{{ detail.addressDesc }}</view>
</view>
<view class="flex row">
<view class="row-label">时间</view>
<view class="row-content highlight">{{ detail.timeDesc }}</view>
</view>
<view class="flex row">
<view class="row-label">电话</view>
<view class="row-content">{{ detail.phone }}</view>
</view>
</view>
</template>
<!-- 到店 -->
<template v-else-if="detail.type == 3">
<view class="card">
<view class="card-header">到店检测预约信息</view>
<view class="flex row">
<view class="row-label">姓名</view>
<view class="row-content">{{ detail.userName }}</view>
</view>
<view class="flex row">
<view class="row-label">时间</view>
<view class="row-content highlight">{{ detail.timeDesc }}</view>
</view>
<view class="flex row">
<view class="row-label">电话</view>
<view class="row-content">{{ detail.phone }}</view>
</view>
<view class="flex row">
<view class="row-label">医院</view>
<view class="row-content">{{ detail.hospitalDesc }}</view>
</view>
</view>
</template>
</view>
<view class="flex bottom">
<!-- 自采 -->
<template v-if="detail.status == 1">
<button class="btn btn-plain" open-type="contact">客服</button>
<button class="btn" @click="openTrackingNoPopup">已回寄</button>
</template>
<!-- 上门, 到店, 预约失败 -->
<template v-else-if="[2, 3, 6].includes(detail.status)">
<button class="btn btn-plain" @click="onCancel">取消</button>
<button class="btn" @click="onEdit">修改</button>
</template>
<!-- 已完成 -->
<template v-else-if="detail.status == 4">
<button class="btn" @click="jumpToReportDetail">报告查看</button>
</template>
</view>
</template>
<checkupServicePopup ref="checkupServicePopup"></checkupServicePopup>
<checkupTrackingNoPopup ref="checkupTrackingNoPopup" @submitted="getData"></checkupTrackingNoPopup>
</view>
</template>
<script>
import checkupServicePopup from '@/pages_order/checkup/checkupServicePopup.vue'
import checkupTrackingNoPopup from '@/pages_order/checkup/checkupTrackingNoPopup.vue'
export default {
components: {
checkupServicePopup,
checkupTrackingNoPopup,
},
data() {
return {
id: null,
detail: null,
hospitalOptions: [
{ id: '001', label: '湖南省长沙市湘雅第一医院' },
{ id: '002', label: '湖南省长沙市湘雅第二医院' },
{ id: '003', label: '湖南省长沙市湘雅第三医院' },
{ id: '004', label: '湖南省长沙市湘雅第四医院' },
]
}
},
onLoad(arg) {
this.id = arg.id
this.getData()
},
computed: {
orderSuccess() {
// todo: check status
return this.detail?.status < 5
},
statusDesc() {
const status = this.detail?.status
if (status < 4) {
return '预约成功'
}
if (status == 4) {
return '已完成'
}
if (status == 6) {
return '预约失败'
}
return ''
},
},
methods: {
getData() {
// todo: fetch by id
let detail = {
id: '003',
url: '/pages_order/static/product/detect-8.png',
title: '孕产妇体检套餐',
userName: '周小艺',
phone: '15558661691',
amount: 688,
area: ['海南省', '海口市', '秀英区'],
address: '秀英街道5单元183室',
hospital: '001',
createTime: '2025-04-28 08:14',
appointmentDate: '2025-04-28',
appointmentTime: ['08:00', '09:00'],
type: 2,
status: 4,
}
detail.addressDesc = this.getAddressDesc(detail)
detail.timeDesc = this.getTimeDesc(detail)
detail.hospitalDesc = this.getHospitalDesc(detail)
this.detail = detail
if ([2, 3].includes(this.detail.status)) {
this.$refs.checkupServicePopup.open()
}
},
getAddressDesc(data) {
if (!data) {
return ''
}
console.log('detail', data)
const { area, address } = data
console.log('area', area, 'address', address)
return `${(area || []).join('')}${address}`
},
getTimeDesc(data) {
if (!data) {
return ''
}
const { appointmentDate, appointmentTime } = data
return `${appointmentDate} ${(appointmentTime || []).join('~')}`
},
getHospitalDesc(data) {
const { hospital } = data
return this.hospitalOptions.find(item => item.id == hospital)?.label
},
openTrackingNoPopup() {
this.$refs.checkupTrackingNoPopup.open(this.id)
},
onEdit() {
this.$utils.navigateTo(`/pages_order/checkup/checkupBook/apply?id=${this.detail.id}&type=${this.detail.type}`)
},
onCancel() {
// todo: fetch
uni.showToast({
title: '取消成功',
});
setTimeout(() => {
this.$utils.navigateBack()
}, 800)
},
jumpToReportDetail() {
this.$utils.navigateTo(`/pages_order/checkup/checkupReport/index?id=${this.detail.id}`)
},
},
}
</script>
<style scoped lang="scss">
@import './style.scss';
.status {
margin-bottom: 40rpx;
row-gap: 4rpx;
width: 100%;
padding: 24rpx 0;
box-sizing: border-box;
font-family: PingFang SC;
font-weight: 400;
font-size: 28rpx;
line-height: 1.4;
color: #252545;
background: #FFFFFF;
border-radius: 24rpx;
.icon {
width: 84rpx;
height: auto;
}
}
.row {
&-content {
text-align: right;
font-size: 28rpx;
color: #393939;
&.highlight {
font-weight: 500;
color: #7451DE;
}
}
}
.bottom {
align-items: flex-start;
column-gap: 32rpx;
.btn {
flex: 1;
&-plain {
padding: 14rpx 0;
color: #252545;
background: transparent;
border: 2rpx solid #252545;
}
}
}
</style>

+ 126
- 0
pages_order/checkup/checkupBook/style.scss View File

@ -0,0 +1,126 @@
.page__view {
width: 100vw;
min-height: 100vh;
background-color: $uni-bg-color;
position: relative;
/deep/ .nav-bar__view {
position: fixed;
top: 0;
left: 0;
}
}
.main {
padding: calc(var(--status-bar-height) + 144rpx) 32rpx 224rpx 32rpx;
}
.card {
padding: 32rpx;
background: #FAFAFF;
border: 2rpx solid #FFFFFF;
border-radius: 32rpx;
& + & {
margin-top: 40rpx;
}
&-header {
font-family: PingFang SC;
font-weight: 500;
font-size: 36rpx;
line-height: 1.4;
color: #252545;
margin-bottom: 32rpx;
}
}
.row {
justify-content: space-between;
font-family: PingFang SC;
font-weight: 400;
line-height: 1.4;
column-gap: 24rpx;
& + & {
margin-top: 32rpx;
}
&-label {
flex: none;
font-size: 26rpx;
color: #8B8B8B;
}
&-content {
font-size: 32rpx;
color: #181818;
}
}
.form {
padding: 8rpx 0 0 0;
&-item {
border-bottom: 2rpx solid #EEEEEE;
&:last-child {
border: none;
}
& + & {
margin-top: 40rpx;
}
&-label {
font-family: PingFang SC;
font-weight: 400;
font-size: 26rpx;
line-height: 1.4;
color: #181818;
}
&-content {
margin-top: 14rpx;
padding: 6rpx 0;
.text {
padding: 2rpx 0;
font-family: PingFang SC;
font-weight: 400;
font-size: 32rpx;
line-height: 1.4;
&.placeholder {
color: #C6C6C6;
}
}
}
}
}
.bottom {
position: fixed;
left: 0;
bottom: 0;
width: 100vw;
height: 200rpx;
padding: 24rpx 40rpx;
background: #FFFFFF;
box-sizing: border-box;
.btn {
width: 100%;
padding: 16rpx 0;
box-sizing: border-box;
font-family: PingFang SC;
font-weight: 500;
font-size: 36rpx;
line-height: 1;
color: #FFFFFF;
background-image: linear-gradient(to right, #4B348F, #845CFA);
border-radius: 41rpx;
}
}

+ 241
- 0
pages_order/checkup/checkupCard.vue View File

@ -0,0 +1,241 @@
<template>
<view class="card" @click="jumpToProductDetail">
<view class="flex top">
<view class="title">{{ data.title }}</view>
<view :class="['flex', 'status', `status-${data.status}`]">{{ statusDesc }}</view>
</view>
<view class="flex main">
<image class="img" :src="data.url" mode="scaleToFill"></image>
<view class="info">
<view class="flex row">
<view class="row-label">客户姓名</view>
<view class="row-content">{{ data.userName }}</view>
</view>
<template v-if="data.status == 0">
<view class="flex row">
<view class="row-label">下单时间</view>
<view class="row-content">{{ data.createTime }}</view>
</view>
</template>
<template v-else>
<view class="flex row">
<view class="row-label">检测类型</view>
<view class="row-content">{{ data.typeDesc }}</view>
</view>
<view class="flex row">
<view class="row-label">预约时间</view>
<view class="row-content">{{ data.appointmentTime || '-' }}</view>
</view>
</template>
<view class="flex row">
<view class="row-label">联系电话</view>
<view class="row-content">{{ data.phone }}</view>
</view>
</view>
</view>
<view class="flex bottom">
<view class="flex price">
<text class="price-label">总价格</text>
<text class="price-unit">¥</text><text class="price-value">{{ data.amount.toFixed(2) }}</text>
</view>
<view class="flex btns">
<!-- 待预约 -->
<template v-if="data.status == 0">
<button class="btn" @click.stop="onBook">检测预约</button>
</template>
<!-- 自采 -->
<template v-else-if="data.status == 1">
<button class="btn" @click.stop="onSendBackOnline">线上回寄试剂盒</button>
</template>
</view>
</view>
</view>
</template>
<script>
const STATUS_AND_DESC_MAPPING = {
0: '待预约',
1: '自采',
2: '上门',
3: '到店',
4: '已完成',
5: '已取消',
6: '预约失败',
}
export default {
props: {
data: {
type: Object,
default() {
return {}
}
}
},
computed: {
statusDesc() {
return STATUS_AND_DESC_MAPPING[this.data.status]
},
},
methods: {
onBook() {
this.$utils.navigateTo(`/pages_order/checkup/checkupBook/apply?id=${this.data.id}&type=${this.data.type}`)
},
onSendBackOnline() {
this.$emit('sendBack')
},
jumpToProductDetail() {
console.log(this.data.id, 'jumpToProductDetail')
if ([0, 5].includes(this.data.status) == 5) { // 0- 5-
this.$utils.navigateTo(`/pages_order/order/orderDetail/index?id=${this.data.id}`)
// todo: check product id
// this.$utils.navigateTo(`/pages_order/product/productDetail?id=${this.data.id}`)
} else {
this.$utils.navigateTo(`/pages_order/checkup/checkupBook/detail?id=${this.data.id}`)
}
},
},
}
</script>
<style scoped lang="scss">
.card {
width: 100%;
padding: 32rpx;
box-sizing: border-box;
background: #FAFAFF;
border: 2rpx solid #FFFFFF;
border-radius: 32rpx;
}
.top {
justify-content: space-between;
.title {
font-family: PingFang SC;
font-weight: 500;
font-size: 36rpx;
line-height: 1.4;
color: #252545;
}
.status {
display: inline-flex;
min-width: 120rpx;
padding: 6rpx 0;
box-sizing: border-box;
font-family: PingFang SC;
font-weight: 400;
font-size: 24rpx;
line-height: 1.4;
color: #252545;
background: #F3F3F3;
border-radius: 12rpx;
&-0 {
color: #7D27E0;
background: #F5EEFD;
}
&-1 {
color: #2799E0;
background: #EEF7FD;
}
&-2 {
color: #E53C29;
background: #FDE7E5;
}
&-3 {
color: #10A934;
background: #E2FDE9;
}
}
}
.main {
margin: 24rpx 0;
column-gap: 24rpx;
.img {
flex: none;
width: 120rpx;
height: 120rpx;
}
.info {
flex: 1;
padding: 24rpx;
background: #FFFFFF;
border-radius: 32rpx;
}
.row {
justify-content: flex-start;
column-gap: 4rpx;
font-family: PingFang SC;
font-weight: 400;
font-size: 28rpx;
line-height: 1.4;
&-label {
flex: none;
color: #8B8B8B;
}
&-content {
color: #393939;
}
}
.row + .row {
margin-top: 16rpx;
}
}
.bottom {
justify-content: space-between;
.price {
column-gap: 8rpx;
font-family: PingFang SC;
font-weight: 500;
line-height: 1.4;
&-label {
font-weight: 400;
font-size: 26rpx;
color: #8B8B8B;
}
&-unit {
font-size: 24rpx;
color: #7451DE;
}
&-value {
font-size: 32rpx;
color: #7451DE;
}
}
.btns {
flex: 1;
justify-content: flex-end;
column-gap: 16rpx;
}
.btn {
padding: 10rpx 22rpx;
font-family: PingFang SC;
font-weight: 400;
font-size: 28rpx;
line-height: 1.4;
color: #393939;
border: 2rpx solid #252545;
border-radius: 32rpx;
}
}
</style>

+ 196
- 0
pages_order/checkup/checkupRecords.vue View File

@ -0,0 +1,196 @@
<template>
<view class="page__view">
<navbar title="检测预约" leftClick @leftClick="$utils.navigateBack" color="#191919" bgColor="#FFFFFF" />
<view class="main">
<view class="tabs">
<uv-tabs
:list="tabs"
:scrollable="false"
lineColor="#7451DE"
lineWidth="48rpx"
lineHeight="4rpx"
:activeStyle="{
'font-family': 'PingFang SC',
'font-weight': 500,
'font-size': '32rpx',
'line-height': 1.4,
'color': '#7451DE',
}"
:inactiveStyle="{
'font-family': 'PingFang SC',
'font-weight': 400,
'font-size': '32rpx',
'line-height': 1.4,
'color': '#181818',
}"
@click="clickTabs"
></uv-tabs>
</view>
<view class="list">
<view class="list-item" v-for="item in list" :key="item.id">
<checkupCard :data="item" @sendBack="openTrackingNoPopup(item.id)"></checkupCard>
</view>
</view>
</view>
<checkupTrackingNoPopup ref="checkupTrackingNoPopup" @submitted="getData"></checkupTrackingNoPopup>
</view>
</template>
<script>
import mixinsList from '@/mixins/list.js'
import checkupCard from './checkupCard.vue'
import checkupTrackingNoPopup from '@/pages_order/checkup/checkupTrackingNoPopup.vue'
export default {
mixins: [mixinsList],
components: {
checkupCard,
checkupTrackingNoPopup,
},
data() {
return {
tabs: [
{ name: '全部' },
{ name: '自采' },
{ name: '上门' },
{ name: '到店' },
{ name: '已取消' },
],
mixinsListApi: '',
}
},
methods: {
//tab
clickTabs({ index }) {
if (index == 0) {
delete this.queryParams.status
} else {
this.queryParams.status = index - 1
}
this.getData()
},
// todo: delete
getData() {
this.list = [
{
id: '001',
url: '/pages_order/static/product/detect-8.png',
title: '月度装定制营养包',
userName: '周小艺',
phone: '15558661691',
amount: 688,
appointmentTime: '2025-04-28 08:14',
type: 3,
typeDesc: '到店检测',
status: 5,
},
{
id: '002',
url: '/pages_order/static/product/detect-8.png',
title: '青少年体检套餐',
userName: '周小艺',
phone: '15558661691',
amount: 688,
appointmentTime: '2025-04-28 08:00~09:00',
type: 3,
typeDesc: '到店检测',
status: 3,
},
{
id: '003',
url: '/pages_order/static/product/detect-8.png',
title: '孕产妇体检套餐',
userName: '周小艺',
phone: '15558661691',
amount: 688,
createTime: '2025-04-28 08:14',
type: 1,
status: 0,
},
{
id: '004',
url: '/pages_order/static/product/detect-8.png',
title: '青少年体检套餐',
userName: '周小艺',
phone: '15558661691',
amount: 688,
appointmentTime: null,
type: 1,
typeDesc: '自采检测',
status: 1,
},
{
id: '005',
url: '/pages_order/static/product/detect-8.png',
title: '青少年体检套餐',
userName: '周小艺',
phone: '15558661691',
amount: 688,
appointmentTime: '2025-04-28 08:00~09:00',
type: 2,
typeDesc: '上门检测',
status: 2,
},
]
this.total = this.list.length
},
openTrackingNoPopup(id) {
this.$refs.checkupTrackingNoPopup.open(id)
},
},
}
</script>
<style scoped lang="scss">
.page__view {
width: 100vw;
min-height: 100vh;
background-color: $uni-bg-color;
position: relative;
/deep/ .nav-bar__view {
position: fixed;
top: 0;
left: 0;
}
}
.main {
width: 100vw;
padding: calc(var(--status-bar-height) + 244rpx) 32rpx 40rpx 32rpx;
box-sizing: border-box;
.tabs {
position: fixed;
top: calc(var(--status-bar-height) + 120rpx);
left: 0;
width: 100%;
height: 84rpx;
background: #FFFFFF;
/deep/ .uv-tabs__wrapper__nav__line {
border-radius: 2rpx;
bottom: 0;
}
}
}
.list {
&-item {
& + & {
margin-top: 40rpx;
}
}
}
</style>

+ 331
- 0
pages_order/checkup/checkupReport/index.vue View File

@ -0,0 +1,331 @@
<template>
<view class="page__view">
<navbar title="体检报告" leftClick @leftClick="$utils.navigateBack" color="#191919" bgColor="#FFFFFF" />
<view class="main" v-if="detail">
<view class="section">
<reportSummaryView :data="detail"></reportSummaryView>
</view>
<view class="section">
<reportMainIndexView :list="detail.mainIndexList"></reportMainIndexView>
</view>
<view class="section">
<reportAbnormalView :list="detail.abnormalList"></reportAbnormalView>
</view>
<view class="section">
<reportCommonView :data="detail"></reportCommonView>
</view>
</view>
<view class="bottom">
<button class="flex btn" @click="jumpToNutritionProgram">查看营养方案</button>
</view>
</view>
</template>
<script>
import reportSummaryView from './reportSummaryView.vue'
import reportMainIndexView from './reportMainIndexView.vue'
import reportAbnormalView from './reportAbnormalView.vue'
import reportCommonView from './reportCommonView.vue'
export default {
components: {
reportSummaryView,
reportMainIndexView,
reportAbnormalView,
reportCommonView,
},
data() {
return {
id: null,
detail: null,
}
},
onLoad(arg) {
this.id = arg.id
this.getData()
},
methods: {
getData() {
this.detail = {
score: 77,
BMI: 20.3,
BMIchange: -0.2,
BMItag: '正常',
height: 164,
weight: 46,
mainIndexList: [
{
id: '001',
label: '心率',
value: '75',
unit: 'bpm(次/分钟)',
standrad: '60-100bpm',
status: 1,
},
{
id: '002',
label: '血氧',
value: '99',
unit: '%',
standrad: '≥94%',
status: 1,
},
{
id: '003',
label: '空腹血糖',
value: '4.0',
unit: 'mmol/L',
standrad: '3.9-6.1mmol/L',
status: 1,
},
{
id: '004',
label: '血压',
value: '110/80',
unit: 'mmHg',
standrad: '140/90mmHg~90/60 mmHg',
status: 1,
},
],
abnormalList: [
{
id: '001',
title: '甲状腺结节(甲状腺结节或钙化)',
desc: '建议进一步监测;常规测甲状腺功能全套,甲状腺。',
},
{
id: '002',
title: '甲状腺结节(甲状腺结节或钙化)',
desc: '建议进一步监测;常规测甲状腺功能全套,甲状腺。',
},
{
id: '003',
title: '甲状腺结节(甲状腺结节或钙化)',
desc: '建议进一步监测;常规测甲状腺功能全套,甲状腺。',
},
{
id: '004',
title: '甲状腺结节(甲状腺结节或钙化)',
desc: '建议进一步监测;常规测甲状腺功能全套,甲状腺。',
},
],
chronicFoodAllergyList: [
{
id: '001',
label: '血小板',
value: '5.2',
standrad: '125~350×10^9/L',
status: 1,
},
{
id: '002',
label: '血红蛋白',
value: '4.8',
standrad: '110 ~ 150g/L',
status: 1,
},
{
id: '003',
label: '白细胞',
value: '3.0',
standrad: '3.9-6.1mmol/L',
status: 1,
},
{
id: '004',
label: '转氨酶',
value: '7.0',
standrad: '3.9-6.1mmol/L',
status: 1,
},
{
id: '005',
label: '胆红素',
value: '17',
standrad: '3.9-6.1mmol/L',
status: 1,
},
{
id: '006',
label: '白蛋白',
value: '15',
standrad: '3.9-6.1mmol/L',
status: 1,
},
{
id: '007',
label: '肌酐',
value: '4',
standrad: '3.9-6.1mmol/L',
status: 1,
},
{
id: '008',
label: '尿酸',
value: '556',
standrad: '3.9-6.1mmol/L',
status: 0,
},
{
id: '009',
label: '血压',
value: '23',
standrad: '3.9-6.1mmol/L',
status: 0,
},
{
id: '010',
label: '甘油三酯',
value: '56',
standrad: '3.9-6.1mmol/L',
status: 0,
},
],
gutMicrobiomeList: [
{
id: '001',
label: '血小板',
value: '5.2',
standrad: '125~350×10^9/L',
status: 1,
},
{
id: '002',
label: '血红蛋白',
value: '4.8',
standrad: '110 ~ 150g/L',
status: 1,
},
{
id: '003',
label: '白细胞',
value: '3.0',
standrad: '3.9-6.1mmol/L',
status: 1,
},
{
id: '004',
label: '转氨酶',
value: '7.0',
standrad: '3.9-6.1mmol/L',
status: 1,
},
{
id: '005',
label: '胆红素',
value: '17',
standrad: '3.9-6.1mmol/L',
status: 1,
},
{
id: '006',
label: '白蛋白',
value: '15',
standrad: '3.9-6.1mmol/L',
status: 1,
},
{
id: '007',
label: '肌酐',
value: '4',
standrad: '3.9-6.1mmol/L',
status: 1,
},
{
id: '008',
label: '尿酸',
value: '556',
standrad: '3.9-6.1mmol/L',
status: 0,
},
{
id: '009',
label: '血压',
value: '23',
standrad: '3.9-6.1mmol/L',
status: 0,
},
{
id: '010',
label: '甘油三酯',
value: '56',
standrad: '3.9-6.1mmol/L',
status: 0,
},
],
scoreList: [
{ id: '001', label: '饮食', value: 23 },
{ id: '002', label: '饮食', value: 23 },
{ id: '003', label: '作息', value: 44 },
{ id: '004', label: '作息', value: 44 },
{ id: '005', label: '运动', value: 88 },
{ id: '006', label: '运动', value: 88 },
{ id: '007', label: '体质', value: 56 },
{ id: '008', label: '体质', value: 56 },
{ id: '009', label: '心理', value: 78 },
{ id: '010', label: '心理', value: 100 },
],
}
console.log('detail', this.detail)
},
jumpToNutritionProgram() {
this.$utils.navigateTo(`/pages_order/report/nutritionProgram/index?id=${this.id}`)
},
},
}
</script>
<style scoped lang="scss">
.page__view {
width: 100vw;
min-height: 100vh;
background-color: $uni-bg-color;
position: relative;
/deep/ .nav-bar__view {
position: fixed;
top: 0;
left: 0;
}
}
.main {
padding: calc(var(--status-bar-height) + 168rpx) 32rpx 280rpx 32rpx;
}
.section {
& + & {
margin-top: 40rpx;
}
}
.bottom {
width: 100%;
height: 200rpx;
position: fixed;
left: 0;
bottom: 0;
padding: 24rpx 40rpx;
box-sizing: border-box;
background: #FFFFFF;
.btn {
padding: 16rpx 0;
font-family: PingFang SC;
font-size: 36rpx;
font-weight: 500;
line-height: 1;
color: #FFFFFF;
background-image: linear-gradient(to right, #4B348F, #845CFA);
border-radius: 41rpx;
}
}
</style>

+ 336
- 0
pages_order/checkup/checkupReport/progressCircleLine.vue View File

@ -0,0 +1,336 @@
<template>
<view class="progress_box">
<div class="bg-outer"></div>
<canvas class="progress progress-outer" canvas-id="cpouterline" type="2d"></canvas>
<div class="bg-inner"></div>
<canvas class="progress progress-inner" canvas-id="cpinnerline" type="2d"></canvas>
<div class="bg-score bg-score-min">0</div>
<div class="bg-score bg-score-max">100</div>
<div class="progress progress-info">
<div class="flex">
<div class="progress-score">{{ progress }}</div>
<div class="progress-unit"></div>
</div>
<div class="progress-desc">体检分数</div>
</div>
</view>
</template>
<script>
export default {
props: {
progress: {
type: Number,
default: 0
}
},
data() {
return {
outerCanvas: {},
innerCanvas: {},
ratio: 1,
dpr: 1,
}
},
mounted() {
this.init()
},
methods: {
init() {
uni.createSelectorQuery().in(this)
.select('.progress-outer')
.fields({
node: true,
size: true
})
.exec(async (res) => {
// Canvas
const width = res[0].width
const dpr = wx.getWindowInfo().pixelRatio
let Ratio = width / 446
this.outerCanvas = res[0]
this.ratio = Ratio
this.dpr = dpr
uni.createSelectorQuery().in(this)
.select('.progress-inner')
.fields({
node: true,
size: true
})
.exec(async (res) => {
this.innerCanvas = res[0]
this.drawOuterLine(this.progress)
this.drawInnerLine(this.progress)
})
})
},
drawOuterLine(step) {
uni.createSelectorQuery().in(this)
.select('.progress-outer')
.fields({
node: true,
size: true
})
.exec(async (res) => {
const canvas = res[0].node
// Canvas
const width = res[0].width
const height = res[0].height
//dpr
const dpr = wx.getWindowInfo().pixelRatio
canvas.width = width * dpr
canvas.height = height * dpr
let Ratio = width / 446
//
const ctx = canvas.getContext('2d')
ctx.scale(dpr, dpr)
ctx.clearRect(0, 0, width, height)
let r = 210 * Ratio
let x = this.outerCanvas.width / 2
let y = this.outerCanvas.height - this.innerCanvas.height / 2
let lineWidth = 10 * Ratio;
let lineHeight = 26 * Ratio;
let startAngle = 0
// (x--Yx++Y)
let gradient = ctx.createLinearGradient(x - r, y, x + r, y);
gradient.addColorStop('0', '#7451DE');
gradient.addColorStop('1.0', '#B1A4FF');
ctx.strokeStyle = '#E5E5E5';
ctx.save()
for (let i = 0; i < 11; i++) {
ctx.beginPath();
if (i % 5) {
lineWidth = 6 * Ratio;
lineHeight = 15 * Ratio;
} else {
lineWidth = 10 * Ratio;
lineHeight = 18 * Ratio;
}
if (step >= i * 10) {
ctx.strokeStyle = gradient;
} else {
ctx.restore();
}
ctx.lineWidth = lineWidth;
ctx.lineCap = 'round';
let angle = startAngle - Math.PI * i / 10
let x0 = x - r * Math.cos(angle)
let y0 = y + r * Math.sin(angle)
let x1 = x0 + lineHeight * Math.cos(angle)
let y1 = y0 - lineHeight * Math.sin(angle)
ctx.moveTo(x0, y0);
ctx.lineTo(x1, y1);
ctx.stroke();
ctx.closePath();
}
})
},
drawInnerLine(step) {
uni.createSelectorQuery().in(this)
.select('.progress-inner')
.fields({
node: true,
size: true
})
.exec(async (res) => {
const canvas = res[0].node
// Canvas
const width = res[0].width
const height = res[0].height
//dpr
const dpr = wx.getWindowInfo().pixelRatio
canvas.width = width * dpr
canvas.height = height * dpr
let Ratio = width / 356
//
const ctx = canvas.getContext('2d')
ctx.scale(dpr, dpr)
ctx.clearRect(0, 0, width, height)
let r = 140 * Ratio
let x = this.innerCanvas.width / 2
let y = this.innerCanvas.height / 2
let lineWidth = 5 * Ratio;
let lineHeight = 28 * Ratio;
// (x--Yx++Y)
let gradient = ctx.createLinearGradient(x - r, y, x + r, y);
gradient.addColorStop('0', '#E81717');
gradient.addColorStop('0.5', '#ECBD00');
gradient.addColorStop('1.0', '#0DB556');
ctx.lineWidth = lineWidth;
ctx.strokeStyle = '#989898';
ctx.save()
let angle = 0
let i = 0
while (angle > - Math.PI) {
ctx.beginPath();
if (i % 2) {
angle -= Math.PI / 50
i++
continue
}
if (step * Math.PI / 100 >= -angle) {
ctx.strokeStyle = gradient;
} else {
ctx.restore();
}
let x0 = x - r * Math.cos(angle)
let y0 = y + r * Math.sin(angle)
let x1 = x0 + lineHeight * Math.cos(angle)
let y1 = y0 - lineHeight * Math.sin(angle)
ctx.moveTo(x0, y0);
ctx.lineTo(x1, y1);
ctx.stroke();
ctx.closePath();
angle -= Math.PI / 100
i++
}
})
},
}
}
</script>
<style lang="scss" scoped>
$size: 356rpx;
.progress_box {
position: relative;
width: 100vw;
height: 402rpx;
}
.progress {
position: absolute;
left: 50%;
transform: translateX(-50%);
&-outer {
width: 446rpx;
height: 100%;
}
&-inner {
width: $size;
height: $size;
bottom: 0;
}
&-info {
position: absolute;
left: 50%;
bottom: calc(#{$size} / 2);
transform: translate(-50%, calc(50% + 5rpx));
}
&-score {
font-size: 48rpx;
font-weight: 600;
font-family: PingFang SC;
color: #000000;
}
&-unit {
font-size: 28rpx;
font-weight: 600;
font-family: PingFang SC;
color: #000000;
margin-left: 8rpx;
}
&-desc {
font-size: 26rpx;
font-weight: 400;
font-family: PingFang SC;
color: #989898;
text-align: center;
}
}
.bg {
&-outer {
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 0;
width: $size;
height: $size;
border-radius: 50%;
box-shadow: inset 4px 4px 16px #8D96B466,
4px 4px 16px 0 rgba(141, 150, 180, 0.3),
// 2px 2px 2px 0 #8D96B466,
-2px -2px 8px 0 #FFFFFF,
-1px -1px 14px 0 #FFFFFF;
}
&-inner {
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 28rpx;
width: 300rpx;
height: 300rpx;
background-color: #EEF0F6;
border-radius: 50%;
box-shadow: 6px 6px 6px 0 #0000000F,
-4px -4px 13px 0 #FFFFFF,
4px 4px 7px 0 #00000012,
}
&-score {
color: #989898;
font-size: 26rpx;
font-weight: 400;
position: absolute;
left: 50%;
top: 50%;
&-min {
transform: translate(-303rpx, 0);
}
&-max {
transform: translate(263rpx, 0);
}
}
}
</style>

+ 86
- 0
pages_order/checkup/checkupReport/progressLine.vue View File

@ -0,0 +1,86 @@
<template>
<view class="view"
:style="style"
>
<view class="bar bg">
<view
v-for="(item, index) in list"
:key="index"
:class="['bar-item', index % 2 == 0 ? 'is-active' : '']"
></view>
</view>
<view class="bar fg">
<view
v-for="(item, index) in fgList"
:key="index"
:class="['bar-item', index % 2 == 0 ? 'is-active' : '']"
></view>
</view>
</view>
</template>
<script>
export default {
props: {
progress: {
type: Number,
default: 0,
},
activeColor: {
type: String,
default: '#7451DE',
},
},
data() {
return {
list: new Array(51).fill(1),
}
},
computed: {
fgList() {
const num = Math.floor(51 * this.progress / 100)
return this.list.slice(0, num + 1)
},
style() {
return `--color: ${this.activeColor}`
}
},
}
</script>
<style lang="scss" scoped>
.view {
position: relative;
width: 100%;
height: 28rpx;
}
.bar {
width: 100%;
height: 28rpx;
display: grid;
grid-template-columns: repeat(51, 1fr);
&-item {
}
&.bg {
.is-active {
background: #989898;
}
}
&.fg {
position: absolute;
top: 0;
left: 0;
.is-active {
background: #7451DE;
background: var(--color);
}
}
}
</style>

+ 80
- 0
pages_order/checkup/checkupReport/reportAbnormalView.vue View File

@ -0,0 +1,80 @@
<template>
<view class="card">
<view class="card-header">
<view class="title">体检异常项</view>
<view class="desc">Abnormal items from health check-up</view>
</view>
<view class="flex row" v-for="item in list" :key="item.id" @click="onClick(item)">
<view>
<view class="title">{{ item.title }}</view>
<view class="desc">{{ item.desc }}</view>
</view>
<uv-icon name="arrow-right" color="#C6C6C6" size="24rpx"></uv-icon>
</view>
</view>
</template>
<script>
export default {
props: {
list: {
type: Array,
default() {
return []
}
}
},
methods: {
onClick(data) {
// todo
},
},
}
</script>
<style lang="scss" scoped>
.card {
padding: 32rpx;
background: #FAFAFF;
border-radius: 24rpx;
&-header {
font-family: PingFang SC;
line-height: 1.4;
color: #252545;
.title {
font-weight: 600;
font-size: 32rpx;
}
.desc {
font-weight: 400;
font-size: 24rpx;
}
}
.row {
margin-top: 24rpx;
justify-content: space-between;
padding: 24rpx;
font-family: PingFang SC;
font-weight: 400;
background: #F3F2F7;
border-radius: 16rpx;
.title {
font-size: 30rpx;
line-height: 1.4;
color: #252545;
}
.desc {
margin-top: 4rpx;
font-size: 22rpx;
line-height: 1.6;
color: #989898;
}
}
}
</style>

+ 239
- 0
pages_order/checkup/checkupReport/reportCommonView.vue View File

@ -0,0 +1,239 @@
<template>
<view class="card">
<view class="flex card-header">
<view class="title">检测数据</view>
</view>
<view class="section">
<view class="section-header">
<view class="title">慢性食物过敏检测</view>
<view class="desc">Chronic food allergy testing</view>
</view>
<view class="section-content index">
<view
class="index-item"
v-for="item in data.chronicFoodAllergyList"
:key="item.id"
>
<view class="flex top">
<view class="label">{{ item.label }}</view>
<view class="tag is-error" v-if="item.status === 0">{{ item.label }}</view>
</view>
<view class="flex main">
<text>当前</text><text class="value">{{ item.value }}</text>
</view>
<view class="bottom desc">{{ `* 标准值:${item.standrad}` }}</view>
</view>
</view>
</view>
<view class="section">
<view class="section-header">
<view class="title">肠道菌群检测</view>
<view class="desc">Gut microbiome testing</view>
</view>
<view class="section-content index">
<view
class="index-item"
v-for="item in data.gutMicrobiomeList"
:key="item.id"
>
<view class="flex top">
<view class="label">{{ item.label }}</view>
<view class="tag is-error" v-if="item.status === 0">{{ item.label }}</view>
</view>
<view class="flex main">
<text>当前</text><text class="value">{{ item.value }}</text>
</view>
<view class="bottom desc">{{ `* 标准值:${item.standrad}` }}</view>
</view>
</view>
</view>
<view class="section">
<view class="section-header">
<view class="title">肠道菌群检测</view>
<view class="desc">Gut microbiome testing</view>
</view>
<view class="section-content score">
<view
class="score-item"
v-for="(item, index) in data.scoreList"
:key="item.id"
>
<view>
<progressLine :progress="item.value" :activeColor="getColor(index)"></progressLine>
</view>
<view class="flex info">
<view class="label">{{ `${item.label}` }}</view>
<view class="value">{{ item.value }}</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import progressLine from './progressLine.vue';
const COLORS = [
'#43B741',
'#43B741',
'#ECB501',
'#ECB501',
'#EB7F09',
'#EB7F09',
'#009CEF',
'#009CEF',
'#7451DE',
'#7451DE',
]
export default {
components: {
progressLine,
},
props: {
data: {
type: Object,
default() {
return {
chronicFoodAllergyList: [],
gutMicrobiomeList: [],
scoreList: [],
}
}
},
},
data() {
return {
}
},
onLoad() {
console.log('onLoad', this.data)
},
methods: {
getColor(index) {
return COLORS[index % 10]
},
},
}
</script>
<style lang="scss" scoped>
@import './style.scss';
.card {
padding: 32rpx 24rpx;
background-image: linear-gradient(#F2EDFF, #FCFEFE);
border: 8rpx solid #F9F7FF;
border-radius: 64rpx;
&-header {
.title {
padding: 6rpx 14rpx;
display: inline-flex;
font-family: PingFang SC;
font-weight: 400;
font-size: 28rpx;
line-height: 1.5;
color: #252545;
border: 2rpx solid #252545;
border-radius: 30rpx;
}
}
.section {
margin-top: 40rpx;
&-header {
font-family: PingFang SC;
line-height: 1.4;
color: #252545;
.title {
font-weight: 600;
font-size: 32rpx;
}
.desc {
font-weight: 400;
font-size: 24rpx;
}
}
&-content {
margin-top: 32rpx;
}
}
}
.index {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32rpx;
&-item {
font-family: PingFang SC;
font-weight: 400;
line-height: 1.4;
.top {
justify-content: space-between;
}
.main {
justify-content: flex-start;
column-gap: 12rpx;
font-size: 24rpx;
color: #8B8B8B;
}
.label {
font-size: 30rpx;
color: #000000;
}
.value {
font-weight: 400;
font-size: 28rpx;
color: #000000;
}
.desc {
font-size: 22rpx;
line-height: 1.6;
color: #989898;
}
}
}
.score {
display: grid;
grid-template-columns: repeat(2, 1fr);
column-gap: 32rpx;
row-gap: 36rpx;
&-item {
.info {
margin-top: 18rpx;
justify-content: flex-start;
column-gap: 12rpx;
font-family: PingFang SC;
line-height: 1.4;
.label {
font-weight: 400;
font-size: 24rpx;
}
.value {
font-weight: 500;
font-size: 28rpx;
}
}
}
}
</style>

+ 86
- 0
pages_order/checkup/checkupReport/reportMainIndexView.vue View File

@ -0,0 +1,86 @@
<template>
<view class="view">
<view class="card" v-for="item in list" :key="item.id">
<view class="flex top">
<view class="label">{{ item.label }}</view>
<view :class="['tag', item.status === 0 ? 'is-error' : '' ]">{{ item.status == 0 ? '异常' : '正常' }}</view>
</view>
<view class="flex main">
<view class="value">{{ item.value }}</view>
<view class="unit">{{ item.unit }}</view>
</view>
<view class="desc">{{ `* 标准值:${item.standrad}` }}</view>
</view>
</view>
</template>
<script>
export default {
props: {
list: {
type: Array,
default() {
return []
}
}
}
}
</script>
<style lang="scss" scoped>
@import './style.scss';
.view {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16rpx;
}
.card {
flex: 1;
padding: 24rpx;
box-sizing: border-box;
font-family: PingFang SC;
font-weight: 400;
line-height: 1.4;
background-image: linear-gradient(#FAFAFF, #F3F3F3);
border: 2rpx solid #FFFFFF;
border-radius: 32rpx;
.top {
justify-content: space-between;
}
.main {
margin: 4rpx 0;
justify-content: flex-start;
column-gap: 8rpx;
}
}
.label {
font-size: 30rpx;
color: #000000;
}
.value {
font-weight: 600;
font-size: 56rpx;
color: transparent;
background-image: linear-gradient(to right, #4B348F, #845CFA);
background-clip: text;
display: inline-block;
}
.unit {
font-size: 24rpx;
color: #252545CC;
}
.desc {
font-size: 22rpx;
line-height: 1.6;
color: #989898;
}
</style>

+ 241
- 0
pages_order/checkup/checkupReport/reportSummaryView.vue View File

@ -0,0 +1,241 @@
<template>
<view class="card">
<view class="card-header">
<image class="card-header-bg" src="@/pages_order/static/report/report-card-header-bg.png" mode="scaleToFill"></image>
<view class="flex card-header-content">
<view class="avatar">
<!-- todo -->
<image class="avatar-img" src="@/pages_order/static/report/avatar.png" mode="scaleToFill"></image>
</view>
<view class="info">
<!-- todo -->
<view class="nickname">李星星</view>
<view class="desc">
<text class="age">25</text>
<text class="gender"></text>
</view>
</view>
</view>
</view>
<view class="card-content">
<view class="score-comprehensive">
<progressCircleLine :progress="data.score"></progressCircleLine>
</view>
<view class="flex cols">
<view class="flex flex-column col">
<view class="flex">
<view class="value">{{ data.BMI || '--' }}</view>
<view v-if="data.BMIchange < 0" class="flex change">
<text>{{ data.BMIchange }}</text>
<image class="change-icon" src="@/pages_order/static/report/arrow-down.png" mode="widthFix"></image>
</view>
<view v-else-if="data.BMIchange > 0" class="flex change rise">
<text>{{ data.BMIchange }}</text>
<image class="change-icon" src="@/pages_order/static/report/arrow-down.png" mode="widthFix"></image>
</view>
</view>
<view class="flex">
<view class="label">BMI</view>
<view class="tag">
{{ data.BMItag || '-' }}
</view>
</view>
</view>
<view class="divider"></view>
<view class="flex flex-column col">
<view class="flex">
<view class="value">{{ data.height || '--' }}</view>
<view class="unit">cm</view>
</view>
<view class="label">身高</view>
</view>
<view class="divider"></view>
<view class="flex flex-column col">
<view class="flex">
<view class="value">{{ data.weight || '--' }}</view>
<view class="unit">kg</view>
</view>
<view class="label">体重</view>
</view>
</view>
</view>
</view>
</template>
<script>
import progressCircleLine from './progressCircleLine.vue'
export default {
components: {
progressCircleLine,
},
props: {
data: {
type: Object,
default() {
return {}
}
}
},
}
</script>
<style scoped lang="scss">
@import './style.scss';
$header-height: 240rpx;
$score-bar-height: 402rpx;
.card {
width: 100%;
background-image: linear-gradient(#FAFAFF, #F3F3F3);
border: 2rpx solid #FFFFFF;
border-bottom-left-radius: 64rpx;
border-bottom-right-radius: 64rpx;
box-sizing: border-box;
position: relative;
&-header {
background-color: $uni-bg-color;
width: calc(100% + 2rpx * 2);
height: $header-height;
position: absolute;
top: -2rpx;
left: -2rpx;
&-bg {
width: 100%;
height: 100%;
}
&-content {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 0 32rpx;
box-sizing: border-box;
justify-content: flex-start;
.avatar {
width: 104rpx;
height: 104rpx;
border: 4rpx solid #FFFFFF;
border-radius: 50%;
overflow: hidden;
&-img {
width: 100%;
height: 100%;
}
}
.info {
margin-left: 24rpx;
.nickname {
font-family: PingFang SC;
font-weight: 600;
font-size: 40rpx;
line-height: 1.1;
color: #252545;
}
.desc {
margin-top: 8rpx;
font-family: PingFang SC;
font-weight: 400;
font-size: 36rpx;
line-height: 1.2;
color: #252545CC;
.gender {
margin-left: 16rpx;
}
}
}
}
}
&-content {
position: relative;
width: 100%;
padding: calc(#{$header-height} + 32rpx + #{$score-bar-height} + 24rpx) 0 32rpx 0;
box-sizing: border-box;
}
}
.score-comprehensive {
position: absolute;
top: calc(#{$header-height} + 32rpx);
left: 50%;
transform: translateX(-50%);
}
.cols {
padding: 0 32rpx 6rpx 32rpx;
align-items: flex-start;
.col {
flex: 1;
}
.divider {
width: 2rpx;
height: 32rpx;
background: #B5B8CE;
margin: 0 15rpx;
}
.label {
font-family: PingFang SC;
font-weight: 400;
font-size: 26rpx;
line-height: 1.4;
color: #6D6D6D;
}
.unit {
margin-left: 8rpx;
font-family: PingFang SC;
font-weight: 400;
font-size: 24rpx;
line-height: 1.4;
color: #252545;
}
.value {
font-family: PingFang SC;
line-height: 1.4;
font-weight: 600;
font-size: 40rpx;
color: $uni-color;
}
.change {
margin-left: 8rpx;
font-family: PingFang SC;
font-weight: 400;
font-size: 24rpx;
line-height: 1.4;
color: #F79205;
&-icon {
width: 24rpx;
height: auto;
}
&.rise {
.change-icon {
transform: rotate(180deg);
}
}
}
.tag {
margin-left: 16rpx;
}
}
</style>

+ 17
- 0
pages_order/checkup/checkupReport/style.scss View File

@ -0,0 +1,17 @@
.tag {
padding: 6rpx 16rpx;
font-family: PingFang SC;
font-weight: 400;
font-size: 20rpx;
line-height: 1.4;
color: #FFFFFF;
background-image: linear-gradient(90deg, #4B348F, #845CFA);
border-top-left-radius: 24rpx;
border-bottom-right-radius: 24rpx;
&.is-error {
background-image: linear-gradient(90deg, #8F3434, #FA5C5C);
}
}

+ 67
- 0
pages_order/checkup/checkupServicePopup.vue View File

@ -0,0 +1,67 @@
<template>
<view>
<uv-popup ref="popup" mode="center" bgColor="none" >
<view class="flex flex-column popup__view">
<view class="header">添加客服微信</view>
<view class="content">
<image class="qrcode" src="@/pages_order/static/checkup/qrcode.png" mode="widthFix"></image>
</view>
</view>
</uv-popup>
</view>
</template>
<script>
import { mapState } from 'vuex'
export default {
data() {
return {
}
},
computed : {
...mapState(['configList'])
},
methods: {
open() {
this.$refs.popup.open()
},
close() {
this.$refs.popup.close()
},
},
}
</script>
<style lang="scss" scoped>
.popup__view {
width: 550rpx;
padding: 32rpx 0;
box-sizing: border-box;
background: #F3F2F7;
border: 2rpx solid #FFFFFF;
border-radius: 64rpx;
}
.header {
margin-bottom: 40rpx;
text-align: center;
font-family: PingFang SC;
font-weight: 500;
font-size: 40rpx;
line-height: 1.4;
color: #181818;
}
.content {
width: 486rpx;
height: auto;
.qrcode {
width: 100%;
height: auto;
}
}
</style>

+ 184
- 0
pages_order/checkup/checkupTrackingNoPopup.vue View File

@ -0,0 +1,184 @@
<template>
<view>
<uv-popup ref="popup" mode="bottom" bgColor="none" >
<view class="popup__view">
<view class="flex header">
<view class="title">线上试剂盒回寄</view>
<button class="btn" @click="close">关闭</button>
</view>
<view class="form">
<uv-form
ref="form"
:model="form"
:rules="rules"
errorType="toast"
>
<view class="form-item">
<uv-form-item prop="trackingNo" :customStyle="formItemStyle">
<view class="form-item-label">回寄订单号</view>
<view class="form-item-content">
<formInput v-model="form.trackingNo" inputAlign="right"></formInput>
</view>
</uv-form-item>
</view>
</uv-form>
</view>
<view class="footer">
<button class="flex btn" @click="close">取消</button>
<button class="flex btn" @click="onConfirm">确认</button>
</view>
</view>
</uv-popup>
</view>
</template>
<script>
import formInput from '@/pages_order/components/formInput.vue'
export default {
components: {
formInput,
},
data() {
return {
id: null,
form: {
trackingNo: null,
},
rules: {
'trackingNo': {
type: 'string',
required: true,
message: '请输入联系人',
},
},
formItemStyle: { padding: 0 },
}
},
methods: {
open(id) {
this.id = id
this.$refs.popup.open()
},
close() {
this.$refs.popup.close()
},
async onConfirm() {
try {
const res = await this.$refs.form.validate()
console.log('onConfirm res', res)
// todo: save
this.$emit('submitted')
this.close()
} catch (err) {
console.log('onConfirm err', err)
}
},
},
}
</script>
<style lang="scss" scoped>
.popup__view {
width: 100vw;
display: flex;
flex-direction: column;
box-sizing: border-box;
background: #FFFFFF;
border-top-left-radius: 32rpx;
border-top-right-radius: 32rpx;
}
.header {
position: relative;
width: 100%;
padding: 24rpx 0;
box-sizing: border-box;
border-bottom: 2rpx solid #EEEEEE;
.title {
font-family: PingFang SC;
font-weight: 500;
font-size: 34rpx;
line-height: 1.4;
color: #181818;
}
.btn {
font-family: PingFang SC;
font-weight: 500;
font-size: 32rpx;
line-height: 1.4;
color: #8B8B8B;
position: absolute;
top: 26rpx;
left: 40rpx;
}
}
.form {
padding: 32rpx 40rpx;
&-item {
padding: 8rpx 0 6rpx 0;
& + & {
padding-top: 24rpx;
border-top: 2rpx solid #EEEEEE;
}
&-label {
margin-bottom: 14rpx;
font-family: PingFang SC;
font-weight: 400;
font-size: 26rpx;
line-height: 1.4;
color: #181818;
}
&-content {
.placeholder {
color: #C6C6C6;
font-size: 32rpx;
font-weight: 400;
}
.region {
min-height: 44rpx;
justify-content: flex-start;
}
}
}
}
.footer {
width: 100%;
// todocheck
// height: 214rpx;
padding: 32rpx 40rpx;
box-sizing: border-box;
border-top: 2rpx solid #F1F1F1;
.btn {
width: 100%;
padding: 16rpx 0;
font-family: PingFang SC;
font-weight: 500;
font-size: 36rpx;
line-height: 1.4;
color: #FFFFFF;
background-image: linear-gradient(to right, #4B348F, #845CFA);
border-radius: 41rpx;
}
}
</style>

+ 7
- 6
pages_order/order/orderDetail/index.vue View File

@ -113,6 +113,7 @@
</view>
<button class="flex col btn btn-primary" @click="onPay">立即支付</button>
</template>
<!-- todo: check 体检套餐 检测预约 -->
<!-- 待发货 -->
<template v-else-if="orderData.status == 1">
<button class="flex col btn" @click="onApplyService">申请售后</button>
@ -134,7 +135,7 @@
</view>
</view>
<payPopup ref="payPopup"></payPopup>
<payPopup ref="payPopup" @submitted="getData"></payPopup>
<serviceSelectPopup ref="serviceSelectPopup"></serviceSelectPopup>
</view>
@ -143,7 +144,7 @@
<script>
import addressView from '@/pages_order/address/addressView.vue'
import payPopup from '@/pages_order/order/payPopup.vue'
import serviceSelectPopup from '@/pages_order/service/serviceSelectPopup.vue'
import serviceSelectPopup from '@/pages_order/applyService/serviceSelectPopup.vue'
const STATUS_AND_DESC_MAPPING = {
0: '待支付',
@ -179,17 +180,17 @@
return
}
this.fetchOrderDetail()
this.getData()
},
onLoad(arg) {
console.log('onLoad')
this.id = arg.id
this.fetchOrderDetail()
this.getData()
},
methods: {
async fetchOrderDetail() {
async getData() {
console.log('fetch data')
// todo: fetch data by id
@ -297,7 +298,7 @@
onConfirmReceipt() {
// todo: fetch confirm
this.fetchOrderDetail()
this.getData()
},
onComment() {
this.$utils.navigateTo(`/pages_order/comment/commentWrite?id=${this.orderData.id}`)


+ 1
- 1
pages_order/order/orderList/index.vue View File

@ -55,7 +55,7 @@
import orderCard from './orderCard.vue'
import payPopup from '@/pages_order/order/payPopup.vue'
import serviceSelectPopup from '@/pages_order/service/serviceSelectPopup.vue'
import serviceSelectPopup from '@/pages_order/applyService/serviceSelectPopup.vue'
export default {
mixins: [mixinsList],


+ 8
- 8
pages_order/order/orderList/orderCard.vue View File

@ -29,25 +29,25 @@
<view class="flex btns">
<!-- 待支付 -->
<template v-if="data.status == 0">
<button class="btn" @click="onPay">立即支付</button>
<button class="btn" @click.stop="onPay">立即支付</button>
</template>
<!-- 待发货 -->
<template v-else-if="data.status == 1">
<button class="btn" @click="onApplyService">申请售后</button>
<button class="btn" @click.stop="onApplyService">申请售后</button>
</template>
<!-- 待收货 -->
<template v-else-if="data.status == 2">
<button class="btn" @click="onApplyService">申请售后</button>
<button class="btn" @click="onConfirmReceipt">确认收货</button>
<button class="btn" @click.stop="onApplyService">申请售后</button>
<button class="btn" @click.stop="onConfirmReceipt">确认收货</button>
</template>
<!-- 待评价 -->
<template v-else-if="data.status == 3">
<button class="btn" @click="onApplyService">申请售后</button>
<button class="btn" @click="onComment">立即评价</button>
<button class="btn" @click.stop="onApplyService">申请售后</button>
<button class="btn" @click.stop="onComment">立即评价</button>
</template>
<!-- 已完成 -->
<template v-else-if="data.status == 4">
<button class="btn" @click="onApplyService">申请售后</button>
<button class="btn" @click.stop="onApplyService">申请售后</button>
</template>
<!-- 售后 -->
<template v-else-if="data.status == 5">
@ -242,4 +242,4 @@
border-radius: 32rpx;
}
}
</style>
</style>

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

@ -1,6 +1,6 @@
<template>
<view class="page__view">
<navbar title="详情报告" leftClick @leftClick="$utils.navigateBack" color="#191919" bgColor="#FFFFFF" />
<navbar title="营养方案" leftClick @leftClick="$utils.navigateBack" color="#191919" bgColor="#FFFFFF" />
<view class="main">
<reportCard>


BIN
pages_order/static/checkup/icon-error.png View File

Before After
Width: 43  |  Height: 42  |  Size: 2.1 KiB

BIN
pages_order/static/checkup/icon-success.png View File

Before After
Width: 43  |  Height: 42  |  Size: 2.1 KiB

BIN
pages_order/static/checkup/qrcode.png View File

Before After
Width: 122  |  Height: 122  |  Size: 8.3 KiB

Loading…
Cancel
Save