525266afaf
- 新增 pages/quit-plan/index.vue 戒烟计划页面 - 展示30天戒烟计划总览和进度 - 显示当前阶段(记录期/减量期/巩固期) - 展示每日目标和建议 - 支持生成计划和重置计划功能 - 在 api/smoke.js 添加相关 API 调用 - 在 pages.json 注册路由
824 lines
17 KiB
Vue
824 lines
17 KiB
Vue
<template>
|
||
<view class="page">
|
||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
||
|
||
<view class="container">
|
||
<view v-if="pageLoading" class="skeleton">
|
||
<view class="skeleton-card"></view>
|
||
<view class="skeleton-card"></view>
|
||
<view class="skeleton-list">
|
||
<view v-for="i in 3" :key="i" class="skeleton-row"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-else>
|
||
<!-- 无计划状态 -->
|
||
<view v-if="!planData" class="no-plan-card">
|
||
<view class="no-plan-icon">📋</view>
|
||
<text class="no-plan-title">暂无戒烟计划</text>
|
||
<text class="no-plan-desc">生成专属30天戒烟计划,按阶段轻松戒烟</text>
|
||
<view class="generate-btn" @tap="handleGenerate">
|
||
<text class="generate-btn-text">生成戒烟计划</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 有计划状态 -->
|
||
<view v-else>
|
||
<!-- 计划总览卡片 -->
|
||
<view class="stage-card">
|
||
<view class="stage-badge">第 {{ currentDay }}/30 天</view>
|
||
<text class="stage-label">戒烟计划进度</text>
|
||
<text class="stage-name">{{ stageName }}</text>
|
||
<text class="stage-days">{{ stageDesc }}</text>
|
||
<view class="stage-progress-row">
|
||
<text class="stage-progress-label">计划进度</text>
|
||
<text class="stage-progress-value">{{ Math.round(planProgress * 100) }}%</text>
|
||
</view>
|
||
<view class="stage-progress-bar">
|
||
<view class="stage-progress-fill" :style="{ width: planProgress * 100 + '%' }"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 阶段说明卡片 -->
|
||
<view class="section">
|
||
<view class="section-header">
|
||
<text class="section-title">当前阶段</text>
|
||
</view>
|
||
<view class="stage-info-card">
|
||
<view class="stage-item" :class="{ 'stage-item-active': planData.current_stage === 'recording' }">
|
||
<view class="stage-number">1</view>
|
||
<view class="stage-content">
|
||
<text class="stage-item-title">记录期</text>
|
||
<text class="stage-item-desc">记录每日吸烟情况,了解习惯</text>
|
||
</view>
|
||
</view>
|
||
<view class="stage-line"></view>
|
||
<view class="stage-item" :class="{ 'stage-item-active': planData.current_stage === 'reducing' }">
|
||
<view class="stage-number">2</view>
|
||
<view class="stage-content">
|
||
<text class="stage-item-title">减量期</text>
|
||
<text class="stage-item-desc">逐步减少吸烟数量</text>
|
||
</view>
|
||
</view>
|
||
<view class="stage-line"></view>
|
||
<view class="stage-item" :class="{ 'stage-item-active': planData.current_stage === 'consolidating' }">
|
||
<view class="stage-number">3</view>
|
||
<view class="stage-content">
|
||
<text class="stage-item-title">巩固期</text>
|
||
<text class="stage-item-desc">保持成果,彻底戒烟</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 每日目标和建议 -->
|
||
<view class="section">
|
||
<view class="section-header">
|
||
<text class="section-title">每日目标</text>
|
||
<text class="section-badge">{{ todayTarget }}</text>
|
||
</view>
|
||
<view class="daily-tips-card">
|
||
<text class="daily-tips-title">今日建议</text>
|
||
<text class="daily-tips-text">{{ dailyTip }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 每日计划列表 -->
|
||
<view class="section">
|
||
<view class="section-header">
|
||
<text class="section-title">每日计划详情</text>
|
||
</view>
|
||
<view v-if="daysLoading" class="days-loading">
|
||
<text class="days-loading-text">加载中...</text>
|
||
</view>
|
||
<view v-else-if="daysList.length > 0" class="days-list">
|
||
<view
|
||
v-for="day in daysList"
|
||
:key="day.day"
|
||
class="day-item"
|
||
:class="{ 'day-item-today': day.isToday, 'day-item-past': day.isPast }"
|
||
@tap="showDayDetail(day)"
|
||
>
|
||
<view class="day-header">
|
||
<text class="day-number">第 {{ day.day }} 天</text>
|
||
<text v-if="day.isToday" class="day-today-badge">今天</text>
|
||
<text v-else-if="day.isPast" class="day-past-badge">已完成</text>
|
||
</view>
|
||
<view class="day-target">
|
||
<text class="day-target-label">目标:</text>
|
||
<text class="day-target-value">{{ day.target_cigs }} 支</text>
|
||
</view>
|
||
<view v-if="day.tip" class="day-tip">
|
||
<text class="day-tip-text">{{ day.tip }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view v-else class="days-empty">
|
||
<text class="days-empty-text">暂无计划详情</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 操作按钮 -->
|
||
<view class="actions">
|
||
<view class="reset-btn" @tap="handleReset">
|
||
<text class="reset-btn-text">重置计划</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 每日详情弹窗 -->
|
||
<view v-if="showDayModal" class="modal-mask" @tap="closeDayModal">
|
||
<view class="modal-content" @tap.stop>
|
||
<view class="modal-header">
|
||
<text class="modal-title">第 {{ selectedDay.day }} 天计划</text>
|
||
<text class="modal-close" @tap="closeDayModal">×</text>
|
||
</view>
|
||
<view class="modal-body">
|
||
<view class="modal-item">
|
||
<text class="modal-label">目标吸烟量</text>
|
||
<text class="modal-value">{{ selectedDay.target_cigs }} 支</text>
|
||
</view>
|
||
<view v-if="selectedDay.tip" class="modal-item">
|
||
<text class="modal-label">建议</text>
|
||
<text class="modal-value">{{ selectedDay.tip }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { onShareAppMessage } from '@dcloudio/uni-app'
|
||
import { useLogin } from '@/hooks/useLogin'
|
||
import * as api from '@/api'
|
||
|
||
const { waitForLogin } = useLogin()
|
||
|
||
const statusBarHeight = ref(0)
|
||
const pageLoading = ref(true)
|
||
const daysLoading = ref(false)
|
||
const generating = ref(false)
|
||
|
||
const planData = ref(null)
|
||
const daysList = ref([])
|
||
|
||
const showDayModal = ref(false)
|
||
const selectedDay = ref({})
|
||
|
||
// 阶段名称映射
|
||
const stageNames = {
|
||
recording: '记录期',
|
||
reducing: '减量期',
|
||
consolidating: '巩固期'
|
||
}
|
||
|
||
const stageDescs = {
|
||
recording: '记录每日吸烟情况,了解您的吸烟习惯',
|
||
reducing: '按计划逐步减少吸烟数量',
|
||
consolidating: '保持戒烟成果,彻底摆脱烟瘾'
|
||
}
|
||
|
||
// 计算当前是第几天
|
||
const currentDay = computed(() => {
|
||
if (!planData.value?.plan_start_date) return 1
|
||
const start = new Date(planData.value.plan_start_date)
|
||
const now = new Date()
|
||
const diff = Math.floor((now - start) / (24 * 60 * 60 * 1000))
|
||
return Math.min(Math.max(diff + 1, 1), 30)
|
||
})
|
||
|
||
// 计划进度
|
||
const planProgress = computed(() => {
|
||
return currentDay.value / 30
|
||
})
|
||
|
||
// 当前阶段名称
|
||
const stageName = computed(() => {
|
||
if (!planData.value) return ''
|
||
return stageNames[planData.value.current_stage] || '记录期'
|
||
})
|
||
|
||
// 阶段描述
|
||
const stageDesc = computed(() => {
|
||
if (!planData.value) return ''
|
||
return stageDescs[planData.value.current_stage] || ''
|
||
})
|
||
|
||
// 今日目标
|
||
const todayTarget = computed(() => {
|
||
const today = daysList.value.find(d => d.isToday)
|
||
return today ? `${today.target_cigs} 支` : '--'
|
||
})
|
||
|
||
// 每日建议
|
||
const dailyTip = computed(() => {
|
||
const today = daysList.value.find(d => d.isToday)
|
||
return today?.tip || '按计划执行,保持决心!'
|
||
})
|
||
|
||
// 获取戒烟计划
|
||
async function fetchQuitPlan() {
|
||
try {
|
||
const res = await api.getQuitPlan()
|
||
planData.value = res?.data || null
|
||
if (planData.value?.id) {
|
||
await fetchDays()
|
||
}
|
||
} catch (e) {
|
||
console.error('fetchQuitPlan error:', e)
|
||
planData.value = null
|
||
}
|
||
}
|
||
|
||
// 获取每日计划
|
||
async function fetchDays() {
|
||
if (!planData.value?.id) return
|
||
|
||
daysLoading.value = true
|
||
try {
|
||
const res = await api.getQuitPlanDays(planData.value.id)
|
||
const days = res?.data || []
|
||
|
||
// 计算今天的日期
|
||
const today = new Date()
|
||
today.setHours(0, 0, 0, 0)
|
||
|
||
daysList.value = days.map(day => {
|
||
const dayDate = new Date(planData.value.plan_start_date)
|
||
dayDate.setDate(dayDate.getDate() + day.day - 1)
|
||
dayDate.setHours(0, 0, 0, 0)
|
||
|
||
return {
|
||
...day,
|
||
isToday: dayDate.getTime() === today.getTime(),
|
||
isPast: dayDate.getTime() < today.getTime()
|
||
}
|
||
})
|
||
} catch (e) {
|
||
console.error('fetchDays error:', e)
|
||
daysList.value = []
|
||
} finally {
|
||
daysLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 生成计划
|
||
async function handleGenerate() {
|
||
if (generating.value) return
|
||
|
||
generating.value = true
|
||
try {
|
||
await api.generateQuitPlan()
|
||
uni.showToast({
|
||
title: '计划生成成功',
|
||
icon: 'success'
|
||
})
|
||
await fetchQuitPlan()
|
||
} catch (e) {
|
||
uni.showToast({
|
||
title: e?.message || '生成失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
generating.value = false
|
||
}
|
||
}
|
||
|
||
// 重置计划
|
||
function handleReset() {
|
||
uni.showModal({
|
||
title: '确认重置',
|
||
content: '重置后将清除当前计划,重新开始,确定要重置吗?',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
await api.resetQuitPlan()
|
||
uni.showToast({
|
||
title: '计划已重置',
|
||
icon: 'success'
|
||
})
|
||
planData.value = null
|
||
daysList.value = []
|
||
} catch (e) {
|
||
uni.showToast({
|
||
title: e?.message || '重置失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 显示每日详情
|
||
function showDayDetail(day) {
|
||
selectedDay.value = day
|
||
showDayModal.value = true
|
||
}
|
||
|
||
// 关闭弹窗
|
||
function closeDayModal() {
|
||
showDayModal.value = false
|
||
}
|
||
|
||
// 初始化页面
|
||
async function initPage() {
|
||
pageLoading.value = true
|
||
try {
|
||
const sys = uni.getSystemInfoSync()
|
||
statusBarHeight.value = sys.statusBarHeight || 0
|
||
|
||
await waitForLogin()
|
||
await fetchQuitPlan()
|
||
} catch (e) {
|
||
console.error('initPage error:', e)
|
||
} finally {
|
||
pageLoading.value = false
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
initPage()
|
||
})
|
||
|
||
onShareAppMessage(() => {
|
||
return {
|
||
title: '戒烟助手 - 30天戒烟计划',
|
||
path: 'pages/index/index'
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.page {
|
||
min-height: 100vh;
|
||
background: linear-gradient(to bottom, #D1FAE5 0%, #F0FDF4 45%, #FFFFFF 100%);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.status-bar {
|
||
background: linear-gradient(to bottom, #D1FAE5, #E9FDF2);
|
||
}
|
||
|
||
.container {
|
||
padding: 24rpx 32rpx 180rpx;
|
||
}
|
||
|
||
.skeleton {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 24rpx;
|
||
}
|
||
|
||
.skeleton-card,
|
||
.skeleton-row {
|
||
background: linear-gradient(90deg, #E5E7EB 25%, #F3F4F6 50%, #E5E7EB 75%);
|
||
background-size: 200% 100%;
|
||
animation: shimmer 1.6s infinite;
|
||
}
|
||
|
||
.skeleton-card {
|
||
height: 260rpx;
|
||
border-radius: 24rpx;
|
||
}
|
||
|
||
.skeleton-list {
|
||
padding: 24rpx;
|
||
background-color: #FFFFFF;
|
||
border-radius: 24rpx;
|
||
}
|
||
|
||
.skeleton-row {
|
||
height: 92rpx;
|
||
border-radius: 18rpx;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.skeleton-row:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
@keyframes shimmer {
|
||
0% { background-position: -200% 0; }
|
||
100% { background-position: 200% 0; }
|
||
}
|
||
|
||
/* 无计划状态 */
|
||
.no-plan-card {
|
||
background-color: #FFFFFF;
|
||
border-radius: 28rpx;
|
||
padding: 60rpx 40rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
box-shadow: 0 10rpx 28rpx rgba(16, 185, 129, 0.12);
|
||
border: 2rpx solid #ECFDF3;
|
||
}
|
||
|
||
.no-plan-icon {
|
||
font-size: 80rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.no-plan-title {
|
||
font-size: 36rpx;
|
||
font-weight: 700;
|
||
color: #111827;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.no-plan-desc {
|
||
font-size: 26rpx;
|
||
color: #6B7280;
|
||
text-align: center;
|
||
margin-bottom: 40rpx;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.generate-btn {
|
||
background: linear-gradient(135deg, #10B981, #059669);
|
||
padding: 24rpx 60rpx;
|
||
border-radius: 48rpx;
|
||
box-shadow: 0 8rpx 20rpx rgba(16, 185, 129, 0.3);
|
||
}
|
||
|
||
.generate-btn-text {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
/* 阶段卡片 */
|
||
.stage-card {
|
||
background: #FFFFFF;
|
||
border-radius: 28rpx;
|
||
padding: 32rpx;
|
||
margin-bottom: 32rpx;
|
||
position: relative;
|
||
box-shadow: 0 10rpx 28rpx rgba(16, 185, 129, 0.12);
|
||
border: 2rpx solid #ECFDF3;
|
||
}
|
||
|
||
.stage-badge {
|
||
position: absolute;
|
||
top: 24rpx;
|
||
right: 24rpx;
|
||
background-color: #10B981;
|
||
color: #FFFFFF;
|
||
padding: 8rpx 20rpx;
|
||
border-radius: 20rpx;
|
||
font-size: 24rpx;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.stage-label {
|
||
font-size: 24rpx;
|
||
color: #059669;
|
||
display: block;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.stage-name {
|
||
font-size: 42rpx;
|
||
font-weight: 700;
|
||
color: #111827;
|
||
display: block;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.stage-days {
|
||
font-size: 24rpx;
|
||
color: #6B7280;
|
||
display: block;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.stage-progress-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.stage-progress-label {
|
||
font-size: 24rpx;
|
||
color: #6B7280;
|
||
}
|
||
|
||
.stage-progress-value {
|
||
font-size: 24rpx;
|
||
font-weight: 600;
|
||
color: #10B981;
|
||
}
|
||
|
||
.stage-progress-bar {
|
||
height: 12rpx;
|
||
background-color: #E5E7EB;
|
||
border-radius: 6rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.stage-progress-fill {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, #10B981, #34D399);
|
||
border-radius: 6rpx;
|
||
}
|
||
|
||
/* 阶段信息 */
|
||
.section {
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.section-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #111827;
|
||
}
|
||
|
||
.section-badge {
|
||
font-size: 24rpx;
|
||
color: #059669;
|
||
background-color: #ECFDF3;
|
||
padding: 8rpx 16rpx;
|
||
border-radius: 16rpx;
|
||
}
|
||
|
||
.stage-info-card {
|
||
background-color: #FFFFFF;
|
||
border-radius: 24rpx;
|
||
padding: 28rpx;
|
||
border: 2rpx solid #ECFDF3;
|
||
box-shadow: 0 8rpx 22rpx rgba(16, 185, 129, 0.08);
|
||
}
|
||
|
||
.stage-item {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.stage-item-active .stage-number {
|
||
background-color: #10B981;
|
||
color: #FFFFFF;
|
||
border-color: #10B981;
|
||
}
|
||
|
||
.stage-item-active .stage-item-title {
|
||
color: #10B981;
|
||
}
|
||
|
||
.stage-number {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
border-radius: 50%;
|
||
border: 3rpx solid #D1D5DB;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 26rpx;
|
||
font-weight: 600;
|
||
color: #6B7280;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.stage-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.stage-item-title {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #111827;
|
||
display: block;
|
||
margin-bottom: 6rpx;
|
||
}
|
||
|
||
.stage-item-desc {
|
||
font-size: 24rpx;
|
||
color: #6B7280;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.stage-line {
|
||
width: 2rpx;
|
||
height: 32rpx;
|
||
background-color: #E5E7EB;
|
||
margin: 16rpx 0 16rpx 22rpx;
|
||
}
|
||
|
||
/* 每日目标 */
|
||
.daily-tips-card {
|
||
background-color: #FFFFFF;
|
||
border-radius: 24rpx;
|
||
padding: 28rpx;
|
||
border: 2rpx solid #ECFDF3;
|
||
box-shadow: 0 8rpx 22rpx rgba(16, 185, 129, 0.08);
|
||
}
|
||
|
||
.daily-tips-title {
|
||
font-size: 26rpx;
|
||
font-weight: 600;
|
||
color: #059669;
|
||
display: block;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.daily-tips-text {
|
||
font-size: 28rpx;
|
||
color: #111827;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* 每日计划列表 */
|
||
.days-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.day-item {
|
||
background-color: #FFFFFF;
|
||
border-radius: 20rpx;
|
||
padding: 24rpx;
|
||
border: 2rpx solid #ECFDF3;
|
||
box-shadow: 0 6rpx 16rpx rgba(16, 185, 129, 0.08);
|
||
}
|
||
|
||
.day-item-today {
|
||
border-color: #10B981;
|
||
background: linear-gradient(135deg, #ECFDF5, #F0FDF4);
|
||
}
|
||
|
||
.day-item-past {
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.day-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.day-number {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #111827;
|
||
}
|
||
|
||
.day-today-badge {
|
||
font-size: 22rpx;
|
||
color: #FFFFFF;
|
||
background-color: #10B981;
|
||
padding: 4rpx 12rpx;
|
||
border-radius: 12rpx;
|
||
}
|
||
|
||
.day-past-badge {
|
||
font-size: 22rpx;
|
||
color: #6B7280;
|
||
background-color: #F3F4F6;
|
||
padding: 4rpx 12rpx;
|
||
border-radius: 12rpx;
|
||
}
|
||
|
||
.day-target {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.day-target-label {
|
||
font-size: 24rpx;
|
||
color: #6B7280;
|
||
}
|
||
|
||
.day-target-value {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #10B981;
|
||
}
|
||
|
||
.day-tip {
|
||
padding-top: 12rpx;
|
||
border-top: 1rpx solid #F3F4F6;
|
||
}
|
||
|
||
.day-tip-text {
|
||
font-size: 24rpx;
|
||
color: #6B7280;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.days-loading,
|
||
.days-empty {
|
||
background-color: #FFFFFF;
|
||
border-radius: 24rpx;
|
||
padding: 40rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.days-loading-text,
|
||
.days-empty-text {
|
||
font-size: 26rpx;
|
||
color: #6B7280;
|
||
}
|
||
|
||
/* 操作按钮 */
|
||
.actions {
|
||
margin-top: 32rpx;
|
||
padding-bottom: 40rpx;
|
||
}
|
||
|
||
.reset-btn {
|
||
background-color: #FFFFFF;
|
||
border: 2rpx solid #EF4444;
|
||
padding: 24rpx;
|
||
border-radius: 24rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.reset-btn-text {
|
||
font-size: 28rpx;
|
||
color: #EF4444;
|
||
}
|
||
|
||
/* 弹窗 */
|
||
.modal-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.modal-content {
|
||
background-color: #FFFFFF;
|
||
border-radius: 28rpx;
|
||
width: 600rpx;
|
||
max-height: 70vh;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 32rpx 28rpx 24rpx;
|
||
border-bottom: 1rpx solid #F3F4F6;
|
||
}
|
||
|
||
.modal-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #111827;
|
||
}
|
||
|
||
.modal-close {
|
||
font-size: 40rpx;
|
||
color: #9CA3AF;
|
||
line-height: 1;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 28rpx;
|
||
}
|
||
|
||
.modal-item {
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.modal-item:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.modal-label {
|
||
font-size: 24rpx;
|
||
color: #6B7280;
|
||
display: block;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.modal-value {
|
||
font-size: 28rpx;
|
||
color: #111827;
|
||
line-height: 1.5;
|
||
}
|
||
</style>
|