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>
|