Commit 310d0547 authored by spc's avatar spc

Merge remote-tracking branch 'origin/dev' into 20250715_喂养记录

parents 0d9a4eb3 3c380543
// 产检提醒模块相关接口
import requestModule from './request.js';
const { api } = requestModule;
/**
* 获取banner
* @returns
*/
export const postnatalJSON = () => api.get('/c/front/content',{type:'postnatal'});
/**
* 获取产检信息
* @returns
*/
export const getInfo = (data) => api.get('/c/maternityCheckup/home',data);
/**
* 产看产检详情
* @returns
*/
export const getDetail = (data) => api.get('/c/maternityCheckup/detail',data);
/**
* 删除报告单
* @returns
*/
export const getDelete = (data) => api.get('/c/maternityCheckup/delete',data);
/**
* 产检项目列表
* @returns
*/
export const getExaminationItems = (data) => api.get('/c/maternityCheckup/examinationItems',data);
/**
* 修改产检记录
* @returns
*/
export const getUpdate = (data) => api.post('/c/maternityCheckup/update',data);
/**
*
* /**
* 新增产检记录
* @returns
*/
export const getAdd = (data) => api.post('/c/maternityCheckup/add',data);
/**
* 我的报告单列表
* @returns
*/
export const getReportList = () => api.get('/c/maternityCheckup/reportList');
/**
* 保存订阅消息
* @returns
*/
export const getWxNotification = (data) => api.post('/c/wxNotification/save',data);
\ No newline at end of file
......@@ -43,7 +43,6 @@ const request = (options = {}) => {
},
})
.then((data) => {
console.log('request data6666666 ===>', data);
if (data.statusCode !== HTTP_STATUS.SUCCESS) {
uni.showToast({
title: data.errMsg,
......@@ -72,7 +71,6 @@ const request = (options = {}) => {
};
const get = (url, data, options = {}) => {
console.log('6666666666');
options.type = "GET";
options.data = data;
options.url = url;
......@@ -80,7 +78,6 @@ const get = (url, data, options = {}) => {
};
const post = (url, data, options = {}) => {
console.log('5555555555=', url, data, options);
options.type = "POST";
options.data = data;
......
......@@ -12,4 +12,8 @@ export const growthHome = (babyId) => api.post('/c/growth/home', { babyId });
export const guideCompleted = () => api.post('/c/growth/guide/Completed');
export const assessmentSave = (data) => api.post('/c/growth/assessment/save', data);
export const getGrowthCurveData = (babyData) => api.post('/c/growth/curve/data', babyData);
export const getGrowthHistoryList = () => api.post('/c/growth/history/list');
// export const fetchHomeJSON = () => api.get('/c/front/content',{type:'home'});
\ No newline at end of file
<!-- components/DatePicker.vue -->
<template>
<!-- 底部弹窗遮罩层 -->
<view v-if="visible" class="picker-layer-mask" @click="close"></view>
<!-- 弹窗内容区 -->
<view v-if="visible" class="picker-layer-popup">
<view class="picker-layer-panel">
<!-- 可自定义头部 -->
<view class="picker-layer-header">
<text class="picker-layer-cancel" @click="close">取消</text>
<text class="picker-layer-confirm" @click="handleConfirm">确定</text>
</view>
<view class="picker-layer-view-mask-top"></view>
<view class="picker-layer-view-mask-bottom"></view>
<!-- picker-view 选择器核心 -->
<picker-view
class="picker-layer-view"
mask-style="background: rgb(246, 248, 250); z-index: 0;"
indicator-style="border-radius: 10px; height: 50px; background:#ffffff; z-index:0"
:value="timeValue"
:immediate-change="true"
@change="bindChange"
>
<picker-view-column>
<view class="picker-layer-item" v-for="(item,index) in years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="picker-layer-item" v-for="(item,index) in months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="picker-layer-item" v-for="(item,index) in days" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
</view>
</view>
</template>
<script setup>
import { ref, watch, onMounted, computed } from 'vue'
const props = defineProps({
visible: {
type: Boolean,
default: false
},
defaultDate: {
type: String,
default: ''
}
})
const emit = defineEmits(['update:visible', 'confirm'])
// 日期选择器相关状态
const dueDate = ref(uni.getStorageSync('dueDate') || '')
const date = new Date()
const timeValue = ref([]) // 日期选择器当前值
// 计算日期范围
const minDate = computed(() => {
if (!dueDate.value) return new Date(2010, 0, 1)
const due = new Date(dueDate.value)
const min = new Date(due)
min.setDate(min.getDate() - 280) // 280天前
return min
})
const maxDate = computed(() => {
if (!dueDate.value) return new Date(date.getFullYear() + 2, 11, 31)
return new Date(dueDate.value)
})
// 年份数据 (minDate的年份到maxDate的年份)
const years = computed(() => {
const yearList = []
const minYear = minDate.value.getFullYear()
const maxYear = maxDate.value.getFullYear()
for (let i = minYear; i <= maxYear; i++) {
yearList.push(i)
}
return yearList
})
// 月份数据 (根据当前选中的年份动态生成)
const months = computed(() => {
const monthList = []
let minMonth = 1
let maxMonth = 12
if (selectedYear.value === minDate.value.getFullYear()) {
minMonth = minDate.value.getMonth() + 1
}
if (selectedYear.value === maxDate.value.getFullYear()) {
maxMonth = maxDate.value.getMonth() + 1
}
for (let i = minMonth; i <= maxMonth; i++) {
monthList.push(i)
}
return monthList
})
// 日期数据 (动态生成)
const days = computed(() => {
const dayList = []
let minDay = 1
let maxDay = getDaysInMonth(selectedYear.value, selectedMonth.value)
// 如果是范围的最小年月
if (selectedYear.value === minDate.value.getFullYear() &&
selectedMonth.value === minDate.value.getMonth() + 1) {
minDay = minDate.value.getDate()
}
// 如果是范围的最大年月
if (selectedYear.value === maxDate.value.getFullYear() &&
selectedMonth.value === maxDate.value.getMonth() + 1) {
maxDay = maxDate.value.getDate()
}
for (let i = minDay; i <= maxDay; i++) {
dayList.push(i)
}
return dayList
})
// 当前选中的年月日
const selectedYear = ref(date.getFullYear())
const selectedMonth = ref(date.getMonth() + 1)
const selectedDay = ref(date.getDate())
// 获取某年某月的天数
function getDaysInMonth(year, month) {
return new Date(year, month, 0).getDate()
}
// 更新选择器的索引值
const updateValueIndices = () => {
const yearIndex = years.value.indexOf(selectedYear.value)
const monthIndex = months.value.indexOf(selectedMonth.value)
const dayIndex = days.value.indexOf(selectedDay.value)
if (yearIndex !== -1 && monthIndex !== -1 && dayIndex !== -1) {
timeValue.value = [yearIndex, monthIndex, dayIndex]
} else {
// 如果当前选中的值不在范围内,自动调整到最近的合法值
adjustToValidDate()
}
}
// 调整到合法的日期
const adjustToValidDate = () => {
let year = selectedYear.value
let month = selectedMonth.value
let day = selectedDay.value
// 调整年份
if (year < minDate.value.getFullYear()) {
year = minDate.value.getFullYear()
} else if (year > maxDate.value.getFullYear()) {
year = maxDate.value.getFullYear()
}
// 调整月份
if (year === minDate.value.getFullYear() && month < minDate.value.getMonth() + 1) {
month = minDate.value.getMonth() + 1
} else if (year === maxDate.value.getFullYear() && month > maxDate.value.getMonth() + 1) {
month = maxDate.value.getMonth() + 1
}
// 调整日期
const maxDays = getDaysInMonth(year, month)
if (day > maxDays) {
day = maxDays
}
if (year === minDate.value.getFullYear() &&
month === minDate.value.getMonth() + 1 &&
day < minDate.value.getDate()) {
day = minDate.value.getDate()
}
if (year === maxDate.value.getFullYear() &&
month === maxDate.value.getMonth() + 1 &&
day > maxDate.value.getDate()) {
day = maxDate.value.getDate()
}
selectedYear.value = year
selectedMonth.value = month
selectedDay.value = day
updateValueIndices()
}
// 设置日期
const setDate = (indices) => {
if (!indices || indices.length !== 3) return
const year = years.value[indices[0]]
const month = months.value[indices[1]]
const day = days.value[indices[2]]
selectedYear.value = year
selectedMonth.value = month
selectedDay.value = day
}
// 时间选择变化时间
const bindChange = (e) => {
const values = e.detail.value
timeValue.value = values
selectedYear.value = years.value[values[0]]
selectedMonth.value = months.value[values[1]]
selectedDay.value = days.value[values[2]]
}
// 关闭弹窗
const close = () => {
emit('update:visible', false)
}
// 确认选择
const handleConfirm = () => {
const formattedDate = `${selectedYear.value}-${String(selectedMonth.value).padStart(2, '0')}-${String(selectedDay.value).padStart(2, '0')}`
emit('confirm', formattedDate)
close()
}
// 初始化日期数据
const initDate = () => {
// 如果有默认日期,解析日期并设置选中值
if (props.defaultDate) {
const dateParts = props.defaultDate.split('-')
const year = parseInt(dateParts[0])
const month = parseInt(dateParts[1])
const day = parseInt(dateParts[2])
// 更新选中值
selectedYear.value = year
selectedMonth.value = month
selectedDay.value = day
// 调整到合法日期
adjustToValidDate()
} else {
// 如果没有时间,设置为当前日期或最大允许日期
const today = new Date()
if (today > maxDate.value) {
selectedYear.value = maxDate.value.getFullYear()
selectedMonth.value = maxDate.value.getMonth() + 1
selectedDay.value = maxDate.value.getDate()
} else if (today < minDate.value) {
selectedYear.value = minDate.value.getFullYear()
selectedMonth.value = minDate.value.getMonth() + 1
selectedDay.value = minDate.value.getDate()
} else {
selectedYear.value = today.getFullYear()
selectedMonth.value = today.getMonth() + 1
selectedDay.value = today.getDate()
}
updateValueIndices()
}
}
// 监听选择器值变化,更新选中日期
watch(() => timeValue.value, (newVal) => {
setDate(newVal)
})
// 监听visible变化,初始化数据
watch(() => props.visible, (newVal) => {
if (newVal) {
initDate()
}
})
// 监听dueDate变化,重新初始化
watch(() => uni.getStorageSync('dueDate'), (newVal) => {
dueDate.value = newVal
if (props.visible) {
initDate()
}
})
onMounted(() => {
dueDate.value = uni.getStorageSync('dueDate')
initDate()
})
</script>
<style lang="less" scoped>
/* 保持原有样式不变 */
.picker-layer-mask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 3999;
}
.picker-layer-popup {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 4000;
display: flex;
flex-direction: column;
align-items: center;
animation: picker-layer-up 0.3s;
}
@keyframes picker-layer-up {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}
.picker-layer-panel {
width: 100vw;
height: 50vh;
background: #f6f8fa;
border-top-left-radius: 24rpx;
border-top-right-radius: 24rpx;
overflow: hidden;
display: flex;
flex-direction: column;
position: relative;
}
.picker-layer-header {
display: flex;
justify-content: space-between;
align-items: center;
height: 140rpx;
box-sizing: border-box;
background: #f6f8fa;
padding: 0 32rpx;
.picker-layer-cancel {
color: #6f6d67;
font-size: 28rpx;
width: 136rpx;
height: 74rpx;
border-radius: 20rpx;
background: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
.picker-layer-confirm {
color: #ffffff;
font-size: 28rpx;
width: 136rpx;
height: 74rpx;
border-radius: 20rpx;
background: #d3a358;
display: flex;
align-items: center;
justify-content: center;
}
.picker-layer-title {
color: #222;
font-size: 32rpx;
font-weight: bold;
}
}
.picker-layer-view {
flex: 1;
width: 100%;
height: 100%;
}
.picker-layer-item {
height: 100rpx;
line-height: 100rpx;
text-align: center;
font-size: 32rpx;
color: #1d1e25;
}
.picker-layer-view-mask-top {
position: absolute;
top: 140rpx;
left: 0;
width: 100%;
height: calc(50% - 100rpx);
z-index: 1;
background: linear-gradient(to bottom,
rgb(246, 248, 250) 0%,
rgba(255, 255, 255, 0.5) 100%);
pointer-events: none;
}
.picker-layer-view-mask-bottom {
position: absolute;
bottom: 0rpx;
left: 0;
width: 100%;
height: calc(50% - 100rpx);
z-index: 1;
background: linear-gradient(to top,
rgb(246, 248, 250) 0%,
rgba(255, 255, 255, 0.5) 100%);
pointer-events: none;
}
</style>
\ No newline at end of file
<template>
<view class="customize">
<view class="navbar">
<view class="navbar-t" :style="{height:statusBarHeight+'px'}">
</view>
<view class="navbar-b" :style="{height:navBarHeight+'px',marginLeft:leftIconWidth+'px'}">
<slot name="navbar-content"></slot>
</view>
</view>
<view class="fill" :style="{height:fillHeight+'px'}">
</view>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
// const props = defineProps({
// title: {
// type: String,
// default: '首页'
// }
// })
const leftIconWidth = ref(0)
const navBarHeight = ref(0)
// 获取系统信息
const {statusBarHeight} = uni.getSystemInfoSync()
// 获取胶囊按钮信息
// #ifndef MP-ALIPAY || H5
const {top, height} = uni.getMenuButtonBoundingClientRect()
// 计算导航栏高度
navBarHeight.value = (top - statusBarHeight) *2 + height
// #endif
// 计算填充高度
const fillHeight = statusBarHeight + navBarHeight.value
</script>
<style lang="scss" scoped>
.customize {
.navbar{
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 100;
.navbar-t{
}
.navbar-b{
display: flex;
align-items: center;
padding: 0 35rpx;
}
}
}
</style>
\ No newline at end of file
{
"pages": [
"pages": [
{
"path": "pages/postnatalCheckUp/postnatalCheckUp",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
},
{
"path": "pages/index/index",
"style": {
......@@ -22,7 +30,6 @@
}
},
{
"path": "pages/search/search",
"style": {
"navigationStyle": "custom",
......@@ -33,12 +40,11 @@
}
},
{
"path" : "pages/product/product",
"style" :
{
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
"path": "pages/product/product",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
},
{
......@@ -48,45 +54,80 @@
}
},
{
"path" : "pages/goodsDetail/goodsDetail",
"style" :
{
"navigationBarTitleText" : ""
"path": "pages/goodsDetail/goodsDetail",
"style": {
"navigationBarTitleText": ""
}
},
{
"path" : "pages/rightsDetail/rightsDetail",
"style" :
{
"navigationBarTitleText" : ""
"path": "pages/rightsDetail/rightsDetail",
"style": {
"navigationBarTitleText": ""
}
},
},
{
"path" : "pages/naming/naming",
"style" :
{
"navigationBarTitleText" : "宝宝取名",
"enablePullDownRefresh" : false,
"path": "pages/naming/naming",
"style": {
"navigationBarTitleText": "宝宝取名",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
},
{
"path" : "pages/naming/namingResult",
"style" :
{
"navigationBarTitleText" : "AI-宝宝取名",
"enablePullDownRefresh" : false,
"path": "pages/naming/namingResult",
"style": {
"navigationBarTitleText": "AI-宝宝取名",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
},
{
"path" : "pages/feedingRecord/feedingRecord",
"style" :
{
"navigationBarTitleText" : "",
"path": "pages/feedingRecord/feedingRecord",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "pages/addPostnatal/addPostnatal",
"style": {
"navigationBarTitleText": "新增产检",
"navigationBarBackgroundColor": "#ffffff",
"enablePullDownRefresh": false
}
},
{
"path": "pages/myReportCard/myReportCard",
"style": {
"navigationBarTitleText": "我的报告单",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
},
{
"path": "pages/productionCalendar/productionCalendar",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
},
{
"path": "pages/productionDetails/productionDetails",
"style": {
"navigationBarTitleText": "产检详情",
"navigationBarBackgroundColor": "#ffffff",
"enablePullDownRefresh": false
}
},
{
"path": "pages/editPostnatal/editPostnatal",
"style": {
"navigationBarTitleText": "产检项目",
"navigationBarBackgroundColor": "#ffffff",
"enablePullDownRefresh": false
}
},
{
"path": "pages/shengzhangTools/shengzhangTools",
"style": {
......@@ -94,18 +135,16 @@
}
},
{
"path" : "pages/feedingAnalysis/feedingAnalysis",
"style" :
{
"navigationBarTitleText" : "",
"path": "pages/feedingAnalysis/feedingAnalysis",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path" : "pages/feedingIndex/feedingIndex",
"style" :
{
"navigationBarTitleText" : "添加喂养记录"
"path": "pages/feedingIndex/feedingIndex",
"style": {
"navigationBarTitleText": "添加喂养记录"
}
},
{
......@@ -121,7 +160,6 @@
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8",
"navigationStyle": "default"
},
"uniIdRouter": {}
}
\ No newline at end of file
<template>
<view class="add-postnatal">
<!-- 产检时间 -->
<view class="add-postnatal-time style1">
<view class="time-l">
<image src="/static/chanjianTool/icon7.png" />
产检时间
</view>
<view class="time-r" @click="onChangeTime">
{{ time }}
<image src="/static/chanjianTool/icon13.png" />
</view>
</view>
<!-- 产检项目 -->
<view class="add-postnatal-project">
<view class="project-info style1">
<view class="time-l">
<image src="/static/chanjianTool/icon8.png" />
产检项目
</view>
<view class="project-r" @click="onAdd">
<image src="/static/chanjianTool/icon10.png" />
添加
</view>
</view>
<!-- 添加的产品项目 -->
<view class="project-list" v-if="listData.length > 0">
<view class="list-item" v-for="({ itemName, id }, index) in listData" :key="index">
<view class="item-name">
{{ itemName }}
</view>
<image @click="onDelete(id)" class="item-img" src="/static/chanjianTool/delete.png"></image>
</view>
</view>
</view>
<!-- 报告单 -->
<view class="add-postnatal-bgd">
<view class="style1">
<view class="time-l">
<image src="/static/chanjianTool/icon9.png" />
本次报告单
</view>
<view class="time-r" @click="onSeeBtn">
查看全部
<image src="/static/chanjianTool/icon13.png" />
</view>
</view>
<view class="img-list">
<view class="upload-image" @click="onUpload">
<image src="/static/chanjianTool/icon11.png"></image>
</view>
<view class="img-list-item" v-for="(item, index) in bgdImgList" :key="index">
<view class="item-image" @click="onPreviewImage(item)">
<image class="img1" :src="item" mode="widthFix"></image>
</view>
<image @click="onImageDel(item)" class="img" src="/static/chanjianTool/icon14.png"></image>
</view>
</view>
</view>
<!-- 上传报告单 -->
<button class="form-btn" @click="onSave">
保存
</button>
<!-- 产检选择时间 -->
<!-- 使用封装后的日期选择器组件 -->
<DatePicker v-model:visible="visible" :default-date="time" @confirm="handleDateConfirm" />
<!-- 产检项目底部弹窗 -->
<uni-popup ref="popup" type="bottom" :mask-click="false" background-color="#f6f8fa"
border-radius="32rpx 32rpx 0 0">
<view class="popup container">
<view class="popup-close" @click="onPopupClose">
<image src="/static/chanjianTool/icon15.png"></image>
</view>
<!-- 项目列表 -->
<scroll-view scroll-y @touchmove.stop.prevent="" class="airport-list">
<view v-for="group in examinationList" :key="group.groupName" class="letter-group">
<view class="letter-title">{{ group.groupName }}</view>
<view class="box">
<view v-for="item in group.list" :key="item.id" class="airport-item">
<view class="airport-item-radio" @click="toggleSelect(item)">
<image class="image-1" v-if="isSelected(item.id)"
src="/static/chanjianTool/icon17.png" mode=""></image>
<image class="image-2" v-else src="/static/chanjianTool/icon18.png"></image>
<view class="image-3">
{{ item.itemName }}
</view>
</view>
<view class="airport-item-image" @click.stop="onView(item)">
<image src="/static/chanjianTool/icon16.png"></image>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 底部操作栏 -->
<button class="action-bar" @click="saveSelection">确认</button>
</view>
</uni-popup>
<!-- 项目介绍 -->
<uni-popup ref="popupText" type="bottom" :mask-click="true" background-color="#FFFFFF"
border-radius="32rpx 32rpx 0 0">
<view class="container2">
<view class="text-t">
<view class="t-1">
{{ popupTextObj.itemName }}
</view>
<image @click="onPopupClose2" src="/static/chanjianTool/icon15.png" mode=""></image>
</view>
<view class="text-c">
{{ popupTextObj.introduction }}
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
import {
dateFormatter,
throttleTap,
showLoading,
hideLoading
} from '@/utils/index.js';
import { onLoad } from '@dcloudio/uni-app'
import {
uploadImage
} from "../../api/common.js";
import {
getAdd,
getExaminationItems
} from '../../api/obstetric.js';
// 导入日期选择器组件
import DatePicker from '@/components/DatePicker.vue'
// 默认产检时间
const time = ref('');
// 控制选择时间显示
const visible = ref(false)
// 产品项目底部弹窗
const popup = ref(null)
// 产品项目介绍底部弹窗
const popupText = ref(null)
// 存储查看项目介绍内容
const popupTextObj = ref({})
// 模拟项目列表
const examinationList = ref([])
// 已选项目(存储ID和name)
const selectedAirports = ref([]);
// 添加的产品列表
const listData = ref([]);
// 报告单图片
const bgdImgList = ref([])
// 查看项目介绍
const onView = (item) => {
console.log(item)
popupTextObj.value = item
popupText.value.open()
}
// 关闭项目介绍弹窗
const onPopupClose2 = () => {
popupText.value.close()
}
// 修改时间
const onChangeTime = () => {
visible.value = true;
}
// 选择日期回调确认
const handleDateConfirm = (date) => {
console.log('选择的日期是:', date);
time.value = date;
}
// 添加项目
const onAdd = () => {
wx.setPageStyle({
style: {
overflow: 'hidden'
}
})
popup.value.open()
// 回显数据
if (listData.value.length > 0) {
// 回显数据 - 将 listData 中的数据同步到 selectedAirports
selectedAirports.value = [...listData.value]
}
}
// 关闭项目弹窗
const onPopupClose = () => {
wx.setPageStyle({
style: {
overflow: 'auto'
}
})
selectedAirports.value = []
popup.value.close()
}
// 检查是否已选中
const isSelected = (id) => {
return selectedAirports.value.some(item => item.id == id);
};
// 切换选择状态
const toggleSelect = (airport) => {
const index = selectedAirports.value.findIndex(item => item.id == airport.id);
if (index === -1) {
selectedAirports.value.push({
id: airport.id,
itemName: airport.itemName
});
} else {
selectedAirports.value.splice(index, 1);
}
};
// 保存选择
const saveSelection = () => {
console.log('已选项目ID:', selectedAirports.value);
// 合并 selectedAirports.value 到 listData.value,并去重
const combined = [...listData.value, ...selectedAirports.value];
const uniqueMap = new Map();
combined.forEach(item => uniqueMap.set(item.id, item));
listData.value = Array.from(uniqueMap.values());
onPopupClose();
};
// // 移除已选项目
// const removeSelected = (airport) => {
// const index = selectedAirports.value.findIndex(item => item.id === airport.id);
// if (index !== -1) {
// selectedAirports.value.splice(index, 1);
// }
// };
// 删除所选产品项目
const onDelete = (id) => {
listData.value.filter((item, index) => {
if (item.id == id) {
listData.value.splice(index, 1)
}
})
}
// 图片预览
const onPreviewImage = (url) => {
console.log(url)
uni.previewImage({
current: '1',
urls: [url]
})
}
// 删除上传图片
const onImageDel = (e) => {
bgdImgList.value.filter((item, index) => {
if (item === e) {
bgdImgList.value.splice(index, 1)
}
})
}
// 上传图片
const onUpload = throttleTap(() => {
if (bgdImgList.value.length == 15) {
uni.showToast({
title: "最多上传15张图片",
icon: "none",
});
return;
}
// 唤起图片选择器
uni.chooseImage({
count: 1,
sizeType: ["original", "compressed"],
sourceType: ["album", "camera"],
success: async (res) => {
showLoading();
const tempFilePath = res.tempFilePaths[0];
console.log(tempFilePath)
const fs = uni.getFileSystemManager();
console.log(fs)
const base64 =
"data:image/jpeg;base64," + fs.readFileSync(tempFilePath, "base64");
const uploadRes = await uploadImage(base64);
hideLoading();
if (uploadRes.success) {
console.log(uploadRes)
bgdImgList.value.push(uploadRes.data.url)
} else {
uni.showToast({
title: uploadRes.message,
icon: "none",
});
}
},
});
})
// 保存
const onSave = throttleTap( async () => {
if (listData.value.length == 0) {
uni.showToast({
title: '还没有添加产检项哦',
icon: 'none'
})
return
}
if (bgdImgList.value.length == 0) {
uni.showToast({
title: '还没有上传报告单哦',
icon: 'none'
})
return
}
const ids = listData.value.map(item => item.id).join(',');
const param = {
checkupDate: time.value,
checkupItems: ids,
reportImages: bgdImgList.value
}
console.log(param, '参数')
showLoading();
const {success, data } = await getAdd(param);
hideLoading();
if (success) {
uni.showToast({
title: "保存成功",
icon: "success",
});
uni.navigateBack()
} else {
uni.showToast({
title: message,
icon: "none",
});
}
})
// 查看更多
const onSeeBtn = () => {
// 跳转
uni.navigateTo({
url: '/pages/myReportCard/myReportCard'
})
}
// 获取产检项目列表
const getList = async () => {
const {success, data, message } = await getExaminationItems()
if (success) {
examinationList.value = data
}
}
onLoad((options) => {
if (Object.keys(options).length > 0 && options.time) {
time.value = options.time
} else {
time.value = dateFormatter(new Date(), 'yyyy-MM-dd')
}
console.log(options, '新增页面')
getList()
})
</script>
<style lang="less" scoped>
.add-postnatal {
height: 100vh;
background-color: #f7f8fa;
padding: 25rpx 35rpx;
.style1 {
width: 680rpx;
height: 115rpx;
background: #ffffff;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 30rpx;
padding: 0 27rpx;
box-sizing: border-box;
.time-l,
.time-r,
.project-r {
display: flex;
align-items: center;
}
.time-l {
color: #000000;
font-weight: 600;
image {
width: 30rpx;
height: 30rpx;
margin-right: 10rpx;
}
}
}
&-time {
margin-top: 22rpx;
border-radius: 16rpx;
.time-r {
color: #1d1e26;
image {
width: 8rpx;
height: 16rpx;
margin-left: 12rpx;
}
}
}
&-project {
width: 680rpx;
border-radius: 16rpx;
overflow: hidden;
margin-top: 22rpx;
background: #ffffff;
.project-r {
color: #b27c1e;
image {
width: 27rpx;
height: 27rpx;
margin-right: 5rpx;
}
}
}
&-bgd {
width: 680rpx;
border-radius: 16rpx;
margin-top: 22rpx;
background: #ffffff;
.time-r {
color: #1d1e26;
image {
width: 8rpx;
height: 16rpx;
margin-left: 12rpx;
}
}
.img-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
padding: 0 27rpx;
box-sizing: border-box;
padding-bottom: 180rpx;
.upload-image {
width: 200rpx;
height: 200rpx;
border-radius: 10rpx;
overflow: hidden;
margin-right: 10rpx;
image {
width: 200rpx;
height: 200rpx;
}
}
.img-list-item {
// margin-right: 10rpx;
margin-bottom: 15rpx;
position: relative;
.item-image {
width: 200rpx;
height: 200rpx;
border-radius: 10rpx;
overflow: hidden;
.img1 {
width: 200rpx;
height: 200rpx;
}
}
.img {
position: absolute;
right: -12rpx;
top: -12rpx;
width: 25rpx;
height: 25rpx;
}
}
}
}
.project-list {
.list-item {
width: 680rpx;
height: 115rpx;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 30rpx;
padding: 0 27rpx;
border-top: 1rpx solid #e9e9e9;
box-sizing: border-box;
.item-name {
width: 500rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 30rpx;
color: #000000;
font-weight: 500;
}
.item-img {
width: 31rpx;
height: 31rpx;
}
}
}
.form-btn {
width: 686rpx;
height: 94rpx;
border-radius: 46rpx;
background-color: #d3a358;
color: #ffffff;
font-size: 32rpx;
display: flex;
align-items: center;
justify-content: center;
position: fixed;
bottom: 60rpx;
left: 50%;
transform: translateX(-50%);
z-index: 2;
}
// 索引栏
.container {
display: flex;
flex-direction: column;
height: 1200rpx;
padding: 20rpx 35rpx 30rpx 35rpx;
overflow: hidden;
background: #f6f8fa;
position: relative;
border-radius: 32rpx 32rpx 0 0;
}
.popup-close {
position: absolute;
right: 34rpx;
top: 45rpx;
z-index: 10;
image {
width: 28rpx;
height: 29rpx;
}
}
.airport-list {
flex: 1;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
.letter-group {
margin-top: 30rpx;
margin-bottom: 30rpx;
}
.letter-title {
padding: 0 38rpx;
font-weight: bold;
// position: sticky;
// top: 0;
z-index: 10;
color: #000000;
font-weight: 600;
font-size: 34rpx;
margin-bottom: 30rpx;
}
.box {
width: 100%;
overflow: hidden;
border-radius: 16rpx;
padding: 0 0 0 36rpx;
box-sizing: border-box;
background: #fff;
}
.airport-item {
width: 100%;
height: 115rpx;
box-sizing: border-box;
background-color: #ffffff;
touch-action: none;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1rpx solid #e2e2e2;
&:last-child {
border: none;
}
.airport-item-image {
width: 72rpx;
text-align: center;
height: 115rpx;
line-height: 115rpx;
image {
width: 11rpx;
height: 21rpx;
}
}
}
.airport-item-radio {
flex: 1;
display: flex;
align-items: center;
overflow: hidden;
image {
margin-right: 8rpx;
margin-top: 5rpx;
}
.image-1 {
width: 29rpx;
height: 29rpx;
}
.image-2 {
width: 29rpx;
height: 29rpx;
}
.image-3{
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.action-bar {
width: 686rpx;
height: 94rpx;
border-radius: 46rpx;
background-color: #d3a358;
color: #ffffff;
font-size: 32rpx;
display: flex;
align-items: center;
justify-content: center;
position: sticky;
bottom: 0;
z-index: 10;
}
.selected-list {
margin-top: 20rpx;
padding: 20rpx;
background-color: #f9f9f9;
border-radius: 10rpx;
max-height: 200rpx;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
.title {
font-weight: bold;
margin-bottom: 15rpx;
position: sticky;
top: 0;
background-color: #f9f9f9;
z-index: 10;
}
.selected-items {
display: flex;
flex-wrap: wrap;
}
.selected-item {
position: relative;
background-color: #e6f7ff;
color: #1890ff;
padding: 10rpx 20rpx;
margin: 0 15rpx 15rpx 0;
border-radius: 30rpx;
touch-action: none;
}
.remove {
margin-left: 10rpx;
color: #ff4d4f;
cursor: pointer;
}
.container2 {
height: 595rpx;
padding: 50rpx 35rpx 30rpx 35rpx;
overflow: hidden;
background: #ffffff;
border-radius: 32rpx 32rpx 0 0;
color: #000000;
display: flex;
flex-direction: column;
.text-t {
flex-shrink: 0;
position: relative;
.t-1 {
flex: 1;
font-size: 34rpx;
font-weight: 700;
margin-right: 37rpx;
}
image {
position: absolute;
top: 9.5rpx;
right: 0rpx;
width: 28rpx;
height: 29rpx;
}
}
.text-c {
flex: 1;
overflow: auto;
margin-top: 22rpx;
font-weight: 500;
}
}
}
</style>
\ No newline at end of file
<template>
<view class="add-postnatal">
<!-- 产检项目 -->
<view class="add-postnatal-project">
<view class="project-info style1">
<view class="time-l">
<image src="/static/chanjianTool/icon8.png" />
产检项目
</view>
<view class="project-r" @click="onAdd">
<image src="/static/chanjianTool/icon10.png" />
添加
</view>
</view>
<!-- 添加的产品项目 -->
<view class="project-list" v-if="listData.length > 0">
<view class="list-item" v-for="({ itemName, id }, index) in listData" :key="index">
<view class="item-name">
{{ itemName }}
</view>
<image @click="onDelete(id)" class="item-img" src="/static/chanjianTool/delete.png"></image>
</view>
</view>
</view>
<!-- 上传报告单 -->
<button class="form-btn" @click="onSave">
保存
</button>
<!-- 产检项目底部弹窗 -->
<uni-popup ref="popup" type="bottom" :mask-click="false" background-color="#f6f8fa"
border-radius="32rpx 32rpx 0 0">
<view class="popup container">
<view class="popup-close" @click="onPopupClose">
<image src="/static/chanjianTool/icon15.png"></image>
</view>
<!-- 项目列表 -->
<scroll-view scroll-y @touchmove.stop.prevent="" class="airport-list">
<view v-for="group in examinationList" :key="group.groupName" class="letter-group">
<view class="letter-title">{{ group.groupName }}</view>
<view class="box">
<view v-for="item in group.list" :key="item.id" class="airport-item">
<view class="airport-item-radio" @click="toggleSelect(item)">
<image class="image-1" v-if="isSelected(item.id)"
src="/static/chanjianTool/icon17.png" mode=""></image>
<image class="image-2" v-else src="/static/chanjianTool/icon18.png"></image>
<view class="image-3">
{{ item.itemName }}
</view>
</view>
<view class="airport-item-image" @click.stop="onView(item)">
<image src="/static/chanjianTool/icon16.png"></image>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 底部操作栏 -->
<button class="action-bar" @click="saveSelection">确认</button>
</view>
</uni-popup>
<!-- 项目介绍 -->
<uni-popup ref="popupText" type="bottom" :mask-click="true" background-color="#FFFFFF"
border-radius="32rpx 32rpx 0 0">
<view class="container2">
<view class="text-t">
<view class="t-1">
{{ popupTextObj.itemName }}
</view>
<image @click="onPopupClose2" src="/static/chanjianTool/icon15.png" mode=""></image>
</view>
<view class="text-c">
{{ popupTextObj.introduction }}
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
import {
throttleTap,
showLoading,
hideLoading
} from '@/utils/index.js';
import { onLoad } from '@dcloudio/uni-app'
import {
getUpdate,
getExaminationItems
} from '../../api/obstetric.js';
// 产检id
const editId = ref('')
// 产品项目底部弹窗
const popup = ref(null)
// 产品项目介绍底部弹窗
const popupText = ref(null)
// 存储查看项目介绍内容
const popupTextObj = ref({})
// 项目列表数据
const examinationList = ref([])
// 已选项目(存储ID和name)
const selectedAirports = ref([]);
// 添加的产品列表
const listData = ref([]);
// 查看项目介绍
const onView = (item) => {
console.log(item)
popupTextObj.value = item
popupText.value.open()
}
// 关闭项目介绍弹窗
const onPopupClose2 = () => {
popupText.value.close()
}
// 添加项目
const onAdd = () => {
wx.setPageStyle({
style: {
overflow: 'hidden'
}
})
popup.value.open()
// 回显数据
if (listData.value.length > 0) {
// 回显数据 - 将 listData 中的数据同步到 selectedAirports
selectedAirports.value = [...listData.value]
}
}
// 关闭项目弹窗
const onPopupClose = () => {
wx.setPageStyle({
style: {
overflow: 'auto'
}
})
selectedAirports.value = []
popup.value.close()
}
// 检查是否已选中
const isSelected = (id) => {
return selectedAirports.value.some(item => item.id == id);
};
// 切换选择状态
const toggleSelect = (airport) => {
const index = selectedAirports.value.findIndex(item => item.id == airport.id);
if (index === -1) {
selectedAirports.value.push({
id: airport.id,
itemName: airport.itemName
});
} else {
selectedAirports.value.splice(index, 1);
}
};
// 保存选择
const saveSelection = () => {
console.log('已选项目ID:', selectedAirports.value);
// 合并 selectedAirports.value 到 listData.value,并去重
const combined = [...listData.value, ...selectedAirports.value];
const uniqueMap = new Map();
combined.forEach(item => uniqueMap.set(item.id, item));
listData.value = Array.from(uniqueMap.values());
onPopupClose();
};
// 移除已选项目
// const removeSelected = (airport) => {
// const index = selectedAirports.value.findIndex(item => item.id == airport.id);
// if (index !== -1) {
// selectedAirports.value.splice(index, 1);
// }
// };
// 删除所选产品项目
const onDelete = (id) => {
listData.value.filter((item, index) => {
if (item.id == id) {
listData.value.splice(index, 1)
}
})
}
// 保存
const onSave = throttleTap(async () => {
if (listData.value.length == 0) {
uni.showToast({
title: '还没有添加产检项哦',
icon: 'none'
})
return
}
const ids = listData.value.map(item => item.id).join(',');
const param = {
id: editId.value,
checkupItems: ids
}
console.log(param, '参数')
showLoading();
const {success, data} = await getUpdate(param);
hideLoading();
if (success) {
uni.showToast({
title: "保存成功",
icon: "success",
});
// 返回B页面并刷新数据
const pages = getCurrentPages();
const prevPage = pages[pages.length - 2]; // 获取上一页(详情页面)实例
if (prevPage) {
prevPage.onLoad({ id: editId.value }); // 调用页面的刷新方法
};
uni.navigateBack({
delta: 1 // 返回上一页
});
} else {
uni.showToast({
title: message,
icon: "none",
});
}
})
// 获取产检项目列表
const getList = async () => {
const {success, data, message} = await getExaminationItems()
if (success) {
examinationList.value = data
}
}
onLoad((options) => {
const { id, examinationItems } = JSON.parse(options.item)
console.log(id, examinationItems)
editId.value = id
listData.value = examinationItems
getList()
})
</script>
<style lang="less" scoped>
.add-postnatal {
height: 100vh;
background-color: #f7f8fa;
padding: 25rpx 35rpx;
.style1 {
width: 680rpx;
height: 115rpx;
background: #ffffff;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 30rpx;
padding: 0 27rpx;
box-sizing: border-box;
.time-l,
.time-r,
.project-r {
display: flex;
align-items: center;
}
.time-l {
color: #000000;
font-weight: 600;
image {
width: 30rpx;
height: 30rpx;
margin-right: 10rpx;
}
}
}
&-time {
margin-top: 22rpx;
border-radius: 16rpx;
.time-r {
color: #1d1e26;
image {
width: 8rpx;
height: 16rpx;
margin-left: 12rpx;
}
}
}
&-project {
width: 680rpx;
border-radius: 16rpx;
overflow: hidden;
margin-top: 22rpx;
background: #ffffff;
.project-r {
color: #b27c1e;
image {
width: 27rpx;
height: 27rpx;
margin-right: 5rpx;
}
}
}
&-bgd {
width: 680rpx;
border-radius: 16rpx;
margin-top: 22rpx;
background: #ffffff;
.time-r {
color: #1d1e26;
image {
width: 8rpx;
height: 16rpx;
margin-left: 12rpx;
}
}
.img-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
padding: 0 27rpx;
box-sizing: border-box;
padding-bottom: 180rpx;
.upload-image {
width: 200rpx;
height: 200rpx;
border-radius: 10rpx;
overflow: hidden;
margin-right: 10rpx;
image {
width: 200rpx;
height: 200rpx;
}
}
.img-list-item {
// margin-right: 10rpx;
margin-bottom: 15rpx;
position: relative;
.item-image {
width: 200rpx;
height: 200rpx;
border-radius: 10rpx;
overflow: hidden;
.img1 {
width: 200rpx;
height: 200rpx;
}
}
.img {
position: absolute;
right: -12rpx;
top: -12rpx;
width: 25rpx;
height: 25rpx;
}
}
}
}
.project-list {
.list-item {
width: 680rpx;
height: 115rpx;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 30rpx;
padding: 0 27rpx;
border-top: 1rpx solid #e9e9e9;
box-sizing: border-box;
.item-name {
width: 500rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 30rpx;
color: #000000;
font-weight: 500;
}
.item-img {
width: 31rpx;
height: 31rpx;
}
}
}
.form-btn {
width: 686rpx;
height: 94rpx;
border-radius: 46rpx;
background-color: #d3a358;
color: #ffffff;
font-size: 32rpx;
display: flex;
align-items: center;
justify-content: center;
position: fixed;
bottom: 60rpx;
left: 50%;
transform: translateX(-50%);
z-index: 2;
}
// 索引栏
.container {
display: flex;
flex-direction: column;
height: 1200rpx;
padding: 20rpx 35rpx 30rpx 35rpx;
overflow: hidden;
background: #f6f8fa;
position: relative;
border-radius: 32rpx 32rpx 0 0;
}
.popup-close {
position: absolute;
right: 34rpx;
top: 45rpx;
z-index: 10;
image {
width: 28rpx;
height: 29rpx;
}
}
.airport-list {
flex: 1;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
.letter-group {
margin-top: 30rpx;
margin-bottom: 30rpx;
}
.letter-title {
padding: 0 38rpx;
font-weight: bold;
// position: sticky;
// top: 0;
z-index: 10;
color: #000000;
font-weight: 600;
font-size: 34rpx;
margin-bottom: 30rpx;
}
.box {
width: 100%;
overflow: hidden;
border-radius: 16rpx;
padding: 0 0 0 36rpx;
box-sizing: border-box;
background: #fff;
}
.airport-item {
width: 100%;
height: 115rpx;
box-sizing: border-box;
background-color: #ffffff;
touch-action: none;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1rpx solid #e2e2e2;
&:last-child {
border: none;
}
.airport-item-image {
width: 72rpx;
text-align: center;
height: 115rpx;
line-height: 115rpx;
image {
width: 11rpx;
height: 21rpx;
}
}
}
.airport-item-radio {
flex: 1;
display: flex;
align-items: center;
overflow: hidden;
image {
margin-right: 8rpx;
margin-top: 5rpx;
}
.image-1 {
width: 29rpx;
height: 29rpx;
}
.image-2 {
width: 29rpx;
height: 29rpx;
}
.image-3 {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.action-bar {
width: 686rpx;
height: 94rpx;
border-radius: 46rpx;
background-color: #d3a358;
color: #ffffff;
font-size: 32rpx;
display: flex;
align-items: center;
justify-content: center;
position: sticky;
bottom: 0;
z-index: 10;
}
.selected-list {
margin-top: 20rpx;
padding: 20rpx;
background-color: #f9f9f9;
border-radius: 10rpx;
max-height: 200rpx;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
.title {
font-weight: bold;
margin-bottom: 15rpx;
position: sticky;
top: 0;
background-color: #f9f9f9;
z-index: 10;
}
.selected-items {
display: flex;
flex-wrap: wrap;
}
.selected-item {
position: relative;
background-color: #e6f7ff;
color: #1890ff;
padding: 10rpx 20rpx;
margin: 0 15rpx 15rpx 0;
border-radius: 30rpx;
touch-action: none;
}
.remove {
margin-left: 10rpx;
color: #ff4d4f;
cursor: pointer;
}
.container2 {
height: 595rpx;
padding: 50rpx 35rpx 30rpx 35rpx;
overflow: hidden;
background: #ffffff;
border-radius: 32rpx 32rpx 0 0;
color: #000000;
display: flex;
flex-direction: column;
.text-t {
flex-shrink: 0;
position: relative;
.t-1 {
flex: 1;
font-size: 34rpx;
font-weight: 700;
margin-right: 37rpx;
}
image {
position: absolute;
top: 9.5rpx;
right: 0rpx;
width: 28rpx;
height: 29rpx;
}
}
.text-c {
flex: 1;
overflow: auto;
margin-top: 22rpx;
font-weight: 500;
}
}
}
</style>
\ No newline at end of file
<template>
<view class="report-card" style="background-image: url( '//yun.duiba.com.cn/aurora/assets/c1e4ce971b7ffb55ed23ec8a9480b857a6328e98.png');">
<!-- <view class="page-top">
<image @tap="backHandler" class="btnback" src="/static/chanjianTool/back.png"></image>
<view class="page_title">我的报告单</view>
</view> -->
<customize-navigation>
<template v-slot:navbar-content>
<view class="page-top">
<image @tap="backHandler" class="btnback" src="/static/chanjianTool/back.png"></image>
<view class="page_title">我的报告单</view>
</view>
</template>
</customize-navigation>
<view class="report-card-con">
<view class="con-box">
<template v-if="listData.length > 0">
<view class="list-item" v-for="(item, index) in listData" :key="index">
<view class="item-time">
<view class="item-time-l">
<image src="/static/chanjianTool/icon19.png"></image>
{{item.checkupDate}}
</view>
<view class="item-time-r" @click="onDelete(item.id)">
<image src="/static/chanjianTool/delete.png"></image>
</view>
</view>
<view class="item-line">
</view>
<view class="item-content">
<view class="content-1">
{{item.checkupTimes}}次产检:孕{{item.pregnancyCycle}}
</view>
<view class="content-2">
重点: {{getProject(item.items)}}
</view>
<view class="content-3">
<template v-for="(e, i) in item.reportImages.slice(0, 3)" :key="i">
<view class="content-3-v" :class="{ 'has-more': i === 2 && item.reportImages.length > 3 }">
<image @click="onPreviewImage(e)" :src="e" mode="widthFix"></image>
<view @click="onMoreImage(item.reportImages)" class="more-count"
v-if="i === 2 && item.reportImages.length > 3">
+{{ item.reportImages.length - 3 }}
</view>
</view>
</template>
</view>
</view>
</view>
</template>
<template v-else>
<view class="no-data">
<image src="/static/chanjianTool/icon28.png"></image>
</view>
</template>
</view>
</view>
<!-- 查看更多图片 -->
<view class="more-popup" v-if="showMore">
<view class="more-popup-list">
<view class="title">
我的报告单
</view>
<view class="more-image">
<view v-for="(item, index) in imageListData" :key="index" class="more-image-1"
@click="onPreviewImage(item)">
<image :src="item"></image>
</view>
</view>
</view>
<view class="close-btn-bottom" @tap="onClose">
<image src="/static/chanjianTool/icon21.png"></image>
</view>
</view>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
import {
onLoad,
onShow
} from '@dcloudio/uni-app'
import {
throttleTap
} from '@/utils/index.js';
import {
getReportList,
getDelete
} from '../../api/obstetric.js';
import md from '../../md';
// 控制查看更多图片
const showMore = ref(false)
// 存储查看的图片数组
const imageListData = ref([])
// 报告单数据
const listData = ref([])
// 查看更多图片
const onMoreImage = (item) => {
console.log(item)
showMore.value = true
imageListData.value = item
}
// 关闭
const onClose = () => {
showMore.value = false;
}
// 图片预览
const onPreviewImage = (url) => {
console.log(url)
uni.previewImage({
current: '1',
urls: [url]
})
}
// 拼接检查项目名称
const getProject = (projects) => {
return projects.map(project => project.itemName).join('、');
}
// 删除事件
const onDelete = throttleTap((id) => {
console.log(id)
uni.showModal({
// title: '提示',
content: '确认删除吗?',
success: function (res) {
if (res.confirm) {
getDeleteFn(id)
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
// getDeleteFn(id)
})
// 返回上一页面
const backHandler = () => {
uni.navigateBack();
}
// 删除报告单
const getDeleteFn = async (id) => {
const { code, message, data, success } = await getDelete({id})
if (success) {
uni.showToast({
title: '删除成功',
icon: 'none'
})
// 我的报告单
getReportListFn()
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
// 获取报告单
const getReportListFn = async () => {
console.log('获取报告单')
const { code, message, data, success } = await getReportList()
if (success) {
listData.value = data
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
onLoad(() => {
// 我的报告单
getReportListFn()
})
</script>
<style lang="less" scoped>
.report-card {
// position: absolute;
width: 100%;
height: 100vh;
background-size: 100% 100%;
&-con {
position: absolute;
// width: 750rpx;
top: 190rpx;
left: 31rpx;
.con-box {
width: 682rpx;
height: calc(100vh - 220rpx);
overflow-y: auto;
.no-data{
height: 100%;
display: flex;
align-items: center;
justify-content: center;
image{
width: 168rpx;
height: 167rpx;
}
}
}
.list-item {
width: 682rpx;
border-radius: 24rpx;
background: #ffffff;
padding: 35rpx 34rpx 45rpx 34rpx;
box-sizing: border-box;
margin-bottom: 30rpx;
.item-time {
display: flex;
justify-content: space-between;
align-items: center;
.item-time-l {
display: flex;
align-items: center;
font-weight: 600;
font-size: 32rpx;
image {
width: 32rpx;
height: 32rpx;
margin-right: 15rpx;
}
}
.item-time-r {
image {
width: 31rpx;
height: 31rpx;
}
}
}
.item-line {
width: 100%;
height: 1rpx;
margin: 30rpx 0;
background: #f4e1c4;
}
.item-content {
.content-1,
.content-2 {
font-size: 26rpx;
font-weight: 600;
}
.content-2 {
margin-top: 15rpx;
}
.content-3 {
display: flex;
flex-wrap: wrap;
margin-top: 35rpx;
.content-3-v {
width: 192rpx;
height: 192rpx;
border-radius: 10rpx;
overflow: hidden;
margin-right: 13rpx;
position: relative;
}
.content-3-v.has-more image {
opacity: 0.7;
}
.more-count {
position: absolute;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
color: white;
width: 192rpx;
height: 192rpx;
border-radius: 10rpx;
font-size: 38rpx;
display: flex;
justify-content: center;
align-items: center;
}
image {
width: 192rpx;
height: 192rpx;
}
}
}
}
}
.more-popup {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 9999;
.more-popup-list {
width: 661rpx;
height: 881rpx;
background: #ffffff;
border-radius: 52rpx;
padding: 50rpx 58rpx;
box-sizing: border-box;
.title {
text-align: center;
font-size: 36rpx;
color: #b27c1e;
margin-bottom: 45rpx;
}
.more-image {
width: 100%;
height: 695rpx;
overflow: auto;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.more-image-1 {
width: 169rpx;
height: 169rpx;
border-radius: 10rpx;
overflow: hidden;
margin-right: 12rpx;
margin-bottom: 30rpx;
flex-shrink: 0;
image {
width: 100%;
height: 100%;
/* 新增:确保图片填充模式与父容器一致 */
object-fit: cover;
/* 可选:加载时避免闪烁 */
display: block;
}
}
}
}
.close-btn-bottom {
margin: 40rpx auto 0 auto;
width: 70rpx;
height: 70rpx;
text-align: center;
z-index: 10001;
display: flex;
align-items: center;
justify-content: center;
image {
width: 70rpx;
height: 70rpx;
}
}
// 自定义头部
.page-top {
width: 100%;
display: flex;
align-items: center;
}
.btnback {
width: 16rpx;
height: 29rpx;
// margin-top: 110rpx;
// margin-left: 35rpx;
}
.page_title {
width: 100%;
font-size: 34rpx;
font-weight: 500;
text-align: center;
color: #1d1e25;
line-height: 36rpx;
// margin-top: -46rpx;
// margin-left: 0rpx;
}
}
</style>
\ No newline at end of file
<template>
<view>
<view class="postnatal">
<!-- <view class="page-top">
<image @tap="backHandler" class="btnback" src="/static/chanjianTool/back.png"></image>
<view class="page_title">产检提醒</view>
</view> -->
<!-- 自定义导航 -->
<customize-navigation>
<template v-slot:navbar-content>
<view class="page-top">
<image @tap="backHandler" class="btnback" src="/static/chanjianTool/back.png"></image>
<view class="page_title">产检提醒</view>
</view>
</template>
</customize-navigation>
<view class="postnatal-con">
<!-- 轮播图 -->
<swiper class="banner-swiper" :autoplay="true" :circular="true">
<swiper-item v-for="(item, index) in bannerList" :key="index">
<image class="banner-img" :src="`${item.img}`" mode="aspectFill"
@click="handleBannerClick(item, index)" />
</swiper-item>
</swiper>
<!-- 孕期信息 -->
<view class="postnatal-con-info">
<view class="info-l">
孕{{ homeInfo.gestationalWeeks }}周
</view>
<view class="info-c">
<image class="info-img" src="/static/chanjianTool/line.png"></image>
</view>
<view class="info-r">
预产期:{{ homeInfo.dueDate }}
</view>
</view>
<!-- 按钮 -->
<view class="postnatal-con-btn">
<view class="btn-item" v-for="({ name, imageSrc, type }, index) in btnList" :key="index"
@click="onBtn(type)">
<image class="image1" :src="`${imageSrc}`"></image>
<view class="btn-item-text">
{{ name }}
</view>
<image class="image2" src="/static/chanjianTool/right.png"></image>
</view>
</view>
<!-- 产检记录 -->
<view class="postnatal-con-record">
<view class="record-item"
v-for="({ id, checkupDate, index, pregnancyWeek, status, examinationItems, type }, i) in homeInfo.checkupList"
@click="onDetails(id)" :key="i">
<image class="add-image" v-if="type === '1'" src="/static/chanjianTool/bs.png"></image>
<view class="item-t">
<view class="item-t-l">
第{{ index }}次产检
</view>
<view class="item-t-r">
<view class="">
产检时间:{{ checkupDate ? checkupDate : '---' }}
</view>
<image @click.stop="onEdit(id, checkupDate)" class="edit-img"
src="/static/chanjianTool/edit.png">
</image>
</view>
</view>
<view class="item-line">
</view>
<view class="item-c">
<view class="item-c-l">
孕期{{ pregnancyWeek }}周
</view>
<view class="">
<image class="record-img" :src="getSrcUrl(status)"></image>
</view>
</view>
<view class="item-b">
重点:{{ getProject(examinationItems) }}
</view>
</view>
</view>
</view>
<!-- 新增产检 -->
<view class="postnatal-add" @click="onAdd">
<image src="/static/chanjianTool/add.png"></image>
</view>
<!-- 使用封装后的日期选择器组件 -->
<DatePicker v-model:visible="visible" :default-date="time" @confirm="handleDateConfirm" />
</view>
</view>
</template>
<script setup>
import {
ref,
getCurrentInstance,
onMounted
} from 'vue'
import {
onLoad,
onShow
} from '@dcloudio/uni-app'
import {
jump,
JumpType,
throttleTap
} from '@/utils/index.js';
import md from '../../md';
import {
getInfo,
getUpdate,
postnatalJSON
} from '../../api/obstetric.js';
// 导入日期选择器组件
import DatePicker from '@/components/DatePicker.vue'
const {
proxy
} = getCurrentInstance();
const $baseUrl = proxy.$baseUrl;
const back_btn = ref('')
// 时间弹窗控制
const visible = ref(false)
// 时间
const time = ref('')
// 保存要修改的id
const editId = ref(null)
// 轮播图
const bannerList = ref([])
// 首页信息
const homeInfo = ref({})
// 按钮列表
const btnList = ref([{
name: '报告单',
imageSrc: '/static/chanjianTool/icon2.png',
type: 1
},
{
name: '日历',
imageSrc: '/static/chanjianTool/icon3.png',
type: 2
}
])
// 跳转产检详情页面
const onDetails = (id) => {
uni.navigateTo({
url: `/pages/productionDetails/productionDetails?id=${id}`
})
}
// 拼接检查项目名称
const getProject = (projects) => {
return projects.map(project => project.itemName).join('、');
}
// 根据状态返回图片
const getSrcUrl = (status) => {
const imageMap = {
'pending': '/static/chanjianTool/icon4.png',
'expired': '/static/chanjianTool/icon5.png',
'completed': '/static/chanjianTool/icon6.png',
};
return imageMap[status];
}
// 首页组件逻辑
const backHandler = () => {
if (!back_btn) {
uni.navigateBack();
} else {
jump({
type: JumpType.INNER,
url: `/pages/index/index`
})
}
}
// 点击轮播图事件
const handleBannerClick = (item, index) => {
console.log(item)
// 跳转
if (item?.url != "") {
jump({
type: item.type,
url: item.url
})
}
// let buttonName = '';
// switch(index){
// case 0:
// buttonName = '第一张焦点图';
// break;
// case 1:
// buttonName = '第二张焦点图';
// break;
// case 2:
// buttonName = '第三张焦点图';
// break;
// }
// md.sensorLogTake({
// xcxClick: "产品提醒页-首屏页面点击",
// pageName: "产品提醒页-首屏",
// buttonName: buttonName,
// });
}
// 新增体检
const onAdd = () => {
uni.navigateTo({
url: '/pages/addPostnatal/addPostnatal'
})
}
// 按钮点击
const onBtn = (type) => {
// const items = JSON.stringify(homeInfo.value)
// type 1 报告单 2 日历
switch (type) {
case 1:
uni.navigateTo({
url: '/pages/myReportCard/myReportCard'
})
break;
case 2:
uni.navigateTo({
url: `/pages/productionCalendar/productionCalendar`
})
break;
default:
break;
}
}
// 编辑时间
const onEdit = (id, newTime) => {
console.log(id, newTime)
time.value = newTime
visible.value = true
editId.value = id
}
// 选择日期回调确认
const handleDateConfirm = (date) => {
console.log('选择的日期是:', date);
time.value = date;
console.log(editId.value, time.value)
onEditTime()
}
// 修改产检时间
const onEditTime = async () => {
const { code, success, message } = await getUpdate({id: editId.value,checkupDate: time.value})
if (success) {
uni.showToast({
title: '修改成功',
icon: 'none'
})
// 重新获取信息
getInfoFn()
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
// 获取信息接口
const getInfoFn = async () => {
console.log('获取信息')
// 获取信息
const { code, success, message, data } = await getInfo()
if (success) {
homeInfo.value = data
uni.setStorageSync('dueDate', data.dueDate);
} else {
uni.showToast({
title: message,
icon: "none",
});
}
}
// 获取banner图
const postnatalJSONFn = async () => {
const { success, data, message } = await postnatalJSON()
if (success) {
bannerList.value = data.bannerList
} else {
uni.showToast({
title: message,
icon: "none",
});
}
}
onShow(() => {
// 获取banner图
postnatalJSONFn()
// 获取信息
getInfoFn()
})
</script>
<style lang="less" scoped>
@import "@/common.less";
.postnatal {
// position: absolute;
width: 100%;
min-height: 100vh;
background-color: #fdf6eb;
&-con {
position: absolute;
// width: 750rpx;
top: 190rpx;
left: 31rpx;
&-info {
font-size: 24rpx;
display: flex;
align-items: center;
margin-top: 50rpx;
font-weight: 500;
.info-c {
width: 2rpx;
height: 22rpx;
margin: 0 16rpx;
.info-img {
display: block;
width: 100%;
height: 100%;
}
}
}
&-btn {
margin-top: 55rpx;
display: flex;
justify-content: space-between;
margin-bottom: 30rpx;
.btn-item {
display: flex;
align-items: center;
// justify-content: center;
width: 334rpx;
height: 115rpx;
background: #ffffff;
border-radius: 16rpx;
font-size: 34rpx;
.image1 {
width: 58rpx;
height: 58rpx;
margin-left: 80rpx;
}
.btn-item-text {
margin-left: 12rpx;
margin-right: 52rpx;
}
.image2 {
width: 12rpx;
height: 23rpx;
}
}
}
&-record {
height: calc(100vh - 650rpx);
overflow-y: auto;
.record-item {
width: 687rpx;
background-color: #fff;
margin-bottom: 30rpx;
border-radius: 24rpx;
box-sizing: border-box;
padding: 40rpx 32rpx;
position: relative;
.add-image {
position: absolute;
left: 0;
top: 0;
width: 111rpx;
height: 30rpx;
}
.item-t,
.item-c {
display: flex;
align-items: center;
justify-content: space-between;
}
.item-line {
width: 100%;
height: 2rpx;
background-color: #fcf5ec;
margin: 20rpx 0;
}
.item-t {
font-size: 24rpx;
font-weight: 500px;
color: #000000;
.item-t-r {
display: flex;
align-items: center;
}
}
.edit-img {
width: 21rpx;
height: 21rpx;
margin-left: 14rpx;
}
.record-img {
width: 142rpx;
height: 35rpx;
}
.item-c {
&-l {
font-size: 38rpx;
color: @color-gold-cover;
}
}
.item-b {
width: 100%;
white-space: nowrap;
/* 禁止换行 */
overflow: hidden;
/* 超出部分隐藏 */
text-overflow: ellipsis;
/* 显示省略号 */
margin-top: 20rpx;
color: #6f6d67;
font-size: 24rpx
}
}
}
}
&-add {
position: fixed;
right: 31rpx;
bottom: 230rpx;
image {
width: 112rpx;
height: 113rpx;
}
}
}
.banner-swiper {
width: 687rpx;
height: 177rpx;
border-radius: 12rpx;
overflow: hidden;
.banner-img {
width: 100%;
height: 100%;
// border-radius: 16rpx;
}
}
.page-top {
width: 100%;
display: flex;
align-items: center;
}
.btnback {
width: 16rpx;
height: 29rpx;
// margin-top: 110rpx;
// margin-left: 35rpx;
}
.page_title {
width: 100%;
font-size: 34rpx;
font-weight: 500;
text-align: center;
color: #1d1e25;
line-height: 36rpx;
// margin-top: -46rpx;
// margin-left: 0rpx;
}
</style>
\ No newline at end of file
<template>
<view class="production-calendar">
<!-- <view class="page-top">
<view class="header-content">
<image @tap="backHandler" class="btnback" src="/static/chanjianTool/back.png"></image>
<view class="page_title">
<view class="info-l">
<image :src="$baseUrl + 'common/default_avatar.png'"></image>
</view>
<view class="info-r">
<view class="info-r-t">
孕期
</view>
<view class="info-r-b">
怀孕{{ info.gestationalWeeks }}
</view>
</view>
</view>
</view>
</view> -->
<customize-navigation>
<template v-slot:navbar-content>
<view class="page-top">
<image @tap="backHandler" class="btnback" src="/static/chanjianTool/back.png"></image>
<view class="page_title">
<view class="info-l">
<image :src="$baseUrl + 'common/default_avatar.png'"></image>
</view>
<view class="info-r">
<view class="info-r-t">
孕期
</view>
<view class="info-r-b">
怀孕{{ info.gestationalWeeks }}
</view>
</view>
</view>
</view>
</template>
</customize-navigation>
<!-- 显示区域 -->
<view class="production-calendar-con">
<view class="calendar-box">
<!-- 日期选择器和统计按钮 -->
<view class="date-stats-row">
<view class="date-selector">
<uni-datetime-picker ref="datePickerRef" v-model="currentSelectedDate" type="date"
:start="earliestDateString" :end="todayDateString" @change="onDateChange" :clear-icon="false" :border="false">
<view class="date-display">
<text class="date-text">{{ formatCurrentDate() }}</text>
<image src="/static/chanjianTool/icon27.png" class="arrow-icon" />
</view>
</uni-datetime-picker>
</view>
</view>
<!-- 日历组件 -->
<view class="calendar-wrapper">
<!-- 星期标题 -->
<view class="weekday-row">
<view class="weekday-cell" v-for="day in weekdays" :key="day">{{ day }}</view>
</view>
<!-- 日期网格 -->
<view class="date-grid collapsed">
<view v-for="(date, index) in calendarDates" :key="index" class="date-cell" :class="{
'selected': date.isSelected,
'today': date.isToday,
'other-month': !date.isCurrentMonth,
'future': date.isFuture,
'past-with-record': date.isPast && hasRecordOnDate(date.dateString) && date.isCurrentMonth,
'past-no-record': date.isPast && !hasRecordOnDate(date.dateString) && date.isCurrentMonth,
'other-month-clickable': !date.isCurrentMonth && !date.isFuture,
'disabled': date.isFuture
}" @click="selectDate(date)">
<text class="date-text">
{{ date.isToday ? '今' : date.day }}
</text>
<view v-if="hasRecordOnDate(date.dateString)" class="record-indicator"></view>
</view>
</view>
</view>
</view>
<view class="con-list">
<view v-if="todayRecords.length === 0" class="empty-state">
<text class="empty-text">当日暂无记录</text>
</view>
<view class="con-list-item" v-for="(item, index) in todayRecords" :key="index"
@click="onDetails(item.id)">
<view class="item-time">
<view class="">
第{{ item.index }}次产检
</view>
<view class="">
产检时间:{{ item.checkupDate }}
</view>
</view>
<view class="item-line">
</view>
<view class="item-week">
孕期{{ item.pregnancyWeek }}周
</view>
<view class="item-proect">
重点:{{ getProject(item.examinationItems) }}
</view>
</view>
</view>
</view>
<!-- 新增产检 -->
<button class="form-btn" @click="onAdd">
<image src="/static/chanjianTool/icon23.png"></image>
新增产检
</button>
</view>
</template>
<script setup>
import { ref, computed, onMounted, watch, nextTick } from 'vue'
import {
onLoad,
onShow
} from '@dcloudio/uni-app'
import {
throttleTap,
dateFormatter
} from '@/utils/index.js';
import {
getInfo
} from '../../api/obstetric.js';
// 新增日历组件 start-------
const currentSelectedDate = ref('')
const currentDate = ref(new Date())
const datePickerRef = ref(null)
// 添加一个强制更新标志
const forceUpdate = ref(0)
// 添加当前显示月份的字符串表示,确保响应性
const currentMonthKey = ref('')
// 星期标题
const weekdays = ['日', '一', '二', '三', '四', '五', '六']
// 新增日历组件 end -------
// 监听currentDate变化
watch(currentDate, (newDate, oldDate) => {
console.log('====== currentDate变化监听 ======')
console.log('旧值:', oldDate)
console.log('新值:', newDate)
console.log('新月份:', newDate.getMonth() + 1)
// 避免无限循环:只在月份真正改变时才调用handleMonthChange
if (oldDate && (oldDate.getMonth() !== newDate.getMonth() || oldDate.getFullYear() !== newDate.getFullYear())) {
console.log('月份发生变化,调用handleMonthChange')
// 只更新月份键和强制更新,不再次修改currentDate
const newMonthKey = `${newDate.getFullYear()}-${newDate.getMonth()}`
currentMonthKey.value = newMonthKey
forceUpdate.value++
nextTick(() => {
console.log('watch: nextTick后再次强制更新')
forceUpdate.value++
})
}
}, { immediate: false })
// 用户信息
const info = ref({})
function formatDateString(date) {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
// 日期格式化
function formatCurrentDate() {
if (!currentSelectedDate.value) return ''
const date = new Date(currentSelectedDate.value)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}${month}${day}日`
}
// 标记是否有记录
function hasRecordOnDate(date) {
return info.value.checkupList && info.value.checkupList.some(item => item.checkupDate == date);
}
function isSameDay(date1, date2) {
return date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate()
}
// 方法
function createDateObject(date) {
const today = new Date()
const currentMonth = currentDate.value.getMonth()
const currentYear = currentDate.value.getFullYear()
const dateString = formatDateString(date)
// 创建只包含日期部分的Date对象进行比较
const dateOnly = new Date(date.getFullYear(), date.getMonth(), date.getDate())
// const todayOnly = new Date(today.getFullYear(), today.getMonth(), today.getDate())
const todayOnly = new Date(today.getFullYear(), today.getMonth() + 1, 0); // 下个月的第1天
// 修复:确保正确判断是否为当前月份
const isCurrentMonth = date.getMonth() === currentMonth && date.getFullYear() === currentYear
const isToday = isSameDay(date, today)
const isFuture = dateOnly > todayOnly
const isPast = dateOnly < todayOnly
const dateObj = {
date: new Date(date),
day: date.getDate(),
dateString,
isToday,
isCurrentMonth,
isSelected: currentSelectedDate.value === dateString,
isFuture,
isPast
}
return dateObj
}
// 日期对应的产检数组
const todayRecords = computed(() => {
if (!currentSelectedDate.value || !info.value.checkupList) return [];
const records = info.value.checkupList.filter(item => item.checkupDate === currentSelectedDate.value);
return records;
});
// 计算属性 - 最早可选日期(往前一年)
const earliestDateString = computed(() => {
const today = new Date()
const earliestDate = new Date(today.getFullYear() - 1, today.getMonth(), today.getDate())
const dateStr = formatDateString(earliestDate)
console.log('uni-datetime-picker最早可选日期:', dateStr)
return dateStr
})
// 日期最晚可选日期
const todayDateString = computed(() => {
const today = new Date();
// 获取下个月的第0天(即当前月的最后一天)
const nextMonthLastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0);
const dateStr = formatDateString(nextMonthLastDay);
console.log('uni-datetime-picker最大可选日期:', dateStr);
return dateStr;
});
// const todayDateString = computed(() => {
// const today = new Date()
// const dateStr = formatDateString(today)
// console.log('uni-datetime-picker最大可选日期:', dateStr)
// return dateStr
// })
function selectDate(dateObj) {
// 只禁止选择未来日期
if (dateObj.isFuture) {
console.log('不能选择未来日期:', dateObj.dateString)
return
}
console.log('====== selectDate ======')
console.log('点击日期:', dateObj.dateString, '当月:', dateObj.isCurrentMonth)
console.log('当前设置月份:', currentDate.value.getFullYear() + '-' + (currentDate.value.getMonth() + 1))
console.log('点击日期月份:', dateObj.date.getFullYear() + '-' + (dateObj.date.getMonth() + 1))
// 选择日期
currentSelectedDate.value = dateObj.dateString
// 如果点击的是非当月日期,切换到该日期所在的月份
if (!dateObj.isCurrentMonth) {
console.log('切换到:', dateObj.date.getFullYear(), '年', dateObj.date.getMonth() + 1, '月')
// 直接设置新的日期和月份键
const newCurrentDate = new Date(dateObj.date.getFullYear(), dateObj.date.getMonth(), 1)
const newMonthKey = `${dateObj.date.getFullYear()}-${dateObj.date.getMonth()}`
console.log('设置新月份:', newMonthKey)
// 同步更新所有相关状态
currentDate.value = newCurrentDate
currentMonthKey.value = newMonthKey
forceUpdate.value++
// 使用 nextTick 确保 DOM 更新后再强制更新一次
nextTick(() => {
console.log('selectDate: nextTick后再次强制更新')
forceUpdate.value++
// 再次使用 nextTick 确保所有更新完成
nextTick(() => {
console.log('selectDate: 第二次nextTick后强制更新')
forceUpdate.value++
})
})
console.log('月份切换完成')
}
}
// 日期时间
function onDateChange(event) {
if (event) {
console.log('====== uni-datetime-picker变化 ======')
console.log('选择的日期:', event)
// 验证选择的日期不能是未来日期
const selectedDate = new Date(event)
const today = new Date()
// const todayOnly = new Date(today.getFullYear(), today.getMonth(), today.getDate())
const todayOnly = new Date(today.getFullYear(), today.getMonth()+1, 0)
const selectedOnly = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate())
if (selectedOnly > todayOnly) {
console.warn('警告:尝试选择未来日期,已阻止')
uni.showToast({
title: '不能选择未来日期',
icon: 'none'
})
return
}
currentSelectedDate.value = event
console.log('currentSelectedDate', currentSelectedDate)
// 计算新的月份(使用已声明的selectedDate变量)
const newCurrentDate = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), 1)
const newMonthKey = `${selectedDate.getFullYear()}-${selectedDate.getMonth()}`
console.log('设置新月份:', newMonthKey)
// 同步更新所有相关状态
currentDate.value = newCurrentDate
currentMonthKey.value = newMonthKey
forceUpdate.value++
// 使用 nextTick 确保 DOM 更新后再强制更新一次
nextTick(() => {
console.log('onDateChange: nextTick后再次强制更新')
forceUpdate.value++
// 再次使用 nextTick 确保所有更新完成
nextTick(() => {
console.log('onDateChange: 第二次nextTick后强制更新')
forceUpdate.value++
})
})
console.log('uni-datetime-picker月份切换完成')
}
}
// 获取日历数据
const calendarDates = computed(() => {
// 依赖多个响应性值来确保更新
const year = currentDate.value.getFullYear()
const month = currentDate.value.getMonth()
// 获取当月第一天
const firstDay = new Date(year, month, 1)
// 获取第一周的开始日期(周日为0)
const startDate = new Date(firstDay)
startDate.setDate(firstDay.getDate() - firstDay.getDay())
const dates = []
// 展开状态显示完整月份(6周)
for (let i = 0; i < 42; i++) {
const date = new Date(startDate)
date.setDate(startDate.getDate() + i)
dates.push(createDateObject(date))
}
return dates
})
// 返回
const backHandler = () => {
uni.navigateBack()
}
// 跳转新增产检页面
const onAdd = () => {
uni.navigateTo({
url: `/pages/addPostnatal/addPostnatal?time=${currentSelectedDate.value}`
})
}
// 拼接检查项目名称
const getProject = (projects) => {
return projects.map(project => project.itemName).join('、');
}
// 跳转产检详情页面
const onDetails = (id) => {
uni.navigateTo({
url: `/pages/productionDetails/productionDetails?id=${id}`
})
}
// 获取信息接口
const getInfoFn = async () => {
console.log('获取信息')
// 获取信息
const {code,success, message, data } = await getInfo()
if (success) {
info.value = data
} else {
uni.showToast({
title: message,
icon: "none",
});
}
}
// onLoad((option) => {
// console.log('option:', option)
// info.value = JSON.parse(option.item)
// // 使用真实的今天日期
// const today = new Date()
// const todayString = formatDateString(today)
// currentSelectedDate.value = todayString
// currentDate.value = new Date(today.getFullYear(), today.getMonth(), 1) // 当前月份的第一天
// currentMonthKey.value = `${today.getFullYear()}-${today.getMonth()}` // 设置月份键
// console.log('初始化今日日期:', todayString, '当前月份:', currentDate.value, '月份键:', currentMonthKey.value)
// })
onShow(() => {
// 使用真实的今天日期
const today = new Date()
const todayString = formatDateString(today)
currentSelectedDate.value = todayString
currentDate.value = new Date(today.getFullYear(), today.getMonth(), 1) // 当前月份的第一天
currentMonthKey.value = `${today.getFullYear()}-${today.getMonth()}` // 设置月份键
console.log('初始化今日日期:', todayString, '当前月份:', currentDate.value, '月份键:', currentMonthKey.value)
// 获取信息
getInfoFn()
})
</script>
<style lang="less" scoped>
.production-calendar {
width: 100%;
height: 100vh;
background-color: #fdf6eb;
&-con {
// position: absolute;
// top: 176rpx;
// left: 31rpx;
width: 100%;
box-sizing: border-box;
.calendar-box {
width: 100%;
background-color: #fdf6eb;
// height: 473rpx;
padding: 31rpx 0 0 0;
box-sizing: border-box;
}
.con-list {
position: absolute;
left: 0;
top: 820rpx;
height: calc(100vh - 820rpx);
background: #fff;
border-radius: 24rpx;
padding: 30rpx;
overflow-y: auto;
width: 100%;
box-sizing: border-box;
padding-bottom: 100px;
.con-list-item {
width: 689rpx;
// height: 221rpx;
border-radius: 24rpx;
background: #f7f8fa;
padding: 40rpx 63rpx 40rpx 35rpx;
box-sizing: border-box;
margin-bottom: 25rpx;
.item-time {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 24rpx;
font-weight: bold;
color: #1d1e25;
}
.item-line {
width: 100%;
height: 1rpx;
background-color: #e6e6e8;
margin: 20rpx 0;
}
.item-week {
color: #b27c1e;
font-size: 38rpx;
}
.item-proect {
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-top: 12rpx;
color: #6f6d67;
font-size: 23rpx;
}
}
}
}
// 自定义头部
.page-top {
width: 100%;
display: flex;
align-items: center;
background-color: #fdf6eb;
}
.header-content {
display: flex;
align-items: center;
}
.btnback {
width: 16rpx;
height: 29rpx;
}
.page_title {
color: #1d1e25;
margin-left: 70rpx;
display: flex;
align-items: center;
.info-l {
width: 87rpx;
height: 88rpx;
border-radius: 50%;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
}
.info-r {
display: flex;
flex-direction: column;
margin-left: 11rpx;
.info-r-t {
display: flex;
align-items: center;
font-size: 30rpx;
font-weight: bold;
image {
width: 18rpx;
height: 12rpx;
margin-left: 10rpx;
}
}
.info-r-b {
margin-top: 10rpx;
font-size: 24rpx;
}
}
}
// 保存
.form-btn {
width: 686rpx;
height: 94rpx;
border-radius: 46rpx;
background-color: #d3a358;
color: #ffffff;
font-size: 38rpx;
display: flex;
align-items: center;
justify-content: center;
position: fixed;
bottom: 60rpx;
left: 50%;
transform: translateX(-50%);
z-index: 2;
image {
width: 42rpx;
height: 42rpx;
margin-right: 12rpx;
}
}
// 日期选择器和统计按钮行
.date-stats-row {
display: flex;
justify-content: space-between;
align-items: center;
background: #fef6eb;
padding: 24rpx 30rpx;
.date-selector {
cursor: pointer;
.date-display {
display: flex;
align-items: center;
}
.date-text {
font-size: 28rpx;
font-weight: 500;
color: #1d1e25;
margin-right: 10rpx;
}
.arrow-icon {
width: 20rpx;
height: 12rpx;
margin-top: 3rpx;
}
}
.stats-section {
display: flex;
align-items: center;
gap: 8rpx;
.stats-icon {
width: 28rpx;
height: 33rpx;
}
.stats-btn {
font-size: 28rpx;
color: #1d1e25;
line-height: 33rpx;
vertical-align: bottom;
font-weight: 500;
}
}
}
// 日历容器
.calendar-wrapper {
background: #fef6eb;
position: relative;
.weekday-row {
display: flex;
padding: 20rpx 30rpx 0;
.weekday-cell {
flex: 1;
text-align: center;
font-size: 22rpx;
color: #b27c1e;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
.collapsed {
grid-template-rows: repeat(6, 1fr);
max-height: 480rpx;
}
.date-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
padding: 0 30rpx;
transition: all 0.3s ease;
.date-cell {
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
cursor: pointer;
.date-text {
font-size: 28rpx;
color: #333; // 默认为当月日期的颜色
font-weight: 400;
transition: all 0.2s ease;
min-width: 56rpx;
height: 56rpx;
display: flex;
align-items: center;
justify-content: center;
}
.record-indicator {
position: absolute;
bottom: 18rpx;
left: 50%;
transform: translateX(-50%);
width: 6rpx;
height: 6rpx;
background: #D4A574;
border-radius: 50%;
}
// 当月日期默认样式(确保优先级)
&:not(.other-month):not(.disabled) {
.date-text {
color: #333;
}
}
// 1. 置灰且禁用:未来日期
&.disabled {
cursor: not-allowed;
.date-text {
color: #bab5af;
}
.record-indicator {
background: #bab5af;
}
}
// 非当月日期(可点击切换月份)
&.other-month-clickable {
cursor: pointer;
.date-text {
color: #bab5af;
transition: all 0.2s ease;
}
.record-indicator {
background: #bab5af;
}
&:hover {
.date-text {
color: #888;
background: rgba(212, 165, 116, 0.1);
border-radius: 50%;
}
}
&:active {
.date-text {
transform: scale(0.95);
}
}
}
// 2. 高亮带黄点:当月已过日期且有喂养记录
&.past-with-record:not(.selected) {
.date-text {
color: #333;
font-weight: 500;
}
}
// 3. 仅高亮无黄点:当月已过日期但无喂养记录
&.past-no-record:not(.selected) {
.date-text {
color: #333;
font-weight: 500;
}
}
// 4. 日期带黄圈:选中状态
&.selected {
.date-text {
background: #D4A574;
color: #fff !important;
border-radius: 50%;
font-weight: 600;
}
.record-indicator {
background: #fff;
}
}
// 5. 当日显示【今】- 已在模板中处理
&.today.selected {
.date-text {
color: #fff;
}
}
&.today:not(.selected) {
.date-text {
background: #fff;
border-radius: 50%;
font-weight: 600;
}
}
// 非当月未来日期样式(既是other-month又是future的情况)
&.other-month.future {
.date-text {
color: #bab5af;
}
}
// 未来日期样式
&.future {
.date-text {
color: #bab5af;
}
}
}
}
.expand-btn {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
background: white;
margin-top: 0rpx;
.expand-icon {
width: 136rpx;
height: 46rpx;
transition: all 0.3s ease;
}
}
}
}
.empty-state {
display: flex;
justify-content: center;
align-items: center;
height: 400rpx;
.empty-text {
font-size: 28rpx;
color: #999;
}
}
// uni-datetime-picker样式覆盖(与naming页面保持一致)
::v-deep .uni-datetime-picker--btn {
background-color: #D4A574 !important;
}
::v-deep .uni-calendar-item--checked {
background-color: #D4A574 !important;
}
::v-deep .uni-datetime-picker-btn-text {
color: #D4A574 !important;
}
::v-deep .uni-calendar--fixed{
z-index: 100 !important;
}
::v-deep .uni-calendar__mask{
z-index: 100 !important;
}
</style>
\ No newline at end of file
<template>
<view class="production-details">
<!-- tab 切换 -->
<view class="production-details-tabs" :class="{ 'fixed-tabs': isFixedTabs }" v-show="showTabs">
<view class="custom-tabs">
<view v-for="(tab, index) in tabInfo" :key="index" class="tab-item"
:class="{ active: activeIndex === index }" @click="scrollToSection(index)">
<view class="tab-line1">{{ tab?.line1 }}</view>
<view class="tab-line2" v-if="activeIndex === index"></view>
</view>
</view>
</view>
<!-- 内容区域 -->
<scroll-view class="content-scroll" scroll-y :scroll-top="scrollTop" @scroll="handleScroll"
scroll-with-animation>
<view class="production-details-container">
<view class="container-1 pad">
{{ infoData.index }}次检查
</view>
<view class="container-2 pad">
<view class="container-2-l">
孕期{{ infoData.pregnancyWeek }}
</view>
<view class="container-2-r">
<view class="r-complete" @click="onComplete">
<image v-if="!isCompleted" src="/static/chanjianTool/icon24.png"></image>
<image v-else src="/static/chanjianTool/icon25.png"></image>
完成检查
</view>
<view class="r-remind" @click="onRemind">
<image src="/static/chanjianTool/icon26.png"></image>
</view>
</view>
</view>
<view class="container-line pad">
</view>
<view class="container-3 pad">
<view class="container-3-l">
产检时间
</view>
<view class="container-3-r" @click="onChangeTime(infoData.checkupDate)">
{{ infoData.checkupDate ? infoData.checkupDate : '选择时间' }}
<image src="/static/chanjianTool/edit.png"></image>
</view>
</view>
<view class="container-4 pad">
建议时间:{{ infoData.suggestionCheckup}}
</view>
<!-- 产检须知 -->
<view class="project-box" id="section-0">
<view class="project-info">
<view class="time-l">
<image src="/static/chanjianTool/icon19.png" />
产检须知
</view>
</view>
<view class="project-content">
<rich-text class="desc" :nodes="infoData.content"></rich-text>
</view>
</view>
<!-- 产检项目 -->
<view class="project-box" id="section-1">
<view class="project-info">
<view class="time-l">
<image src="/static/chanjianTool/icon19.png" />
产检项目
</view>
<view class="style1" @click="onModify(infoData)">
修改项目
</view>
</view>
<view class="project-content">
<view class="project-list" v-for="(item, index) in infoData.examinationItems" :key="index">
<view class="list-itemName">
{{ numberToChinese(index + 1) }}{{ item.itemName }}
</view>
<view class="list-introduction">
<rich-text :nodes="item.introduction"></rich-text>
</view>
</view>
</view>
</view>
<!-- 本次报告单 -->
<view class="project-box" id="section-2">
<view class="project-info">
<view class="time-l">
<image src="/static/chanjianTool/icon19.png" />
本次报告单
</view>
<view class="time-r style2" @click="onSeeBtn">
查看全部
<image src="/static/chanjianTool/icon13.png" />
</view>
</view>
<view class="img-list">
<view class="upload-image" @click="onUpload">
<image src="/static/chanjianTool/icon11.png"></image>
</view>
<view class="img-list-item" v-for="(item, index) in bgdImgList" :key="index">
<view class="item-image" @click="onPreviewImage(item)">
<image class="img1" :src="item" mode="widthFix"></image>
</view>
<image @click="onImageDel(item)" class="img" src="/static/chanjianTool/icon14.png"></image>
</view>
</view>
</view>
<!-- 选择提醒弹窗 -->
<view>
<!-- 底部弹窗遮罩层 -->
<view v-if="showPicker" class="picker-layer-mask" @click="close"></view>
<!-- 弹窗内容区 -->
<view v-if="showPicker" class="picker-layer-popup">
<view class="picker-layer-panel">
<view class="picker-layer-title">
设置提醒时间
</view>
<view class="picker-layer-content">
<view class="content-item"
v-for="(item, index) in options"
:key="index"
:class="{'item-active': pickerValue === index}"
@click="handleChange(index)"
>
{{ item.name }}
</view>
</view>
<view class="picker-layer-bottom">
<text class="bottom-close btn" @click="close">取消</text>
<text class="bottom-confirm btn" @click="handleConfirm">确定</text>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 产检选择时间 -->
<!-- 使用封装后的日期选择器组件 -->
<DatePicker v-model:visible="visible" :default-date="time" @confirm="handleDateConfirm" />
</view>
</template>
<script setup>
import {
ref,
onMounted
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app'
import {
throttleTap,
showLoading,
hideLoading
} from '@/utils/index.js';
// 时间组件
import DatePicker from '@/components/DatePicker.vue'
import { uploadImage } from '../../api/common.js'
import {
getDetail,
getUpdate,
getWxNotification
} from '../../api/obstetric.js';
// 提醒选择器相关状态
const showPicker = ref(false)
const options = [
{
name: '当天',
id: 0
},
{
name: '前一天',
id: 1
},
{
name: '前三天',
id: 3
}
] // 提醒时间选项
const pickerValue = ref(0) // 当前选中的索引
const selectedValue = ref(0) // 选中的值
const tabInfo = [{
"line1": "产检须知",
},
{
"line1": "产检项目",
},
{
"line1": "本次报告单",
}
]
// 存储id
const editId = ref('')
// 详情
const infoData = ref({})
// 临时储存报告单
const bgdImgList = ref([])
// 时间组件参数
const time = ref('')
// 新增完成状态
const isCompleted = ref(false)
// 日期选择器
const visible = ref(false)
// 滚动相关状态
const scrollTop = ref(0)
const isFixedTabs = ref(false)
const showTabs = ref(false)
const activeIndex = ref(0)
const lastScrollTop = ref(0)
const scrolling = ref(false)
// 滚动处理函数
const handleScroll = (e) => {
const scrollTop = e.detail.scrollTop;
const scrollDirection = scrollTop > lastScrollTop.value ? 'down' : 'up';
// 优化:更灵敏的 tabs 显示/隐藏逻辑
if (scrollTop <= 0) {
// 滚动到顶部时立即隐藏 tabs
showTabs.value = false;
isFixedTabs.value = false;
} else if (scrollDirection === 'down' && scrollTop > 50) {
// 向下滚动超过阈值时显示固定 tabs
showTabs.value = true;
isFixedTabs.value = true;
} else if (scrollDirection === 'up') {
// 向上滚动时保持显示 tabs
showTabs.value = true;
isFixedTabs.value = true;
}
lastScrollTop.value = scrollTop;
// 优化:只在滚动停止时更新 activeIndex
if (scrolling.value) return;
updateActiveTab(scrollTop);
}
// 更新当前激活的tab
const updateActiveTab = (scrollTop) => {
const query = uni.createSelectorQuery()
query.select('#section-0').boundingClientRect()
query.select('#section-1').boundingClientRect()
query.select('#section-2').boundingClientRect()
query.exec((res) => {
const [section0, section1, section2] = res
// 优化:使用更精确的阈值计算
const threshold = 50 // 距离顶部的阈值
if (scrollTop >= section2.top - threshold) {
activeIndex.value = 2
} else if (scrollTop >= section1.top - threshold) {
activeIndex.value = 1
} else {
activeIndex.value = 0
}
})
}
// 点击tab滚动到对应区域
const scrollToSection = (index) => {
activeIndex.value = index;
scrolling.value = true;
const query = uni.createSelectorQuery();
query.select(`#section-${index}`).boundingClientRect();
query.select('.content-scroll').scrollOffset(); // 获取 scroll-view 当前的滚动位置
query.exec((res) => {
if (res[0] && res[1]) {
const sectionTop = res[0].top; // 目标区域距离视口顶部的距离
const currentScrollTop = res[1].scrollTop; // scroll-view 当前的滚动位置
const fixedTabsHeight = 120; // 固定 tabs 的高度(根据实际样式调整)
// 计算新的滚动位置
scrollTop.value = currentScrollTop + sectionTop - fixedTabsHeight;
}
setTimeout(() => {
scrolling.value = false;
}, 300);
});
}
// 更完整的转换函数(支持0-99)
const numberToChinese = (num) => {
const chineseNumbers = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
const units = ['', '十'];
if (num === 0) return chineseNumbers[0];
if (num <= 10) return num === 10 ? '十' : chineseNumbers[num];
let str = '';
if (num > 10 && num < 20) {
str = '十' + chineseNumbers[num % 10];
} else if (num >= 20 && num < 100) {
str = chineseNumbers[Math.floor(num / 10)] + '十' + chineseNumbers[num % 10];
} else {
return num.toString(); // 超过范围返回原数字
}
return str.replace(/零$/, ''); // 去掉末尾的零
}
// 图片预览
const onPreviewImage = (url) => {
console.log(url)
uni.previewImage({
current: '1',
urls: [url]
})
}
// 删除上传图片
const onImageDel = (id) => {
bgdImgList.value.filter((item, index) => {
if (item === id) {
bgdImgList.value.splice(index, 1)
}
})
console.log("🚀 ~ bgdImgList.value.filter ~ bgdImgList:", bgdImgList)
// 直接保存
const params = {
id: editId.value,
reportImages: bgdImgList.value
}
onEditTime(params)
}
// 上传图片
const onUpload = throttleTap(() => {
if (bgdImgList.value.length == 15) {
uni.showToast({
title: "最多上传15张图片",
icon: "none",
});
return;
}
// 唤起图片选择器
uni.chooseImage({
count: 15,
sizeType: ["original", "compressed"],
sourceType: ["album", "camera"],
success: async (res) => {
showLoading();
const tempFilePath = res.tempFilePaths[0];
console.log(tempFilePath)
const fs = uni.getFileSystemManager();
console.log(fs)
const base64 =
"data:image/jpeg;base64," + fs.readFileSync(tempFilePath, "base64");
const uploadRes = await uploadImage(base64);
hideLoading();
if (uploadRes.success) {
console.log(uploadRes)
bgdImgList.value.push(uploadRes.data.url)
console.log("🚀 ~ success: ~ bgdImgList:", bgdImgList)
// 直接保存
const params = {
id: editId.value,
reportImages: bgdImgList.value
}
onEditTime(params)
} else {
uni.showToast({
title: uploadRes.message,
icon: "none",
});
}
},
});
})
// 查看更多
const onSeeBtn = () => {
uni.navigateTo({
url: '/pages/myReportCard/myReportCard'
})
}
// 打开提醒弹窗
const onRemind = () => {
showPicker.value = true;
}
// 提醒关闭
const close = () => {
showPicker.value = false;
}
// 选择提醒事件
const handleChange = (e) => {
pickerValue.value = e;
}
// 确认选择
const handleConfirm = () => {
selectedValue.value = options[pickerValue.value].id;
close();
// 订阅提醒
uni.requestSubscribeMessage({
tmplIds: [infoData.wxTemplateId],
success(res) {
console.log(res)
getWxNotificationFn(selectedValue.value, infoData.wxTemplateId, infoData.value.id)
},
fail(res) {
console.log(res)
}
})
}
// 保存订阅消息
const getWxNotificationFn = async (notificationTimeType, wxTemplateId, examinationRecordId)=>{
const params = {
type: 1,
notificationTimeType,
wxTemplateId,
examinationRecordId,
}
const { code, message, data, success } = await getWxNotification(params)
if(success){
uni.showToast({
title: '保存成功',
icon: 'success',
duration: 2000
})
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
// 完成检查
const onComplete = () => {
if(infoData.value.checkupDate == ''){
uni.showToast({
title: '请选择产检日期',
icon: 'none',
duration: 2000
})
return
}
// 切换状态
isCompleted.value = !isCompleted.value
console.log(isCompleted.value)
const params = {
id: editId.value,
status: isCompleted.value ? 'completed' : 'pending'
}
onEditTime(params)
}
// 修改时间
const onChangeTime = throttleTap((date) => {
visible.value = true
time.value = date
})
// 选择时间
const handleDateConfirm = (date) => {
visible.value = false
time.value = date
const params = {
id: editId.value,
checkupDate: time.value
}
onEditTime(params)
}
// 修改项目
const onModify = (item) => {
console.log(item)
const items = JSON.stringify(item)
uni.navigateTo({
url: `/pages/editPostnatal/editPostnatal?item=${items}`
})
}
// 修改产检接口
const onEditTime = async (params) => {
console.log(params)
const { code, success, message } = await getUpdate(params)
if (success) {
// 重新获取信息
getDetailFn(editId.value)
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
// 获取详情接口
const getDetailFn = async (id) => {
console.log('获取信息', id)
// 获取信息
const {code, success, message, data } = await getDetail({id})
if (success) {
infoData.value = data
bgdImgList.value = data.reportImages
isCompleted.value = data.status === 'completed'
} else {
uni.showToast({
title: message,
icon: "none",
});
}
}
// 获取传过来的参数
onLoad((options) => {
console.log(options)
editId.value = options.id
getDetailFn(options.id)
})
</script>
<style lang="less" scoped>
.production-details {
position: relative;
height: 100vh;
overflow: hidden;
background: #fef7f2;
&-tabs {
width: 100%;
height: auto;
margin-top: 22rpx;
background: #ffffff;
transition: all 0.3s;
z-index: 100;
&.fixed-tabs {
position: fixed;
top: 0;
left: 0;
right: 0;
margin-top: 0;
background: #fff;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
height: 120rpx;
}
.custom-tabs {
display: flex;
justify-content: space-between;
padding: 55rpx 42rpx 32rpx 48rpx;
box-sizing: border-box;
.tab-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
position: relative;
&.active {
.tab-line1 {
color: #000000;
}
}
.tab-line1 {
font-size: 28rpx;
font-weight: bold;
color: #6f6d67;
line-height: 35rpx;
}
.tab-line2 {
position: absolute;
bottom: -8rpx;
left: 50%;
transform: translateX(-50%);
width: 28rpx;
height: 4rpx;
background: #d3a358;
border-radius: 6rpx;
}
}
}
}
.content-scroll {
height: 100vh;
box-sizing: border-box;
/* 给固定tabs留出空间 */
// padding-top: 120rpx;
}
&-container {
padding: 50rpx 35rpx;
box-sizing: border-box;
color: #000000;
.pad {
padding-left: 13rpx;
box-sizing: border-box;
}
.container-1 {
font-size: 24rpx;
font-weight: bold;
}
.container-2 {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20rpx;
.container-2-l {
font-size: 38rpx;
color: #b27c1e;
font-weight: bold;
}
.container-2-r {
font-size: 24rpx;
color: #a68dbb;
display: flex;
align-items: center;
.r-complete {
display: flex;
align-items: center;
image {
width: 21rpx;
height: 22rpx;
margin-right: 7rpx;
}
}
.r-remind {
width: 121rpx;
height: 35rpx;
margin-left: 22rpx;
image {
width: 100%;
height: 100%;
}
}
}
}
.container-line {
width: 100%;
height: 1rpx;
background: #f7e4cd;
margin: 30rpx 0;
}
.container-3 {
display: flex;
align-items: center;
justify-content: space-between;
.container-3-l {
font-size: 28rpx;
color: #000000;
font-weight: bold;
}
.container-3-r {
display: flex;
align-items: center;
font-size: 28rpx;
line-height: 26rpx;
image {
width: 21rpx;
height: 21rpx;
margin-left: 15rpx;
margin-top: 1rpx;
}
}
}
.container-4 {
font-size: 24rpx;
color: #6f6d67;
margin: 20rpx 0 45rpx 0;
}
.project-box {
background: #ffffff;
border-radius: 24rpx;
padding: 0 27rpx 35rpx 27rpx;
box-sizing: border-box;
// width: 682rpx;
box-shadow: 0 4rpx 12rpx rgba(253, 240, 223, 0.8);
margin-bottom: 35rpx;
}
.project-info {
// width: 682rpx;
height: 105rpx;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 32rpx;
border-bottom: 1rpx solid #f7e4cd;
margin-bottom: 25rpx;
.time-l,
.time-r {
display: flex;
align-items: center;
}
.time-l {
color: #000000;
font-weight: 600;
image {
width: 32rpx;
height: 32rpx;
margin-right: 10rpx;
}
}
.style1 {
width: 121rpx;
height: 35rpx;
text-align: center;
line-height: 35rpx;
border-radius: 17.5rpx;
border: 1px solid #b27c1e;
font-size: 24rpx;
color: #b27c1e;
}
.style2 {
font-size: 24rpx;
color: #1d1e26;
image {
width: 8rpx;
height: 16rpx;
margin-left: 12rpx;
}
}
}
.project-content {
.project-list {
.list-itemName {
font-size: 28rpx;
font-weight: bold
}
.list-introduction {
font-size: 26rpx;
}
.list-itemName,
.list-introduction {
margin-bottom: 15rpx;
}
}
}
.img-list {
display: flex;
flex-wrap: wrap;
box-sizing: border-box;
justify-content: flex-start;
.upload-image {
width: 200rpx;
height: 200rpx;
border-radius: 10rpx;
overflow: hidden;
margin-right: 10rpx;
image {
width: 200rpx;
height: 200rpx;
}
}
.img-list-item {
// margin-right: 10rpx;
margin-bottom: 15rpx;
position: relative;
.item-image {
width: 200rpx;
height: 200rpx;
border-radius: 10rpx;
overflow: hidden;
.img1 {
width: 200rpx;
height: 200rpx;
}
}
.img {
position: absolute;
right: -12rpx;
top: -12rpx;
width: 25rpx;
height: 25rpx;
}
}
}
}
/* 遮罩层样式 */
.picker-layer-mask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 3999;
}
.picker-layer-popup {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 4000;
display: flex;
flex-direction: column;
align-items: center;
animation: picker-layer-up 0.3s;
}
@keyframes picker-layer-up {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}
/* 弹窗容器样式 */
.picker-layer-panel {
width: 100vw;
height: 52vh;
background: #f6f8fa;
border-top-left-radius: 24rpx;
border-top-right-radius: 24rpx;
overflow: hidden;
padding: 32rpx;
box-sizing: border-box;
}
.picker-layer-title{
font-size: 34rpx;
color: #000000;
font-weight: bold;
}
.picker-layer-content{
margin-top: 40rpx;
margin-bottom: 50rpx;
.content-item{
width: 100%;
height: 126rpx;
border-radius: 16rpx;
font-size: 28rpx;
display: flex;
align-items: center;
justify-content: center;
border: 2rpx solid #fff;
box-sizing: border-box;
margin-bottom: 20rpx;
background: #ffffff;
}
.item-active{
background: #fffbed;
border-color: #d3a358;
}
}
.picker-layer-bottom {
display: flex;
justify-content: space-between;
align-items: center;
.btn{
width: 334rpx;
height: 97rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 48.5rpx;
font-size: 38rpx;
}
.bottom-close {
color: #d3a358;
background: #ffffff;
}
.bottom-confirm {
color: #ffffff;
background: #d3a358;
}
}
}
</style>
\ No newline at end of file
......@@ -8,9 +8,9 @@
<!-- <view class="back-btn" @click="backHandler">
<text class="back-text"></text>
</view> -->
<text class="title">生长测评</text>
<image @tap="backHandler" class="back-btn" :src="`/static/shengzhangTool/backBtn.png`"></image>
<text class="title">生长测评</text>
<view class="content-wrapper">
......@@ -35,41 +35,41 @@
<view class="baby-info-card">
<view class="card-header">
<image class="name-icon" src="/static/shengzhangTestResult/nameIcon.png" mode="aspectFit"></image>
<text class="card-title">宝宝名称</text>
<text class="card-title">{{babyInfo.babyName}}</text>
</view>
<view class="baby-basic-info">
<text class="gender"></text>
<text class="age">2月21天</text>
<text class="test-date">测评于2025年06月06日</text>
<text class="gender">{{babyInfo.gender == 'M' ? '男' : '女'}}</text>
<text class="age">{{babyInfo.monthAge}}</text>
<text class="test-date">测评于{{babyInfo.assessmentDate}}</text>
</view>
<view class="measurement-summary">
<view class="values-row">
<text class="measurement-value">60.6cm</text>
<text class="measurement-value">5.8kg</text>
<text class="measurement-value">39.0cm</text>
<text class="measurement-value">16.0</text>
<text class="measurement-value">{{assessmentData.height}}cm</text>
<text class="measurement-value">{{assessmentData.weight}}kg</text>
<text class="measurement-value">{{assessmentData.headCircumference}}cm</text>
<text class="measurement-value">{{assessmentData.bmi}}</text>
</view>
<view class="labels-row">
<view class="measurement-item">
<text class="measurement-label">身高</text>
<text class="measurement-status normal">正常</text>
<text class="measurement-status normal">{{statusMap[analysisData.heightStatus]}}</text>
</view>
<view class="measurement-item">
<text class="measurement-label">体重</text>
<text class="measurement-status normal">正常</text>
<text class="measurement-status normal">{{statusMap[analysisData.weightStatus]}}</text>
</view>
<view class="measurement-item">
<text class="measurement-label">头围</text>
<text class="measurement-status normal">正常</text>
<text class="measurement-status normal">{{statusMap[analysisData.headStatus]}}</text>
</view>
<view class="measurement-item">
<text class="measurement-label">BMI</text>
<text class="measurement-status normal">正常</text>
<text class="measurement-status normal">{{statusMap[analysisData.bmiStatus]}}</text>
</view>
</view>
</view>
<view class="growth-evaluation">
<text class="evaluation-text">宝宝发育的非常棒,身高、体重和头围都处于正常的发育水平。TA与大多数宝宝一样,正在健康苗壮地成长。建议定期观察和记录宝宝的体格发育数据,这样有利于评估宝宝的生长发育情况,能够及时发现宝宝生长发育过程中的存在的问题和异常。</text>
<text class="evaluation-text">{{contentText.evaluation}}</text>
</view>
</view>
......@@ -102,48 +102,48 @@
</view>
</view>
<view class="measurement-bars">
<view class="bar-item">
<view class="value-triangle-container">
<text class="bar-value">60.6cm 正常</text>
<view class="bar-item" >
<view class="value-triangle-container" :style="{marginLeft: statusBarPercentileMap[analysisData.heightStatus] + 'rpx'}">
<text class="bar-value">{{assessmentData.height}}cm {{statusMap[analysisData.heightStatus]}}</text>
<image class="triangle" src="/static/shengzhangTestResult/triangle.png" mode="aspectFit"></image>
</view>
<view class="bar-row">
<text class="measurement-label">身高</text>
<image class="value-bar" src="/static/shengzhangTestResult/valueBar.png" mode="aspectFit"></image>
<text class="bar-percentage">超过25%同龄宝宝</text>
<text class="bar-percentage">超过{{analysisData.heightPercentile}}%同龄宝宝</text>
</view>
</view>
<view class="bar-item">
<view class="value-triangle-container">
<text class="bar-value">5.75kg 正常</text>
<view class="value-triangle-container" :style="{marginLeft: statusBarPercentileMap[analysisData.weightStatus] + 'rpx'}">
<text class="bar-value">{{assessmentData.weight}}kg {{statusMap[analysisData.weightStatus]}}</text>
<image class="triangle" src="/static/shengzhangTestResult/triangle.png" mode="aspectFit"></image>
</view>
<view class="bar-row">
<text class="measurement-label">体重</text>
<image class="value-bar" src="/static/shengzhangTestResult/valueBar.png" mode="aspectFit"></image>
<text class="bar-percentage">超过25%同龄宝宝</text>
<text class="bar-percentage">超过{{analysisData.weightPercentile}}%同龄宝宝</text>
</view>
</view>
<view class="bar-item">
<view class="value-triangle-container">
<text class="bar-value">39.0cm 正常</text>
<view class="value-triangle-container" :style="{marginLeft: statusBarPercentileMap[analysisData.headStatus] + 'rpx'}">
<text class="bar-value">{{assessmentData.headCircumference}}cm {{statusMap[analysisData.headStatus]}}</text>
<image class="triangle" src="/static/shengzhangTestResult/triangle.png" mode="aspectFit"></image>
</view>
<view class="bar-row">
<text class="measurement-label">头围</text>
<image class="value-bar" src="/static/shengzhangTestResult/valueBar.png" mode="aspectFit"></image>
<text class="bar-percentage">超过25%同龄宝宝</text>
<text class="bar-percentage">超过{{analysisData.headPercentile}}%同龄宝宝</text>
</view>
</view>
<view class="bar-item">
<view class="value-triangle-container">
<text class="bar-value">15.0 正常</text>
<view class="value-triangle-container" :style="{marginLeft: statusBarPercentileMap[analysisData.bmiStatus] + 'rpx'}">
<text class="bar-value">{{assessmentData.bmi}} {{statusMap[analysisData.bmiStatus]}}</text>
<image class="triangle" src="/static/shengzhangTestResult/triangle.png" mode="aspectFit"></image>
</view>
<view class="bar-row">
<text class="measurement-label">BMI</text>
<image class="value-bar" src="/static/shengzhangTestResult/valueBar.png" mode="aspectFit"></image>
<text class="bar-percentage">超过25%同龄宝宝</text>
<text class="bar-percentage">超过{{analysisData.bmiPercentile}}%同龄宝宝</text>
</view>
</view>
</view>
......@@ -177,6 +177,7 @@
<view class="legend-color normal"></view>
<text class="legend-text">正常</text>
</view>
<view class="legend-item">
<view class="legend-color slightly-high"></view>
<text class="legend-text">略高</text>
......@@ -201,10 +202,7 @@
<view class="curve-tips" @click="showCurveTips">
<image class="tips-icon" src="/static/shengzhangTestResult/shengzhangTips.png" mode="aspectFit"></image>
</view>
<!-- 专家咨询按钮 -->
<view class="expert-consult-btn" @click="consultExpert">
<image class="consult-bg" src="/static/shengzhangTestResult/zhuanjiazixunBtn.png" mode="aspectFit"></image>
</view>
</view>
</view>
......@@ -255,11 +253,19 @@
</view>
</view>
</view>
<!-- 专家咨询按钮 -->
<view class="expert-consult-btn" @click="consultExpert">
<image class="consult-bg" src="/static/shengzhangTestResult/zhuanjiazixunBtn.png" mode="aspectFit"></image>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useShengzhangStore } from '../../stores/shengzhangResult.js';
import { formatDate, jump, JumpType } from '../../utils/index.js';
import { getGrowthHistoryList } from '../../api/shengzhangTools';
// 导航标签状态
const activeTab = ref('latest') // 默认显示最新内容
......@@ -270,6 +276,47 @@ const activeCurveTab = ref('height')
// 滑动相关状态
const scrollLeft = ref(0)
const totalWidth = ref(1080) // 36个月 * 10px
const babyInfo = ref({
babyName: '宝宝名称',
gender: 'M',
monthAge: '12',
avatar: '',
assessmentDate: '1749139200000',
})
const analysisData = ref({
bmiPercentile: 0,
bmiStatus: "LOW",
headPercentile: 0,
headStatus: "LOW",
heightPercentile: 0,
heightStatus: "LOW",
weightPercentile: 0,
weightStatus: "LOW"
})
const assessmentData = ref({
bmi: 13.12,
feedingType: "MIXED",
headCircumference: 34.5,
height: 50.3,
weight: 3.32
})
const contentText = ref({
evaluation: "描述",
shareText: "描述",
summary: "描述"
});
const statusBarPercentileMap = {
NORMAL: 177,
SLIGHT_LOW: 110,
SLIGHT_HIGH: 247,
LOW: 43,
HIGH: 314
}
// 历史数据列表
const historyList = ref([
......@@ -314,6 +361,9 @@ const curveData = ref([
{ month: 7, height: 72 }
])
const curveDataPostHeight = ref([]);
const curveDataPostWeight = ref([]);
const curveDataPostHead = ref([]);
// 生成0-36个月的数据点
/**
* @param {number} startValue - 起始值(0个月时的数值)
......@@ -332,25 +382,51 @@ const generateCurveData = (startValue, endValue, type) => {
return data
}
//获取宝宝曲线数据范围4条线,用于绘制曲线
const generateCurveData1 = (lineData, type, arrayName, minOrMax) =>{
const data = [];
lineData.forEach(element => {
const point = { month: element.monthAge }
point[type] = minOrMax === 'min' ? element?.[arrayName]?.min : element?.[arrayName]?.max
data.push(point);
});
console.log('data888888=', data);
return data;
}
// slightHighRange
// slightLowRange
// 标准生长曲线数据(多条线,延长到36个月)
const standardCurves = ref({
height: {
slightlyLow: generateCurveData(45, 105, 'height'),
normal: generateCurveData(50, 110, 'height'),
slightlyHigh: generateCurveData(55, 115, 'height')
slightlyLow: generateCurveData1(curveDataPostHeight.value, 'height', 'slightLowRange', 'min'),
// normal: generateCurveData(50, 110, 'height'),
normal: generateCurveData1(curveDataPostHeight.value, 'height', 'normalRange', 'min'),
normal2: generateCurveData1(curveDataPostHeight.value, 'height', 'normalRange', 'max'), // 新增的a8e6cf颜色曲线
slightlyHigh: generateCurveData1(curveDataPostHeight.value, 'height', 'slightHighRange', 'max')
},
weight: {
slightlyLow: generateCurveData(2.5, 15.0, 'weight'),
normal: generateCurveData(3.0, 16.5, 'weight'),
slightlyHigh: generateCurveData(3.5, 18.0, 'weight')
slightlyLow: generateCurveData1(curveDataPostWeight.value, 'weight', 'slightLowRange', 'min'),
normal: generateCurveData1(curveDataPostWeight.value, 'weight', 'normalRange', 'min'),
normal2: generateCurveData1(curveDataPostWeight.value, 'weight', 'normalRange', 'max'), // 新增的a8e6cf颜色曲线
slightlyHigh: generateCurveData1(curveDataPostWeight.value, 'weight', 'slightHighRange', 'max')
},
head: {
slightlyLow: generateCurveData(32, 52, 'head'),
normal: generateCurveData(34, 54, 'head'),
slightlyHigh: generateCurveData(36, 56, 'head')
slightlyLow: generateCurveData1(curveDataPostHead.value, 'head', 'slightLowRange', 'min'),
normal: generateCurveData1(curveDataPostHead.value, 'head', 'normalRange', 'min'),
normal2: generateCurveData1(curveDataPostHead.value, 'head', 'normalRange', 'max'), // 新增的a8e6cf颜色曲线
slightlyHigh: generateCurveData1(curveDataPostHead.value, 'head', 'slightHighRange', 'max')
}
})
const statusMap = {
NORMAL: '正常',
SLIGHT_LOW: '略低',
SLIGHT_HIGH: '略高',
LOW: '偏低',
HIGH: '偏高'
}
// 切换导航标签
/**
* @param {string} tab - 要切换的标签名称('latest'|'history')
......@@ -408,6 +484,17 @@ const switchCurveTab = (tab) => {
}, 100)
}
//获取当前宝宝曲线数据,用于绘制曲线
const generateCurveData2 = (lineData, type, arrayName, minOrMax) =>{
const data = [];
lineData.forEach(element => {
const point = { month: element.monthAge }
point[type] = minOrMax === 'min' ? element?.[arrayName]?.min : element?.[arrayName]?.max
data.push(point);
});
return data;
}
// 获取Y轴标签
/**
* @returns {string} 根据当前曲线类型返回对应的Y轴标签
......@@ -468,6 +555,7 @@ const consultExpert = () => {
* 处理返回按钮点击事件,尝试返回上一页或跳转到首页
*/
const backHandler = () => {
console.log('backHandler');
try {
uni.navigateBack({
success: () => {
......@@ -484,11 +572,72 @@ const backHandler = () => {
}
}
onMounted(() => {
const backFailHandler = () => {
console.log('backFailHandler');
}
onMounted(async () => {
// 初始化绘制曲线
setTimeout(() => {
drawGrowthCurve()
}, 100)
//分析结果处理
const shengzhangStore = useShengzhangStore();
const data = {...shengzhangStore.shengzhangInfo.babyInfo};
data.assessmentDate = formatDate(shengzhangStore.shengzhangInfo.assessmentDate);
babyInfo.value = data;
analysisData.value = shengzhangStore.shengzhangInfo.analysis;
assessmentData.value = shengzhangStore.shengzhangInfo.assessmentData;
contentText.value = shengzhangStore.shengzhangInfo.content;
//生长曲线处理
curveDataPostHeight.value = shengzhangStore.getGrowthCurveDataInfoHeight.curveData;
curveDataPostWeight.value = shengzhangStore.getGrowthCurveDataInfoWeight.curveData;
curveDataPostHead.value = shengzhangStore.getGrowthCurveDataInfoHead.curveData;
console.log('curveDataPostHeight.value=', curveDataPostHeight.value);
standardCurves.value = {
height: {
// slightlyLow: generateCurveData(45, 105, 'height'),
// normal: generateCurveData(50, 110, 'height'),
slightlyLow: generateCurveData1(curveDataPostHeight.value, 'height', 'slightLowRange', 'min'),
normal: generateCurveData1(curveDataPostHeight.value, 'height', 'normalRange', 'min'),
normal2: generateCurveData1(curveDataPostHeight.value, 'height', 'normalRange', 'max'), // 新增的a8e6cf颜色曲线
slightlyHigh: generateCurveData1(curveDataPostHeight.value, 'height', 'slightHighRange', 'max'),
// slightlyHigh: generateCurveData(55, 115, 'height')slightHighRange
},
weight: {
// slightlyLow: generateCurveData(2.5, 15.0, 'weight'),
// normal: generateCurveData(3.0, 16.5, 'weight'),
// normal2: generateCurveData(3.2, 17.0, 'weight'), // 新增的a8e6cf颜色曲线
// slightlyHigh: generateCurveData(3.5, 18.0, 'weight')
slightlyLow: generateCurveData1(curveDataPostWeight.value, 'weight', 'slightLowRange', 'min'),
normal: generateCurveData1(curveDataPostWeight.value, 'weight', 'normalRange', 'min'),
normal2: generateCurveData1(curveDataPostWeight.value, 'weight', 'normalRange', 'max'), // 新增的a8e6cf颜色曲线
slightlyHigh: generateCurveData1(curveDataPostWeight.value, 'weight', 'slightHighRange', 'max'),
},
head: {
// slightlyLow: generateCurveData(32, 52, 'head'),
// normal: generateCurveData(34, 54, 'head'),
// normal2: generateCurveData(35, 55, 'head'), // 新增的a8e6cf颜色曲线
// slightlyHigh: generateCurveData(36, 56, 'head')
slightlyLow: generateCurveData1(curveDataPostHead.value, 'head', 'slightLowRange', 'min'),
normal: generateCurveData1(curveDataPostHead.value, 'head', 'normalRange', 'min'),
normal2: generateCurveData1(curveDataPostHead.value, 'head', 'normalRange', 'max'), // 新增的a8e6cf颜色曲线
slightlyHigh: generateCurveData1(curveDataPostHead.value, 'head', 'slightHighRange', 'max'),
}
};
debugger;
const historyListData = await getGrowthHistoryList();
console.log('historyListData=', historyListData);
})
// 绘制生长曲线
......@@ -662,6 +811,9 @@ const drawStandardCurves = (ctx, curves, margin, chartWidth, chartHeight, type)
// 绘制正常曲线 - 浅绿色
drawCurve(ctx, curves.normal, margin, chartWidth, chartHeight, type, '#a8e6cf', 2)
// 绘制新增的正常曲线2 - 浅绿色(不重合)
drawCurve(ctx, curves.normal2, margin, chartWidth, chartHeight, type, '#a8e6cf', 2)
// 绘制略高曲线 - 紫色
drawCurve(ctx, curves.slightlyHigh, margin, chartWidth, chartHeight, type, '#d4a5f5', 2)
}
......@@ -1239,6 +1391,7 @@ const onScroll = (e) => {
.value-triangle-container {
display: flex;
flex-direction: column;
width: 150rpx;
align-items: center;
.bar-value {
......@@ -1247,9 +1400,12 @@ const onScroll = (e) => {
}
.triangle {
// position: relative;
top:8rpx;
// margin-left: 20rpx;
width: 20rpx;
height: 20rpx;
// align-self: center;
}
}
......@@ -1368,6 +1524,10 @@ const onScroll = (e) => {
background-color: #89caa2; // 浅绿色
}
&.normal2 {
background-color: #a8e6cf; // 浅绿色(第二条线)
}
&.slightly-high {
background-color: #f3d1e9; // 紫色
}
......@@ -1436,7 +1596,9 @@ const onScroll = (e) => {
}
// 专家咨询按钮
.expert-consult-btn {
position: relative;
position: fixed;
bottom: 60rpx;
left: 0;
width: 100%;
height: 100rpx;
display: flex;
......
......@@ -179,6 +179,17 @@
<image v-if="guideIndex == 1" class="guide-img1" src="/static/shengzhangTool/guide1.png" mode="aspectFit"></image>
<image v-if="guideIndex == 2" class="guide-img2" src="/static/shengzhangTool/guide2.png" mode="aspectFit"></image>
</view>
<view class="loading-container" v-if="showLoading">
<view class="loading-content">
<view class="star-container">
<image class="loading-star" src="/static/shengzhangTool/loadingActImg.png" mode="aspectFit"></image>
</view>
<view class="loading-text">
<text class="loading-title">正在计算宝宝的生长测评得分</text>
<text class="loading-subtitle">请耐心等待~</text>
</view>
</view>
</view>
</view>
<!-- 在页面底部添加弹窗组件 -->
......@@ -216,9 +227,10 @@ import BabySwitchPopup from '@/components/BabySwitchPopup.vue'
import BabyFeedSwitchPopup from '@/components/BabyFeedSwitchPopup.vue'
import DatePickerPopup from '@/components/DatePickerPopup.vue'
import BabyTestTipsPopup from '@/components/BabyTestTipsPopup.vue'
import { growthHome, guideCompleted, assessmentSave } from '../../api/shengzhangTools'
import { growthHome, guideCompleted, getGrowthCurveData } from '../../api/shengzhangTools'
import { onLoad } from "@dcloudio/uni-app";
import { throttleTap } from '../../utils/index.js';
import { throttleTap,jump, JumpType, formatDate } from '../../utils/index.js';
import { useShengzhangStore } from '../../stores/shengzhangResult.js';
const swiperData = ref([
{ bannerImg: '/static/shengzhangTool/banner1.png' },
......@@ -230,9 +242,12 @@ const babyName = ref('宝宝名称')
const babyAge = ref('8月龄')
const babyBirthday = ref('2024-10-20')
const babyGender = ref('M')
const babyAvatar = ref('/static/shengzhangTool/avatar.png')
const babyAvatar = ref('/static/shengzhangTool/avatar.png');
const shengzhangStore = useShengzhangStore();
const guideFlag = ref(false);
const showLoading = ref(false);
const bannerHandler = (item) => {
console.log(item)
......@@ -271,8 +286,6 @@ const headCircumference = ref('34.5')
const babyId = ref(0);
const assessmentDate = ref('');
const feedingType = ref('');
// 添加picker-view相关数据
const indicatorStyle = `height: 40px; border: none;`
......@@ -366,6 +379,7 @@ const convertFeedingType = (type) => {
}
}
const submitData = throttleTap(async () => {
showLoading.value = true;
const submitData = {
babyId: babyId.value,
height: height.value,
......@@ -375,15 +389,44 @@ const submitData = throttleTap(async () => {
feedingType: convertFeedingType(selectedFeedText.value)
};
console.log('提交数据', submitData);
const data = await assessmentSave(submitData);
if(data.success){
uni.showToast({
title: '提交成功',
icon: 'success'
await shengzhangStore.assessmentSave(submitData);
const babyDataHeight = {
babyId: babyId.value,
curveType: 'HEIGHT',
startMonth: 0,
endMonth: 36,
};
await shengzhangStore.getGrowthCurveData(babyDataHeight);
const babyDataWeight = {
babyId: babyId.value,
curveType: 'WEIGHT',
startMonth: 0,
endMonth: 36,
};
await shengzhangStore.getGrowthCurveData(babyDataWeight);
const babyDataHead = {
babyId: babyId.value,
curveType: 'HEAD',
startMonth: 0,
endMonth: 36,
};
await shengzhangStore.getGrowthCurveData(babyDataHead);
//跳转测评结果页
if(shengzhangStore.shengzhangInfo.success){
jump({
type: JumpType.INNER,
url: "/pages/shengzhangTestResult/shengzhangTestResult"
})
}
showLoading.value = false;
}, 1000)
// 添加以下数据
......@@ -542,18 +585,7 @@ onMounted(async () => {
})
const formatDate = (timestamp) => {
const date = new Date(timestamp);
if (isNaN(date.getTime())) {
console.error('无效的时间戳:', timestamp);
return '';
}
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
</script>
<style lang="less" scoped>
......@@ -972,5 +1004,74 @@ const formatDate = (timestamp) => {
left: 0;
}
}
.loading-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.star-container {
margin-bottom: 60rpx;
position: relative;
.loading-star {
width: 750rpx;
height: 1624rpx;
animation: breathe 2s ease-in-out infinite;
}
}
.loading-text {
// background-color: rgba(128, 59, 59, 0.7);
position: absolute;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
top: 850rpx;
.loading-title {
font-size: 32rpx;
color: #ffffff;
font-weight: 500;
line-height: 1.4;
}
.loading-subtitle {
top: 40rpx;
font-size: 32rpx;
color: #ffffff;
font-weight: 500;
line-height: 1.4;
}
}
}
}
@keyframes breathe {
0% {
opacity: 0.6;
}
50% {
opacity: 1;
}
100% {
opacity: 0.6;
}
}
}
</style>
\ No newline at end of file
No preview for this file type
import { defineStore } from "pinia";
import {
assessmentSave,
getGrowthCurveData
} from "../api/shengzhangTools.js";
export const useShengzhangStore = defineStore("shengzhangInfo", {
state: () => {
return {
shengzhangInfo: null,
getGrowthCurveDataInfoHeight: null,
getGrowthCurveDataInfoWeight: null,
getGrowthCurveDataInfoHead: null,
getGrowthCurveDataInfoBmi: null,
};
},
actions: {
/**
* 设置生长测评结果
* @param {Object} userInfo
*/
setShengzhangInfo(shengzhangInfo) {
this.shengzhangInfo = shengzhangInfo;
},
async assessmentSave(submitData) {
const data = await assessmentSave(submitData);
if(data.success){
data.data.success = data.success;
this.setShengzhangInfo(data.data);
}
},
setGetGrowthCurveDataHeightInfo(getGrowthCurveDataInfo) {
this.getGrowthCurveDataInfoHeight = getGrowthCurveDataInfo;
},
setGetGrowthCurveDataWeightInfo(getGrowthCurveDataInfo) {
this.getGrowthCurveDataInfoWeight = getGrowthCurveDataInfo;
},
setGetGrowthCurveDataHeadInfo(getGrowthCurveDataInfo) {
this.getGrowthCurveDataInfoHead = getGrowthCurveDataInfo;
},
setGetGrowthCurveDataBmiInfo(getGrowthCurveDataInfo) {
this.getGrowthCurveDataInfoBmi = getGrowthCurveDataInfo;
},
async getGrowthCurveData(babyData) {
const data = await getGrowthCurveData(babyData);
if(data.success){
data.data.success = data.success;
switch(babyData.curveType){
case 'HEIGHT':
this.setGetGrowthCurveDataHeightInfo(data.data);
break;
case 'WEIGHT':
this.setGetGrowthCurveDataWeightInfo(data.data);
break;
case 'HEAD':
this.setGetGrowthCurveDataHeadInfo(data.data);
break;
case 'BMI':
this.setGetGrowthCurveDataBmiInfo(data.data);
break;
}
}
},
},
});
## 1.2.1(2021-11-22)
- 修复 vue3中某些scss变量无法找到的问题
## 1.2.0(2021-11-19)
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-indexed-list](https://uniapp.dcloud.io/component/uniui/uni-indexed-list)
## 1.1.0(2021-07-30)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.0.11(2021-05-12)
- 新增 组件示例地址
## 1.0.10(2021-04-21)
- 优化 添加依赖 uni-icons, 导入后自动下载依赖
## 1.0.9(2021-02-05)
- 优化 组件引用关系,通过uni_modules引用组件
## 1.0.8(2021-02-05)
- 调整为uni_modules目录规范
- 新增 支持 PC 端
<template>
<view>
<view v-if="loaded || list.itemIndex < 15" class="uni-indexed-list__title-wrapper">
<text v-if="list.items && list.items.length > 0" class="uni-indexed-list__title">{{ list.key }}</text>
</view>
<view v-if="(loaded || list.itemIndex < 15) && list.items && list.items.length > 0" class="uni-indexed-list__list">
<view v-for="(item, index) in list.items" :key="index" class="uni-indexed-list__item" hover-class="uni-indexed-list__item--hover">
<view class="uni-indexed-list__item-container" @click="onClick(idx, index)">
<view class="uni-indexed-list__item-border" :class="{'uni-indexed-list__item-border--last':index===list.items.length-1}">
<view v-if="showSelect" style="margin-right: 20rpx;">
<uni-icons :type="item.checked ? 'checkbox-filled' : 'circle'" :color="item.checked ? '#007aff' : '#C0C0C0'" size="24" />
</view>
<text class="uni-indexed-list__item-content">{{ item.name }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'UniIndexedList',
emits:['itemClick'],
props: {
loaded: {
type: Boolean,
default: false
},
idx: {
type: Number,
default: 0
},
list: {
type: Object,
default () {
return {}
}
},
showSelect: {
type: Boolean,
default: false
}
},
methods: {
onClick(idx, index) {
this.$emit("itemClick", {
idx,
index
})
}
}
}
</script>
<style lang="scss" scoped>
.uni-indexed-list__list {
background-color: $uni-bg-color;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
border-top-style: solid;
border-top-width: 1px;
border-top-color: #DEDEDE;
}
.uni-indexed-list__item {
font-size: 14px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.uni-indexed-list__item-container {
padding-left: 15px;
flex: 1;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
justify-content: space-between;
align-items: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-indexed-list__item-border {
flex: 1;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 50px;
padding: 25px;
padding-left: 0;
border-bottom-style: solid;
border-bottom-width: 1px;
border-bottom-color: #DEDEDE;
}
.uni-indexed-list__item-border--last {
border-bottom-width: 0px;
}
.uni-indexed-list__item-content {
flex: 1;
font-size: 14px;
color: #191919;
}
.uni-indexed-list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-indexed-list__title-wrapper {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
/* #endif */
background-color: #f7f7f7;
}
.uni-indexed-list__title {
padding: 6px 12px;
line-height: 24px;
font-size: 16px;
font-weight: 500;
}
</style>
<template>
<view class="uni-indexed-list" ref="list" id="list">
<!-- #ifdef APP-NVUE -->
<list class="uni-indexed-list__scroll" scrollable="true" show-scrollbar="false">
<cell v-for="(list, idx) in lists" :key="idx" :ref="'uni-indexed-list-' + idx">
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<scroll-view :scroll-into-view="scrollViewId" class="uni-indexed-list__scroll" scroll-y>
<view v-for="(list, idx) in lists" :key="idx" :id="'uni-indexed-list-' + idx">
<!-- #endif -->
<indexed-list-item :list="list" :loaded="loaded" :idx="idx" :showSelect="showSelect"
@itemClick="onClick"></indexed-list-item>
<!-- #ifndef APP-NVUE -->
</view>
</scroll-view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
</cell>
</list>
<!-- #endif -->
<view class="uni-indexed-list__menu" @touchstart="touchStart" @touchmove.stop.prevent="touchMove"
@touchend="touchEnd" @mousedown.stop="mousedown" @mousemove.stop.prevent="mousemove"
@mouseleave.stop="mouseleave">
<view v-for="(list, key) in lists" :key="key" class="uni-indexed-list__menu-item"
:class="touchmoveIndex == key ? 'uni-indexed-list__menu--active' : ''">
<text class="uni-indexed-list__menu-text"
:class="touchmoveIndex == key ? 'uni-indexed-list__menu-text--active' : ''">{{ list.key }}</text>
</view>
</view>
<view v-if="touchmove" class="uni-indexed-list__alert-wrapper">
<text class="uni-indexed-list__alert">{{ lists[touchmoveIndex].key }}</text>
</view>
</view>
</template>
<script>
import indexedListItem from './uni-indexed-list-item.vue'
// #ifdef APP-NVUE
const dom = weex.requireModule('dom');
// #endif
// #ifdef APP-PLUS
function throttle(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function touchMove(e) {
let pageY = e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
if (this.touchmoveIndex === index) {
return false
}
let item = this.lists[index]
if (item) {
// #ifndef APP-NVUE
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
// #endif
// #ifdef APP-NVUE
dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
animated: false
})
this.touchmoveIndex = index
// #endif
}
}
const throttleTouchMove = throttle(touchMove, 40)
// #endif
/**
* IndexedList 索引列表
* @description 用于展示索引列表
* @tutorial https://ext.dcloud.net.cn/plugin?id=375
* @property {Boolean} showSelect = [true|false] 展示模式
* @value true 展示模式
* @value false 选择模式
* @property {Object} options 索引列表需要的数据对象
* @event {Function} click 点击列表事件 ,返回当前选择项的事件对象
* @example <uni-indexed-list options="" showSelect="false" @click=""></uni-indexed-list>
*/
export default {
name: 'UniIndexedList',
components: {
indexedListItem
},
emits: ['click'],
props: {
options: {
type: Array,
default () {
return []
}
},
showSelect: {
type: Boolean,
default: false
}
},
data() {
return {
lists: [],
winHeight: 0,
itemHeight: 0,
winOffsetY: 0,
touchmove: false,
touchmoveIndex: -1,
scrollViewId: '',
touchmovable: true,
loaded: false,
isPC: false
}
},
watch: {
options: {
handler: function() {
this.setList()
},
deep: true
}
},
mounted() {
// #ifdef H5
this.isPC = this.IsPC()
// #endif
setTimeout(() => {
this.setList()
}, 50)
setTimeout(() => {
this.loaded = true
}, 300);
},
methods: {
setList() {
let index = 0;
this.lists = []
this.options.forEach((value, index) => {
if (value.data.length === 0) {
return
}
let indexBefore = index
let items = value.data.map(item => {
let obj = {}
obj['key'] = value.letter
obj['name'] = item
obj['itemIndex'] = index
index++
obj.checked = item.checked ? item.checked : false
return obj
})
this.lists.push({
title: value.letter,
key: value.letter,
items: items,
itemIndex: indexBefore
})
})
// #ifndef APP-NVUE
uni.createSelectorQuery()
.in(this)
.select('#list')
.boundingClientRect()
.exec(ret => {
this.winOffsetY = ret[0].top
this.winHeight = ret[0].height
this.itemHeight = this.winHeight / this.lists.length
})
// #endif
// #ifdef APP-NVUE
dom.getComponentRect(this.$refs['list'], (res) => {
this.winOffsetY = res.size.top
this.winHeight = res.size.height
this.itemHeight = this.winHeight / this.lists.length
})
// #endif
},
touchStart(e) {
this.touchmove = true
let pageY = this.isPC ? e.pageY : e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
let item = this.lists[index]
if (item) {
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
// #ifdef APP-NVUE
dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
animated: false
})
// #endif
}
},
touchMove(e) {
// #ifndef APP-PLUS
let pageY = this.isPC ? e.pageY : e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
if (this.touchmoveIndex === index) {
return false
}
let item = this.lists[index]
if (item) {
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
}
// #endif
// #ifdef APP-PLUS
throttleTouchMove.call(this, e)
// #endif
},
touchEnd() {
this.touchmove = false
// this.touchmoveIndex = -1
},
/**
* 兼容 PC @tian
*/
mousedown(e) {
if (!this.isPC) return
this.touchStart(e)
},
mousemove(e) {
if (!this.isPC) return
this.touchMove(e)
},
mouseleave(e) {
if (!this.isPC) return
this.touchEnd(e)
},
// #ifdef H5
IsPC() {
var userAgentInfo = navigator.userAgent;
var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
var flag = true;
for (let v = 0; v < Agents.length - 1; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
},
// #endif
onClick(e) {
let {
idx,
index
} = e
let obj = {}
for (let key in this.lists[idx].items[index]) {
obj[key] = this.lists[idx].items[index][key]
}
let select = []
if (this.showSelect) {
this.lists[idx].items[index].checked = !this.lists[idx].items[index].checked
this.lists.forEach((value, idx) => {
value.items.forEach((item, index) => {
if (item.checked) {
let obj = {}
for (let key in this.lists[idx].items[index]) {
obj[key] = this.lists[idx].items[index][key]
}
select.push(obj)
}
})
})
}
this.$emit('click', {
item: obj,
select: select
})
}
}
}
</script>
<style lang="scss" scoped>
.uni-indexed-list {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-indexed-list__scroll {
flex: 1;
}
.uni-indexed-list__menu {
width: 24px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-indexed-list__menu-item {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
align-items: center;
justify-content: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-indexed-list__menu-text {
font-size: 12px;
text-align: center;
color: #aaa;
}
.uni-indexed-list__menu--active {
// background-color: rgb(200, 200, 200);
}
.uni-indexed-list__menu--active {}
.uni-indexed-list__menu-text--active {
border-radius: 16px;
width: 16px;
height: 16px;
line-height: 16px;
background-color: #007aff;
color: #fff;
}
.uni-indexed-list__alert-wrapper {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
}
.uni-indexed-list__alert {
width: 80px;
height: 80px;
border-radius: 80px;
text-align: center;
line-height: 80px;
font-size: 35px;
color: #fff;
background-color: rgba(0, 0, 0, 0.5);
}
</style>
{
"id": "uni-indexed-list",
"displayName": "uni-indexed-list 索引列表",
"version": "1.2.1",
"description": "索引列表组件,右侧带索引的列表,方便快速定位到具体内容,通常用于城市/机场选择等场景",
"keywords": [
"uni-ui",
"索引列表",
"索引",
"列表"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}
## IndexedList 索引列表
> **组件名:uni-indexed-list**
> 代码块: `uIndexedList`
用于展示索引列表。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-indexed-list)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
......@@ -54,6 +54,19 @@ export function jump({ type, url, extra = {} }) {
}
}
export function formatDate(timestamp){
const date = new Date(timestamp);
if (isNaN(date.getTime())) {
console.error('无效的时间戳:', timestamp);
return '';
}
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
/**
* 防连点函数
* @param {Function} fn 需要防连点的函数
......@@ -110,3 +123,45 @@ export function hideLoading() {
uni.hideLoading();
}
}
/**
* 日期格式化
* @param date 接收可以被new Date()方法转换的内容
* @param format 字符串,需要的格式例如:'yyyy/MM/dd hh:mm:ss'
* @returns {String}
*/
const dateFormatter = (date, format = "yyyy/MM/dd") => {
if (!date) return "-";
date = new Date(
typeof date === "string" && isNaN(date)
? date.replace(/-/g, "/")
: Number(date)
);
const o = {
"M+": date.getMonth() + 1,
"d+": date.getDate(),
"h+": date.getHours(),
"m+": date.getMinutes(),
"s+": date.getSeconds(),
"q+": Math.floor((date.getMonth() + 3) / 3),
S: date.getMilliseconds(),
};
if (/(y+)/.test(format)) {
format = format.replace(
RegExp.$1,
(date.getFullYear() + "").substr(4 - RegExp.$1.length)
);
}
for (const k in o) {
if (new RegExp("(" + k + ")").test(format)) {
format = format.replace(
RegExp.$1,
RegExp.$1.length === 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
);
}
}
return format;
};
export {
dateFormatter
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment