Files
lgp-wx-plus/components/theme/ThemeSearch.vue
2026-08-19 08:17:18 +08:00

238 lines
5.7 KiB
Vue
Raw Permalink 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>
<!-- 主题搜索条:细描边 + 线框图标,不用 uview 灰底大按钮 -->
<view
class="ts"
:class="['ts--' + variant, { 'is-on': focused, 'is-off': disabled }]"
:style="wrapStyle"
@tap="onWrapTap"
>
<view class="ts-ico">
<view class="ts-ring" :style="{ borderColor: iconColor }" />
<view class="ts-stem" :style="{ background: iconColor }" />
</view>
<input
class="ts-field"
:style="{ color: text }"
:value="keyword"
:disabled="disabled"
:placeholder="placeholder"
:placeholder-style="phStyle"
confirm-type="search"
@input="onInput"
@confirm="emitSearch"
@focus="focused = true"
@blur="focused = false"
/>
<view v-if="keyword && !disabled" class="ts-x" @tap.stop="clear">
<view class="ts-x-bar ts-x-a" :style="{ background: textSoft }" />
<view class="ts-x-bar ts-x-b" :style="{ background: textSoft }" />
</view>
<view
v-if="showAction"
class="ts-go"
:style="{ color: primary, borderColor: hairline }"
@tap.stop="emitSearch"
>
{{ actionText }}
</view>
</view>
</template>
<script>
import { getActiveVars } from '@/utils/theme'
/**
* 全站搜索框。首页只作入口(disabled + 整条可点),列表/搜索页可输入。
* 形态跟装修 search 变体:bar / pill / line / overlay / float。
*/
export default {
name: 'ThemeSearch',
props: {
// Vue3 v-model;value 只作兼容,避免旧写法丢词
modelValue: { type: String, default: '' },
value: { type: String, default: '' },
placeholder: { type: String, default: '搜索心仪的产品型号' },
disabled: { type: Boolean, default: false },
showAction: { type: Boolean, default: true },
actionText: { type: String, default: '检索' },
variant: { type: String, default: 'bar' },
},
data() {
return { focused: false }
},
computed: {
keyword() {
return this.modelValue !== '' ? this.modelValue : this.value
},
vars() {
return getActiveVars() || {}
},
primary() {
return this.vars['--color-primary'] || '#B08D57'
},
surface() {
return this.vars['--color-surface'] || '#FFFFFF'
},
soft() {
return this.vars['--color-bg-soft'] || this.vars['--color-surface-soft'] || '#F6F1EA'
},
text() {
return this.vars['--color-text'] || '#1C1917'
},
textSoft() {
return this.vars['--color-text-soft'] || '#8A8178'
},
border() {
return this.vars['--color-border'] || '#E7DFD3'
},
hairline() {
return this.hexAlpha(this.primary, 0.28)
},
iconColor() {
return this.focused ? this.primary : this.textSoft
},
phStyle() {
return `color:${this.textSoft};font-size:26rpx;letter-spacing:1rpx;`
},
wrapStyle() {
const radius = this.variant === 'pill' ? '999rpx' : this.vars['--radius-md'] || '16rpx'
if (this.variant === 'line') {
return {
background: 'transparent',
borderColor: this.focused ? this.primary : this.border,
borderRadius: '0',
}
}
if (this.variant === 'overlay' || this.variant === 'float') {
return {
background: this.hexAlpha(this.surface, 0.92),
borderColor: this.focused ? this.primary : this.hairline,
borderRadius: radius,
}
}
return {
background: this.soft,
borderColor: this.focused ? this.primary : this.hairline,
borderRadius: radius,
}
},
},
methods: {
sync(next) {
this.$emit('update:modelValue', next)
this.$emit('input', next)
},
onInput(e) {
this.sync((e && e.detail && e.detail.value) || '')
},
emitSearch() {
this.$emit('search', this.keyword || '')
},
onWrapTap() {
if (this.disabled) {
this.$emit('click')
}
},
clear() {
this.sync('')
},
hexAlpha(hex, alpha) {
let raw = String(hex || '').replace('#', '')
if (raw.length === 3) {
raw = raw[0] + raw[0] + raw[1] + raw[1] + raw[2] + raw[2]
}
if (raw.length !== 6) {
return 'rgba(176,141,87,' + alpha + ')'
}
const n = parseInt(raw, 16)
const r = (n >> 16) & 255
const g = (n >> 8) & 255
const b = n & 255
return 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')'
},
},
}
</script>
<style lang="scss" scoped>
.ts {
display: flex;
align-items: center;
height: 76rpx;
padding: 0 8rpx 0 24rpx;
border: 1rpx solid;
box-sizing: border-box;
}
.ts--line {
height: 72rpx;
padding: 0 4rpx 0 8rpx;
border-width: 0;
border-bottom-width: 1rpx;
}
.ts--float {
box-shadow: 0 12rpx 32rpx rgba(28, 25, 23, 0.06);
}
.ts-ico {
position: relative;
width: 28rpx;
height: 28rpx;
flex-shrink: 0;
margin-right: 16rpx;
}
.ts-ring {
width: 20rpx;
height: 20rpx;
border: 2rpx solid;
border-radius: 50%;
box-sizing: border-box;
}
.ts-stem {
position: absolute;
right: 0;
bottom: 1rpx;
width: 10rpx;
height: 2rpx;
border-radius: 2rpx;
transform: rotate(42deg);
transform-origin: left center;
}
.ts-field {
flex: 1;
min-width: 0;
height: 76rpx;
font-size: 26rpx;
letter-spacing: 1rpx;
background: transparent;
}
.ts-x {
position: relative;
width: 36rpx;
height: 36rpx;
flex-shrink: 0;
}
.ts-x-bar {
position: absolute;
left: 10rpx;
top: 17rpx;
width: 16rpx;
height: 2rpx;
border-radius: 2rpx;
}
.ts-x-a {
transform: rotate(45deg);
}
.ts-x-b {
transform: rotate(-45deg);
}
.ts-go {
flex-shrink: 0;
height: 52rpx;
margin-left: 8rpx;
padding: 0 20rpx;
border-left: 1rpx solid;
font-size: 22rpx;
letter-spacing: 4rpx;
line-height: 52rpx;
}
</style>