feat(buyer): 统一全站页面顶部为现代TopBar与SearchHeader,支持商品/店铺双模搜索与占位词规范

This commit is contained in:
pikachu1995@126.com
2026-10-29 17:05:53 +08:00
parent d113f5c553
commit 5973d43b9d
15 changed files with 579 additions and 181 deletions

View File

@@ -45,7 +45,7 @@
<div class="section-title">搜索设置</div>
<div class="form-row">
<span class="row-label">搜索占位</span>
<el-input v-model="options.placeholder" size="small" placeholder="请输入商品名称、品牌或型号" class="flex-1" />
<el-input v-model="options.placeholder" size="small" placeholder="请输入商品名称" class="flex-1" />
</div>
<div class="form-row mt-10 flex-between">
@@ -57,6 +57,24 @@
<span class="row-label">显示热门推荐词</span>
<el-switch v-model="options.showHotWords" size="small" />
</div>
<div class="form-row mt-10 flex-between">
<span class="row-label">商品/店铺搜索切换</span>
<el-switch v-model="options.showSearchTypes" size="small" />
</div>
<div class="form-row mt-10" v-if="options.showSearchTypes !== false">
<span class="row-label">默认搜索类型</span>
<el-radio-group v-model="options.defaultSearchType" size="small">
<el-radio-button label="goods">商品</el-radio-button>
<el-radio-button label="store">店铺</el-radio-button>
</el-radio-group>
</div>
<div class="form-row mt-10" v-if="options.showSearchTypes !== false">
<span class="row-label">店铺占位文案</span>
<el-input v-model="options.storePlaceholder" size="small" placeholder="请输入店铺名称" class="flex-1" />
</div>
</div>
@@ -147,9 +165,19 @@ const props = defineProps({
required: true
}
});
if (props.options.showSearchTypes === undefined) {
props.options.showSearchTypes = true;
}
if (!props.options.defaultSearchType) {
props.options.defaultSearchType = 'goods';
}
if (!props.options.storePlaceholder) {
props.options.storePlaceholder = '请输入店铺名称';
}
if (props.options.logoLink === undefined) props.options.logoLink = '/';
if (props.options.placeholder === undefined) props.options.placeholder = '请输入商品名称、品牌或型号';
if (props.options.placeholder === undefined) props.options.placeholder = '请输入商品名称';
if (props.options.showCart === undefined) props.options.showCart = true;
if (props.options.showHotWords === undefined) props.options.showHotWords = true;
if (props.options.cartText === undefined) props.options.cartText = '购物车';

View File

@@ -12,10 +12,28 @@
<span v-else class="text-logo">LiliShop</span>
</div>
<!-- 2. 搜索核心区(紧凑居中,含搜索框和热门推荐词) -->
<!-- 2. 搜索核心区(紧凑居中,含商品/店铺 Tab、搜索框和热门推荐词) -->
<div class="search-main">
<!-- 搜索类型切换:商品 / 店铺 -->
<div class="search-tabs" v-if="options.showSearchTypes !== false">
<span
class="tab-item"
:class="{ active: (activeTab || options.defaultSearchType || 'goods') === 'goods' }"
@click.stop="activeTab = 'goods'"
>
商品
</span>
<span
class="tab-item"
:class="{ active: (activeTab || options.defaultSearchType || 'goods') === 'store' }"
@click.stop="activeTab = 'store'"
>
店铺
</span>
</div>
<!-- 搜索输入框与按钮 -->
<div class="search-box">
<div class="search-box" :class="{ 'tab-store-active': (activeTab || options.defaultSearchType || 'goods') === 'store' }">
<input
type="text"
class="mock-input"
@@ -29,7 +47,7 @@
</div>
<!-- 热门推荐词 -->
<div class="hot-words" v-if="options.showHotWords !== false && hotWordsList.length">
<div class="hot-words" v-if="options.showHotWords !== false && (activeTab || options.defaultSearchType || 'goods') === 'goods' && hotWordsList.length">
<span
v-for="(w, idx) in hotWordsList"
:key="idx"
@@ -55,7 +73,7 @@
</template>
<script setup>
import { computed } from 'vue';
import { computed, ref, watch } from 'vue';
import { Search, ShoppingCart } from '@element-plus/icons-vue';
const props = defineProps({
@@ -69,8 +87,27 @@ const props = defineProps({
}
});
const activeTab = ref(props.options?.defaultSearchType || 'goods');
watch(
() => props.options?.defaultSearchType,
(v) => {
if (v) activeTab.value = v;
}
);
const placeholderText = computed(() => {
return props.options?.placeholder || '请输入商品名称、品牌或型号';
const currentType = activeTab.value || props.options?.defaultSearchType || 'goods';
if (currentType === 'store') {
const sph = props.options?.storePlaceholder;
if (!sph || sph === '请输入店铺名称、品牌或经营范围') return '请输入店铺名称';
return sph;
}
const ph = props.options?.placeholder;
if (!ph || ph === '搜索品牌或商品' || ph === '请输入商品名称、品牌或型号') {
return '请输入商品名称';
}
return ph;
});
const hotWordsList = computed(() => {
@@ -84,7 +121,7 @@ const customStyle = computed(() => {
const s = props.componentStyle || {};
return {
backgroundColor: s.backgroundColor || '#ffffff',
minHeight: `${s.height || 90}px`
minHeight: `${s.height || 120}px`
};
});
@@ -110,11 +147,12 @@ const cartBtnStyle = computed(() => {
.header-inner {
width: 1200px;
max-width: 100%;
padding: 18px 20px 14px;
margin: 0 auto;
padding: 16px 0 14px 0;
box-sizing: border-box;
display: flex;
align-items: flex-start;
justify-content: center; /* 紧凑聚拢居中,Logo、搜索框、购物车不向大屏两端散落 */
gap: 42px; /* Logo、搜索框、购物车三者紧凑贴近 */
align-items: center;
justify-content: space-between;
.logo-area {
height: 40px;
@@ -140,17 +178,52 @@ const cartBtnStyle = computed(() => {
}
.search-main {
width: 560px;
width: 580px;
flex-shrink: 0;
margin: 0 auto;
.search-tabs {
display: flex;
align-items: flex-end;
gap: 4px;
margin-bottom: 0px;
padding-left: 2px;
z-index: 2;
.tab-item {
padding: 4px 16px;
font-size: 13px;
font-weight: 500;
color: #64748b;
cursor: pointer;
border-radius: 4px 4px 0 0;
transition: all 0.15s ease;
user-select: none;
&:hover {
color: #409EFF;
}
&.active {
color: #ffffff;
background: #409EFF;
font-weight: 600;
}
}
}
.search-box {
display: flex;
border: 2px solid #409EFF;
border-radius: 4px;
border-radius: 0 4px 4px 4px;
overflow: hidden;
height: 40px;
box-sizing: border-box;
&.tab-store-active {
border-radius: 4px;
}
.mock-input {
flex: 1;
border: none;
@@ -185,7 +258,7 @@ const cartBtnStyle = computed(() => {
}
.hot-words {
margin-top: 8px;
margin-top: 6px;
display: flex;
align-items: center;
gap: 12px;