Browse Source

feat(search): 搜索页调整tab

master
peijunlei 2 weeks ago
parent
commit
3860885b84
  1. 98
      app/controller/frontend/SearchController.php
  2. 1
      public/themes/dist_static/static/css/search.css
  3. 30
      public/themes/dist_static/static/js/search.js
  4. 65
      public/themes/website/1/zh/demo/single_search.html

98
app/controller/frontend/SearchController.php

@ -12,6 +12,29 @@ use app\model\SysSetting;
class SearchController extends BaseController class SearchController extends BaseController
{ {
/** /**
* 按栏目标题映射搜索 Tab 类型
*/
private function resolveSearchType(string $title): string
{
if ($title === '') {
return 'other';
}
if (mb_strpos($title, '产品') !== false) {
return 'product';
}
if (mb_strpos($title, '解决方案') !== false || mb_strpos($title, '方案') !== false) {
return 'solution';
}
if (mb_strpos($title, '新闻') !== false) {
return 'news';
}
if (mb_strpos($title, '常见问题') !== false || mb_stripos($title, 'FAQ') !== false || mb_strpos($title, '问题') !== false) {
return 'faq';
}
return 'other';
}
/**
* @throws \app\exception\ModelEmptyException * @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelException * @throws \app\exception\ModelException
* @throws \think\db\exception\DbException * @throws \think\db\exception\DbException
@ -41,15 +64,24 @@ class SearchController extends BaseController
//搜索 //搜索
$param = $this->request->param(); $param = $this->request->param();
$keywords = $param['q'] ?? ''; $keywords = $param['q'] ?? '';
$this->assign('keywords',$keywords);
$type = $param['type'] ?? 'all';
if (!in_array($type, ['all', 'product', 'solution', 'news', 'faq'], true)) {
$type = 'all';
}
$this->assign('keywords', $keywords);
$this->assign('sr_type', $type);
// 获取可以查询的栏目 // 获取可以查询的栏目
$category = new Category(); $category = new Category();
// $categories = $category -> getAllCustomArrayData(['is_search' => 1],'id desc','id')['data'];
// status: 1 正常 2 禁用 // status: 1 正常 2 禁用
$categories = $category -> getAllCustomArrayData(['status' => 1],'id desc','id')['data']; $categories = $category -> getAllCustomArrayData(['status' => 1],'id desc','id')['data'];
$cateIds = array_column($categories,'id'); $cateIds = array_column($categories,'id');
$cateTypeMap = [];
foreach ($categories as $cate) {
$cateTypeMap[(int)$cate['id']] = $this->resolveSearchType((string)($cate['title'] ?? ''));
}
$cateSub = new CategorySubContent(); $cateSub = new CategorySubContent();
$cateSubWhere = [ $cateSubWhere = [
['seller_id','=',$this->siteId], ['seller_id','=',$this->siteId],
@ -58,16 +90,60 @@ class SearchController extends BaseController
$cateSubCon = $cateSub->getAllCategorySubContent($cateSubWhere)['data']; $cateSubCon = $cateSub->getAllCategorySubContent($cateSubWhere)['data'];
$subIds = array_column($cateSubCon,'sub_content_id'); $subIds = array_column($cateSubCon,'sub_content_id');
// 内容 -> 首个栏目(与列表展示 category[0] 一致)
$contentCateMap = [];
foreach ($cateSubCon as $row) {
$sid = (int)$row['sub_content_id'];
if (!isset($contentCateMap[$sid])) {
$contentCateMap[$sid] = (int)$row['category_id'];
}
}
$SubContent = new SubContent(); $SubContent = new SubContent();
$content = $SubContent->with(['thumbnail'=>function($q){
$q->field('id,name,url,type');
},'category'])->where(['seller_id'=>$this->sellerId,'is_del'=>1]);
if(!empty($param['q'])){
$content = $content -> whereLike('title|description|sub_title','%' . $keywords . '%');
$baseWhere = ['seller_id' => $this->sellerId, 'is_del' => 1];
// 关键词命中的全量 ID(用于 Tab 全量统计)
$countQuery = (new SubContent())->where($baseWhere)->whereIn('id', $subIds ?: [0]);
if (!empty($param['q'])) {
$countQuery = $countQuery->whereLike('title|description|sub_title', '%' . $keywords . '%');
}
$matchedIds = $countQuery->column('id');
$srCounts = [
'all' => count($matchedIds),
'product' => 0,
'solution' => 0,
'news' => 0,
'faq' => 0,
];
foreach ($matchedIds as $mid) {
$cid = $contentCateMap[(int)$mid] ?? 0;
$itemType = $cateTypeMap[$cid] ?? 'other';
if (isset($srCounts[$itemType])) {
$srCounts[$itemType]++;
}
} }
$this->assign('sr_counts', $srCounts);
// 按 Tab 类型筛选列表用的内容 ID
$listSubIds = $matchedIds;
if ($type !== 'all') {
$listSubIds = [];
foreach ($matchedIds as $mid) {
$cid = $contentCateMap[(int)$mid] ?? 0;
if (($cateTypeMap[$cid] ?? 'other') === $type) {
$listSubIds[] = (int)$mid;
}
}
}
$content = (new SubContent())->with(['thumbnail'=>function($q){
$q->field('id,name,url,type');
},'category'])->where($baseWhere)->whereIn('id', $listSubIds ?: [0]);
$param['siteId'] = $this->siteId; $param['siteId'] = $this->siteId;
$param['sellerId'] = $this->sellerId; $param['sellerId'] = $this->sellerId;
$data = $content->whereIn('id',$subIds)
$data = $content
->order('id','desc') ->order('id','desc')
->paginate([ ->paginate([
'page' => $param['page'] ?? 1, 'page' => $param['page'] ?? 1,
@ -79,7 +155,11 @@ class SearchController extends BaseController
} }
$item['thumbnail'] = $item->toArray()['thumbnail']; $item['thumbnail'] = $item->toArray()['thumbnail'];
}); });
$data->appends(['q' => $keywords]);
$append = ['q' => $keywords];
if ($type !== 'all') {
$append['type'] = $type;
}
$data->appends($append);
$this->assign('list', $data->items()); $this->assign('list', $data->items());
$this->assign('list_page', $data->render()); $this->assign('list_page', $data->render());
return $this->fetch(config('view.view_search_page_name')); return $this->fetch(config('view.view_search_page_name'));

1
public/themes/dist_static/static/css/search.css

@ -93,6 +93,7 @@
font-size: var(--font-18); font-size: var(--font-18);
font-weight: 400; font-weight: 400;
line-height: var(--font-24); line-height: var(--font-24);
text-decoration: none;
cursor: pointer; cursor: pointer;
transition: color var(--duration-fast) var(--easing); transition: color var(--duration-fast) var(--easing);
} }

30
public/themes/dist_static/static/js/search.js

@ -1,43 +1,13 @@
import "./common.js"; import "./common.js";
import "./pagination.js"; import "./pagination.js";
//#region js/search.js //#region js/search.js
function matchItem($item, type) {
const itemType = $item.attr("data-type") || "";
return type === "all" || itemType === type;
}
function updateCounts($items) {
["all", "product", "solution", "news", "faq"].forEach((type) => {
const count = $items.filter((_, el) => matchItem($(el), type)).length;
$(`[data-count="${type}"]`).text(String(count));
});
}
function applyFilter() {
const type = $(".sr-tabs__item.is-active").attr("data-type") || "all";
const $items = $(".sr-item");
let visible = 0;
$items.each(function() {
const show = matchItem($(this), type);
this.hidden = !show;
if (show) visible += 1;
});
updateCounts($items);
const $empty = $(".sr-tabs-empty");
if ($empty.length) $empty.prop("hidden", visible > 0 || !$items.length);
}
$(function() { $(function() {
const q = new URLSearchParams(window.location.search).get("q")?.trim() || ""; const q = new URLSearchParams(window.location.search).get("q")?.trim() || "";
if (q) $(".sr-search .s-search__input").val(q); if (q) $(".sr-search .s-search__input").val(q);
$(".sr-tabs__item").on("click", function() {
const $btn = $(this);
$btn.addClass("is-active").attr("aria-selected", "true");
$btn.siblings(".sr-tabs__item").removeClass("is-active").attr("aria-selected", "false");
applyFilter();
});
$(".sr-hot__tag").on("click", function() { $(".sr-hot__tag").on("click", function() {
const keyword = $(this).attr("data-keyword") || $(this).text().trim(); const keyword = $(this).attr("data-keyword") || $(this).text().trim();
$(".sr-search .s-search__input").val(keyword); $(".sr-search .s-search__input").val(keyword);
$(".sr-search").trigger("submit"); $(".sr-search").trigger("submit");
}); });
if ($(".sr-tabs").length) applyFilter();
}); });
//#endregion //#endregion

65
public/themes/website/1/zh/demo/single_search.html

@ -86,22 +86,40 @@
</div> </div>
</div> </div>
{php}
$sr_type_cur = $sr_type ?? 'all';
$sr_q = $keywords ?? '';
$sr_counts = $sr_counts ?? ['all' => 0, 'product' => 0, 'solution' => 0, 'news' => 0, 'faq' => 0];
$sr_tabs = [];
foreach (['all', 'product', 'solution', 'news', 'faq'] as $tabType) {
$query = ['type' => $tabType];
if ($sr_q !== '') {
$query['q'] = $sr_q;
}
$sr_tabs[$tabType] = '/search.html?' . http_build_query($query);
}
{/php}
<div class="sr-tabs" role="tablist" aria-label="结果分类"> <div class="sr-tabs" role="tablist" aria-label="结果分类">
<button type="button" class="sr-tabs__item is-active" role="tab" aria-selected="true" data-type="all">
全部 ( <span data-count="all">0</span> )
</button>
<button type="button" class="sr-tabs__item" role="tab" aria-selected="false" data-type="product">
产品 ( <span data-count="product">0</span> )
</button>
<button type="button" class="sr-tabs__item" role="tab" aria-selected="false" data-type="solution">
解决方案 ( <span data-count="solution">0</span> )
</button>
<button type="button" class="sr-tabs__item" role="tab" aria-selected="false" data-type="news">
新闻 ( <span data-count="news">0</span> )
</button>
<button type="button" class="sr-tabs__item" role="tab" aria-selected="false" data-type="faq">
常见问题 ( <span data-count="faq">0</span> )
</button>
<a class="sr-tabs__item {if $sr_type_cur=='all'}is-active{/if}" role="tab" aria-selected="{if $sr_type_cur=='all'}true{else /}false{/if}"
href="{$sr_tabs.all}">
全部 ( <span data-count="all">{$sr_counts.all}</span> )
</a>
<a class="sr-tabs__item {if $sr_type_cur=='product'}is-active{/if}" role="tab" aria-selected="{if $sr_type_cur=='product'}true{else /}false{/if}"
href="{$sr_tabs.product}">
产品 ( <span data-count="product">{$sr_counts.product}</span> )
</a>
<a class="sr-tabs__item {if $sr_type_cur=='solution'}is-active{/if}" role="tab" aria-selected="{if $sr_type_cur=='solution'}true{else /}false{/if}"
href="{$sr_tabs.solution}">
解决方案 ( <span data-count="solution">{$sr_counts.solution}</span> )
</a>
<a class="sr-tabs__item {if $sr_type_cur=='news'}is-active{/if}" role="tab" aria-selected="{if $sr_type_cur=='news'}true{else /}false{/if}"
href="{$sr_tabs.news}">
新闻 ( <span data-count="news">{$sr_counts.news}</span> )
</a>
<a class="sr-tabs__item {if $sr_type_cur=='faq'}is-active{/if}" role="tab" aria-selected="{if $sr_type_cur=='faq'}true{else /}false{/if}"
href="{$sr_tabs.faq}">
常见问题 ( <span data-count="faq">{$sr_counts.faq}</span> )
</a>
</div> </div>
<div class="sr-list"> <div class="sr-list">
@ -115,19 +133,8 @@
$sr_cid = $vo['category_id'] ?? ($vo['category'][0]['id'] ?? 0); $sr_cid = $vo['category_id'] ?? ($vo['category'][0]['id'] ?? 0);
$sr_thumb = is_array($vo['thumbnail'] ?? null) ? ($vo['thumbnail']['url'] ?? '') : ''; $sr_thumb = is_array($vo['thumbnail'] ?? null) ? ($vo['thumbnail']['url'] ?? '') : '';
$sr_url = hcUrl('detail/index', ['id' => $vo['id'], 'cid' => $sr_cid]); $sr_url = hcUrl('detail/index', ['id' => $vo['id'], 'cid' => $sr_cid]);
$sr_cate_title = $vo['category'][0]['title'] ?? '';
$sr_type = 'other';
if (mb_strpos($sr_cate_title, '产品') !== false) {
$sr_type = 'product';
} elseif (mb_strpos($sr_cate_title, '解决方案') !== false || mb_strpos($sr_cate_title, '方案') !== false) {
$sr_type = 'solution';
} elseif (mb_strpos($sr_cate_title, '新闻') !== false) {
$sr_type = 'news';
} elseif (mb_strpos($sr_cate_title, '常见问题') !== false || mb_strpos($sr_cate_title, 'FAQ') !== false || mb_strpos($sr_cate_title, '问题') !== false) {
$sr_type = 'faq';
}
{/php} {/php}
<article class="sr-item" data-type="{$sr_type}" data-keywords="{$vo.title}">
<article class="sr-item" data-keywords="{$vo.title}">
<a class="sr-item__media" href="{$sr_url}"> <a class="sr-item__media" href="{$sr_url}">
<img src="{$sr_thumb}" alt="" width="300" height="200" loading="lazy" /> <img src="{$sr_thumb}" alt="" width="300" height="200" loading="lazy" />
</a> </a>
@ -157,10 +164,6 @@
{/volist} {/volist}
</div> </div>
<div class="s-empty sr-tabs-empty" hidden>
<p class="s-empty__text">当前分类下暂无结果</p>
</div>
{php} {php}
$list_empty = isHcListEmpty($list ?? null); $list_empty = isHcListEmpty($list ?? null);
if ($list_empty) { if ($list_empty) {

Loading…
Cancel
Save