ufutx.dma.case.h5/src/views/UserDetailView.vue
2026-10-29 23:37:27 +08:00

476 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="user-base-line">
<div class="user-block">
<t-cell-group title="用户基本信息" >
<t-cell title="用户名" :note="`${user.name}`" class="line-start line-end" ></t-cell>
<t-cell title="性别" :note="`${user.sex == 1? '男':'女'}`" class="line-end" ></t-cell>
<t-cell title="出生年月日" :note="`${user.birthday}(${new Date().getFullYear() - +user.birthday?.slice(0, 4)}岁)`" class="line-end" ></t-cell>
<!-- <t-cell title="方案开始时间" :note="user.before?.day" class="line-end"></t-cell>
<t-cell title="方案结束时间" :note="user.after?.day" class="line-end"></t-cell> -->
</t-cell-group>
</div>
<t-cell-group title="用户标签" v-if="user.labelList">
<div class="user-label-line ">
<span class="user-label user-label-active" v-for="label in user.labelList">{{ label.label }}</span>
</div>
</t-cell-group>
<div class="user-block" v-if="user.before?.picList.length > 0">
<t-cell-group title="方案前照片">
<t-row style="margin:16px;">
<t-col span="8" class="dark" v-for="pic in user.before?.picList">
<t-image
class="user-pic-item"
fit="fill"
:lazy="true"
:src="pic"
@click="beforeVisible = true"
></t-image>
</t-col>
</t-row>
</t-cell-group>
<t-image-viewer v-model:images="beforePicList" :visible="beforeVisible" @close="beforeVisible = false">
<template #image="{ src, className, onLoad, onTransitionstart, onTransitionend, style }">
<div>
<img
:src="src"
:style="style"
:class="className"
@load="onLoad"
@transitionstart="onTransitionstart"
@transitionend="onTransitionend"
/>
</div>
</template>
</t-image-viewer>
</div>
<div class="user-block" v-if="user?.before?.videoList.length > 0">
<t-cell-group title="方案前视频">
<t-row style="margin:16px;">
<t-col span="8" class="dark" v-for="(video,index) in user.before?.videoList">
<div class="video-cover-wrapper">
<!-- 视频封面:使用 TDesign Image 组件 -->
<t-image
:src="video.cover"
fit="fill"
:lazy="true"
class="user-pic-item"
@click="openVideoPreview('before', index)"
>
</t-image>
<div class="play-mask">
<t-icon name="play-circle-filled" size="48px" />
</div>
</div>
</t-col>
</t-row>
</t-cell-group>
<!-- 预览弹窗 -->
<t-dialog
v-model:visible="visibleVideo"
header="视频预览"
:footer="false"
width="100vw"
close-btn
destroy-on-close
@close="handleVideoClose"
>
<video
v-if="visibleVideo"
ref="videoRef"
:src="videoUrl"
:poster="videoCover"
controls
autoplay
playsinline
style="width: 100%; height: auto; border-radius: 4px"
/>
</t-dialog>
</div>
<div class="user-block" v-if="user.after?.picList.length > 0">
<t-cell-group title="方案后照片">
<t-row style="margin:16px;">
<t-col span="8" class="dark" v-for="pic in user.after?.picList">
<t-image
class="user-pic-item"
fit="fill"
:lazy="true"
:src="pic"
@click="afterVisible = true"
></t-image>
</t-col>
</t-row>
</t-cell-group>
<t-image-viewer v-model:images="afterPicList" :visible="afterVisible" @close="afterVisible = false">
<template #image="{ src, className, onLoad, onTransitionstart, onTransitionend, style }">
<div>
<img
:src="src"
:style="style"
:class="className"
@load="onLoad"
@transitionstart="onTransitionstart"
@transitionend="onTransitionend"
/>
</div>
</template>
</t-image-viewer>
</div>
<div class="user-block" v-if="user.after?.videoList.length > 0">
<t-cell-group title="方案后视频">
<t-row style="margin:16px;">
<t-col span="8" class="dark" v-for="(video,index) in user.after?.videoList">
<div class="video-cover-wrapper">
<!-- 视频封面:使用 TDesign Image 组件 -->
<t-image
:src="video.cover"
fit="fill"
:lazy="true"
class="user-pic-item"
@click="openVideoPreview('after', index)"
>
</t-image>
<div class="play-mask">
<t-icon name="play-circle-filled" size="48px" />
</div>
</div>
</t-col>
</t-row>
</t-cell-group>
<!-- 预览弹窗 -->
</div>
<div v-if="user.weightList">
<t-cell-group title="体重变化" v-if="weightData">
<t-cell title="方案开始体重" :note="weightData.weightStart + '斤('+weightData.dayStart+')'"></t-cell>
<t-cell title="方案开始体重" :note="weightData.weightEnd + '斤('+weightData.dayEnd+')'"></t-cell>
<t-cell title="体重变化" :note="weightData.weightTotal + '斤(共'+weightData.dayTotal+'天)'"></t-cell>
<Canvas :style="{ width: '100%', height: '300px' }" v-if="weightChartData.length > 1">
<Chart :data="weightChartData" :scale="scaleConfig">
<Axis field="day" />
<Axis field="weight" />
<Line x="day" y="weight" />
</Chart>
</Canvas>
</t-cell-group>
</div>
<div v-if="user.comment">
<t-cell-group title="教练小结" v-if="user.comment">
<div class="comment">{{ user.comment }}</div>
</t-cell-group>
</div>
<div>
</div>
<div class="empty-bar"></div>
<div class="empty-bar"></div>
</div>
</template>
<script lang="ts" setup>
import { Icon as TIcon } from 'tdesign-icons-vue-next';
import { onMounted, ref } from 'vue';
import {useRoute, useRouter} from 'vue-router'
import Canvas from '@antv/f-vue';
import { Chart, Interval, Axis, Line } from '@antv/f2';
import {labelList, userList} from '../api/user'
const router = useRouter()
const route = useRoute()
const userID = ref("")
const beforeVisible = ref(false)
const afterVisible = ref(false)
const beforePicList = ref([])
const afterPicList = ref([])
const visibleVideo = ref(false)
const videoUrl = ref('')
const videoCover = ref('')
const weightData = ref({})
const weightChartData = ref([
{weight: 0, day: '2026-09-01'}
])
const openVideoPreview = (source, index) => {
visibleVideo.value = true
if (source == 'before'){
videoCover.value = user.value.before.videoList[index].cover
videoUrl.value = user.value.before.videoList[index].url
}else{
videoCover.value = user.value.after.videoList[index].cover
videoUrl.value = user.value.after.videoList[index].url
}
}
const handleVideoClose = () => {
visibleVideo.value = false
}
const user1 = ref({
username: '用户名 1',
id: 1,
sex: 1,
birthday: '1985-09-01',
before: {
day: '2026-09-01',
picList: [
'https://tdesign.gtimg.com/mobile/demos/example4.png',
'https://tdesign.gtimg.com/mobile/demos/example6.png',
'https://tdesign.gtimg.com/mobile/demos/example4.png',
'https://tdesign.gtimg.com/mobile/demos/example6.png',
'https://tdesign.gtimg.com/mobile/demos/example4.png',
'https://tdesign.gtimg.com/mobile/demos/example6.png',
'https://tdesign.gtimg.com/mobile/demos/example4.png',
],
videoList: [
{cover: 'https://tdesign.gtimg.com/mobile/demos/example4.png', url: 'https://lorem.video/bunny_720p_h264_30fps_20s_23crf_aac_192kbps.mp4'},
{cover: 'https://tdesign.gtimg.com/mobile/demos/example6.png', url: 'https://lorem.video/bunny_720p_h264_30fps_20s_23crf_aac_192kbps.mp4'},
{cover: 'https://tdesign.gtimg.com/mobile/demos/example4.png', url: 'https://lorem.video/bunny_720p_h264_30fps_20s_23crf_aac_192kbps.mp4'},
{cover: 'https://tdesign.gtimg.com/mobile/demos/example6.png', url: 'https://lorem.video/bunny_720p_h264_30fps_20s_23crf_aac_192kbps.mp4'},
{cover: 'https://tdesign.gtimg.com/mobile/demos/example4.png', url: 'https://lorem.video/bunny_720p_h264_30fps_20s_23crf_aac_192kbps.mp4'},
]
},
after: {
day: '2026-09-01',
picList: [
'https://tdesign.gtimg.com/mobile/demos/example4.png',
'https://tdesign.gtimg.com/mobile/demos/example6.png',
'https://tdesign.gtimg.com/mobile/demos/example4.png',
'https://tdesign.gtimg.com/mobile/demos/example6.png',
'https://tdesign.gtimg.com/mobile/demos/example4.png',
'https://tdesign.gtimg.com/mobile/demos/example6.png',
'https://tdesign.gtimg.com/mobile/demos/example4.png',
],
videoList: [
{cover: 'https://tdesign.gtimg.com/mobile/demos/example4.png', url: 'https://lorem.video/bunny_720p_h264_30fps_20s_23crf_aac_192kbps.mp4'},
{cover: 'https://tdesign.gtimg.com/mobile/demos/example6.png', url: 'https://lorem.video/bunny_720p_h264_30fps_20s_23crf_aac_192kbps.mp4'},
{cover: 'https://tdesign.gtimg.com/mobile/demos/example4.png', url: 'https://lorem.video/bunny_720p_h264_30fps_20s_23crf_aac_192kbps.mp4'},
{cover: 'https://tdesign.gtimg.com/mobile/demos/example6.png', url: 'https://lorem.video/bunny_720p_h264_30fps_20s_23crf_aac_192kbps.mp4'},
{cover: 'https://tdesign.gtimg.com/mobile/demos/example4.png', url: 'https://lorem.video/bunny_720p_h264_30fps_20s_23crf_aac_192kbps.mp4'},
]
},
labelList:[
'病症 1','病症病症 2','病症病症病症 3','病症病 4','病病症 5','病症 6','病症病症病症病症 7','病症 8','病症 9'
],
weightList:[
{day: '2026-09-01', weight: 180.4},
{day: '2026-09-02', weight: 175.5},
{day: '2026-09-03', weight: 169.6},
{day: '2026-09-04', weight: 158.7},
{day: '2026-09-05', weight: 154.8},
{day: '2026-09-06', weight: 152.9},
{day: '2026-09-07', weight: 145.1},
{day: '2026-09-08', weight: 143.2},
{day: '2026-09-09', weight: 140.3},
{day: '2026-09-10', weight: 138.4},
{day: '2026-09-11', weight: 135.5},
]
})
const user = ref({})
const setWeightData = (weightList) => {
const total = weightList.length
const weightStart = weightList[0].weight
const weightEnd = weightList[total - 1].weight
const dayStart = weightList[0].day
const dayEnd = weightList[total - 1].day
const weightTotal = (weightEnd - weightStart).toFixed(2)
weightData.value = {
weightStart : weightStart.toFixed(2),
weightEnd : weightEnd.toFixed(2),
dayStart : dayStart,
dayEnd: dayEnd,
dayTotal: total,
weightTotal: weightTotal,
}
}
onMounted(() => {
console.log('route', route)
console.log('router', router)
const id = route.params.id
userID.value = id
userList.forEach(u => {
console.log('u', u)
if (u.id == id){
user.value = u
console.log('user', user)
// 1. body_images 下所有值变成数组
u.body_images = Object.fromEntries(
Object.entries(u.body_images).map(([k, v]) => [k, [v]])
);
// 2. after_body_images 下所有值变成数组
u.after_body_images = Object.fromEntries(
Object.entries(u.after_body_images).map(([k, v]) => [k, [v]])
);
// 3. 新增 user.before.picList —— 取 body_images 的数组值
u.before = {
picList: Object.values(u.body_images)
};
// 4. 新增 user.after.picList —— 取 after_body_images 的数组值
u.after = {
picList: Object.values(u.after_body_images)
};
if (u.weightList){
weightChartData.value = u.weightList
beforePicList.value = u.before.picList
afterPicList.value = u.after.picList
setWeightData(u.weightList)
}
u.before.videoList = []
u.after.videoList = []
user.value = u
}
})
})
const scaleConfig = {
day: { range: [0, 1], tickCount: 5 },
};
</script>
<style lang="less">
.card {
position: relative;
margin: 16px;
border-radius: 6px;
// overflow: hidden;
box-sizing: border-box;
border: 1.5px solid var(--bg-color-demo, #fff);
border-color: var(--td-success-color-4);
}
.card--active {
border-color: var(--td-brand-color);
}
.card--active::after {
content: '';
display: block;
position: absolute;
left: 0;
top: 0;
width: 0;
border: 14px solid var(--td-success-color-4);
border-bottom-color: transparent;
border-right-color: transparent;
}
.card__icon {
color: var(--td-text-color-anti);
position: absolute;
left: 1.5px;
top: 1.5px;
z-index: 1;
}
/* 横向布局 */
.horizontal-box {
width: calc(100% - 32px);
display: flex;
align-items: start;
margin: 16px;
white-space: nowrap
}
.horizontal-box .card {
flex: 1;
margin: 0;
}
.horizontal-box .card + .card {
margin-left: 12px;
}
.horizontal-box .card::after {
border-width: 12px;
}
.horizontal-box .card__icon {
font-size: 12px;
}
.user-line{
width: calc(100vw - 8px);
margin: 4px;
}
.user-pic-item{
padding: 4px;
}
.line-start{
border-top: 1px solid var(--td-success-color-4);
}
.line-end{
border-bottom: 1px solid var(--td-success-color-4);
}
.user-line-block{
width: 50%;
}
.user-pic-block{
width: 100%;
}
.user-pic{
padding: 16px;
}
.user-day{
padding-top: 8px;
display: flex;
justify-content: center;
}
.user-label-line{
margin:16px;
}
.user-label{
margin: 4px;
padding: 4px 12px 4px 12px;
line-height: 36px;
border-radius: 16px;
color: #000;
white-space: nowrap;
border: 1px solid var(--td-success-color-4);
}
.user-label-active{
color: #fff;
background-color: var(--td-success-color-4);
}
.user-name-line{
border-bottom: 1px solid var(--td-success-color-4);
// border-radius: 8px;
}
.user-name{
border-radius: 8px;
}
.t-checkbox--block {
padding: 4px;
padding-left: 16px;
}
.video-cover-wrapper {
position: relative;
display: inline-block;
width: 100%;
}
.play-mask {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
// background: rgba(0, 0, 0, 0.35);
pointer-events: none; /* 让点击穿透到外层 */
// padding: 4px;
// margin: 4px;
}
.comment{
text-indent: 2em;
padding: 16px;
}
</style>