Initial commit

This commit is contained in:
jackyu66git
2026-03-23 11:52:05 +08:00
commit c3c9db3151
267 changed files with 24984 additions and 0 deletions
+855
View File
@@ -0,0 +1,855 @@
<template>
<view class="content">
<!-- <myNav title="" bgColor="nonoe" titleColor="#000" :backIcon="true" :backHome="false" :flex="false"></myNav> -->
<view class="back-btn" :style="{'height':backBtnHeight+'px','width':backBtnHeight+'px'}" @click="toBack">
<image src="/static/back/n-back-mini.png" mode="widthFix" :style="{'width':backBtnImgWidth+'px'}"></image>
</view>
<view style="display: flex;flex-direction: column;justify-content: center;">
<image :src="showDetail.coverUrl" style="width: 750rpx;height:600rpx;"></image>
<view style="position: relative;display: flex;justify-content: center;height: 402rpx;">
<view class="header-detail">
<view class="header-detail-top">
</view>
<view class="header-detail-show">
<view style="display: flex;justify-content: space-between;align-items: first baseline;">
<view>
<text class="name-class">{{ showDetail.name }}</text>
<!-- <text class="school-class">{{ showDetail.education }}</text> -->
</view>
<view>
<text class="momey-text">¥{{ advancedFormat(showDetail.price) }}</text>
<text class="unit-text">/</text>
</view>
</view>
<view style="display: flex;flex-wrap: wrap;margin-top: 15rpx;">
<view class="tag" :key="index" v-for="(item,index) in showDetail.tagList">
<text class="tag-text">{{ item }}</text>
</view>
</view>
<view style="margin-left: 5rpx;margin-top: 15rpx;">
<text class="showjj">{{showDetail.introduction}}</text>
</view>
</view>
<view style="position: absolute; bottom: 20rpx; left: 0; right: 0;">
<view style="display: flex;justify-content: space-evenly;align-items: center;">
<view style="display: flex;flex-direction: column;">
<view>
<text class="service-time-text-big">{{ showDetail.workExperience }}+</text>
<text class="service-time-text-mini">小时</text>
</view>
<view
style="margin-top: 10rpx;display: flex;justify-content: center;align-items: center;">
<image src="/static/n-zx/l-icon.png" style="width: 25rpx;height: 36rpx;"></image>
<text class="service-text" style="margin-left: 6rpx;">咨询经验</text>
</view>
</view>
<view style="height: 92rpx;width: 2rpx;background: #DFD6D3;">
</view>
<view style="display: flex;flex-direction: column;">
<view>
<text
class="service-time-text-big">{{ preciseYearDiff(showDetail.workStartTime) }}+</text>
<text class="service-time-text-mini"></text>
</view>
<view
style="margin-top: 10rpx;display: flex;justify-content: center;align-items: center;">
<image src="/static/n-zx/r-icon.png" style="width: 25rpx;height: 36rpx;"></image>
<text class="service-text" style="margin-left: 6rpx;">从业年限</text>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 简介 可约时间 预约须知 -->
<view id="section1" style="display: flex;align-items: center;margin-top: 20rpx;flex-direction: column;">
<view
style="background-color:#FFFFFF;border-radius: 10rpx;width: 670rpx;display:flex;flex-direction: column;">
<view style="display: flex;align-items: center;justify-content: space-around;margin-top: 29rpx;">
<view v-for="(item,index) in types" :key="index"
style="display: flex;flex-direction: column;justify-content: center;align-items: center;"
@click="choice(item)">
<text :class="{ 'choice-text': choiceTypeCode==item.code,
'no-choice-text': choiceTypeCode!=item.code }">{{ item.name }}
</text>
<view :class="{'choice-icon':choiceTypeCode==item.code}"></view>
</view>
</view>
<view style="padding: 28rpx;">
<rich-text :nodes="showDetail.resume"></rich-text>
</view>
</view>
<view id="section2" style="width: 670rpx;border-radius: 10rpx;background-color: #FFFFFF;display: flex;flex-direction: column;
margin-top: 20rpx;padding-top: 28rpx;padding-bottom: 40rpx;">
<view style="display: flex;padding-left: 40rpx;align-items: baseline;">
<text class="reservation-title-text-large">可预约时间</text>
<text class="reservation-title-text-mini" style="margin-left: 10rpx;">(北京时间)</text>
</view>
<text class="reservation-title-text-mini"
style="padding-left: 40rpx;padding-right: 40rpx;">如果暂时无法确定具体的咨询时间可以在预约成功后再与咨询师协商
</text>
<view style="background: #F37155;width: 100%;height: 64rpx;border-top-right-radius:10rpx ;
border-top-left-radius:10rpx ;margin-top: 20rpx;display: flex;align-items: center;">
<text class="reservation-time-month" style="margin-left:54rpx ;">
{{ yearMonth }}
</text>
</view>
<view
style="padding-left: 10rpx;padding-right: 10rpx;display:flex;flex-direction: column;align-items: center;">
<view class="row">
<view class="row-unit">
<text class="reservation-time-week"></text>
</view>
<view class="row-unit">
<text class="reservation-time-week"></text>
</view>
<view class="row-unit">
<text class="reservation-time-week"></text>
</view>
<view class="row-unit">
<text class="reservation-time-week"></text>
</view>
<view class="row-unit">
<text class="reservation-time-week"></text>
</view>
<view class="row-unit">
<text class="reservation-time-week"></text>
</view>
<view class="row-unit">
<text class="reservation-time-week"></text>
</view>
</view>
<view class="line"></view>
<view style="display: flex;flex-wrap: wrap;width: 630rpx;">
<view style="height: 104rpx;width: 90rpx;" :key="idx" v-for="(item,idx) in allDates">
<view class="day-unit" v-if="item.dateStr"
:style="{'backgroundColor': idx == choiceIdx?'#F37155':'transparent' }"
@click="choiceDay(idx,item)">
<text class="reservation-time-day"
:style="{'color': idx == choiceIdx?'#FFFFFF':'#000' }">{{ item.nextMonth? item.nextMonth:item.day }}
</text>
<text class="reservation-time-day-text" v-if="item.remainNum"
:style="{'color': idx == choiceIdx?'#FFFFFF':'#F37155' }">
{{ item.remainNum }}
</text>
<text class="reservation-time-day-text" v-else
:style="{'color': idx == choiceIdx?'#FFFFFF':'#9B918C' }">-
</text>
</view>
</view>
</view>
</view>
</view>
<view id="section3"
style="width: 670rpx;border-radius: 10rpx;background-color: #FFFFFF;display: flex;flex-direction: column;box-sizing: border-box;
align-items: center;justify-content: center;
margin-top: 20rpx;padding-top: 20rpx;padding-bottom: 14rpx;padding-left: 34rpx;padding-right: 34rpx;margin-bottom: 196rpx;">
<text class="reservation-notice-text">预约须知</text>
<rich-text :nodes="procotol.context" style="margin-top: 34rpx;"></rich-text>
</view>
</view>
</view>
<view
style="width: 750rpx;position: fixed;height:176rpx ;background-color: #FFFFFF;display: flex;align-items: center;bottom: 0;left: 0;">
<view style="margin-left: 50rpx;width: 100rpx;display: flex;flex-direction: column;align-items: center;"
@click="focusDoctor">
<image v-if="!showDetail.focus" src="/static/n-zx/gz.png" style="width: 43.4rpx;height: 40.02rpx;">
</image>
<image v-if="showDetail.focus" src="/static/n-menu/wd-c.png" style="width: 46rpx;height: 48rpx;">
</image>
<text v-if="!showDetail.focus" class="operate-text" style="margin-top: 6rpx;">关注</text>
<text v-if="showDetail.focus" class="operate-text" style="margin-top: 6rpx;">取消关注</text>
</view>
<view
style="width: 50rpx;display: flex;flex-direction: column;align-items: center;margin-left: 84rpx;">
<button plain class="contact-btn" open-type="contact">
<image src="/static/n-zx/kf.png" style="width: 40rpx;height: 40rpx;"></image>
</button>
<text class="operate-text" style="margin-top: 6rpx;">客服</text>
</view>
<view style="margin-left: 100rpx;width: 250rpx;height: 86rpx;border-radius: 2002rpx;
background: linear-gradient(180deg, #E84B2A 0%, #FF846A 100%);display: flex; flex-direction: column;
justify-content: center; align-items: center; padding: 8rpx 40rpx;" @click="toAppoint">
<text class="button-text">立即预约</text>
</view>
</view>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
import myNav from '@/components/my-nav.vue'
import {
urls
} from '@/util/apiUrl.js'
import dohttp from '@/util/requestConfig.js'
const choiceTypeCode = ref("code1")
const choiceIdx = ref()
const backBtnHeight = ref()
const backBtnImgWidth = ref()
const types = ref([{
code: "code1",
name: '个人简介'
},
{
code: "code2",
name: '可约时间'
},
{
code: "code3",
name: '预约须知'
},
])
const doctorId = ref()
const choiceDayFlag = ref(1)
const procotol=ref()
const getProcotol = () => {
dohttp.post(urls.getByCode + "?code=psychological_show_appointment_agreement").then((result) => {
procotol.value = result
})
}
const choiceDay = (idx, item) => {
choiceIdx.value = idx
if (item.remainNum) {
choiceDayFlag.value = 2
} else {
choiceDayFlag.value = 1
}
}
const onShareAppMessage = () => {
return {
title: '快来体验一下吧~',
path: '/pages/index/index', // 分享的路径
};
};
const onShareTimeline = () => {
return {
title: '快来体验一下吧~',
query: 'id=21', // 分享路径的参数
};
};
const choice = (item) => {
let sectionId = null
if (item.code == 'code1') {
sectionId = "#section1"
}
if (item.code == 'code2') {
sectionId = "#section2"
}
if (item.code == 'code3') {
sectionId = "#section3"
}
choiceTypeCode.value = item.code
if (sectionId) {
uni.pageScrollTo({
selector: sectionId, // 目标元素选择器
duration: 300, // 滚动动画时长
success: () => {
console.log('滚动成功')
}
})
}
}
const toBack = () => {
uni.navigateBack({
delta: 1
})
}
onMounted(() => {
const systemInfo = uni.getSystemInfoSync()
// 胶囊位置信息
let rect = uni.getMenuButtonBoundingClientRect();
let navBarHeight = (rect.top - systemInfo.statusBarHeight) * 2 + rect.height;
backBtnHeight.value = navBarHeight
backBtnImgWidth.value = navBarHeight * 0.3
getProcotol()
})
const todayWeekdayIndex = ref(0)
onLoad((option) => {
const currentDate = new Date();
todayWeekdayIndex.value = currentDate.getDay()
// 必须根据今天星期几先插入相关数据 构建数据 让日历对齐
for (var i = 0; i < todayWeekdayIndex.value; i++) {
allDates.value.push({
dateStr: "",
day: ""
})
}
getYearAndMonth()
getOneMonth()
if (option && option.id) {
doctorId.value = option.id
getDetail(option.id)
getAppointDate()
} else {
uni.showModal({
title: '数据异常',
content: '页面数据发生异常,即将返回主页',
showCancel: false,
success: function (res) {
if (res.confirm) {
uni.reLaunch({
url: "/pages/index/index"
})
}
}
})
}
})
const getDateRange = (start, end) => {
const dates = [];
let current = new Date(start);
while (current <= end) {
let dateStr = formatDate(new Date(current))
dates.push(dateStr);
current.setDate(current.getDate() + 1);
}
return dates;
}
const formatDate = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
let obj = {
dateStr: `${year}-${month}-${day}`,
day: `${day}`
}
if(day=="01"&&currentMonth.value!=month){
obj.nextMonth = Number(month)+"月";
}
return obj;
}
const allDates = ref([])
const getOneMonth = () => {
const startDate = new Date();
const endDate = new Date(startDate);
endDate.setMonth(startDate.getMonth() + 1);
allDates.value.push(...getDateRange(startDate, endDate));
allDates.value.length = allDates.value.length - todayWeekdayIndex.value
}
const havingTime = ref(true)
const getAppointDate = () => {
let queryData = {
doctorId: doctorId.value
}
dohttp.post(urls.appointTotalList, queryData).then((result) => {
if (result&&result.length>0) {
const map = new Map(result.map(item => [item.scheduleDate, item]));
allDates.value.forEach(ele => {
if (ele.dateStr && map.get(ele.dateStr)) {
ele.remainNum = map.get(ele.dateStr).remainNum
}
})
}else{
havingTime.value = false
}
})
}
const yearMonth = ref()
const currentMonth=ref("")
const getYearAndMonth = () => {
// 创建Date对象
const currentDate = new Date();
// 获取年份(四位数)
const year = currentDate.getFullYear(); // 例如2025[1,2,3](@ref)
// 获取月份(0-11),需+1并补零
const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // 例如04[1,4,7](@ref)
// 拼接为“YYYY-MM”格式
yearMonth.value = `${year}${month}`;
currentMonth.value = month
}
const showDetail = ref({})
const getDetail = (id) => {
dohttp.get(urls.getDoctorInfo + "?id=" + id).then((result) => {
if (result) {
if (result.tags) {
let tags = result.tags.split(",")
result.tagList = tags
}
showDetail.value = result
} else {
uni.showModal({
title: '数据异常',
content: '未获取到该咨询师信息,请选择其他咨询师',
showCancel: false,
success: function (res) {
if (res.confirm) {
uni.reLaunch({
url: "/pages/consult/consult"
})
}
}
})
}
})
}
const advancedFormat = (num) => {
const divided = Number(num) / 100;
// 转换为字符串处理科学计数法(如1e-7)
const str = divided.toLocaleString('en-US', {
maximumFractionDigits: 2,
useGrouping: false
});
return str.replace(/(\.\d*?[1-9])0+$/, '$1').replace(/\.$/, '');
}
const preciseYearDiff = (dateStr) => {
const target = new Date(dateStr);
const now = new Date();
// 计算完整年数
let years = now.getFullYear() - target.getFullYear();
// 检查月份和日期是否已超过
const monthDiff = now.getMonth() - target.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && now.getDate() < target.getDate())) {
years--;
}
// 极端情况处理:闰年2月29日
if (target.getMonth() === 1 && target.getDate() === 29) {
const isLeapYear = (year) => (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
if (!isLeapYear(now.getFullYear()) && now.getMonth() === 1 && now.getDate() === 28) {
years++; // 闰日未到但当年非闰年时特殊处理
}
}
return years;
}
const focusDoctor = () => {
if (!showDetail.value.focus) {
dohttp.post(urls.focus + "?id=" + doctorId.value).then((result) => {
getDetail(doctorId.value)
})
} else {
dohttp.post(urls.cancelFocus + "?id=" + doctorId.value).then((result) => {
getDetail(doctorId.value)
})
}
}
const toAppoint = () => {
if(!havingTime.value){
uni.showModal({
title: '暂无排班',
content: '该咨询师暂无排班信息,请选择其他咨询师',
showCancel: false,
success: function (res) {
if (res.confirm) {
uni.reLaunch({
url: "/pages/consult/consult"
})
}
}
})
return
}
dohttp.navigateTo("/pages/appoint/appoint?id=" + doctorId.value + "&choiceDayFlag=" + choiceDayFlag.value)
}
</script>
<style scoped>
.content {
width: 100vw;
height: fit-content;
background-color: #F9F6F5;
display: flex;
flex-direction: column;
font-family: SourceHanSans;
box-sizing: border-box;
}
.header-detail {
width: 670rpx;
min-height: 378rpx;
border-radius: 10rpx;
opacity: 1;
/* 自动布局 */
display: flex;
flex-direction: column;
background: #FFFFFF;
margin-top: -26rpx;
}
.header-detail-top {
height: 60rpx;
border-radius: 10rpx 10rpx 0px 0px;
background: linear-gradient(180deg, #FFE7D9 0%, rgba(255, 255, 255, 0) 98%);
}
.header-detail-show {
display: flex;
flex-direction: column;
padding-left: 40rpx;
padding-right: 40rpx;
padding-bottom: 26rpx;
margin-top: -26rpx;
}
.name-class {
font-family: SourceHanSansBold;
font-size: 40rpx;
line-height: normal;
text-align: center;
letter-spacing: 0px;
font-variation-settings: "opsz" auto;
color: #614D49;
}
.school-class {
font-family: SourceHanSans;
font-size: 24rpx;
font-weight: normal;
line-height: normal;
letter-spacing: 0px;
font-variation-settings: "opsz" auto;
/* 正文色/正文辅助4 */
color: #9B918C;
margin-left: 18rpx;
}
.momey-text {
color: #F37155;
/* 标题/标题二加粗 */
font-family: SourceHanSansBold;
font-size: 38rpx;
font-weight: 700;
font-variation-settings: "opsz" auto;
}
.unit-text {
/* 正文/正文文本小号加粗 */
font-family: SourceHanSans;
font-weight: 700;
font-size: 22rpx;
font-variation-settings: "opsz" auto;
color: #F37155;
}
.tag {
border-radius: 80rpx;
opacity: 1;
/* 自动布局 */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 4rpx 14rpx;
background: linear-gradient(270deg, #FFF0E7 0%, #FFCEC4 97%);
margin-left: 5rpx;
}
.tag-text {
/* 正文/正文文本超小 */
/* 样式描述:辅助信息 */
font-family: SourceHanSans;
font-size: 16rpx;
font-weight: normal;
line-height: normal;
text-align: center;
letter-spacing: 0px;
/* 背景色/浅色背景2 */
color: #303044;
}
.service-time-text-big {
color: #303044;
font-family: SourceHanSansBold;
font-size: 40rpx;
font-variation-settings: "opsz" auto;
}
.service-time-text-mini {
color: #303044;
font-family: SourceHanSansBold;
font-size: 22rpx;
font-variation-settings: "opsz" auto;
}
.service-text {
font-family: SourceHanSans;
font-size: 24rpx;
font-weight: normal;
line-height: normal;
text-align: center;
letter-spacing: 0px;
font-variation-settings: "opsz" auto;
/* 背景色/浅色背景2 */
color: #303044;
}
.no-choice-text {
/* 按钮/按钮文本中号 */
font-family: SourceHanSans;
font-size: 28rpx;
font-weight: normal;
line-height: normal;
letter-spacing: 0px;
font-variation-settings: "opsz" auto;
/* 背景色/浅色背景2 */
color: #9B918C;
}
.choice-text {
/* 样式描述:常规大按钮 */
font-family: SourceHanSansBold;
font-size: 28rpx;
line-height: normal;
letter-spacing: 0px;
/* 背景色/深色大背景 */
color: #614D49;
z-index: 0;
}
.choice-icon {
width: 60%;
height: 10rpx;
border-radius: 424rpx;
background: #F37155;
}
.reservation-title-text-large {
font-family: SourceHanSansBold;
font-size: 36rpx;
line-height: normal;
text-align: center;
letter-spacing: 0px;
font-variation-settings: "opsz" auto;
/* 背景色/浅色背景2 */
color: #614D49;
}
.reservation-title-text-mini {
font-family: SourceHanSans;
font-size: 20rpx;
font-weight: normal;
line-height: normal;
letter-spacing: 0px;
font-variation-settings: "opsz" auto;
/* 正文色/正文辅助4 */
color: #9B918C;
}
.reservation-time-month {
font-family: SourceHanSansBold;
font-size: 28rpx;
line-height: normal;
text-align: center;
letter-spacing: 0px;
font-variation-settings: "opsz" auto;
color: #FFFFFF;
}
.reservation-time-week {
font-family: SourceHanSans;
font-size: 30rpx;
font-weight: normal;
line-height: 36rpx;
letter-spacing: 0px;
color: #000000;
}
.line {
width: 670rpx;
height: 0px;
opacity: 1;
border: 2rpx solid #EDEDED;
}
.row-unit {
width: 90rpx;
display: flex;
justify-content: center;
align-items: center;
}
.row {
width: 630rpx;
display: flex;
height: 76rpx;
}
.reservation-time-day {
font-family: SourceHanSans;
font-size: 30rpx;
font-weight: normal;
line-height: 36rpx;
letter-spacing: 0px;
color: #000000;
}
.reservation-time-day-text {
font-family: SourceHanSans;
font-size: 20rpx;
font-weight: normal;
line-height: normal;
letter-spacing: 0px;
font-variation-settings: "opsz" auto;
/* 正文色/正文色辅助3 */
color: #9B918C;
}
.reservation-time-today {
font-family: SourceHanSans;
font-size: 30rpx;
font-weight: normal;
line-height: 36rpx;
letter-spacing: 0rpx;
/* 今天 */
color: #186BF9
}
.day-unit {
border-radius: 10rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
}
.reservation-notice-text {
font-family: SourceHanSansBold;
font-size: 28rpx;
line-height: normal;
text-align: center;
letter-spacing: 0px;
font-variation-settings: "opsz" auto;
color: #614D49;
}
.operate-text {
font-family: SourceHanSans;
font-size: 24rpx;
font-weight: 500;
line-height: 36rpx;
letter-spacing: 0rpx;
/* 正文色/正文色辅助3 */
color: #9B918C;
}
.button-text {
font-family: SourceHanSansBold;
font-size: 36rpx;
line-height: normal;
letter-spacing: 0px;
/* 正文色/正文色 */
color: #FFFFFF;
}
.back-btn {
width: 750rpx;
position: absolute;
background-color: rgba(255, 255, 255, 0.3);
display: flex;
align-items: center;
top: 0;
left: 0;
margin-top: 54px;
width: 40px;
border-radius: 50%;
margin-left: 24rpx;
display: flex;
justify-content: center;
align-items: center;
}
.contact-btn {
border: none;
outline: none;
width: 40rpx;
height: 40rpx;
padding: 0rpx;
margin: 0rpx;
background-color: transparent;
display: flex;
align-items: center;
background-color: #fff;
}
.showjj{
font-family: SourceHanSans;
font-size: 24rpx;
font-weight: normal;
line-height: normal;
letter-spacing: 0px;
font-variation-settings: "opsz" auto;
color: #9B918C;
display: -webkit-box; /* 使用 flex 布局 */
-webkit-box-orient: vertical; /* 垂直排列 */
-webkit-line-clamp: 3; /* 限制显示行数 */
overflow: hidden; /* 超出部分隐藏 */
text-overflow: ellipsis; /* 超出部分显示省略号 */
}
</style>