Commit 19462a4a authored by tao.huang's avatar tao.huang

feat: 我的注册

parent e6720212
export const PRIVACY_URL = "https://secret.feihe.com/secret.html";
export const MEMBER_URL = "https://secret.feihe.com/index.html";
export const WHEEL_OPTIONS_YL = [
{ label: "1月龄", desc: "宝宝开始对外界产生兴趣,会追视物体" },
{ label: "2月龄", desc: "能发出咿咿呀呀的声音,会对妈妈微笑" },
{ label: "3月龄", desc: "抬头能力增强,喜欢玩自己的小手" },
{ label: "4月龄", desc: "会翻身了,对周围事物充满好奇" },
{ label: "5月龄", desc: "手眼协调能力提升,喜欢抓取玩具" },
{ label: "6月龄", desc: "可以独自坐立,开始尝试固体食物" },
{ label: "7月龄", desc: "会爬行了,语言能力逐渐发展" },
{ label: "8月龄", desc: "能扶物站立,认知能力显著提升" },
{ label: "9月龄", desc: "开始学步,能说简单的词语" },
{ label: "10月龄", desc: "独立行走能力增强,喜欢模仿大人" },
{ label: "11月龄", desc: "语言理解能力提高,会简单互动" },
{ label: "12月龄", desc: "基本能独立行走,表达欲望增强" },
{ label: "13月龄", desc: "动作更加协调,喜欢探索新事物" },
{ label: "14月龄", desc: "语言表达更丰富,独立意识增强" },
{ label: "15月龄", desc: "手部精细动作发展,创造力萌芽" },
{ label: "16月龄", desc: "记忆力提升,开始学习分类概念" },
{ label: "17月龄", desc: "想象力丰富,喜欢角色扮演游戏" },
{ label: "18月龄", desc: "语言交流更流畅,社交能力发展" },
{ label: "19月龄", desc: "运动技能全面发展,好奇心旺盛" },
{ label: "20月龄", desc: "逻辑思维初步形成,会简单推理" },
{ label: "21月龄", desc: "情感表达更丰富,记忆力增强" },
{ label: "22月龄", desc: "独立性增强,生活技能提升" },
{ label: "23月龄", desc: "语言组织能力提高,喜欢提问" },
{ label: "24月龄", desc: "社交圈子扩大,懂得分享" },
{ label: "25月龄", desc: "创造性思维发展,喜欢音乐" },
{ label: "26月龄", desc: "注意力更集中,会完成简单任务" },
{ label: "27月龄", desc: "空间认知能力提升,喜欢拼图" },
{ label: "28月龄", desc: "语言表达更准确,理解能力增强" },
{ label: "29月龄", desc: "自我意识增强,情绪管理提升" },
{ label: "30月龄", desc: "逻辑思维更成熟,会简单计数" },
{ label: "31月龄", desc: "艺术创造力发展,喜欢绘画" },
{ label: "32月龄", desc: "记忆力显著提升,会讲简单故事" },
{ label: "33月龄", desc: "运动协调性好,喜欢户外活动" },
{ label: "34月龄", desc: "社交技能成熟,懂得合作" },
{ label: "35月龄", desc: "思维更加灵活,解决问题能力增强" },
{ label: "36月龄", desc: "全面发展,准备进入幼儿园阶段" },
];
export const WHEEL_OPTIONS_YZ = [
{ label: "孕1周", desc: "胚胎开始着床,激素水平发生变化,胚胎开始着床,激素水平发生变化,胚胎开始着床,激素水平发生变化,胚胎开始着床,激素水平发生变化,胚胎开始着床,激素水平发生变化,胚胎开始着床,激素水平发生变化" },
{ label: "孕2周", desc: "胎盘开始形成,为胎儿提供营养" },
{ label: "孕3周", desc: "胎儿心脏开始跳动,神经系统发育" },
{ label: "孕4周", desc: "胎儿大小如花生,开始形成四肢" },
{ label: "孕5周", desc: "胎儿面部开始发育,可见眼睛雏形" },
{ label: "孕6周", desc: "胎儿开始运动,脊椎继续发育" },
{ label: "孕7周", desc: "胎儿手指脚趾形成,大脑发育加快" },
{ label: "孕8周", desc: "胎儿所有器官雏形完成,开始生长" },
{ label: "孕9周", desc: "胎儿开始吞咽羊水,肌肉发育" },
{ label: "孕10周", desc: "胎儿指甲开始生长,性别特征显现" },
{ label: "孕11周", desc: "胎儿可以做出简单动作,头部变圆" },
{ label: "孕12周", desc: "胎儿开始产生尿液,骨骼继续钙化" },
{ label: "孕13周", desc: "胎儿vocal cords形成,可发声" },
{ label: "孕14周", desc: "胎儿开始产生胎动,母体可感知" },
{ label: "孕15周", desc: "胎儿皮下脂肪开始形成,体型增大" },
{ label: "孕16周", desc: "胎儿听力开始发育,对声音敏感" },
{ label: "孕17周", desc: "胎儿免疫系统开始发育,产生抗体" },
{ label: "孕18周", desc: "胎儿睡眠周期形成,活动规律" },
{ label: "孕19周", desc: "胎儿肺部发育,为呼吸做准备" },
{ label: "孕20周", desc: "胎儿听力继续发育,对外界声音反应" },
{ label: "孕21周", desc: "胎儿眉毛睫毛生长,面部特征明显" },
{ label: "孕22周", desc: "胎儿皮肤变厚,开始长出胎毛" },
{ label: "孕23周", desc: "胎儿体重快速增长,运动更频繁" },
{ label: "孕24周", desc: "胎儿肺部继续发育,产生肺表面活性物质" },
{ label: "孕25周", desc: "胎儿脂肪积累加快,体型继续增大" },
{ label: "孕26周", desc: "胎儿眼睛可以睁开,对光有反应" },
{ label: "孕27周", desc: "胎儿大脑快速发育,神经系统成熟" },
{ label: "孕28周", desc: "胎儿各器官功能逐渐完善" },
{ label: "孕29周", desc: "胎儿骨骼继续钙化,头部下降" },
{ label: "孕30周", desc: "胎儿体重持续增加,活动空间减少" },
{ label: "孕31周", desc: "胎儿消化系统发育成熟,可消化羊水" },
{ label: "孕32周", desc: "胎儿皮下脂肪继续积累,体温调节能力增强" },
{ label: "孕33周", desc: "胎儿免疫系统继续发育,获得母体抗体" },
{ label: "孕34周", desc: "胎儿肺部发育接近成熟,准备独立呼吸" },
{ label: "孕35周", desc: "胎儿体重快速增加,为出生做准备" },
{ label: "孕36周", desc: "胎儿头部继续下降,为分娩做准备" },
{ label: "孕37周", desc: "胎儿各器官发育完善,可以安全出生" },
{ label: "孕38周", desc: "胎儿继续增重,准备出生" },
{ label: "孕39周", desc: "胎儿位置固定,随时可能出生" },
{ label: "孕40周", desc: "胎儿发育完全成熟,即将出生" },
];
<template>
<view class="baby-switcher-container">
<view class="baby-switcher-mask" @click.self="onClose"> </view>
<view class="baby-switcher-popup">
<view
v-for="baby in babyList"
:key="baby.id"
class="baby-item"
@click="selectBaby(baby)"
>
<text>{{ baby.babyName || "暂无昵称" }}</text>
<view
class="selected-icon"
:style="{ backgroundColor: baby.selected ? '#d3a358' : '#E8E8E8' }"
/>
</view>
<view v-if="babyList.length < 3" class="baby-item add-item" @click="onAdd">
<image :src="addIcon" class="add-icon" />
</view>
</view>
</view>
</template>
<script setup>
import { defineProps, defineEmits } from "vue";
const props = defineProps({
show: Boolean,
babyList: {
type: Array,
default: () => [],
},
addIcon: {
type: String,
default: "", // 传入新增icon路径
},
});
const emits = defineEmits(["close", "select", "add"]);
function onClose() {
emits("close");
}
function selectBaby(baby) {
emits("select", baby);
}
function onAdd() {
emits("add");
}
</script>
<style lang="less" scoped>
.baby-switcher-container {
touch-action: none;
}
.baby-switcher-mask {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 1;
}
.baby-switcher-popup {
position: absolute;
left: 0;
top: 40rpx;
z-index: 2;
background: #fff;
border-radius: 16rpx;
padding: 10rpx 36rpx 22rpx 36rpx;
box-shadow: 0 0 30rpx rgba(204, 204, 204, 0.8);
box-sizing: border-box;
min-width: 247rpx;
}
.baby-item {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 25rpx;
font-size: 26rpx;
font-weight: 500;
color: #1d1e25;
flex-wrap: nowrap;
border-bottom: 1px solid #eee;
&:nth-child(3) {
border-bottom: none;
}
}
.selected-icon {
width: 12rpx;
height: 12rpx;
margin-left: 17rpx;
background-color: #e8e8e8;
border-radius: 50%;
}
.add-item {
margin-top: 10rpx;
padding-top: 20rpx;
padding: 10rpx 0;
border-bottom: none;
}
.add-icon {
width: 96rpx;
height: 36rpx;
display: block;
}
</style>
\ No newline at end of file
......@@ -27,24 +27,40 @@
>
<template v-if="mode === 'date'">
<picker-view-column>
<view v-for="(item, idx) in years" :key="idx" class="picker-layer-item">
<view
v-for="(item, idx) in years"
:key="idx"
class="picker-layer-item"
>
{{ item }}
</view>
</picker-view-column>
<picker-view-column>
<view v-for="(item, idx) in months" :key="idx" class="picker-layer-item">
<view
v-for="(item, idx) in months"
:key="idx"
class="picker-layer-item"
>
{{ item }}
</view>
</picker-view-column>
<picker-view-column>
<view v-for="(item, idx) in days" :key="idx" class="picker-layer-item">
{{ item === '请选择' ? item : `${item}日` }}
<view
v-for="(item, idx) in days"
:key="idx"
class="picker-layer-item"
>
{{ item === "请选择" ? item : `${item}日` }}
</view>
</picker-view-column>
</template>
<template v-else>
<picker-view-column v-for="(col, colIdx) in columns" :key="colIdx">
<view v-for="(item, idx) in col" :key="idx" class="picker-layer-item">
<view
v-for="(item, idx) in col"
:key="idx"
class="picker-layer-item"
>
{{ item }}
</view>
</picker-view-column>
......@@ -60,7 +76,7 @@ import { ref, computed, watch } from "vue";
const props = defineProps({
mode: {
type: String,
default: 'custom', // 'date' | 'custom'
default: "custom", // 'date' | 'custom'
},
range: {
type: Array,
......@@ -78,33 +94,80 @@ const props = defineProps({
type: Function,
default: () => {},
},
onStatusChange: {
type: Function,
default: () => {},
},
disabled: {
type: Boolean,
default: false,
},
});
const show = ref(false);
// date模式相关
const currentYear = new Date().getFullYear();
const today = new Date();
const currentYear = today.getFullYear();
const currentMonth = today.getMonth() + 1;
const currentDay = today.getDate();
// 计算未来280天的日期
const futureDate = new Date();
futureDate.setDate(futureDate.getDate() + 280);
const futureYear = futureDate.getFullYear();
const futureMonth = futureDate.getMonth() + 1;
const futureDay = futureDate.getDate();
const years = computed(() => {
const arr = [];
for (let i = 1970; i <= currentYear; i++) {
const startYear = currentYear - 3;
const endYear = futureYear;
for (let i = startYear; i <= endYear; i++) {
arr.push(i);
}
return arr;
});
const months = computed(() => {
const [yearIdx] = pickerValue.value;
const selectedYear = years.value[yearIdx];
const arr = [];
for (let i = 1; i <= 12; i++) {
let startMonth = 1;
let endMonth = 12;
if (selectedYear === currentYear - 3) {
startMonth = currentMonth;
}
if (selectedYear === futureYear) {
endMonth = futureMonth;
}
for (let i = startMonth; i <= endMonth; i++) {
arr.push(i);
}
return arr;
});
const days = computed(() => {
const [yearIdx, monthIdx] = pickerValue.value;
const year = years.value[yearIdx] || years.value[0];
const month = months.value[monthIdx] || months.value[0];
const dayCount = new Date(year, month, 0).getDate();
const arr = ['请选择'];
for (let i = 1; i <= dayCount; i++) {
const selectedYear = years.value[yearIdx];
const selectedMonth = months.value[monthIdx];
const dayCount = new Date(selectedYear, selectedMonth, 0).getDate();
const arr = ["请选择"];
let startDay = 1;
let endDay = dayCount;
if (selectedYear === currentYear - 3 && selectedMonth === currentMonth) {
startDay = currentDay;
}
if (selectedYear === futureYear && selectedMonth === futureMonth) {
endDay = futureDay;
}
for (let i = startDay; i <= endDay; i++) {
arr.push(i);
}
return arr;
......@@ -120,7 +183,7 @@ const columns = computed(() => {
});
const defaultValue = computed(() => {
if (props.mode === 'date') {
if (props.mode === "date") {
// 默认选中今年1月1日
return [years.value.length - 1, 0, 0];
} else if (Array.isArray(props.value)) {
......@@ -135,7 +198,7 @@ const pickerValue = ref([...defaultValue.value]);
watch(
() => props.value,
(val) => {
if (props.mode === 'date') {
if (props.mode === "date") {
pickerValue.value = [...val];
} else if (Array.isArray(val)) {
pickerValue.value = [...val];
......@@ -146,7 +209,9 @@ watch(
);
function open() {
if (props.disabled) return;
pickerValue.value = [...defaultValue.value];
show.value = true;
props.onLayerVisibleChange(true);
}
......@@ -156,19 +221,35 @@ function close() {
}
function confirm() {
close();
if (props.mode === 'date') {
if (props.mode === "date") {
const [yIdx, mIdx, dIdx] = pickerValue.value;
if(dIdx === 0) {
props.onPickerChange('');
if (dIdx === 0) {
props.onPickerChange("");
return;
}
const year = years.value[yIdx];
const month = months.value[mIdx];
const day = days.value[dIdx];
const dateStr = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
const dateStr = `${year}-${String(month).padStart(2, "0")}-${String(
day
).padStart(2, "0")}`;
props.onPickerChange(dateStr);
// 新增:判断状态并回调
const today = new Date();
const selectedDate = new Date(year, month - 1, day);
// 清除时分秒
today.setHours(0, 0, 0, 0);
selectedDate.setHours(0, 0, 0, 0);
let status = -1;
if (selectedDate.getTime() <= today.getTime()) {
status = 2;
} else {
status = 1;
}
props.onStatusChange(status);
} else {
// 单列时只返回索引,否则返回数组
if (columns.value.length === 1) {
......@@ -180,7 +261,7 @@ function confirm() {
}
function onChange(e) {
let val = e.detail.value;
if (props.mode === 'date') {
if (props.mode === "date") {
// 如果天数溢出,自动修正到最大天数
const maxDay = days.value.length;
if (val[2] >= maxDay) val[2] = maxDay - 1;
......@@ -308,4 +389,8 @@ function onChange(e) {
pointer-events: none;
}
.picker-custom[disabled] {
opacity: 0.5;
pointer-events: none;
}
</style>
\ No newline at end of file
......@@ -2,6 +2,7 @@
<Layer
v-model="visible"
:customHeader="true"
:maskClosable="false"
@confirm="handleConfirm"
@cancel="handleCancel"
>
......@@ -13,16 +14,22 @@
<PickerCustom
mode="date"
:value="dateValue"
:disabled="status == 0"
:onPickerChange="onDateChange"
:onLayerVisibleChange="onLayerVisibleChange"
:onStatusChange="onStatusChange"
>
<view
class="register-baby-info-picker"
:style="status == 0 ? 'background: #e9edf1;' : ''"
>
<view class="register-baby-info-picker">
<view
class="register-baby-info-picker-value"
:style="dateDisplay ? 'color: #1d1e25;' : ''"
>{{ dateDisplay || "请选择宝宝的生日或预产期" }}</view
>
<image
v-if="status != 0"
class="register-baby-info-picker-arrow"
:src="$baseUrl + 'registerLayer/icon_arrow_yellow.png'"
mode="aspectFit"
......@@ -31,18 +38,23 @@
</PickerCustom>
<PickerCustom
mode="selector"
:disabled="status == 0"
:range="genderOptions"
:value="genderIndex"
:onPickerChange="onGenderChange"
:onLayerVisibleChange="onLayerVisibleChange"
>
<view class="register-baby-info-picker">
<view
class="register-baby-info-picker"
:style="status == 0 ? 'background: #e9edf1;' : ''"
>
<view
class="register-baby-info-picker-value"
:style="genderDisplay ? 'color: #1d1e25;' : ''"
>{{ genderDisplay || "请选择宝宝的性别" }}</view
>
<image
v-if="status != 0"
class="register-baby-info-picker-arrow"
:src="$baseUrl + 'registerLayer/icon_arrow_yellow.png'"
mode="aspectFit"
......@@ -51,24 +63,50 @@
</PickerCustom>
<PickerCustom
mode="selector"
:disabled="status == 0"
:range="fetusOptions"
:value="fetusIndex"
:onPickerChange="onFetusChange"
:onLayerVisibleChange="onLayerVisibleChange"
>
<view class="register-baby-info-picker">
<view
class="register-baby-info-picker"
:style="status == 0 ? 'background: #e9edf1;' : ''"
>
<view
class="register-baby-info-picker-value"
:style="fetusDisplay ? 'color: #1d1e25;' : ''"
>{{ fetusDisplay || "请选择胎数" }}</view
>
<image
v-if="status != 0"
class="register-baby-info-picker-arrow"
:src="$baseUrl + 'registerLayer/icon_arrow_yellow.png'"
mode="aspectFit"
/>
</view>
</PickerCustom>
<view class="register-baby-info-status">
<view
class="register-baby-info-status-item"
:class="{ active: status === 0 }"
@click="toggleStatus(0)"
>
<view class="register-baby-info-status-item-icon">
<image
:src="
$baseUrl +
(status === 0
? 'registerLayer/circle_yes.png'
: 'registerLayer/circle_no.png')
"
mode="aspectFit"
/>
</view>
<view class="register-baby-info-status-item-text">备孕中</view>
</view>
</view>
<view class="register-baby-info-agreement">
<image
class="register-baby-info-agreement-icon"
......@@ -109,13 +147,16 @@
@click="checked.option2 = !checked.option2"
/>
</view>
<view class="register-baby-info-btn" @click="handleBabyInfoConfirm">
<view
class="register-baby-info-btn"
:style="isBtnActive ? 'background: #d3a358;' : 'background: #e9edf1;'"
@click="handleBabyInfoConfirm"
>
完成
</view>
</view>
</Layer>
</template>
<script setup>
import { ref, watch, computed } from "vue";
import Layer from "./Layer.vue";
......@@ -132,7 +173,6 @@ const emit = defineEmits([
]);
const visible = ref(props.modelValue);
const imageUrl = ref(props.value);
watch(
() => props.modelValue,
......@@ -142,17 +182,8 @@ watch(
() => visible.value,
(val) => emit("update:modelValue", val)
);
watch(
() => props.value,
(val) => (imageUrl.value = val)
);
function removeImage() {
imageUrl.value = "";
emit("update:value", "");
}
function handleConfirm() {
emit("confirm", imageUrl.value);
visible.value = false;
}
function handleCancel() {
......@@ -169,9 +200,11 @@ const checked = ref({
const date = ref("");
const gender = ref("");
const fetus = ref("");
// 0备孕 1孕中 2出生
const status = ref(-1);
const genderOptions = ["男", "女", "保密"];
const fetusOptions = ["单胎", "多胎"];
const genderOptions = ["男", "女", "未知"];
const fetusOptions = ["一胎", "二胎", "三胎"];
// date picker
const dateValue = ref([50, 0, 0]); // 默认选中今年1月1日
......@@ -194,48 +227,36 @@ function onFetusChange(idx) {
fetusIndex.value = idx;
fetus.value = fetusOptions[idx];
}
function onLayerVisibleChange() {}
function handleBabyInfoConfirm() {
if (!checked.value.option1 || !checked.value.option2) {
uni.showToast({
title: "请先阅读并同意协议",
icon: "none",
});
return;
}
console.log(date.value, gender.value, fetus.value);
function onStatusChange(v) {
status.value = v;
}
if(!date.value) {
uni.showToast({
title: "请选择宝宝的生日或预产期",
icon: "none",
});
return;
}
function onLayerVisibleChange() {}
if(!gender.value) {
uni.showToast({
title: "请选择宝宝的性别",
icon: "none",
});
return;
const isBtnActive = computed(() => {
if (status.value === 0) {
return checked.value.option1 && checked.value.option2;
} else {
return (
checked.value.option1 &&
checked.value.option2 &&
!!date.value &&
!!gender.value &&
!!fetus.value
);
}
});
if(!fetus.value) {
uni.showToast({
title: "请选择胎数",
icon: "none",
});
function handleBabyInfoConfirm() {
if (!isBtnActive.value) {
return;
}
emit("confirm", {
date: date.value,
gender: gender.value,
fetus: fetus.value,
status: status.value,
});
visible.value = false;
}
......@@ -243,6 +264,22 @@ function openAgreement() {
// 跳转协议页面
uni.navigateTo({ url: "/pages/agreement/index" });
}
function toggleStatus(val) {
if (status.value === val) {
status.value = -1; // 取消高亮
} else {
status.value = val; // 选中
if (val === 0) {
date.value = "";
dateDisplay.value = "";
gender.value = "";
genderIndex.value = 0;
fetus.value = "";
fetusIndex.value = 0;
}
}
}
</script>
<style lang="less" scoped>
......@@ -273,7 +310,7 @@ function openAgreement() {
.register-baby-info-picker {
width: 654rpx;
background: #fff;
border-radius: 24rpx;
border-radius: 44rpx;
margin-bottom: 28rpx;
padding: 0 32rpx;
height: 94rpx;
......@@ -282,10 +319,49 @@ function openAgreement() {
align-items: center;
box-shadow: 0 4rpx 24rpx 0 rgba(211, 163, 88, 0.04);
border: none;
margin-left: auto;
margin-right: auto;
position: relative;
}
.register-baby-info-status {
width: 654rpx;
margin-bottom: 28rpx;
display: flex;
justify-content: center;
}
.register-baby-info-status-item {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 94rpx;
background: #fff;
color: #bdbfc3;
border-radius: 44rpx;
cursor: pointer;
transition: background 0.2s, color 0.2s;
box-sizing: border-box;
.register-baby-info-status-item-icon {
width: 32rpx;
height: 32rpx;
image {
width: 100%;
height: 100%;
display: block;
}
}
.register-baby-info-status-item-text {
font-size: 32rpx;
color: #bdbfc3;
margin-left: 12rpx;
transition: color 0.2s;
}
}
.register-baby-info-status-item.active {
border: 2rpx solid #d3a358;
color: #d3a358;
.register-baby-info-status-item-text {
color: #d3a358;
}
}
.register-baby-info-picker-value {
font-size: 32rpx;
color: #bdbfc3;
......@@ -367,3 +443,4 @@ function openAgreement() {
justify-content: center;
}
</style>
<template>
<view class="wheel-selector">
<image class="wheel-bg" :src="bgImg" mode="aspectFit" />
<view
class="wheel-options"
@touchstart="onTouchStart"
@touchmove.prevent="onTouchMove"
@touchend="onTouchEnd"
>
<view
v-for="opt in visibleOptions"
:key="opt.idx + '-' + opt.pos"
class="wheel-option"
:class="{ selected: opt.pos === 0 }"
:style="getOptionStyle(opt)"
>
<image
class="wheel-icon"
:class="{ 'wheel-icon-selected': opt.pos === 0 }"
:src="opt.pos === 0 ? iconSelected : iconNormal"
mode="aspectFit"
/>
<text class="wheel-label">{{ opt.item.label }}</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, watch, computed } from "vue";
const props = defineProps({
options: { type: Array, required: true }, // [{label: '七月龄'}, ...]
selectedIndex: { type: Number, default: 0 },
bgImg: { type: String, required: true },
iconNormal: { type: String, required: true },
iconSelected: { type: String, required: true },
});
const emits = defineEmits(["update:selectedIndex", "change"]);
const currentIndex = ref(props.selectedIndex);
// 计算最多展示三个 option
const visibleOptions = computed(() => {
const total = props.options.length;
const arr = [];
for (let i = 0; i < 3; i++) {
if (currentIndex.value + i < total) {
arr.push({
item: props.options[currentIndex.value + i],
idx: currentIndex.value + i,
pos: i,
});
}
}
return arr;
});
watch(
() => props.selectedIndex,
(val) => {
currentIndex.value = val;
}
);
let startY = 0;
function onTouchStart(e) {
startY = e.touches[0].clientY;
}
function onTouchMove(e) {
const deltaY = e.touches[0].clientY - startY;
if (Math.abs(deltaY) > 30) {
// 滑动阈值
if (deltaY > 0 && currentIndex.value > 0) {
currentIndex.value -= 1;
startY = e.touches[0].clientY;
} else if (
deltaY < 0 &&
currentIndex.value < props.options.length - 1
) {
currentIndex.value += 1;
startY = e.touches[0].clientY;
}
emits("update:selectedIndex", currentIndex.value);
emits("change", currentIndex.value);
}
}
function onTouchEnd() {
// nothing
}
function getOptionStyle(opt) {
const positions = [
{ x: 34, y: 94 },
{ x: 118, y: 278 },
{ x: 50, y: 446 },
];
const pos = positions[opt.pos] || positions[positions.length - 1];
const _oo = {
position: "absolute",
left: `${pos.x}rpx`,
top: `${pos.y}rpx`,
opacity: 1,
zIndex: 10 - opt.pos,
}
return _oo;
}
</script>
<style scoped lang="less">
.wheel-selector {
position: absolute;
top: 128rpx;
left: 0;
width: 340rpx;
height: 540rpx;
}
.wheel-bg {
position: absolute;
left: 0;
top: 94rpx;
width: 143rpx;
height: 436rpx;
z-index: 0;
display: block;
}
.wheel-options {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 1;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
pointer-events: auto;
touch-action: none;
}
.wheel-option {
position: absolute;
width: 220rpx;
height: 60rpx;
display: flex;
align-items: center;
transition: all 0.7s, opacity 0.2s;
.wheel-icon {
width: 24rpx;
height: 24rpx;
display: block;
}
.wheel-icon-selected {
width: 56rpx;
height: 56rpx;
display: block;
}
.wheel-label {
font-size: 28rpx;
color: #FFFFFF;
text-shadow: 0 2rpx 8rpx #333333;
font-weight: bold;
}
}
</style>
\ No newline at end of file
......@@ -91,6 +91,7 @@
:value="getPickerIndex(item)"
:onPickerChange="(e) => onPickerChange(e, item.name)"
:onLayerVisibleChange="(e) => (pageStatus.btnStatus = !e)"
:onStatusChange="onDateStatusChange"
>
<view class="form-input-box">
<view class="form-input">{{
......@@ -373,6 +374,12 @@ watch(
}
}
);
function onDateStatusChange(status) {
if (status === '已出生' || status === '孕中') {
formData.value.currentBaby = status;
}
}
</script>
<style lang="less" scoped>
......
import {
defineStore
} from 'pinia';
import { defineStore } from "pinia";
import {
autoLoginByCode,
fetchUserInfo,
fetchBabyInfo,
fetchAutoPhone
} from '../api/user.js';
import {
useGlobalStore
} from './global.js';
fetchAutoPhone,
} from "../api/user.js";
import { useGlobalStore } from "./global.js";
const globalStore = useGlobalStore();
export const useUserStore = defineStore('userInfo', {
export const useUserStore = defineStore("userInfo", {
state: () => {
return {
userInfo: null,
userInfo: {
// memberId: "12",
// memberName: "hzt",
},
babyInfo: null,
};
},
......@@ -44,39 +43,36 @@ export const useUserStore = defineStore('userInfo', {
*/
async phoneCallback(data) {
uni.login({
provider: 'weixin',
provider: "weixin",
success: async (res) => {
// console.log('wxAutoLogin', res);
if (res.errMsg === 'login:ok') {
if (res.errMsg === "login:ok") {
// 用户手机授权
await fetchAutoPhone({
phoneEncryptedData: data.encryptedData,
phoneIv: data.iv,
code: data.code,
codeLogin: res.code
codeLogin: res.code,
});
// 授权注册成功后做一次登录
this.wxAutoLogin();
} else {
uni.showToast({
title: res.errMsg,
icon: 'error'
icon: "error",
});
}
},
});
},
/**
* 获取用户信息
*/
async loadUserInfo() {
const {
data
} = await fetchUserInfo();
console.log('userInfo', data);
if (data?.memberId !== 'not_login') {
const { data } = await fetchUserInfo();
console.log("userInfo", data);
if (data?.memberId !== "not_login") {
this.userInfo = data;
}
},
......@@ -85,11 +81,9 @@ export const useUserStore = defineStore('userInfo', {
* 获取宝宝信息
*/
async loadBabyInfo() {
const {
data
} = await fetchBabyInfo();
console.log('babyInfo', data);
if (data?.memberId !== 'not_login') {
const { data } = await fetchBabyInfo();
console.log("babyInfo", data);
if (data?.memberId !== "not_login") {
this.babyInfo = data;
}
},
......@@ -99,10 +93,8 @@ export const useUserStore = defineStore('userInfo', {
* @param {String} code
*/
async autoLoginByCode(code) {
const {
data
} = await autoLoginByCode(code);
console.log('autoLoginByCode', data);
const { data } = await autoLoginByCode(code);
console.log("autoLoginByCode", data);
// 如果登录成功,获取用户信息和宝宝信息,更新到state中,方便全局使用
if (data && data.cuk) {
globalStore.setCuk(data.cuk);
......@@ -117,15 +109,15 @@ export const useUserStore = defineStore('userInfo', {
*/
async wxAutoLogin() {
uni.login({
provider: 'weixin',
provider: "weixin",
success: (res) => {
console.log('wxAutoLogin', res);
if (res.errMsg === 'login:ok') {
console.log("wxAutoLogin", res);
if (res.errMsg === "login:ok") {
this.autoLoginByCode(res.code);
} else {
uni.showToast({
title: res.errMsg,
icon: 'error'
icon: "error",
});
}
},
......
<template>
<view class="my-container">
<view v-if="cfgStatus.showDetail" class="wheel-selector-fixed">
<WheelSelector
:options="wheelOptions"
:selectedIndex="wheelSelectedIndex"
:bgImg="$baseUrl + 'my/track/track.png'"
:iconNormal="$baseUrl + 'my/track/icon_stage_nor.png'"
:iconSelected="$baseUrl + 'my/track/icon_stage_sel.png'"
@update:selectedIndex="(val) => (wheelSelectedIndex = val)"
/>
</view>
<view class="bg-container">
<image
class="bg-img"
......@@ -10,47 +20,85 @@
bindload=""
/>
</view>
<button
v-if="!cfgStatus.isRegister"
type="primary"
class="phone-button"
open-type="getPhoneNumber"
@getphonenumber="getRealtimePhoneNumber"
/>
<!-- 用户信息区域 -->
<view class="user-info" @click="handleRegister">
<view
class="user-info"
:style="{ 'min-height': cfgStatus.showDetail ? '343rpx' : '180rpx' }"
@click="handleRegister"
>
<view class="user-header">
<view class="avatar-container">
<image class="avatar" :src="userInfo.avatar" mode="aspectFill" />
<view class="avatar-container" @click="handleEditProfile">
<image
class="avatar"
:src="babyInfo.babyAvatar || $baseUrl + 'common/default_avatar.png'"
mode="aspectFill"
/>
</view>
<image
class="avatar-modify"
:src="$baseUrl + 'my/icon_modify.png'"
mode="aspectFit"
lazy-load="false"
@click="handleEditProfile"
/>
<view class="user-detail">
<text class="nickname">{{ userInfo.nickname || "暂无昵称" }}</text>
<view class="user-detail-nickname" @click="handleChangeBaby">
<text class="nickname">{{ babyInfo.babyName || "暂无昵称" }}</text>
<image
class="user-detail-nickname-icon"
:src="$baseUrl + 'registerLayer/icon_arrow_yellow.png'"
mode="aspectFit"
/>
</view>
<!-- 积分账户 -->
<view class="integral-account">
<text class="integral-account-text"> 积分账户: </text>
<text class="integral-account-value"> --- </text>
<text class="integral-account-value">
{{ babyInfo.points || "---" }}</text
>
</view>
<BabySwitcher
v-if="showBabySwitcher"
:show="showBabySwitcher"
:babyList="babyInfo.allBabyBaseInfo"
:addIcon="$baseUrl + 'my/baby_add_btn.png'"
@close="showBabySwitcher = false"
@select="onSelectBaby"
@add="onAddBaby"
/>
</view>
</view>
<!-- user desc -->
<view class="user-desc">
<view v-if="cfgStatus.showDetail" class="user-desc">
<view class="desc-top">
<text class="desc-title">宝宝变化</text>
<text class="desc-age">
{{ babyDetail[0].age }}
{{ wheelOptions[wheelSelectedIndex]?.label }}
</text>
</view>
<view class="desc-content">
<text class="desc-text"
>{{
cfgStatus.openBabyCardDesc
? babyDetail[0].desc
: babyDetail[0].desc.slice(0, 46) + "..."
wheelOptions[wheelSelectedIndex]?.desc.length > 46
? cfgStatus.openBabyCardDesc
? wheelOptions[wheelSelectedIndex]?.desc
: wheelOptions[wheelSelectedIndex]?.desc.slice(0, 46) + "..."
: wheelOptions[wheelSelectedIndex]?.desc
}}
</text>
<text
v-if="wheelOptions[wheelSelectedIndex]?.desc.length > 46"
class="desc-more"
@click="cfgStatus.openBabyCardDesc = !cfgStatus.openBabyCardDesc"
>{{ cfgStatus.openBabyCardDesc ? "点击收起" : "点击展开" }}</text
......@@ -74,32 +122,28 @@
<view class="hot-member" @click="handleHot" data-type="member"></view>
<view class="hot-privacy" @click="handleHot" data-type="privacy"></view>
</view>
<RegisterLayer
v-model="showRegisterLayer"
:value="registerImageUrl"
@update:value="(val) => (registerImageUrl = val)"
@confirm="onRegisterConfirm"
/>
<RegisterLayer v-model="showRegisterLayer" @confirm="onRegisterConfirm" />
</view>
</template>
<script setup>
import { ref, onMounted, getCurrentInstance } from "vue";
import RegisterLayer from "../components/RegisterLayer.vue";
import BabySwitcher from "../components/BabySwitcher.vue";
import WheelSelector from "../components/WheelSelector.vue";
import { WHEEL_OPTIONS_YL, WHEEL_OPTIONS_YZ } from "@/cfg";
import { useUserStore } from "@/stores/user";
const { proxy } = getCurrentInstance();
const $baseUrl = proxy.$baseUrl;
const cfgStatus = ref({
openBabyCardDesc: false,
showDetail: false,
isRegister: false,
});
const babyDetail = ref([
{
age: "2月龄",
desc: "宝宝开始对周围环境产生兴趣,喜欢看、听、摸,开始对玩具感兴趣,开始对妈妈的声音产生反应。宝宝开始对周围环境产生兴趣,喜欢看、听、摸,开始对玩具感兴趣,开始对妈妈的声音产生反应。宝宝开始对周围环境产生兴趣,喜欢看、听、摸,开始对玩具感兴趣,开始对妈妈的声音产生反应。",
},
]);
const wheelOptions = ref([]);
const wheelSelectedIndex = ref(0);
const toolList = ref([
{
......@@ -129,11 +173,55 @@ const toolList = ref([
},
]);
// 用户信息
const userInfo = ref({});
const babyInfo = ref({
babyName: "et",
babySkill: "occaecat",
babyAge: "2月龄",
babyAvatar: "sint ad sunt anim",
points: "23223",
babyStage: 1,
content: {
dueDate: "esse ea dolor id ipsum",
gestationalWeeks: "culpa",
babyType: "irure",
contentPreference: "occaecat commodo dolore",
followInfo: "est qui ea occaecat ad",
babyName: "Lorem",
babyBirthday: "est mollit do consectetur",
babyGender: "consectetur",
birthWeight: "dolore pariatur dolor",
birthHeight: "fugiat consectetur incididunt",
feedingType: "ipsum labore dolor in Lorem",
productPreference: "id voluptate",
purchaseChannel: "enim veniam in elit",
id: 2212796.2919538617,
memberBabyId: -77291739.14081913,
babyAvatar: "nisi Duis est",
backgroundImg: "laborum in esse ut deserunt",
},
allBabyBaseInfo: [
{
babyName: "",
babyType: 19188017.35935314,
babyStage: "veniam Ut sunt cupidatat",
id: "officia pariatur proident",
typeName: "eu",
selected: false,
},
{
babyName: "",
babyType: 34899428.44859558,
babyStage: "incididunt",
id: "non",
typeName: "dolor irure id cupidatat",
selected: true,
},
],
babyGender: "in voluptate ut",
});
const showRegisterLayer = ref(false);
const registerImageUrl = ref("");
const showBabySwitcher = ref(false);
const handleHot = (e) => {
const type = e.currentTarget.dataset.type;
......@@ -164,7 +252,8 @@ const navigateTo = (url) => {
// 编辑个人资料
const handleEditProfile = () => {
if (!userInfo.value || JSON.stringify(userInfo.value) === "{}") {
const userStore = useUserStore();
if (!userStore.userInfo || JSON.stringify(userStore.userInfo) === "{}") {
return;
}
navigateTo("/pages/person/person");
......@@ -172,42 +261,86 @@ const handleEditProfile = () => {
const handleRegister = () => {
// 判断是否已注册
if (!userInfo.value || JSON.stringify(userInfo.value) === "{}") {
showRegisterLayer.value = true;
if (!cfgStatus.value.isRegister) {
// 调用登录接口
// console.log("phoneButton", phoneButton.value);
// phoneButton.value.triggerEvent("tap");
}
return;
};
function onRegisterConfirm(data) {
const onRegisterConfirm = async (data) => {
console.log("onRegisterConfirm:", data);
showRegisterLayer.value = false;
}
const userStore = useUserStore();
await Promise.all[(userStore.loadUserInfo(), userStore.loadBabyInfo())];
initData();
};
// 获取用户信息
const getUserInfo = async () => {
try {
// TODO: 调用获取用户信息接口
const res = await uni.request({
url: "/api/user/info",
method: "GET",
});
if (res.data.code === 0) {
userInfo.value = res.data.data;
const initData = async () => {
const userStore = useUserStore();
if (!userStore.userInfo || JSON.stringify(userStore.userInfo) == "{}") {
cfgStatus.value.isRegister = false;
cfgStatus.value.showDetail = false;
return;
}
const __showDetail = [1, 2].includes(babyInfo.value.babyStage);
cfgStatus.value.isRegister = true;
// 已出生或孕中显示
cfgStatus.value.showDetail = __showDetail;
if (__showDetail) {
wheelOptions.value =
babyInfo.value.babyStage == 2 ? WHEEL_OPTIONS_YL : WHEEL_OPTIONS_YZ;
const index = wheelOptions.value.findIndex(
(item) => item.label == babyInfo.value.babyAge
);
wheelSelectedIndex.value = index > 0 ? index : 0;
}
} catch (error) {
console.error("获取用户信息失败:", error);
};
const getRealtimePhoneNumber = async (e) => {
console.log("获取手机号码", e);
const userStore = useUserStore();
if (e.detail.errMsg !== "getPhoneNumber:ok") {
uni.showToast({
title: "获取用户信息失败",
title: "请授权使用手机号",
icon: "none",
});
return;
}
console.log("detail", e.detail);
await userStore.phoneCallback(e.detail);
showRegisterLayer.value = true;
};
const handleChangeBaby = () => {
if (!cfgStatus.value.isRegister) {
return;
}
showBabySwitcher.value = true;
};
function onSelectBaby(baby) {
// 处理宝宝切换逻辑
showBabySwitcher.value = false;
console.log("onSelectBaby", baby);
}
function onAddBaby() {
// 跳转到新增宝宝页面
showBabySwitcher.value = false;
navigateTo("/pages/person/person?type=add");
}
// 页面加载
onMounted(() => {
// getUserInfo()
// getOrderCounts()
initData();
});
// 定义页面配置
......@@ -222,6 +355,26 @@ defineExpose({});
padding-bottom: 100rpx;
overflow: hidden;
.wheel-selector-fixed {
position: absolute;
left: 0;
top: 0;
z-index: 10;
}
.phone-button {
position: absolute;
z-index: 3;
width: 686rpx;
height: 180rpx;
top: 676rpx;
left: 50%;
transform: translateX(-50%);
border: none;
background: transparent;
opacity: 0;
}
.bg-container {
width: 750rpx;
height: 840rpx;
......@@ -281,6 +434,7 @@ defineExpose({});
top: 84rpx;
z-index: 1;
display: block;
pointer-events: none;
}
.user-detail {
......@@ -292,6 +446,19 @@ defineExpose({});
top: 48rpx;
z-index: 1;
.user-detail-nickname {
display: flex;
align-items: center;
}
.user-detail-nickname-icon {
width: 20rpx;
height: 12rpx;
display: block;
margin-left: 20rpx;
margin-top: -10rpx;
}
.nickname {
font-size: 32rpx;
font-weight: 500;
......@@ -395,6 +562,10 @@ defineExpose({});
}
}
.user-info-min {
min-height: 180rpx;
}
.tool-container {
margin: 0 auto;
margin-top: 48rpx;
......
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