|
|
@@ -108,7 +108,7 @@
|
|
|
<!-- 广告图编辑区 -->
|
|
|
<div v-else-if="activeSubTab === 'carousel'" class="carousel-editor">
|
|
|
<!-- 模块一:左侧广告设置 -->
|
|
|
- <div class="editor-section">
|
|
|
+ <!-- <div class="editor-section">
|
|
|
<div class="section-header">
|
|
|
<span class="section-title">模块一:左侧广告设置</span>
|
|
|
<span class="section-desc">尺寸要求:790 * 460,支持上传本地图片并设置跳转链接</span>
|
|
|
@@ -142,12 +142,12 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </div> -->
|
|
|
|
|
|
<!-- 模块二:轮播图管理 -->
|
|
|
<div class="editor-section">
|
|
|
<div class="section-header">
|
|
|
- <span class="section-title">模块二:轮播图设置</span>
|
|
|
+ <span class="section-title">轮播图设置</span>
|
|
|
<span class="section-desc">尺寸要求:552 * 190,支持拖拽排序与实时状态切换</span>
|
|
|
</div>
|
|
|
|
|
|
@@ -330,52 +330,73 @@
|
|
|
<div class="editor-section">
|
|
|
<div class="section-header">
|
|
|
<span class="section-title">分类实时预览</span>
|
|
|
- <span class="section-desc">尺寸要求:280 * 398,悬停可查看右滑面板效果 (图1、图2)</span>
|
|
|
+ <span class="section-desc">尺寸要求:248 * 384,悬停可查看右滑面板效果 (图1、图2)</span>
|
|
|
</div>
|
|
|
|
|
|
<div class="category-preview-container">
|
|
|
<!-- 图1: 分类菜单 -->
|
|
|
- <div class="category-menu-mockup">
|
|
|
- <div v-for="item in categoryList.filter((c) => c.status === 1).slice(0, 10)" :key="item.id" class="menu-item">
|
|
|
- <div class="menu-icon">
|
|
|
- <img v-if="item.icon" :src="item.icon" alt="" />
|
|
|
- <el-icon v-else><Menu /></el-icon>
|
|
|
+ <div
|
|
|
+ class="category-menu-mockup"
|
|
|
+ :class="{ 'is-expanded': isPreviewExpanded }"
|
|
|
+ :style="menuMockupStyle"
|
|
|
+ @mouseenter="isPreviewExpanded = true"
|
|
|
+ @mouseleave="
|
|
|
+ isPreviewExpanded = false;
|
|
|
+ activeHoverId = null;
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ v-for="item in categoryList.filter((c) => c.status === 1)"
|
|
|
+ :key="item.id"
|
|
|
+ class="menu-item"
|
|
|
+ :class="{ 'is-hovered': activeHoverId === item.id }"
|
|
|
+ @mouseenter="activeHoverId = item.id"
|
|
|
+ >
|
|
|
+ <div class="menu-item-inner">
|
|
|
+ <div class="menu-icon">
|
|
|
+ <img v-if="item.icon" :src="item.icon" alt="" />
|
|
|
+ <el-icon v-else><Menu /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="menu-name">
|
|
|
+ <template v-for="(subName, subIdx) in getSubNames(item)" :key="subIdx">
|
|
|
+ <span class="sub-menu-link" @click.stop="handleSubMenuClick(subName, item)">{{ subName }}</span>
|
|
|
+ <span v-if="subIdx < getSubNames(item).length - 1" class="menu-sep">/</span>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="menu-name">{{ item.name }}</div>
|
|
|
-
|
|
|
- <!-- 图2: 悬停右滑面板 - CSS 控制显示 -->
|
|
|
- <div class="category-panel-mockup">
|
|
|
- <div class="panel-header-line"></div>
|
|
|
- <div class="panel-content">
|
|
|
- <!-- 顶部标签栏 -->
|
|
|
- <div class="panel-tabs">
|
|
|
- <span v-for="tag in item.tags" :key="tag.name" class="panel-tab-item">{{ tag.name }}</span>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 品牌位 (图3) - 绝对定位至右上角 -->
|
|
|
- <div class="brand-box">
|
|
|
- <div class="brand-main-title">
|
|
|
- {{ item.panelData.mainTitle }}<span class="brand-strong">{{ item.panelData.subTitle }}</span>
|
|
|
- </div>
|
|
|
- <div class="brand-notes">
|
|
|
- <template v-for="(note, nIdx) in item.panelData.notes" :key="nIdx">
|
|
|
- <span class="note-item">{{ note.name }}</span>
|
|
|
- <span v-if="nIdx < item.panelData.notes.length - 1" class="note-sep">|</span>
|
|
|
- </template>
|
|
|
+ <!-- 悬停右滑面板 -->
|
|
|
+ <div
|
|
|
+ class="category-panel-mockup"
|
|
|
+ :class="{ 'is-visible': activeHoverId === item.id }"
|
|
|
+ @mouseenter="
|
|
|
+ isPreviewExpanded = true;
|
|
|
+ activeHoverId = item.id;
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <div class="panel-content-flex">
|
|
|
+ <!-- 左侧:主要分类和顶部的标签 -->
|
|
|
+ <div class="panel-main-side">
|
|
|
+ <!-- 顶部标签栏 -->
|
|
|
+ <div class="panel-tabs">
|
|
|
+ <span v-for="tag in item.tags" :key="tag.name" class="panel-tab-item">{{ tag.name }}</span>
|
|
|
</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="panel-body">
|
|
|
- <!-- 左侧分类列表 -->
|
|
|
- <div class="panel-main">
|
|
|
+ <!-- 二级分类组 -->
|
|
|
+ <div class="panel-groups-container">
|
|
|
<div v-for="group in item.panelData.groups" :key="group.title" class="category-group">
|
|
|
- <div class="group-title">{{ group.title }}</div>
|
|
|
+ <div class="group-title" @click.stop="handleGroupTitleClick(group.title, item)">{{ group.title }}</div>
|
|
|
<div class="group-items">
|
|
|
<span v-for="sub in group.items" :key="sub" class="group-item">{{ sub }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <!-- 右侧:5个广告位 -->
|
|
|
+ <div class="panel-ads-side">
|
|
|
+ <div v-for="(adUrl, adIdx) in getCategoryAds(item)" :key="adIdx" class="panel-ad-card">
|
|
|
+ <img v-if="adUrl" :src="adUrl" class="panel-ad-img" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -550,7 +571,7 @@
|
|
|
</div>
|
|
|
<div class="ad-products-subsidy">
|
|
|
<div v-for="item in adModules[0].items" :key="item.id" class="product-item">
|
|
|
- <div class="product-img"><img :src="item.imageUrl" alt="" /></div>
|
|
|
+ <div class="product-img"><img :src="item.image" alt="" /></div>
|
|
|
<div class="product-price">¥{{ item.price }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -566,10 +587,10 @@
|
|
|
<div class="ad-title-sub" :style="getAdTitleStyle(1, 'sub')">{{ getAdTitleText(1, 'sub') }}</div>
|
|
|
</div>
|
|
|
<div class="ad-products-ranking">
|
|
|
- <div v-for="item in adModules[1].items" :key="item.id" class="ranking-item">
|
|
|
- <div class="ranking-badge">{{ item.tagText || '排行榜' }} ></div>
|
|
|
- <div class="product-img"><img :src="item.imageUrl" alt="" /></div>
|
|
|
- <div class="ranking-footer">已售{{ item.salesCount }}件</div>
|
|
|
+ <div v-for="(item, idx) in adModules[1].items" :key="item.id" class="ranking-item" @click="handleAdItemClick(item)">
|
|
|
+ <div class="product-img"><img :src="item.image" alt="" /></div>
|
|
|
+ <div class="ranking-badge">{{ item.tag || '排行榜' }}</div>
|
|
|
+ <div class="ranking-sales">{{ item.sales }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="ad-hover-mask">
|
|
|
@@ -584,10 +605,10 @@
|
|
|
<div class="ad-title-sub" :style="getAdTitleStyle(2, 'sub')">{{ getAdTitleText(2, 'sub') }}</div>
|
|
|
</div>
|
|
|
<div class="ad-brands-content">
|
|
|
- <div v-for="item in adModules[2].items" :key="item.id" class="brand-item">
|
|
|
- <div class="brand-logo"><img :src="item.imageUrl" alt="" /></div>
|
|
|
- <div class="brand-name">{{ item.tagLink || item.productName }}</div>
|
|
|
- <div class="brand-tag-btn">{{ item.tagText || '品质保障' }}</div>
|
|
|
+ <div v-for="item in adModules[2].items" :key="item.id" class="brand-item" @click="handleAdItemClick(item)">
|
|
|
+ <div class="brand-logo"><img :src="item.image" alt="" /></div>
|
|
|
+ <div class="brand-name-badge">{{ item.tag }}</div>
|
|
|
+ <div class="brand-tag-text">{{ item.sales }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="ad-hover-mask">
|
|
|
@@ -603,7 +624,7 @@
|
|
|
</div>
|
|
|
<div class="ad-products-selection">
|
|
|
<div v-for="item in adModules[3].items" :key="item.id" class="selection-item">
|
|
|
- <div class="product-img"><img :src="item.imageUrl" alt="" /></div>
|
|
|
+ <div class="product-img"><img :src="item.image" alt="" /></div>
|
|
|
<div class="product-price-row">
|
|
|
<span class="p-unit">¥</span>
|
|
|
<span class="p-val">{{ item.price }}</span>
|
|
|
@@ -623,7 +644,7 @@
|
|
|
</div>
|
|
|
<div class="ad-products-selection">
|
|
|
<div v-for="item in adModules[4].items" :key="item.id" class="selection-item">
|
|
|
- <div class="product-img"><img :src="item.imageUrl" alt="" /></div>
|
|
|
+ <div class="product-img"><img :src="item.image" alt="" /></div>
|
|
|
<div class="product-price-row center">
|
|
|
<span class="p-unit">¥</span>
|
|
|
<span class="p-val">{{ item.price }}</span>
|
|
|
@@ -910,64 +931,73 @@
|
|
|
<el-table-column label="位置" width="60" align="center">
|
|
|
<template #default="scope">{{ scope.$index + 1 }}</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column label="图片" width="100" align="center">
|
|
|
- <template #default="{ row }">
|
|
|
- <img :src="row.imageUrl" style="width: 50px; height: 50px; object-fit: contain" />
|
|
|
+ <el-table-column label="图片" width="120" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ <!-- 企采榜单 / 品牌好店:系统默认上传组件 -->
|
|
|
+ <template v-if="currentAdIdx === 1 || currentAdIdx === 2">
|
|
|
+ <el-image
|
|
|
+ v-if="scope.row.image"
|
|
|
+ :src="scope.row.image"
|
|
|
+ :preview-src-list="[scope.row.image]"
|
|
|
+ preview-teleported
|
|
|
+ fit="contain"
|
|
|
+ style="width: 86px; height: 86px; border-radius: 4px"
|
|
|
+ />
|
|
|
+ <el-upload
|
|
|
+ :action="uploadUrl"
|
|
|
+ :headers="uploadHeaders"
|
|
|
+ :show-file-list="false"
|
|
|
+ :before-upload="(file: any) => beforeCellUpload(file)"
|
|
|
+ :on-success="(res: any) => onCellUploadSuccess(res, scope.row)"
|
|
|
+ accept="image/*"
|
|
|
+ >
|
|
|
+ <el-button size="small" type="primary" link>
|
|
|
+ {{ scope.row.image ? '更换' : '上传' }}
|
|
|
+ </el-button>
|
|
|
+ </el-upload>
|
|
|
+ </template>
|
|
|
+ <el-image
|
|
|
+ v-else-if="scope.row.image"
|
|
|
+ :src="scope.row.image"
|
|
|
+ :preview-src-list="[scope.row.image]"
|
|
|
+ fit="contain"
|
|
|
+ style="width: 48px; height: 48px; border-radius: 4px; cursor: pointer"
|
|
|
+ preview-teleported
|
|
|
+ />
|
|
|
+ <span v-else class="text-gray">-</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<!-- 通用商品信息列 (百亿补贴、企采精选、京东新品) -->
|
|
|
<el-table-column v-if="currentAdIdx === 0 || currentAdIdx === 3 || currentAdIdx === 4" label="商品信息" min-width="180">
|
|
|
<template #default="{ row }">
|
|
|
<div class="table-info-cell">
|
|
|
- <div class="info-name">{{ row.productName || '未选择' }}</div>
|
|
|
+ <div class="info-name">{{ row.name || '未选择' }}</div>
|
|
|
<div class="info-price">价格:¥{{ row.price }}</div>
|
|
|
<div class="info-id">ID: {{ row.id || '-' }}</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
|
|
|
- <!-- 企采榜单 专属列 -->
|
|
|
- <template v-if="currentAdIdx === 1">
|
|
|
- <el-table-column label="商品信息" min-width="150">
|
|
|
+ <!-- 企采榜单 & 品牌好店 统一配置列 -->
|
|
|
+ <template v-if="currentAdIdx === 1 || currentAdIdx === 2">
|
|
|
+ <el-table-column label="标签" min-width="150">
|
|
|
<template #default="{ row }">
|
|
|
- <div class="info-name">{{ row.productName || '未选择' }}</div>
|
|
|
- <div class="info-id">ID: {{ row.id || '-' }}</div>
|
|
|
+ <el-input v-model="row.tag" placeholder="请输入标签" />
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column label="排行标签" width="220">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-input v-model="row.tagText" placeholder="标签文字" size="small" class="m-b-5" />
|
|
|
- <WebLinkInput v-model="row.tagLink" placeholder="跳转链接" size="small" />
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="销量数据" width="130">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-input v-model="row.salesCount" placeholder="销量" size="small">
|
|
|
- <template #prepend>已售</template>
|
|
|
- </el-input>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </template>
|
|
|
-
|
|
|
- <!-- 品牌好店 专属列 -->
|
|
|
- <template v-if="currentAdIdx === 2">
|
|
|
- <el-table-column label="品牌名称" min-width="180">
|
|
|
+ <el-table-column label="标题" min-width="150">
|
|
|
<template #default="{ row }">
|
|
|
- <div class="brand-name-display">{{ row.productName || '未选择' }}</div>
|
|
|
- <div class="info-id m-t-5">ID: {{ row.id || '-' }}</div>
|
|
|
+ <el-input v-model="row.sales" placeholder="请输入标题" />
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column label="品牌标签" width="300">
|
|
|
+ <el-table-column label="跳转地址" min-width="200">
|
|
|
<template #default="{ row }">
|
|
|
- <div class="flex-column gap-10">
|
|
|
- <el-input v-model="row.tagText" placeholder="标签文字 (如: 品质保障)" />
|
|
|
- <el-input v-model="row.tagLink" placeholder="描述文字 (控制预览品牌名称)" />
|
|
|
- </div>
|
|
|
+ <WebLinkInput v-model="row.link" placeholder="请输入跳转地址" />
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</template>
|
|
|
|
|
|
- <el-table-column label="操作" width="110" align="center" fixed="right">
|
|
|
+ <el-table-column v-if="currentAdIdx !== 1 && currentAdIdx !== 2" label="操作" width="110" align="center" fixed="right">
|
|
|
<template #default="scope">
|
|
|
<el-button type="primary" link @click="openProductSelect(scope.$index)">
|
|
|
<el-icon class="m-r-5"><Edit /></el-icon>
|
|
|
@@ -983,6 +1013,12 @@
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
|
|
|
+ <!-- 隐藏文件上传input -->
|
|
|
+ <input ref="fileInput" type="file" style="display: none" accept="image/*" @change="onFileChange" />
|
|
|
+
|
|
|
+ <!-- 全局图片大图查看器 -->
|
|
|
+ <el-image-viewer v-if="showImageViewer" :url-list="[previewImageUrl]" @close="showImageViewer = false" />
|
|
|
+
|
|
|
<!-- 选择商品/品牌抽屉 (加宽版) -->
|
|
|
<el-drawer v-model="selectDialogVisible" :title="currentAdIdx === 2 ? '选择品牌' : '选择商品'" size="850px" append-to-body>
|
|
|
<div class="select-dialog-content">
|
|
|
@@ -1178,28 +1214,27 @@
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane label="右滑面板配置">
|
|
|
<div class="panel-config-section">
|
|
|
- <div class="config-subtitle">品牌位设置 (图3)</div>
|
|
|
- <el-row :gutter="20">
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="主标题:">
|
|
|
- <el-input v-model="categoryForm.panelData.mainTitle" placeholder="如:京东" />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="副标题:">
|
|
|
- <el-input v-model="categoryForm.panelData.subTitle" placeholder="如:3C数码" />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
-
|
|
|
- <el-form-item label="便签列表:">
|
|
|
+ <div class="config-subtitle" style="display: flex; align-items: center; gap: 12px">
|
|
|
+ <span>广告位设置 (图3)</span>
|
|
|
+ <span style="font-size: 12px; color: #909399; font-weight: normal">(建议尺寸:116 * 54px,最多支持 5 张广告图)</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-form-item label="广告位列表:">
|
|
|
<div class="notes-config-list">
|
|
|
<div v-for="(note, index) in categoryForm.panelData.notes" :key="index" class="note-config-row">
|
|
|
- <el-input v-model="note.name" placeholder="便签名称" style="width: 120px" />
|
|
|
- <WebLinkInput v-model="note.link" placeholder="跳转地址" style="flex: 1" />
|
|
|
+ <UploadImage v-model="note.name" :limit="1" width="116px" height="54px" />
|
|
|
+ <WebLinkInput v-model="note.link" placeholder="请输入广告跳转地址" style="flex: 1" />
|
|
|
<el-button type="danger" icon="Delete" circle plain size="small" @click="removePanelNote(index)" />
|
|
|
</div>
|
|
|
- <el-button type="primary" icon="Plus" link @click="addPanelNote">添加便签</el-button>
|
|
|
+ <el-button
|
|
|
+ v-if="!categoryForm.panelData.notes || categoryForm.panelData.notes.length < 5"
|
|
|
+ type="primary"
|
|
|
+ icon="Plus"
|
|
|
+ link
|
|
|
+ @click="addPanelNote"
|
|
|
+ >
|
|
|
+ 添加广告位
|
|
|
+ </el-button>
|
|
|
</div>
|
|
|
</el-form-item>
|
|
|
</div>
|
|
|
@@ -1564,6 +1599,7 @@ import WebLinkInput from '@/components/WebLinkInput/index.vue';
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
import { ref, reactive, computed, watch, onMounted, onUnmounted, nextTick } from 'vue';
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
+import { globalHeaders } from '@/utils/request';
|
|
|
import {
|
|
|
listSearchConfig,
|
|
|
getSearchConfig,
|
|
|
@@ -1651,7 +1687,8 @@ import {
|
|
|
Search,
|
|
|
InfoFilled,
|
|
|
ArrowUp,
|
|
|
- ArrowDown
|
|
|
+ ArrowDown,
|
|
|
+ ZoomIn
|
|
|
} from '@element-plus/icons-vue';
|
|
|
import { any } from 'vue-types';
|
|
|
const uploadAction = import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload';
|
|
|
@@ -2151,6 +2188,53 @@ const handleDeleteLeftAd = () => {
|
|
|
|
|
|
// 分类设置模块逻辑
|
|
|
const categoryThemeColor = ref('#e60012');
|
|
|
+const categoryThemeColorAlpha = computed(() => {
|
|
|
+ const hex = categoryThemeColor.value.replace('#', '');
|
|
|
+ const r = parseInt(hex.substring(0, 2), 16);
|
|
|
+ const g = parseInt(hex.substring(2, 4), 16);
|
|
|
+ const b = parseInt(hex.substring(4, 6), 16);
|
|
|
+ return `rgba(${r}, ${g}, ${b}, 0.3)`;
|
|
|
+});
|
|
|
+const isPreviewExpanded = ref(false);
|
|
|
+const activeHoverId = ref<number | null>(null);
|
|
|
+
|
|
|
+// 获取二级分类名称列表
|
|
|
+const getSubNames = (item: any) => {
|
|
|
+ if (item.panelData && item.panelData.groups && item.panelData.groups.length) {
|
|
|
+ return item.panelData.groups.slice(0, 3).map((g: any) => g.title);
|
|
|
+ }
|
|
|
+ return item.name ? item.name.split('/').map((s: string) => s.trim()) : [];
|
|
|
+};
|
|
|
+
|
|
|
+const handleSubMenuClick = (subName: string, item: any) => {
|
|
|
+ ElMessage.success(`您点击了子分类:${subName} (属于 ${item.name} 一级分类)`);
|
|
|
+};
|
|
|
+
|
|
|
+const handleGroupTitleClick = (title: string, item: any) => {
|
|
|
+ ElMessage.success(`您点击了二级分类:${title} (属于 ${item.name} 一级分类)`);
|
|
|
+};
|
|
|
+
|
|
|
+const getCategoryAds = (item: any) => {
|
|
|
+ const result: string[] = [];
|
|
|
+ if (item.panelData && item.panelData.notes && item.panelData.notes.length) {
|
|
|
+ const list = item.panelData.notes
|
|
|
+ .map((n: any) => {
|
|
|
+ const name = n.name || '';
|
|
|
+ const isImg = name.startsWith('data:') || name.startsWith('http') || name.startsWith('/') || name.includes('.');
|
|
|
+ return isImg ? name : '';
|
|
|
+ })
|
|
|
+ .slice(0, 5);
|
|
|
+ result.push(...list);
|
|
|
+ }
|
|
|
+ while (result.length < 5) {
|
|
|
+ result.push('');
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+};
|
|
|
+
|
|
|
+const menuMockupStyle = computed(() => ({
|
|
|
+ '--panel-width': '640px'
|
|
|
+}));
|
|
|
const syncCategoryOptions = computed(() => {
|
|
|
return (categoryOptions.value || []).map((item: any) => ({
|
|
|
value: item.id,
|
|
|
@@ -2860,10 +2944,10 @@ const adModules = ref([
|
|
|
subTitleColor: '#999999',
|
|
|
type: 'subsidy',
|
|
|
items: [
|
|
|
- { id: 101, productName: '企业商用台式机', imageUrl: '/static/images/purchase/pc_desktop.jpg', price: '69.9' },
|
|
|
- { id: 102, productName: '商务笔记本', imageUrl: '/static/images/purchase/laptop_hp.jpg', price: '84.8' },
|
|
|
- { id: 103, productName: '智能打印机', imageUrl: '/static/images/purchase/printer_office.jpg', price: '139.9' },
|
|
|
- { id: 104, productName: '高效办公组网', imageUrl: '/static/images/purchase/network_router.jpg', price: '1749' }
|
|
|
+ { id: 101, name: '企业商用台式机', image: '/static/images/purchase/pc_desktop.jpg', price: '69.9', link: '' },
|
|
|
+ { id: 102, name: '商务笔记本', image: '/static/images/purchase/laptop_hp.jpg', price: '84.8', link: '' },
|
|
|
+ { id: 103, name: '智能打印机', image: '/static/images/purchase/printer_office.jpg', price: '139.9', link: '' },
|
|
|
+ { id: 104, name: '高效办公组网', image: '/static/images/purchase/network_router.jpg', price: '1749', link: '' }
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
@@ -2874,24 +2958,8 @@ const adModules = ref([
|
|
|
subTitleColor: '#f58220',
|
|
|
type: 'ranking',
|
|
|
items: [
|
|
|
- {
|
|
|
- id: 201,
|
|
|
- productName: '办公电脑榜',
|
|
|
- imageUrl: '/static/images/purchase/laptop_lenovo.jpg',
|
|
|
- price: '0',
|
|
|
- tagText: '办公电脑榜',
|
|
|
- tagLink: '',
|
|
|
- salesCount: '1543'
|
|
|
- },
|
|
|
- {
|
|
|
- id: 202,
|
|
|
- productName: '文具榜',
|
|
|
- imageUrl: '/static/images/purchase/stationery_ranking.jpg',
|
|
|
- price: '0',
|
|
|
- tagText: '文具榜',
|
|
|
- tagLink: '',
|
|
|
- salesCount: '1200'
|
|
|
- }
|
|
|
+ { id: 201, image: '/static/images/purchase/laptop_lenovo.jpg', tag: '办公电脑榜', sales: '已售1543件' },
|
|
|
+ { id: 202, image: '/static/images/purchase/stationery_ranking.jpg', tag: '文具榜', sales: '已售1200件' }
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
@@ -2902,8 +2970,8 @@ const adModules = ref([
|
|
|
subTitleColor: '#f58220',
|
|
|
type: 'brand',
|
|
|
items: [
|
|
|
- { id: 301, productName: '鲁花', imageUrl: '/static/images/purchase/oil_luhua.jpg', tagText: '品质保障', tagLink: '鲁花京东自营旗舰店' },
|
|
|
- { id: 302, productName: '金龙鱼', imageUrl: '/static/images/purchase/oil_jinlongyu.jpg', tagText: '热销品牌', tagLink: '金龙鱼京东自营旗舰店' }
|
|
|
+ { id: 301, image: '/static/images/purchase/oil_luhua.jpg', tag: '鲁花', sales: '品质保障' },
|
|
|
+ { id: 302, image: '/static/images/purchase/oil_jinlongyu.jpg', tag: '金龙鱼', sales: '热销品牌' }
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
@@ -2914,8 +2982,8 @@ const adModules = ref([
|
|
|
subTitleColor: '#999999',
|
|
|
type: 'selection',
|
|
|
items: [
|
|
|
- { id: 401, productName: '高性能工作站', imageUrl: '/static/images/purchase/pc_desktop.jpg', price: '10740' },
|
|
|
- { id: 402, productName: '办公咖啡机', imageUrl: '/static/images/purchase/coffee_machine.jpg', price: '877' }
|
|
|
+ { id: 401, name: '高性能工作站', image: '/static/images/purchase/pc_desktop.jpg', price: '10740', link: '' },
|
|
|
+ { id: 402, name: '办公咖啡机', image: '/static/images/purchase/coffee_machine.jpg', price: '877', link: '' }
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
@@ -2926,8 +2994,8 @@ const adModules = ref([
|
|
|
subTitleColor: '#f58220',
|
|
|
type: 'new',
|
|
|
items: [
|
|
|
- { id: 501, productName: '商用冷柜', imageUrl: '/static/images/purchase/freezer.jpg', price: '7188' },
|
|
|
- { id: 502, productName: '得力笔记本', imageUrl: '/static/images/purchase/notebook_deli.jpg', price: '34.9' }
|
|
|
+ { id: 501, name: '商用冷柜', image: '/static/images/purchase/freezer.jpg', price: '7188', link: '' },
|
|
|
+ { id: 502, name: '得力笔记本', image: '/static/images/purchase/notebook_deli.jpg', price: '34.9', link: '' }
|
|
|
]
|
|
|
}
|
|
|
]) as any;
|
|
|
@@ -2936,6 +3004,38 @@ const adDialogVisible = ref(false);
|
|
|
const currentAdIdx = ref(-1);
|
|
|
const currentItemIdx = ref(-1);
|
|
|
const adForm = reactive({ title: '', titleColor: '#333333', subTitle: '', subTitleColor: '#f58220', items: [] });
|
|
|
+const fileInput = ref(null);
|
|
|
+const showImageViewer = ref(false);
|
|
|
+const previewImageUrl = ref('');
|
|
|
+const adUploadIndex = ref(-1);
|
|
|
+
|
|
|
+// 触发广告行图片上传
|
|
|
+const triggerAdRowUpload = (index: number) => {
|
|
|
+ adUploadIndex.value = index;
|
|
|
+ fileInput.value?.click();
|
|
|
+};
|
|
|
+
|
|
|
+// 预览行图片大图
|
|
|
+const handlePreviewRowImage = (url: string) => {
|
|
|
+ previewImageUrl.value = url;
|
|
|
+ showImageViewer.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+// 文件选择变化处理
|
|
|
+const onFileChange = (e: Event) => {
|
|
|
+ const file = (e.target as HTMLInputElement).files?.[0];
|
|
|
+ if (!file) return;
|
|
|
+ const reader = new FileReader();
|
|
|
+ reader.onload = (event: ProgressEvent<FileReader>) => {
|
|
|
+ const base64 = event.target?.result as string;
|
|
|
+ if (adUploadIndex.value >= 0 && adForm.items[adUploadIndex.value]) {
|
|
|
+ adForm.items[adUploadIndex.value].image = base64;
|
|
|
+ }
|
|
|
+ ElMessage.success('图片上传成功');
|
|
|
+ (e.target as HTMLInputElement).value = '';
|
|
|
+ };
|
|
|
+ reader.readAsDataURL(file);
|
|
|
+};
|
|
|
|
|
|
// 实时预览辅助函数
|
|
|
const getAdTitleStyle = (index: number, type: string) => {
|
|
|
@@ -2952,7 +3052,33 @@ const getAdTitleText = (index: number, type: string) => {
|
|
|
return isEditing ? adForm[key] : adModules.value[index]?.[key] || '';
|
|
|
};
|
|
|
|
|
|
-const emptyItem = () => ({ id: null, productId: '', productName: '', imageUrl: '', price: '', tagText: '', tagLink: '', salesCount: '' });
|
|
|
+const emptyItem = () => ({ id: null, name: '', image: '', price: '', tag: '', sales: '', link: '' });
|
|
|
+
|
|
|
+const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload');
|
|
|
+const uploadHeaders = ref(globalHeaders());
|
|
|
+
|
|
|
+const beforeCellUpload = (file: any) => {
|
|
|
+ const isImg = file.type.indexOf('image') > -1;
|
|
|
+ if (!isImg) {
|
|
|
+ ElMessage.error('只能上传图片文件');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ const isLt5M = file.size / 1024 / 1024 < 5;
|
|
|
+ if (!isLt5M) {
|
|
|
+ ElMessage.error('图片大小不能超过 5MB');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+};
|
|
|
+
|
|
|
+const onCellUploadSuccess = (res: any, row: any) => {
|
|
|
+ if (res.code === 200) {
|
|
|
+ row.image = res.data.url;
|
|
|
+ ElMessage.success('上传成功');
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.msg || '上传失败');
|
|
|
+ }
|
|
|
+};
|
|
|
const slotCounts = [4, 2, 2, 2, 2]; // 每个模块固定条目数
|
|
|
|
|
|
const handleEditAd = (index: number) => {
|
|
|
@@ -2968,6 +3094,12 @@ const handleEditAd = (index: number) => {
|
|
|
adDialogVisible.value = true;
|
|
|
};
|
|
|
|
|
|
+const handleAdItemClick = (item: any) => {
|
|
|
+ if (item && item.link) {
|
|
|
+ window.open(item.link, '_blank');
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
const submitAdForm = async () => {
|
|
|
const moduleData = adModules.value[currentAdIdx.value] as any;
|
|
|
if (!moduleData) return;
|
|
|
@@ -2983,15 +3115,15 @@ const submitAdForm = async () => {
|
|
|
status: 1,
|
|
|
sortOrder: currentAdIdx.value,
|
|
|
adModuleItemList: adForm.items
|
|
|
- .filter((item: any) => item.productId || item.imageUrl)
|
|
|
+ .filter((item: any) => item.name || item.image)
|
|
|
.map((item: any) => ({
|
|
|
productId: item.productId || item.id,
|
|
|
- productName: item.productName || '',
|
|
|
- imageUrl: item.imageUrl || '',
|
|
|
+ productName: item.name || '',
|
|
|
+ imageUrl: item.image || '',
|
|
|
price: item.price || 0,
|
|
|
- tagText: item.tagText || '',
|
|
|
- tagLink: item.tagLink || '',
|
|
|
- salesCount: item.salesCount || 0
|
|
|
+ tagText: item.tag || '',
|
|
|
+ tagLink: item.tagLink || item.link || '',
|
|
|
+ salesCount: item.sales || ''
|
|
|
}))
|
|
|
};
|
|
|
|
|
|
@@ -3037,13 +3169,12 @@ const getAdModuleList = async () => {
|
|
|
type: item.moduleCode || '',
|
|
|
items: (item.adModuleItemList || []).map((sub: any) => ({
|
|
|
id: sub.id,
|
|
|
- productId: sub.productId || '',
|
|
|
- productName: sub.productName || '',
|
|
|
- imageUrl: sub.imageUrl || '',
|
|
|
+ name: sub.productName || sub.name || '',
|
|
|
+ image: sub.imageUrl || sub.image || '',
|
|
|
+ tag: sub.tagText || sub.tag || '',
|
|
|
+ sales: sub.sales || sub.salesCount || '',
|
|
|
price: sub.price || 0,
|
|
|
- tagText: sub.tagText || '',
|
|
|
- tagLink: sub.tagLink || '',
|
|
|
- salesCount: sub.salesCount || 0
|
|
|
+ link: sub.linkUrl || sub.link || ''
|
|
|
}))
|
|
|
}));
|
|
|
// 确保始终有 5 个模块(模板固定访问 adModules[0]~[4])
|
|
|
@@ -3531,9 +3662,8 @@ const confirmSelect = () => {
|
|
|
if (item) {
|
|
|
const target = adForm.items[currentItemIdx.value];
|
|
|
target.id = item.id;
|
|
|
- target.productId = item.id;
|
|
|
- target.productName = item.name;
|
|
|
- target.imageUrl = item.image;
|
|
|
+ target.name = item.name;
|
|
|
+ target.image = item.image;
|
|
|
target.price = item.price;
|
|
|
selectDialogVisible.value = false;
|
|
|
ElMessage.success('选择成功');
|
|
|
@@ -4461,51 +4591,62 @@ watch(activeSubTab, (newVal) => {
|
|
|
padding: 20px 0;
|
|
|
display: flex;
|
|
|
justify-content: flex-start;
|
|
|
+ position: relative;
|
|
|
+ height: 424px; /* 384px最低高度 + padding上下40px = 424px,固定占位高度 */
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
/* 图1: 仿真菜单 */
|
|
|
.category-menu-mockup {
|
|
|
- width: 280px;
|
|
|
- height: 398px;
|
|
|
+ width: 248px;
|
|
|
+ height: 384px; /* 默认最低高度为 384px */
|
|
|
background: #f8f9fa;
|
|
|
border-radius: 4px;
|
|
|
- padding: 10px 0;
|
|
|
+ padding: 12px 12px;
|
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
|
|
- position: relative;
|
|
|
- z-index: 10;
|
|
|
- overflow: visible; /* 必须可见,否则遮罩层无法伸出 */
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ top: 20px;
|
|
|
+ z-index: 100; /* 悬浮在下方表格之上 */
|
|
|
+ overflow: hidden; /* 默认截断多余的分类 */
|
|
|
+ box-sizing: border-box;
|
|
|
+ transition:
|
|
|
+ height 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
|
|
+ box-shadow 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+/* 只有悬停在左侧背景区域才会将延伸将全部左侧菜单显示出来 */
|
|
|
+.category-menu-mockup.is-expanded {
|
|
|
+ overflow: visible; /* 必须展开可见,以便右侧面板可以向右伸出 */
|
|
|
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
|
|
}
|
|
|
|
|
|
/* 仿真菜单项 */
|
|
|
.menu-item {
|
|
|
- height: 44px;
|
|
|
+ height: 36px;
|
|
|
+ width: 248px; /* 物理宽度拉满仿真菜单的248px宽度,保证悬浮移动时无缝贴合 */
|
|
|
+ margin: 0 -12px; /* 抵消外层菜单容器左右各12px的padding,使热区无缝延伸到右侧面板 */
|
|
|
+ padding: 0 12px; /* 内部补充12px的padding以维持文字原本对齐位置 */
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- padding: 0 15px;
|
|
|
- cursor: pointer;
|
|
|
- position: relative;
|
|
|
- transition: all 0.1s;
|
|
|
- background: transparent;
|
|
|
box-sizing: border-box;
|
|
|
+ position: static; /* 必须设为static,使子级右滑面板能相对于category-menu-mockup定位实现等高 */
|
|
|
}
|
|
|
|
|
|
-.menu-item:hover {
|
|
|
- background-color: #fff !important;
|
|
|
- color: v-bind(categoryThemeColor);
|
|
|
- border: 1px solid v-bind(categoryThemeColor);
|
|
|
- border-right: none;
|
|
|
- border-radius: 12px 0 0 12px;
|
|
|
- z-index: 1000;
|
|
|
- margin-left: 10px;
|
|
|
- padding-left: 15px;
|
|
|
- width: calc(100% - 10px);
|
|
|
+.menu-item-inner {
|
|
|
+ width: 224px; /* 248px - 12px*2 = 224px */
|
|
|
+ height: 36px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ box-sizing: border-box;
|
|
|
+ cursor: pointer;
|
|
|
}
|
|
|
|
|
|
.menu-icon {
|
|
|
- width: 16px;
|
|
|
- height: 16px;
|
|
|
- margin-right: 15px; /* 增加间距 */
|
|
|
- color: #999;
|
|
|
+ width: 14px;
|
|
|
+ height: 14px;
|
|
|
+ margin-right: 8px;
|
|
|
+ color: #666;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
@@ -4525,74 +4666,83 @@ watch(activeSubTab, (newVal) => {
|
|
|
white-space: nowrap;
|
|
|
overflow: hidden;
|
|
|
text-overflow: ellipsis;
|
|
|
+ transition: color 0.15s ease;
|
|
|
}
|
|
|
|
|
|
-/* 遮盖层:仅抹除中间垂直线,保留上下边框连贯 */
|
|
|
-.menu-item:hover::after {
|
|
|
- content: '';
|
|
|
- position: absolute;
|
|
|
- top: 0; /* 保持在边框内侧 */
|
|
|
- bottom: 0; /* 保持在边框内侧 */
|
|
|
- right: -1px; /* 贴合右边缘 */
|
|
|
- width: 2px; /* 覆盖面板左边框 */
|
|
|
- background: #fff;
|
|
|
- z-index: 1001;
|
|
|
+/* 单独悬停到哪个分类,对应分类文字变色并支持点击跳转 */
|
|
|
+.sub-menu-link {
|
|
|
+ transition: all 0.15s ease;
|
|
|
+ color: #333;
|
|
|
}
|
|
|
|
|
|
-.menu-item:hover .menu-icon {
|
|
|
- color: v-bind(categoryThemeColor);
|
|
|
+.sub-menu-link:hover {
|
|
|
+ color: v-bind(categoryThemeColor) !important;
|
|
|
+ font-weight: bold;
|
|
|
+ text-decoration: underline;
|
|
|
}
|
|
|
|
|
|
-.menu-name {
|
|
|
- font-size: 14px;
|
|
|
- color: #333;
|
|
|
+.menu-sep {
|
|
|
+ margin: 0 4px;
|
|
|
+ color: #bbb;
|
|
|
+ font-weight: normal;
|
|
|
+ pointer-events: none; /* 分隔符不要响应鼠标事件 */
|
|
|
}
|
|
|
|
|
|
-.menu-item:hover .menu-name {
|
|
|
+.menu-item.is-hovered .menu-icon {
|
|
|
color: v-bind(categoryThemeColor);
|
|
|
- font-weight: bold;
|
|
|
}
|
|
|
|
|
|
-/* 图2: 右滑面板 */
|
|
|
+/* 图2: 右侧悬停面板 */
|
|
|
.category-panel-mockup {
|
|
|
position: absolute;
|
|
|
- left: 100%; /* 紧贴菜单项右侧 */
|
|
|
- margin-left: 0;
|
|
|
- top: -1px; /* 顶部边框对齐 */
|
|
|
- width: 980px;
|
|
|
- min-height: 480px;
|
|
|
+ left: 248px; /* 紧贴左侧菜单的总宽度 */
|
|
|
+ top: 0;
|
|
|
+ width: 985px; /* 右侧悬停面板宽度985px */
|
|
|
+ height: 100%; /* 悬停时右侧悬停面板与左侧面板高度一致 */
|
|
|
background: #fff;
|
|
|
box-shadow: 15px 15px 40px rgba(0, 0, 0, 0.1);
|
|
|
- border-radius: 0 12px 12px 12px;
|
|
|
+ border-radius: 12px;
|
|
|
z-index: 500;
|
|
|
border: 1px solid v-bind(categoryThemeColor);
|
|
|
display: none;
|
|
|
- flex-direction: column;
|
|
|
box-sizing: border-box;
|
|
|
cursor: default;
|
|
|
+ overflow: hidden;
|
|
|
+ transform: translateZ(0); /* 开启3D硬件加速,完美触发圆角剪裁 */
|
|
|
}
|
|
|
|
|
|
-/* 悬停时显示面板 */
|
|
|
-.menu-item:hover .category-panel-mockup {
|
|
|
+/* 悬停显示面板 */
|
|
|
+.category-panel-mockup.is-visible {
|
|
|
+ display: block;
|
|
|
+}
|
|
|
+
|
|
|
+.panel-content-flex {
|
|
|
display: flex;
|
|
|
+ height: 100%;
|
|
|
+ width: 100%;
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
-.panel-content {
|
|
|
+/* 面板左侧分类菜单与标签 */
|
|
|
+.panel-main-side {
|
|
|
flex: 1;
|
|
|
- padding: 25px 30px;
|
|
|
- position: relative; /* 为品牌位定位提供基准 */
|
|
|
+ height: 100%;
|
|
|
+ padding: 20px 24px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.panel-tabs {
|
|
|
display: flex;
|
|
|
- gap: 15px;
|
|
|
- margin-bottom: 25px;
|
|
|
- padding-right: 200px; /* 为右上角品牌位留出空间 */
|
|
|
+ gap: 12px;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
.panel-tab-item {
|
|
|
- padding: 6px 14px;
|
|
|
+ padding: 6px 12px;
|
|
|
background-color: #f5f5f5;
|
|
|
color: #666;
|
|
|
font-size: 12px;
|
|
|
@@ -4602,100 +4752,211 @@ watch(activeSubTab, (newVal) => {
|
|
|
}
|
|
|
|
|
|
.panel-tab-item:hover {
|
|
|
- background-color: #f5f5f5; /* 保持浅灰或根据主题调整 */
|
|
|
- color: v-bind(categoryThemeColor);
|
|
|
- filter: brightness(0.95);
|
|
|
+ background-color: v-bind(categoryThemeColor);
|
|
|
+ color: #fff;
|
|
|
}
|
|
|
|
|
|
-.panel-body {
|
|
|
- display: flex;
|
|
|
- flex-direction: column; /* 改为垂直布局,内容横向撑开 */
|
|
|
+/* 二级分类包裹器 - 自定义半透明主题色滚动条 */
|
|
|
+.panel-groups-container {
|
|
|
flex: 1;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding-right: 12px;
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
-.panel-main {
|
|
|
- flex: 1;
|
|
|
- overflow-y: auto;
|
|
|
- max-height: 400px;
|
|
|
+/* 自定义滚动条样式 */
|
|
|
+.panel-groups-container::-webkit-scrollbar {
|
|
|
+ width: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.panel-groups-container::-webkit-scrollbar-track {
|
|
|
+ background: transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.panel-groups-container::-webkit-scrollbar-thumb {
|
|
|
+ background: v-bind(categoryThemeColorAlpha);
|
|
|
+ border-radius: 3px;
|
|
|
+ transition: background 0.2s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.panel-groups-container::-webkit-scrollbar-thumb:hover {
|
|
|
+ background: v-bind(categoryThemeColor);
|
|
|
}
|
|
|
|
|
|
.category-group {
|
|
|
- margin-bottom: 15px;
|
|
|
+ margin-bottom: 18px;
|
|
|
display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
}
|
|
|
|
|
|
.group-title {
|
|
|
- width: 80px;
|
|
|
+ width: 90px;
|
|
|
font-size: 12px;
|
|
|
font-weight: bold;
|
|
|
color: #333;
|
|
|
flex-shrink: 0;
|
|
|
- line-height: 1.6;
|
|
|
+ line-height: 1.5;
|
|
|
+ padding-top: 1px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: color 0.15s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.group-title:hover {
|
|
|
+ color: v-bind(categoryThemeColor);
|
|
|
+ text-decoration: underline;
|
|
|
}
|
|
|
|
|
|
.group-items {
|
|
|
flex: 1;
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
- gap: 6px 15px;
|
|
|
+ gap: 8px 16px;
|
|
|
}
|
|
|
|
|
|
.group-item {
|
|
|
font-size: 12px;
|
|
|
color: #666;
|
|
|
cursor: pointer;
|
|
|
- transition: color 0.2s;
|
|
|
+ transition: color 0.15s;
|
|
|
}
|
|
|
|
|
|
.group-item:hover {
|
|
|
color: v-bind(categoryThemeColor);
|
|
|
}
|
|
|
|
|
|
-/* 品牌位移至右上角 (图3) */
|
|
|
-.panel-side {
|
|
|
- display: none; /* 移除侧边栏布局 */
|
|
|
+/* 面板右侧多张图片广告位 */
|
|
|
+.panel-ads-side {
|
|
|
+ width: 148px;
|
|
|
+ padding: 20px 16px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ flex-shrink: 0;
|
|
|
+ background: #fff;
|
|
|
+ justify-content: flex-start;
|
|
|
+ align-items: center;
|
|
|
+ overflow-y: auto;
|
|
|
+ height: 100%;
|
|
|
}
|
|
|
|
|
|
-.brand-box {
|
|
|
- position: absolute;
|
|
|
- top: 25px;
|
|
|
- right: 30px;
|
|
|
+/* 广告位的隐藏滚动条样式 */
|
|
|
+.panel-ads-side::-webkit-scrollbar {
|
|
|
+ width: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.panel-ads-side::-webkit-scrollbar-track {
|
|
|
+ background: transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.panel-ads-side::-webkit-scrollbar-thumb {
|
|
|
+ background: v-bind(categoryThemeColorAlpha);
|
|
|
+ border-radius: 2px;
|
|
|
+}
|
|
|
+
|
|
|
+.panel-ad-card {
|
|
|
+ width: 116px;
|
|
|
+ height: 54px;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
|
|
|
+ cursor: pointer;
|
|
|
+ transition:
|
|
|
+ transform 0.2s cubic-bezier(0.4, 0, 0.2, 1),
|
|
|
+ box-shadow 0.2s ease;
|
|
|
+ flex-shrink: 0;
|
|
|
+ background: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+/* 图片广告悬浮动效 */
|
|
|
+.panel-ad-card:hover {
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
|
|
+}
|
|
|
+
|
|
|
+.panel-ad-img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ display: block;
|
|
|
+}
|
|
|
+
|
|
|
+/* 广告弹窗图片上传卡片 */
|
|
|
+.custom-upload-card-wrapper {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- align-items: flex-end; /* 右对齐更美观 */
|
|
|
- z-index: 105;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
}
|
|
|
|
|
|
-.brand-main-title {
|
|
|
- font-size: 20px;
|
|
|
- font-weight: 900;
|
|
|
- color: v-bind(categoryThemeColor);
|
|
|
- margin-bottom: 4px;
|
|
|
- display: flex;
|
|
|
- align-items: baseline;
|
|
|
+.custom-upload-card {
|
|
|
+ width: 86px;
|
|
|
+ height: 86px;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px dashed #d9d9d9;
|
|
|
+ overflow: hidden;
|
|
|
+ position: relative;
|
|
|
+ cursor: pointer;
|
|
|
+ background: #fafafa;
|
|
|
+ transition: border-color 0.2s;
|
|
|
}
|
|
|
|
|
|
-.brand-strong {
|
|
|
- color: #333;
|
|
|
- margin-left: 2px;
|
|
|
- font-size: 20px;
|
|
|
+.custom-upload-card:hover {
|
|
|
+ border-color: v-bind(categoryThemeColor);
|
|
|
}
|
|
|
|
|
|
-.brand-notes {
|
|
|
+.custom-upload-card .upload-thumbnail {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: contain;
|
|
|
+}
|
|
|
+
|
|
|
+.upload-placeholder-box {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
display: flex;
|
|
|
- gap: 5px;
|
|
|
- color: v-bind(categoryThemeColor);
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 4px;
|
|
|
font-size: 12px;
|
|
|
- justify-content: flex-end;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+
|
|
|
+.upload-actions-mask {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background: rgba(0, 0, 0, 0.5);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 8px;
|
|
|
+ opacity: 0;
|
|
|
+ transition: opacity 0.2s;
|
|
|
+}
|
|
|
+
|
|
|
+.custom-upload-card:hover .upload-actions-mask {
|
|
|
+ opacity: 1;
|
|
|
}
|
|
|
|
|
|
-.note-item {
|
|
|
+.upload-actions-mask .action-btn {
|
|
|
+ width: 28px;
|
|
|
+ height: 28px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: rgba(255, 255, 255, 0.2);
|
|
|
+ color: #fff;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
cursor: pointer;
|
|
|
+ transition: background 0.2s;
|
|
|
}
|
|
|
|
|
|
-.note-sep {
|
|
|
- color: #eee;
|
|
|
- margin: 0 2px;
|
|
|
+.upload-actions-mask .action-btn:hover {
|
|
|
+ background: rgba(255, 255, 255, 0.4);
|
|
|
}
|
|
|
|
|
|
/* 工具栏主题色设置 - 专业版 */
|