238 lines
5.7 KiB
Vue
238 lines
5.7 KiB
Vue
<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>
|