Files
medical-mall/pages/mall/admin/decoration/home.uvue
2026-02-26 08:46:33 +08:00

758 lines
24 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="admin-decoration-home">
<view class="page-header border-shadow">
<view class="header-left">
<text class="page-title">店铺装修</text>
</view>
<view class="header-right">
<!-- 如果需要右上角按钮可添加 -->
</view>
</view>
<view class="content-container">
<view class="main-card border-shadow">
<!-- 左侧:手机预览区 -->
<view class="preview-section">
<view class="phone-mock">
<view class="phone-inner">
<view class="phone-header-img">
<view class="status-bar-mock"></view>
<view class="search-bar-mock">
<text class="search-ic">🔍</text>
<text class="search-ph">请输入搜索词</text>
</view>
<view class="tabs-mock">
<text class="tab-item active">首页</text>
<text class="tab-item">生活家居</text>
<text class="tab-item">运动专区</text>
<text class="tab-item">电子产品</text>
<text class="tab-item">家用电器</text>
<text class="tab-more">≡</text>
</view>
</view>
<scroll-view class="phone-scroll" scroll-y="true">
<!-- Banner -->
<view class="banner-mock">
<view class="banner-box">
<text class="banner-txt">MUSE FOR ALL MOTHERS</text>
</view>
<view class="dot-box">
<view class="dot active"></view>
<view class="dot"></view>
</view>
</view>
<!-- Grid Menu -->
<view class="grid-menu-mock">
<view class="menu-item" v-for="i in 10" :key="i">
<view :class="['menu-icon', 'ic-'+i]"></view>
<text class="menu-txt">{{ menuNames[i-1] }}</text>
</view>
</view>
<!-- Announcement -->
<view class="notice-mock">
<view class="notice-ic">📢</view>
<text class="notice-txt">CRMEB 年中618活动开启进行中</text>
<text class="notice-arr">></text>
</view>
<!-- Check-in Section -->
<view class="checkin-mock">
<view class="checkin-days">
<view class="day-dot" v-for="i in 7" :key="i">
<view class="dot-circle">⭐</view>
<text class="dot-text">周{{ weekDays[i-1] }}</text>
</view>
</view>
<view class="btn-checkin"><text class="check-txt">签到</text></view>
</view>
<!-- Bottom Space -->
<view style="height: 100px;"></view>
</scroll-view>
<!-- Bottom TabBar -->
<view class="tabbar-mock">
<view class="tb-item active">
<text class="tb-ic">🏠</text>
<text class="tb-txt">首页</text>
</view>
<view class="tb-item">
<text class="tb-ic">📂</text>
<text class="tb-txt">分类</text>
</view>
<view class="tb-item">
<text class="tb-ic">🛒</text>
<text class="tb-txt">购物车</text>
</view>
<view class="tb-item">
<text class="tb-ic">👤</text>
<text class="tb-txt">我的</text>
</view>
</view>
</view>
</view>
</view>
<!-- 右侧:列表管理区 -->
<view class="list-section">
<view class="manage-card">
<view class="action-bar">
<view class="btn-primary-blue mr-10" @click="handleAdd">
<text class="btn-txt">添加页面</text>
</view>
<view class="btn-import-blue" @click="handleImport">
<text class="btn-txt">导入模板</text>
</view>
</view>
<!-- 表格 -->
<view class="table-container">
<view class="table-header-row">
<view class="th" style="width: 80px;">页面ID</view>
<view class="th" style="flex: 2;">模板名称</view>
<view class="th" style="flex: 1;">模板类型</view>
<view class="th" style="flex: 2;">添加时间</view>
<view class="th" style="flex: 2;">更新时间</view>
<view class="th" style="width: 280px;">操作</view>
</view>
<view v-for="(item, index) in tableData" :key="index" class="table-body-row">
<view class="td" style="width: 80px;">{{ item.id }}</view>
<view class="td" style="flex: 2;">{{ item.name }}</view>
<view class="td" style="flex: 1;">
<view :class="['type-tag', item.type === '首页' ? 'type-home' : 'type-topic']">
<text class="tag-label">{{ item.type }}</text>
</view>
</view>
<view class="td" style="flex: 2;">{{ item.addTime }}</view>
<view class="td" style="flex: 2;">{{ item.updateTime }}</view>
<view class="td" style="width: 280px;">
<view class="op-links">
<text class="op-link" @click="handleEdit(item)">编辑</text>
<text class="op-split">|</text>
<text class="op-link text-danger">删除</text>
<text class="op-split">|</text>
<text class="op-link">预览</text>
<text class="op-split">|</text>
<text class="op-link" v-if="item.type !== '首页'">设为首页</text>
<text class="op-split" v-if="item.type !== '首页'">|</text>
<text class="op-link">导出模板</text>
</view>
</view>
</view>
</view>
<!-- 分页器 -->
<view class="pagination-footer">
<view class="page-total">
<text class="total-txt">共 {{ total }} 条</text>
</view>
<view class="page-select">
<text class="page-val">15条/页 ▼</text>
</view>
<view class="page-btns">
<text class="p-btn disabled"><</text>
<text class="p-btn active">1</text>
<text class="p-btn">></text>
</view>
<view class="page-jump">
<text class="jump-txt">前往</text>
<input class="jump-input" value="1" />
<text class="jump-txt">页</text>
</view>
</view>
</view>
</view>
<!-- 添加页面侧边栏 -->
<view v-if="showDrawer" :class="['drawer-mask', isClosing ? 'mask-fade-out' : '']" @click="closeDrawer">
<view :class="['drawer-content', isClosing ? 'slide-out' : '']" @click.stop="">
<view class="drawer-header">
<text class="title-txt">添加页面</text>
<text class="close-btn" @click="closeDrawer">×</text>
</view>
<scroll-view class="drawer-body" :scroll-y="true">
<view class="form-item-v">
<text class="v-label">页面名称</text>
<input class="v-input" v-model="formName" placeholder="请填写页面名称" />
</view>
<view class="form-item-v">
<text class="v-label">页面类型</text>
<view class="radio-group">
<view class="radio-item" @click="formType = '首页'">
<view :class="['radio-dot', formType === '首页' ? 'active' : '']"></view>
<text class="radio-txt">首页</text>
</view>
<view class="radio-item" @click="formType = '专题页'">
<view :class="['radio-dot', formType === '专题页' ? 'active' : '']"></view>
<text class="radio-txt">专题页</text>
</view>
</view>
</view>
<view class="template-select-title">
<text class="t-title">选择模板</text>
<text class="t-sub">请选择要引用的模板</text>
</view>
<view class="template-grid">
<view class="tpl-item" v-for="i in 4" :key="i">
<view class="tpl-thumb">
<text class="tpl-ic">📄</text>
</view>
<text class="tpl-name">通用模板 {{ i }}</text>
</view>
</view>
</scroll-view>
<view class="drawer-footer">
<view class="btn-cancel" @click="closeDrawer">
<text class="btn-cancel-txt">取消</text>
</view>
<view class="btn-save" @click="handleSavePage">
<text class="btn-save-txt">提交</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
const menuNames = ['秒杀活动', '商品分类', '拼团活动', '积分商城', '砍价中心', '行业资讯', '我的地址', '积分抽奖', '我的账户', '订单列表']
const weekDays = ['一', '二', '三', '四', '五', '六', '日']
const total = ref(11)
const tableData = ref([
{ id: 497, name: 'DIY导入数据', type: '专题页', addTime: '2025-03-20 15:18:01', updateTime: '2025-05-21 10:17:45' },
{ id: 496, name: 'DIY导入数据', type: '专题页', addTime: '2025-03-20 15:12:58', updateTime: '2025-03-20 15:12:58' },
{ id: 494, name: '图书类模板,勿动!!', type: '专题页', addTime: '2025-02-27 15:42:08', updateTime: '2025-03-19 10:40:13' },
{ id: 493, name: '健康类模板,勿动!!', type: '专题页', addTime: '2025-02-27 15:40:55', updateTime: '2025-03-07 09:46:14' },
{ id: 492, name: '演出类模板,勿动!!', type: '专题页', addTime: '2025-02-27 15:33:09', updateTime: '2025-03-07 09:49:43' },
{ id: 491, name: '潮玩类模板,勿动!!', type: '专题页', addTime: '2025-02-27 15:31:28', updateTime: '2025-03-07 09:55:53' },
{ id: 490, name: '家居类模板,勿动!!', type: '专题页', addTime: '2025-02-27 15:30:21', updateTime: '2025-03-07 09:57:59' },
{ id: 482, name: '文具类模板,勿动!!', type: '专题页', addTime: '2025-02-26 11:32:07', updateTime: '2025-03-07 09:59:25' },
{ id: 481, name: '模板', type: '专题页', addTime: '2025-02-26 09:21:04', updateTime: '2025-03-12 14:55:46' },
{ id: 480, name: '模板', type: '专题页', addTime: '2025-02-26 09:19:24', updateTime: '2026-02-02 17:11:45' },
{ id: 479, name: '首页模板,勿动!!', type: '首页', addTime: '2025-02-25 20:59:59', updateTime: '2026-01-20 11:16:20' }
])
const showDrawer = ref(false)
const isClosing = ref(false)
const formName = ref('')
const formType = ref('首页')
const viewState = ref('list') // 'list' | 'design'
const editingName = ref('')
const handleAdd = () => {
showDrawer.value = true
isClosing.value = false
}
const closeDrawer = () => {
isClosing.value = true
setTimeout(() => {
showDrawer.value = false
isClosing.value = false
}, 300)
}
const handleEdit = (item: any) => {
editingName.value = item.name as string
viewState.value = 'design'
}
const handleImport = () => { console.log('Importing...') }
const handleSavePage = () => {
console.log('Saving new page:', formName.value)
closeDrawer()
}
const handleSaveDesign = () => {
console.log('Saving design...')
viewState.value = 'list'
}
</script>
<style scoped lang="scss">
.admin-decoration-home {
padding: 0;
background-color: transparent;
min-height: auto;
}
.page-header {
height: 60px;
padding: 0 24px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.page-title { font-size: 16px; font-weight: 600; color: #303133; }
.border-shadow {
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
}
.content-container {
display: flex;
flex-direction: row;
margin-top: 20px;
}
.main-card {
flex: 1;
display: flex;
flex-direction: row;
background-color: #fff;
border-radius: 8px;
overflow: hidden;
}
/* 左侧手机预览区 */
.preview-section {
width: 380px;
height: 800px;
display: flex;
justify-content: center;
align-items: center;
padding: 40px 0;
border-right: 1px solid #f0f0f0;
}
.phone-mock {
width: 320px;
height: 640px;
background-color: #fff;
border: 10px solid #ececec;
border-radius: 36px;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.phone-inner { flex: 1; display: flex; flex-direction: column; }
.phone-header-img {
background-color: #f7f7f7;
}
.status-bar-mock { height: 20px; }
.search-bar-mock {
height: 38px;
background-color: #fff;
margin: 0 12px;
border-radius: 19px;
display: flex;
flex-direction: row;
align-items: center;
padding: 0 15px;
border: 1px solid #eee;
}
.search-ic { font-size: 14px; margin-right: 8px; }
.search-ph { font-size: 12px; color: #999; }
.tabs-mock {
height: 40px;
display: flex;
flex-direction: row;
align-items: center;
padding: 0 12px;
justify-content: space-between;
}
.tab-item { font-size: 13px; color: #333; margin-right: 15px; }
.tab-item.active { color: #f2270c; font-weight: bold; border-bottom: 2px solid #f2270c; }
.tab-more { font-size: 16px; color: #666; }
.phone-scroll { flex: 1; background-color: #f8f8f8; }
.banner-mock {
height: 150px;
position: relative;
margin: 10px;
}
.banner-box {
width: 100%;
height: 100%;
background: linear-gradient(135deg, #eee 0%, #ccc 100%);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.banner-txt { font-size: 18px; font-weight: bold; color: #333; text-align: center; }
.dot-box {
position: absolute;
bottom: 8px;
left: 0;
right: 0;
display: flex;
justify-content: center;
gap: 6px;
}
.dot { width: 6px; height: 6px; background-color: rgba(255,255,255,0.5); border-radius: 3px; }
.dot.active { width: 12px; background-color: #fff; }
.grid-menu-mock {
display: flex;
flex-direction: row;
flex-wrap: wrap;
padding: 10px 5px;
}
.menu-item {
width: 20%;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 12px;
}
.menu-icon {
width: 40px;
height: 40px;
background-color: #ddd;
border-radius: 20px;
margin-bottom: 6px;
}
.ic-1 { background-color: #ff9d00; }
.ic-2 { background-color: #ff5000; }
.ic-3 { background-color: #8a2be2; }
.ic-4 { background-color: #f4ea2a; }
.ic-5 { background-color: #ffb6c1; }
.ic-6 { background-color: #c0c0c0; }
.ic-7 { background-color: #90ee90; }
.ic-8 { background-color: #87cefa; }
.ic-9 { background-color: #ffa07a; }
.ic-10 { background-color: #20b2aa; }
.menu-txt { font-size: 10px; color: #666; }
.notice-mock {
height: 36px;
background-color: #fff;
margin: 0 10px;
border-radius: 4px;
display: flex;
flex-direction: row;
align-items: center;
padding: 0 10px;
}
.notice-ic { font-size: 14px; margin-right: 8px; }
.notice-txt { flex: 1; font-size: 12px; color: #333; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.notice-arr { color: #ccc; font-size: 12px; }
.checkin-mock {
margin: 10px;
background-color: #fff;
border-radius: 8px;
padding: 12px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.checkin-days { display: flex; flex-direction: row; gap: 8px; }
.day-dot { display: flex; flex-direction: column; align-items: center; }
.dot-circle { width: 24px; height: 24px; background-color: #fdf6ec; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 12px; margin-bottom: 4px; }
.dot-text { font-size: 9px; color: #999; }
.btn-checkin { background-color: #ff5000; padding: 4px 12px; border-radius: 12px; }
.check-txt { color: #fff; font-size: 11px; }
.tabbar-mock {
height: 50px;
background-color: #fff;
border-top: 1px solid #eee;
display: flex;
flex-direction: row;
}
.tb-item { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.tb-ic { font-size: 18px; margin-bottom: 2px; }
.tb-txt { font-size: 11px; color: #999; }
.tb-item.active .tb-txt { color: #f2270c; }
/* 右侧列表管理区 */
.list-section { flex: 1; }
.manage-card { display: flex; flex-direction: column; min-height: 800px; }
.action-bar { padding: 20px; display: flex; flex-direction: row; }
.btn-primary-blue { background-color: #2d8cf0; padding: 8px 16px; border-radius: 4px; cursor: pointer; }
.btn-import-blue { border: 1px solid #1890ff; padding: 7px 16px; border-radius: 4px; cursor: pointer; }
.mr-10 { margin-right: 10px; }
.btn-txt { color: #fff; font-size: 14px; }
.btn-import-blue .btn-txt { color: #1890ff; }
.table-container { flex: 1; padding: 0 20px; }
.table-header-row { display: flex; flex-direction: row; background-color: #f8f8f9; border-bottom: 1px solid #e8eaec; }
.th { padding: 12px 10px; font-size: 14px; color: #515a6e; font-weight: bold; }
.table-body-row { display: flex; flex-direction: row; border-bottom: 1px solid #e8eaec; }
.td { padding: 15px 10px; font-size: 14px; color: #515a6e; display: flex; align-items: center; }
.type-tag { padding: 2px 8px; border-radius: 4px; border: 1px solid #dcdfe6; }
.type-topic { background-color: #f5f7fa; }
.type-home { background-color: #f6ffed; border-color: #b7eb8f; }
.tag-label { font-size: 12px; }
.type-home .tag-label { color: #52c41a; }
.op-links { display: flex; flex-direction: row; align-items: center; color: #2d8cf0; }
.op-link { cursor: pointer; margin: 0 5px; }
.op-split { color: #e8eaec; }
.text-danger { color: #ed4014; }
.pagination-footer {
padding: 20px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
gap: 15px;
}
.total-txt { font-size: 14px; color: #606266; }
.page-val { font-size: 14px; color: #606266; border: 1px solid #dcdfe6; padding: 4px 10px; border-radius: 4px; }
.page-btns { display: flex; flex-direction: row; gap: 8px; }
.p-btn {
width: 32px;
height: 32px;
border: 1px solid #dcdfe6;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
}
.p-btn.active { background-color: #2d8cf0; border-color: #2d8cf0; color: #fff; }
.p-btn.disabled { color: #c0c4cc; background-color: #f5f7fa; }
.page-jump { display: flex; flex-direction: row; align-items: center; gap: 8px; }
.jump-txt { font-size: 14px; color: #606266; }
.jump-input { width: 40px; height: 32px; border: 1px solid #dcdfe6; text-align: center; border-radius: 4px; }
/* Design View Styles */
.design-view {
display: flex;
flex-direction: column;
height: calc(100vh - 48px);
}
.design-header {
height: 60px;
background-color: #fff;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 0 20px;
border-bottom: 2px solid #2d8cf0;
z-index: 100;
}
.header-left { display: flex; flex-direction: row; align-items: center; cursor: pointer; }
.back-ic { font-size: 20px; color: #2d8cf0; margin-right: 15px; }
.design-title { font-size: 16px; font-weight: bold; color: #333; }
.header-right { display: flex; flex-direction: row; gap: 12px; }
.btn-ghost { border: 1px solid #dcdfe6; padding: 6px 16px; border-radius: 4px; cursor: pointer; }
.btn-primary { background-color: #2d8cf0; padding: 6px 16px; border-radius: 4px; cursor: pointer; }
.ghost-txt { color: #666; font-size: 14px; }
.primary-txt { color: #fff; font-size: 14px; }
.design-body {
flex: 1;
display: flex;
flex-direction: row;
}
.design-sidebar { width: 280px; background-color: #fff; padding: 15px; border-right: 1px solid #f0f0f0; }
.sidebar-item {
width: 110px;
display: flex;
flex-direction: column;
align-items: center;
padding: 15px 0;
float: left;
}
.side-ic-box { width: 40px; height: 40px; background-color: #f7f8fa; display: flex; align-items: center; justify-content: center; border-radius: 4px; margin-bottom: 8px; font-size: 20px; }
.side-txt { font-size: 12px; color: #666; }
.design-canvas {
flex: 1;
background-color: #f0f2f5;
display: flex;
justify-content: center;
padding-top: 30px;
overflow-y: auto;
}
.canvas-phone {
width: 375px;
min-height: 667px;
background-color: #fff;
box-shadow: 0 0 20px rgba(0,0,0,0.1);
}
.phone-top { height: 60px; background-color: #fff; border-bottom: 1px solid #eee; }
.phone-content-mock {
padding: 100px 20px;
text-align: center;
}
.mock-tip { color: #999; font-size: 14px; }
.design-attr { width: 320px; background-color: #fff; border-left: 1px solid #f0f0f0; }
.attr-header { padding: 15px; border-bottom: 1px solid #f0f0f0; }
.ah-txt { font-size: 15px; font-weight: bold; }
.attr-empty { padding: 50px 20px; text-align: center; }
.ae-txt { color: #999; font-size: 13px; }
.anim-fade-in {
animation: fadeIn 0.4s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* Drawer Styles */
.drawer-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.4);
z-index: 2000;
display: flex;
justify-content: flex-end;
}
.drawer-content {
width: 450px;
height: 100%;
background-color: #fff;
display: flex;
flex-direction: column;
animation: slideIn 0.3s ease-out;
}
.drawer-header {
padding: 20px;
border-bottom: 1px solid #f0f0f0;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.title-txt { font-size: 16px; font-weight: bold; color: #333; }
.close-btn { font-size: 24px; color: #999; cursor: pointer; }
.drawer-body { flex: 1; padding: 20px; }
.form-item-v { margin-bottom: 24px; }
.v-label { font-size: 14px; color: #666; margin-bottom: 10px; display: block; }
.v-input { border: 1px solid #dcdfe6; height: 40px; padding: 0 12px; border-radius: 4px; font-size: 14px; width: 100%; }
.radio-group { display: flex; flex-direction: row; gap: 30px; }
.radio-item { display: flex; flex-direction: row; align-items: center; cursor: pointer; }
.radio-dot { width: 16px; height: 16px; border: 1px solid #dcdfe6; border-radius: 8px; margin-right: 8px; position: relative; }
.radio-dot.active { border-color: #2d8cf0; }
.radio-dot.active::after {
content: '';
width: 8px;
height: 8px;
background-color: #2d8cf0;
border-radius: 4px;
position: absolute;
top: 3px;
left: 3px;
}
.radio-txt { font-size: 14px; color: #333; }
.template-select-title { margin-top: 20px; margin-bottom: 15px; }
.t-title { font-size: 15px; font-weight: bold; color: #333; margin-right: 10px; }
.t-sub { font-size: 12px; color: #999; }
.template-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 15px;
}
.tpl-item {
width: 190px;
border: 1px solid #eee;
border-radius: 4px;
padding: 10px;
background-color: #f9f9f9;
cursor: pointer;
}
.tpl-thumb {
height: 220px;
background-color: #fff;
margin-bottom: 8px;
display: flex;
align-items: center;
justify-content: center;
}
.tpl-ic { font-size: 40px; color: #ccc; }
.tpl-name { font-size: 12px; color: #666; text-align: center; display: block; }
.drawer-footer {
padding: 20px;
border-top: 1px solid #f0f0f0;
display: flex;
flex-direction: row;
justify-content: flex-end;
gap: 12px;
}
.btn-cancel, .btn-save { padding: 8px 20px; border-radius: 4px; cursor: pointer; }
.btn-cancel { border: 1px solid #dcdfe6; }
.btn-save { background-color: #2d8cf0; }
.btn-cancel-txt { color: #666; font-size: 14px; }
.btn-save-txt { color: #fff; font-size: 14px; }
/* Animations */
@keyframes slideIn {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
.slide-out {
animation: slideOut 0.3s ease-in forwards;
}
@keyframes slideOut {
from { transform: translateX(0); }
to { transform: translateX(100%); }
}
.mask-fade-out {
animation: fadeOut 0.3s ease-in forwards;
}
@keyframes fadeOut {
from { background-color: rgba(0, 0, 0, 0.4); }
to { background-color: rgba(0, 0, 0, 0); }
}
</style>