大致完成页面

This commit is contained in:
2026-02-05 09:01:16 +08:00
parent c411c23b9c
commit d51e6a8f72
40 changed files with 11023 additions and 737 deletions

View File

@@ -0,0 +1,737 @@
<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>