|
|
@@ -1,5 +1,6 @@
|
|
|
<template>
|
|
|
<view class="search-result-page">
|
|
|
+ <page-meta :page-style="showPrompt ? 'overflow: hidden;' : ''"></page-meta>
|
|
|
<image class="top-bg-image" src="/pages-sell/static/top-bg.png" mode="widthFix"></image>
|
|
|
|
|
|
<Navbar title="书嗨" :titleSize="36" :title-color="navbarTitleColor" :back-icon-color="navbarIconColor"
|
|
|
@@ -9,7 +10,16 @@
|
|
|
<view class="search-bar-wrapper">
|
|
|
<u-search style="width: 100%;" v-model="keyword" placeholder="书名/作者/ISBN" :show-action="true"
|
|
|
action-text="反馈" :action-style="{ color: '#fff' }" bg-color="#fff" shape="round" :clearabled="true"
|
|
|
- search-icon="/pages-sell/static/search-icon.png" @custom="toFeedback" @search="onSearch"></u-search>
|
|
|
+ search-icon="/pages-sell/static/search-icon.png" @custom="toFeedback" @search="onSearch"
|
|
|
+ @focus="onSearchFocus" @blur="onSearchBlur"></u-search>
|
|
|
+ <scroll-view scroll-y class="prompt-panel" v-if="showPrompt" style="height: 100vh;">
|
|
|
+ <view class="prompt-item" v-for="(item, index) in promptList" :key="index" @click="selectPrompt(item)">
|
|
|
+ <rich-text :nodes="highlightKeyword(item, keyword)" class="prompt-text"></rich-text>
|
|
|
+ <view class="arrow-wrap" @click.stop="fillKeyword(item)">
|
|
|
+ <u-icon name="arrow-up" color="#ccc" size="28" style="transform: rotate(45deg);"></u-icon>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
</view>
|
|
|
|
|
|
<!-- Banner -->
|
|
|
@@ -129,6 +139,29 @@ export default {
|
|
|
minPrice: '',
|
|
|
maxPrice: '',
|
|
|
timestamp: 86400, // 24 hours countdown
|
|
|
+ promptList: [],
|
|
|
+ inputFocused: false,
|
|
|
+ inputTimer: null,
|
|
|
+ blurTimer: null,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ showPrompt() {
|
|
|
+ return this.inputFocused && !!String(this.keyword || '').trim() && this.promptList.length > 0;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ keyword(val) {
|
|
|
+ if (!this.inputFocused) return;
|
|
|
+ const keyword = String(val || '').trim();
|
|
|
+ if (!keyword) {
|
|
|
+ this.promptList = [];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (this.inputTimer) clearTimeout(this.inputTimer);
|
|
|
+ this.inputTimer = setTimeout(() => {
|
|
|
+ this.getPromptData(keyword);
|
|
|
+ }, 300);
|
|
|
}
|
|
|
},
|
|
|
onPullDownRefresh() {
|
|
|
@@ -247,11 +280,55 @@ export default {
|
|
|
});
|
|
|
},
|
|
|
onSearch(val) {
|
|
|
- console.log('Search:', val);
|
|
|
- this.keyword = val;
|
|
|
+ const key = String(val || this.keyword || '').replace(/<[^>]+>/g, '').trim();
|
|
|
+ if (!key) return;
|
|
|
+ this.keyword = key;
|
|
|
+ this.promptList = [];
|
|
|
+ this.inputFocused = false;
|
|
|
this.pageNum = 1;
|
|
|
this.loadData();
|
|
|
},
|
|
|
+ onSearchFocus() {
|
|
|
+ if (this.blurTimer) clearTimeout(this.blurTimer);
|
|
|
+ this.inputFocused = true;
|
|
|
+ const keyword = String(this.keyword || '').trim();
|
|
|
+ if (keyword) this.getPromptData(keyword);
|
|
|
+ },
|
|
|
+ onSearchBlur() {
|
|
|
+ // 延迟收起,避免点击联想词时先失焦导致点不到
|
|
|
+ this.blurTimer = setTimeout(() => {
|
|
|
+ this.inputFocused = false;
|
|
|
+ }, 300);
|
|
|
+ },
|
|
|
+ getPromptData(keyword) {
|
|
|
+ this.$u.api.getSearchPromptAjax({ keyword }).then(res => {
|
|
|
+ if (res.code == 200 && this.inputFocused && String(this.keyword || '').trim() === keyword) {
|
|
|
+ this.promptList = res.data || [];
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ highlightKeyword(text, keyword) {
|
|
|
+ if (!text) return '';
|
|
|
+ if (text.includes('<em>')) {
|
|
|
+ return text
|
|
|
+ .replace(/<em>/g, '<span style="color: #37C148; font-style: normal;">')
|
|
|
+ .replace(/<\/em>/g, '</span>');
|
|
|
+ }
|
|
|
+ if (!keyword) return text;
|
|
|
+ const escapedKeyword = String(keyword).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
|
+ const reg = new RegExp('(' + escapedKeyword + ')', 'gi');
|
|
|
+ return text.replace(reg, '<span style="color: #37C148; font-style: normal;">$1</span>');
|
|
|
+ },
|
|
|
+ selectPrompt(item) {
|
|
|
+ if (this.blurTimer) clearTimeout(this.blurTimer);
|
|
|
+ this.onSearch(item);
|
|
|
+ },
|
|
|
+ fillKeyword(key) {
|
|
|
+ if (!key) return;
|
|
|
+ if (this.blurTimer) clearTimeout(this.blurTimer);
|
|
|
+ this.inputFocused = true;
|
|
|
+ this.keyword = String(key).replace(/<[^>]+>/g, '');
|
|
|
+ },
|
|
|
addToCart(data) {
|
|
|
// data now contains { product, quality, quantity } from the popup
|
|
|
console.log('Added to cart:', data);
|
|
|
@@ -317,7 +394,7 @@ export default {
|
|
|
|
|
|
.search-bar-wrapper {
|
|
|
position: relative;
|
|
|
- z-index: 2;
|
|
|
+ z-index: 20;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
padding: 0 30rpx;
|
|
|
@@ -325,6 +402,33 @@ export default {
|
|
|
width: 100%;
|
|
|
}
|
|
|
|
|
|
+.prompt-panel {
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ top: 80rpx;
|
|
|
+ background: #fff;
|
|
|
+ z-index: 30;
|
|
|
+}
|
|
|
+
|
|
|
+.prompt-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 30rpx 24rpx;
|
|
|
+ border-bottom: 1rpx solid #F5F5F5;
|
|
|
+
|
|
|
+ .prompt-text {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #333;
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ .arrow-wrap {
|
|
|
+ padding: 10rpx 0 10rpx 20rpx;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
.banner-section {
|
|
|
position: relative;
|
|
|
width: 100%;
|