Files
medical-mall/pages/mall/admin/decoration/components/PhonePreview.uvue
2026-02-05 09:01:16 +08:00

738 lines
19 KiB
Plaintext
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>
<view class="preview-column">
<view class="phone-mockup">
<view class="phone-inner">
<!-- 5. 开屏广告预览 (1:1 全屏覆盖) -->
<view v-if="activeKey === 'ad'" class="mock-ad-preview-v2">
<image v-if="activeConfig.items.length > 0" :src="activeConfig.items[0].imageUrl || '/static/logo.png'" mode="aspectFill" class="ad-full-image-v2"></image>
<view v-else class="ad-placeholder-v2">
<text class="ad-ph-txt">开屏广告预览区</text>
</view>
<view class="ad-skip-btn-v2">
<text class="ad-skip-txt">跳过 3s</text>
</view>
</view>
<!-- 常规页面内容 -->
<block v-if="activeKey !== 'ad'">
<view class="status-bar-mock"></view>
<scroll-view class="phone-body" :scroll-y="true">
<!-- 轮播图预览 -->
<view class="banner-area" :key="activeKey + '-' + itemsVersion">
<swiper
v-if="activeConfig.items.length > 0"
:circular="true"
:autoplay="isAutoplay"
:interval="3000"
:duration="500"
@change="onSwiperChange"
>
<swiper-item v-for="(item, index) in activeConfig.items" :key="index">
<view class="swiper-image-wrapper">
<image
:src="item.imageUrl || '/static/logo.png'"
mode="aspectFill"
class="banner-img"
></image>
</view>
</swiper-item>
</swiper>
<!-- 空态占位 -->
<view v-else class="preview-banner-placeholder">
<view class="ph-content">
<text class="ph-icon">🖼️</text>
<text class="ph-txt">暂无图片,请添加~</text>
</view>
</view>
</view>
<!-- 不同分类对应的 Mock 内容 -->
<view class="mock-section" :class="{ 'bg-red': activeKey === 'group' }">
<!-- 1. 精品推荐/热门/新品/促销 (1:1 垂直列表复刻) -->
<view
v-if="['jingpin', 'hot', 'new', 'promo'].includes(activeKey)"
class="mock-product-list"
>
<!-- 1:1 复刻标题栏(带分割线) -->
<view class="section-title-standard">
<view class="title-line"></view>
<view class="title-center">
<text class="title-ic-standard">{{ activeKey === 'hot' ? '🔥' : (activeKey === 'new' ? '🆕' : '💎') }}</text>
<text class="title-txt-standard">{{ activeKey === 'jingpin' ? '精品推荐' : (activeKey === 'hot' ? '热门榜单' : (activeKey === 'new' ? '首发新品' : '促销单品')) }}</text>
</view>
<view class="title-line"></view>
</view>
<!-- 列表布局 -->
<view class="product-list-vertical">
<view class="product-list-item" v-for="(item, i) in 4" :key="i">
<!-- 商品图片(圆角) -->
<view class="p-img-left" :class="'p-img-mock-' + i"></view>
<!-- 商品详情 -->
<view class="p-info-right">
<view class="p-name-box-standard">
<text class="p-name-txt-standard">{{ getMockName(i) }}</text>
</view>
<view class="p-bottom-row-standard">
<view class="p-price-box-standard">
<text class="p-symbol-standard">¥</text>
<text class="p-val-standard">{{ getMockPrice(i) }}</text>
</view>
<text class="p-sales-standard">已售{{ getMockSales(i) }}件</text>
</view>
</view>
</view>
</view>
</view>
<!-- 2. 拼团 (1:1 复刻) -->
<view v-if="activeKey === 'group'" class="mock-group-list-red">
<!-- 参团人数提示 -->
<view class="group-participation-bar">
<view class="avatar-stack">
<view class="a-item a1"></view>
<view class="a-item a2"></view>
<view class="a-item a3"></view>
</view>
<text class="group-p-txt">252人参与</text>
</view>
<view class="group-item-card" v-for="i in 3" :key="i">
<view class="g-img-left" :class="'g-img-mock-' + i"></view>
<view class="g-info-right">
<view class="g-name-box-v2">
<text class="g-name-txt-v2">{{ getGroupMockName(i) }}</text>
</view>
<view class="g-price-row-v2">
<text class="g-p-orig-v2">¥{{ getGroupMockOrigPrice(i) }}</text>
<view class="g-p-main-v2">
<text class="g-p-sym-v2">¥</text>
<text class="g-p-val-v2">{{ getGroupMockPrice(i) }}</text>
</view>
</view>
<view class="g-action-row-v2">
<view class="g-label-count"><text class="g-lc-txt">2人团</text></view>
<view class="g-btn-v2"><text class="g-btn-txt-v2">去拼团</text></view>
</view>
</view>
</view>
</view>
<!-- 3. 积分商城1:1 复刻 -->
<view v-if="activeKey === 'points'" class="mock-points-mall-v2">
<!-- 导航金刚区 -->
<view class="points-nav-row-v2">
<view class="p-nav-item-v2" v-for="(nav, idx) in pointsNavs" :key="idx">
<view class="p-nav-ic-v2" :style="{ backgroundColor: nav.color }">
<text class="p-nav-ic-emoji">{{ nav.emoji }}</text>
</view>
<text class="p-nav-txt-v2">{{ nav.title }}</text>
</view>
</view>
<view class="points-divider-v2"></view>
<!-- 列表标题 -->
<view class="points-section-header">
<text class="ps-title-v2">大家都在换</text>
<view class="ps-more-v2">
<text class="ps-more-txt">查看更多</text>
<text class="ps-more-ic">></text>
</view>
</view>
<view class="points-grid-v2">
<view class="points-card-v2" v-for="i in 4" :key="i">
<view class="pc-img-v2" :class="'pc-img-mock-' + i"></view>
<view class="pc-info-v2">
<text class="pc-title-v2">小米蓝牙耳机新款横板耳机新款横板耳...</text>
<view class="pc-price-v2">
<text class="pc-points-v2">{{ 666 + i*11 }}积分</text>
</view>
<text class="pc-ex-count-v2">999人兑换</text>
</view>
</view>
</view>
</view>
<!-- 4. 登录页预览 -->
<view v-if="activeKey === 'login'" class="mock-login-view">
<view class="login-box">
<view class="login-logo-mock"></view>
<view class="login-input-mock"><text class="l-in-txt">请输入手机号</text></view>
<view class="login-input-mock"><text class="l-in-txt">请输入验证码</text></view>
<view class="login-btn-mock"><text class="l-btn-txt">立即登录</text></view>
</view>
</view>
</view>
</scroll-view>
</block>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref, computed, watch } from 'vue'
import { type ConfigData } from '@/pages/mall/admin/decoration/components/types.uts'
const props = defineProps<{
activeKey: string
activeLabel: string
activeConfig: ConfigData
}>()
// 用于 Swiper 刷新的 key
const itemsVersion = ref(0)
const currentIndex = ref(0)
// 只有当图片数量超过1张时开启自动轮播
const isAutoplay = computed(() => props.activeConfig.items.length >= 2)
// 监听数据变化,更新版本以强制 Swiper 重绘(处理排序和增删)
watch(() => props.activeConfig.items, () => {
itemsVersion.value++
}, { deep: true })
// 切换分类时重置状态
watch(() => props.activeKey, () => {
currentIndex.value = 0
itemsVersion.value++
})
const onSwiperChange = (e : any) => {
currentIndex.value = e.detail.current
}
// 积分商城导航数据
const pointsNavs = [
{ title: '我的积分', color: '#ffb400', emoji: '⭐' },
{ title: '每日签到', color: '#4facfe', emoji: '📅' },
{ title: '积分抽奖', color: '#f06292', emoji: '🎡' },
{ title: '兑换记录', color: '#ffa726', emoji: '📝' }
]
// Mock 数据辅助函数
const getMockName = (i: number): string => {
const names = [
'MIUCHO可爱卡通学生通勤手提电...',
'贝昂智能空气循环风扇家用落地电...',
'真力时 (ZENITH) 瑞士手表DEFY...',
'小米保温杯云米电热杯茶叶杯水杯...'
]
return names[i % names.length]
}
const getMockPrice = (i: number): string => {
const prices = ['158.00', '1299.00', '61000.00', '100.00']
return prices[i % prices.length]
}
const getMockSales = (i: number): string => {
const sales = ['5495', '2899', '1108', '100']
return sales[i % sales.length]
}
const getGroupMockName = (i: number): string => {
const names = [
'FOMIX 蛋壳椅 进口头层牛皮橙色单人沙发椅Egg chair设计师蛋...',
'UR2024夏季新款女装复古纯欲氛围感一字肩短款T恤UWG440...',
'阿迪达斯官网 adidas BBALL CAP COT 男女训练运动帽子FO5270...',
'雅诗兰黛小棕瓶精华液 50ml'
]
return names[i % names.length]
}
const getGroupMockPrice = (i: number): string => {
const prices = ['999', '99', '77', '499']
return prices[i % prices.length]
}
const getGroupMockOrigPrice = (i: number): string => {
const prices = ['7580', '129', '100', '890']
return prices[i % prices.length]
}
</script>
<style scoped lang="scss">
.preview-column {
width: 420px;
background-color: #f7f8fa;
display: flex;
justify-content: center;
padding: 40px;
border-right: 1px solid #f0f0f0;
}
.phone-mockup {
width: 320px;
height: 640px;
background-color: #000;
border-radius: 36px;
padding: 12px;
box-shadow: 0 20px 40px rgba(0,0,0,0.15);
}
.phone-inner {
width: 100%;
height: 100%;
background-color: #f5f5f5;
border-radius: 28px;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.status-bar-mock { height: 24px; background-color: transparent; }
.phone-body { flex: 1; }
/* 轮播图 Swiper 1:1 */
.banner-area {
padding: 10px 12px;
background-color: #fff;
}
.preview-swiper {
width: 100%;
height: 110px; /* 根据 690*240 比例在预览窗口的换算高度 */
border-radius: 10px;
overflow: hidden;
background-color: #f9f9f9;
}
.swiper-image-wrapper {
width: 100%;
height: 100%;
}
.banner-img {
width: 100%;
height: 100%;
}
.preview-banner-placeholder {
width: 100%;
height: 110px;
background-color: #f0f0f0;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
}
.ph-content { display: flex; flex-direction: column; align-items: center; }
.ph-icon { font-size: 24px; margin-bottom: 4px; }
.ph-txt { font-size: 11px; color: #999; }
/* Mock 内容样式 */
.mock-section {
padding: 12px;
min-height: 200px;
transition: background-color 0.3s;
}
.bg-red {
background-color: #e93323;
}
/* 1:1 标准标题栏(带分割线) */
.section-title-standard {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 15px 0;
gap: 10px;
}
.title-line {
flex: 1;
height: 1px;
background-color: #eeeeee;
}
.title-center {
display: flex;
flex-direction: row;
align-items: center;
}
.title-ic-standard {
font-size: 14px;
margin-right: 4px;
}
.title-txt-standard {
font-size: 14px;
font-weight: 500;
color: #333333;
}
/* 垂直列表1:1 复刻 CRMEB */
.product-list-vertical {
display: flex;
flex-direction: column;
}
.product-list-item {
display: flex;
flex-direction: row;
background-color: #fff;
padding: 15px 12px;
border-radius: 8px;
margin-bottom: 10px;
box-shadow: 0 2px 8px rgba(0,0,0,0.02);
}
.p-img-left {
width: 90px;
height: 90px;
background-color: #f7f7f7;
border-radius: 6px;
margin-right: 12px;
}
/* Mock 占位背景色彩 */
.p-img-mock-0 { background-color: #eef2f9; }
.p-img-mock-1 { background-color: #fff1f0; }
.p-img-mock-2 { background-color: #f6ffed; }
.p-img-mock-3 { background-color: #fff7e6; }
.p-info-right {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 2px 0;
}
.p-name-box-standard {
margin-bottom: 4px;
}
.p-name-txt-standard {
font-size: 13px;
color: #333333;
line-height: 1.4;
/* 模拟两行省略 */
display: block;
overflow: hidden;
}
.p-bottom-row-standard {
display: flex;
flex-direction: column;
gap: 2px;
}
.p-price-box-standard {
display: flex;
flex-direction: row;
align-items: baseline;
}
.p-symbol-standard {
font-size: 11px;
color: #e93323;
font-weight: bold;
}
.p-val-standard {
font-size: 16px;
color: #e93323;
font-weight: bold;
}
.p-sales-standard {
font-size: 10px;
color: #999999;
}
/* 拼团 1:1 复刻 (红色背景配套) */
.mock-group-list-red {
display: flex;
flex-direction: column;
}
.group-participation-bar {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 5px 0 15px;
}
.avatar-stack {
display: flex;
flex-direction: row;
margin-right: 8px;
}
.a-item {
width: 18px;
height: 18px;
border-radius: 50%;
border: 1px solid #fff;
margin-left: -6px;
}
.a1 { background-color: #ffcdd2; margin-left: 0; }
.a2 { background-color: #f8bbd0; }
.a3 { background-color: #e1bee7; }
.group-p-txt {
font-size: 11px;
color: #fff;
}
.group-item-card {
display: flex;
flex-direction: row;
background-color: #fff;
border-radius: 10px;
padding: 10px;
margin-bottom: 12px;
}
.g-img-left {
width: 90px;
height: 90px;
background-color: #f5f5f5;
border-radius: 6px;
margin-right: 12px;
}
.g-img-mock-1 { background-color: #ffe0b2; }
.g-img-mock-2 { background-color: #c8e6c9; }
.g-img-mock-3 { background-color: #bbdefb; }
.g-info-right {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.g-name-txt-v2 {
font-size: 12px;
color: #333;
line-height: 1.4;
}
.g-price-row-v2 {
display: flex;
flex-direction: column;
}
.g-p-orig-v2 {
font-size: 10px;
color: #999;
text-decoration: line-through;
}
.g-p-main-v2 {
display: flex;
flex-direction: row;
align-items: baseline;
}
.g-p-sym-v2 {
font-size: 10px;
color: #e93323;
font-weight: bold;
}
.g-p-val-v2 {
font-size: 18px;
color: #e93323;
font-weight: bold;
}
.g-action-row-v2 {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.g-label-count {
border: 1px solid #ff7f50;
border-radius: 4px;
padding: 0 4px;
}
.g-lc-txt {
font-size: 10px;
color: #ff7f50;
}
.g-btn-v2 {
background: linear-gradient(90deg, #ff7f50, #e93323);
padding: 4px 12px;
border-radius: 20px;
}
.g-btn-txt-v2 {
font-size: 11px;
color: #fff;
}
/* 积分商城 1:1 复刻 (白色卡片阴影) */
.mock-points-mall-v2 {
background-color: #fff;
border-radius: 12px;
margin-top: -10px;
padding-top: 15px;
}
.points-nav-row-v2 {
display: flex;
flex-direction: row;
justify-content: space-around;
margin-bottom: 12px;
}
.p-nav-item-v2 {
display: flex;
flex-direction: column;
align-items: center;
}
.p-nav-ic-v2 {
width: 38px;
height: 38px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 6px;
}
.p-nav-ic-emoji {
font-size: 18px;
}
.p-nav-txt-v2 {
font-size: 11px;
color: #666;
}
.points-divider-v2 {
height: 8px;
background-color: #f7f8fa;
}
.points-section-header {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 15px;
}
.ps-title-v2 {
font-size: 15px;
font-weight: bold;
color: #333;
}
.ps-more-v2 {
display: flex;
flex-direction: row;
align-items: center;
}
.ps-more-txt {
font-size: 11px;
color: #999;
}
.ps-more-ic {
font-size: 12px;
color: #ccc;
margin-left: 2px;
}
.points-grid-v2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
padding: 0 12px 12px;
}
.points-card-v2 {
background-color: #fff;
border: 1px solid #f8f8f8;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 6px rgba(0,0,0,0.02);
}
.pc-img-v2 {
width: 100%;
aspect-ratio: 1;
}
.pc-img-mock-1 { background-color: #fce4ec; }
.pc-img-mock-2 { background-color: #f3e5f5; }
.pc-img-mock-3 { background-color: #e1f5fe; }
.pc-img-mock-4 { background-color: #e8f5e9; }
.pc-info-v2 {
padding: 8px;
}
.pc-title-v2 {
font-size: 11px;
color: #333;
line-height: 1.4;
height: 32px;
display: block;
}
.pc-price-v2 {
margin: 4px 0;
}
.pc-points-v2 {
font-size: 13px;
font-weight: bold;
color: #e93323;
}
.pc-ex-count-v2 {
font-size: 9px;
color: #999;
}
/* 登录页 Mock */
.mock-login-view {
padding: 50px 24px;
}
.login-box {
display: flex;
flex-direction: column;
align-items: center;
}
.login-logo-mock {
width: 64px;
height: 64px;
background-color: #e93323;
border-radius: 12px;
margin-bottom: 40px;
}
.login-input-mock {
width: 100%;
height: 48px;
background-color: #fff;
border-radius: 24px;
margin-bottom: 16px;
padding: 0 20px;
display: flex;
align-items: center;
border: 1px solid #f0f0f0;
}
.l-in-txt { color: #ccc; font-size: 14px; }
.login-btn-mock {
width: 100%;
height: 48px;
background-color: #e93323;
border-radius: 24px;
display: flex;
align-items: center;
justify-content: center;
margin-top: 10px;
}
.l-btn-txt { color: #fff; font-size: 16px; font-weight: bold; }
/* 开屏广告 1:1 */
.mock-ad-preview-v2 {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #f5f5f5;
z-index: 100;
}
.ad-full-image-v2 {
width: 100%;
height: 100%;
}
.ad-placeholder-v2 {
width: 100%;
height: 100%;
background-color: #eee;
display: flex;
align-items: center;
justify-content: center;
}
.ad-skip-btn-v2 {
position: absolute;
top: 40px;
right: 20px;
background-color: rgba(0,0,0,0.4);
padding: 5px 12px;
border-radius: 20px;
}
.ad-skip-txt {
color: #fff;
font-size: 12px;
}
</style>