diff --git a/app/controller/frontend/SearchController.php b/app/controller/frontend/SearchController.php
index 9d3e815..a55f258 100644
--- a/app/controller/frontend/SearchController.php
+++ b/app/controller/frontend/SearchController.php
@@ -4,40 +4,132 @@
namespace app\controller\frontend;
-use app\exception\ModelEmptyException;
-use app\exception\ModelException;
use app\model\Category;
use app\model\CategorySubContent;
use app\model\SubContent;
use app\model\SysSetting;
-use app\service\ContentService;
-use app\validate\ContentValidate;
-use think\exception\ValidateException;
-use think\facade\Db;
class SearchController extends BaseController
{
+ /** 产品根栏目 */
+ private const PRODUCT_CATE_ID = 333;
+
+ /** 新闻根栏目 */
+ private const NEWS_CATE_ID = 361;
+
+ // 下载中心
+ private const DOWNLOAD_CATE_ID = 356;
+
+ /** 常见问题根栏目 */
+ private const FAQ_CATE_ID = 357;
+
+ // 视频教程
+ private const VIDEO_CATE_ID = 358;
+
+ //产品公告
+ private const ANNOUNCEMENT_CATE_ID = 359;
+
/**
- * 按栏目标题映射搜索 Tab 类型
+ * 获取栏目自身及全部子栏目 ID
*/
- private function resolveSearchType(string $title): string
+ private function getCategoryTreeIds(int $rootId): array
{
- if ($title === '') {
- return 'other';
+ $category = new Category();
+ $root = $category->where([
+ 'id' => $rootId,
+ 'seller_id' => $this->sellerId,
+ 'website_id' => $this->siteId,
+ ])->find();
+ if (empty($root)) {
+ return [$rootId];
}
- if (mb_strpos($title, '产品') !== false) {
- return 'product';
+ $root = $root->toArray();
+ $parentPath = ($root['path'] ?? '') . $root['id'] . '-';
+ $childIds = $category->where([
+ ['seller_id', '=', $this->sellerId],
+ ['website_id', '=', $this->siteId],
+ ['path', 'like', $parentPath . '%'],
+ ])->column('id');
+
+ return array_values(array_unique(array_merge(
+ [(int)$root['id']],
+ array_map('intval', $childIds ?: [])
+ )));
+ }
+
+ /**
+ * 按栏目 ID 取关联内容 ID
+ */
+ private function getContentIdsByCategoryIds(array $cateIds): array
+ {
+ if (empty($cateIds)) {
+ return [];
}
- if (mb_strpos($title, '解决方案') !== false || mb_strpos($title, '方案') !== false) {
- return 'solution';
+ $cateSub = new CategorySubContent();
+ $ids = $cateSub->where([
+ ['seller_id', '=', $this->siteId],
+ ['category_id', 'in', $cateIds],
+ ])->column('sub_content_id');
+
+ return array_values(array_unique(array_map('intval', $ids ?: [])));
+ }
+
+ /**
+ * 关键词命中的内容 ID
+ */
+ private function getMatchedContentIds(array $subIds, string $keywords, bool $hasKeyword): array
+ {
+ $query = (new SubContent())->where([
+ 'seller_id' => $this->sellerId,
+ 'is_del' => 1,
+ ])->whereIn('id', $subIds ?: [0]);
+ if ($hasKeyword) {
+ $query = $query->whereLike('title|description|sub_title', '%' . $keywords . '%');
}
- if (mb_strpos($title, '新闻') !== false) {
- return 'news';
+ $ids = $query->column('id');
+
+ return array_map('intval', $ids ?: []);
+ }
+
+ /**
+ * 统一格式化为 Y-m-d
+ */
+ private function formatSearchDate($time): string
+ {
+ if ($time === null || $time === '') {
+ return '';
}
- if (mb_strpos($title, '常见问题') !== false || mb_stripos($title, 'FAQ') !== false || mb_strpos($title, '问题') !== false) {
- return 'faq';
+ if (is_numeric($time)) {
+ return date('Y-m-d', (int)$time);
}
- return 'other';
+ $ts = strtotime((string)$time);
+ return $ts ? date('Y-m-d', $ts) : substr((string)$time, 0, 10);
+ }
+
+ /**
+ * 解析搜索结果类型
+ */
+ private function resolveSearchKind(int $contentId, array $sets): array
+ {
+ if (isset($sets['product'][$contentId])) {
+ return ['product', '产品'];
+ }
+ if (isset($sets['news'][$contentId])) {
+ return ['news', '新闻'];
+ }
+ if (isset($sets['faq'][$contentId])) {
+ return ['faq', '常见问题'];
+ }
+ if (isset($sets['download'][$contentId])) {
+ return ['download', '下载中心'];
+ }
+ if (isset($sets['video'][$contentId])) {
+ return ['video', '视频'];
+ }
+ if (isset($sets['announce'][$contentId])) {
+ return ['announce', '公告'];
+ }
+ return ['content', '内容'];
}
/**
@@ -70,99 +162,120 @@ class SearchController extends BaseController
//搜索
$param = $this->request->param();
$keywords = $param['q'] ?? '';
+ $hasKeyword = !empty($param['q']);
$type = $param['type'] ?? 'all';
- if (!in_array($type, ['all', 'product', 'solution', 'news', 'faq'], true)) {
+ if (!in_array($type, ['all', 'product', 'other', 'news', 'faq'], true)) {
$type = 'all';
}
$this->assign('keywords', $keywords);
$this->assign('sr_type', $type);
- // 获取可以查询的栏目
- $category = new Category();
- $categories = $category -> getAllCustomArrayData(['is_search' => 1],'id desc','id')['data'];
- // 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');
-
- $cateTypeMap = [];
- foreach ($categories as $cate) {
- $cateTypeMap[(int)$cate['id']] = $this->resolveSearchType((string)($cate['title'] ?? ''));
- }
-
+ // 全部:可搜索栏目(is_search=1)
+ $categoryModel = new Category();
+ $searchCategories = $categoryModel->getAllCustomArrayData(['is_search' => 1], 'id desc', 'id')['data'];
+ $searchCateIds = array_column($searchCategories, 'id');
$cateSub = new CategorySubContent();
- $cateSubWhere = [
- ['seller_id','=',$this->siteId],
- ['category_id','in',$cateIds]
- ];
- $cateSubCon = $cateSub->getAllCategorySubContent($cateSubWhere)['data'];
- $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'];
- }
- }
+ $searchSubIds = array_column($cateSub->getAllCategorySubContent([
+ ['seller_id', '=', $this->siteId],
+ ['category_id', 'in', $searchCateIds ?: [0]],
+ ])['data'], 'sub_content_id');
- $SubContent = new SubContent();
- $baseWhere = ['seller_id' => $this->sellerId, 'is_del' => 1];
+ // 各 Tab 固定根栏目(含子栏目)
+ $productContentIds = $this->getContentIdsByCategoryIds($this->getCategoryTreeIds(self::PRODUCT_CATE_ID));
+ $newsContentIds = $this->getContentIdsByCategoryIds($this->getCategoryTreeIds(self::NEWS_CATE_ID));
+ $faqContentIds = $this->getContentIdsByCategoryIds($this->getCategoryTreeIds(self::FAQ_CATE_ID));
- // 关键词命中的全量 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');
+ // 其他 = 下载中心 + 视频 + 公告
+ $downloadContentIds = $this->getContentIdsByCategoryIds($this->getCategoryTreeIds(self::DOWNLOAD_CATE_ID));
+ $videoContentIds = $this->getContentIdsByCategoryIds($this->getCategoryTreeIds(self::VIDEO_CATE_ID));
+ $announceContentIds = $this->getContentIdsByCategoryIds($this->getCategoryTreeIds(self::ANNOUNCEMENT_CATE_ID));
+ $otherContentIds = array_values(array_unique(array_merge(
+ $downloadContentIds,
+ $videoContentIds,
+ $announceContentIds
+ )));
+
+ $allMatchedIds = $this->getMatchedContentIds($searchSubIds, $keywords, $hasKeyword);
+ $productMatchedIds = $this->getMatchedContentIds($productContentIds, $keywords, $hasKeyword);
+ $newsMatchedIds = $this->getMatchedContentIds($newsContentIds, $keywords, $hasKeyword);
+ $faqMatchedIds = $this->getMatchedContentIds($faqContentIds, $keywords, $hasKeyword);
+ $otherMatchedIds = $this->getMatchedContentIds($otherContentIds, $keywords, $hasKeyword);
$srCounts = [
- 'all' => count($matchedIds),
- 'product' => 0,
- 'solution' => 0,
- 'news' => 0,
- 'faq' => 0,
+ 'all' => count($allMatchedIds),
+ 'product' => count($productMatchedIds),
+ 'other' => count($otherMatchedIds),
+ 'news' => count($newsMatchedIds),
+ 'faq' => count($faqMatchedIds),
];
- 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;
- }
- }
+ // 列表:全部不变;其余按对应栏目查
+ if ($type === 'product') {
+ $listSubIds = $productMatchedIds;
+ } elseif ($type === 'news') {
+ $listSubIds = $newsMatchedIds;
+ } elseif ($type === 'faq') {
+ $listSubIds = $faqMatchedIds;
+ } elseif ($type === 'other') {
+ $listSubIds = $otherMatchedIds;
+ } else {
+ $listSubIds = $allMatchedIds;
}
+ $baseWhere = ['seller_id' => $this->sellerId, 'is_del' => 1];
$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['sellerId'] = $this->sellerId;
+ $productIdSet = array_flip($productContentIds);
+ $newsIdSet = array_flip($newsContentIds);
+ $faqIdSet = array_flip($faqContentIds);
+ $downloadIdSet = array_flip($downloadContentIds);
+ $videoIdSet = array_flip($videoContentIds);
+ $announceIdSet = array_flip($announceContentIds);
+ $kindSets = [
+ 'product' => $productIdSet,
+ 'news' => $newsIdSet,
+ 'faq' => $faqIdSet,
+ 'download' => $downloadIdSet,
+ 'video' => $videoIdSet,
+ 'announce' => $announceIdSet,
+ ];
+ $tabKindMap = [
+ 'product' => ['product', '产品'],
+ 'news' => ['news', '新闻'],
+ 'faq' => ['faq', '常见问题'],
+ ];
+
$data = $content
->order('id','desc')
->paginate([
'page' => $param['page'] ?? 1,
'list_rows' => $param['limit'] ?? 10,
- ])->each(function (&$item)use($param){
- if(!empty($item['category'])) {
- $item['category'] = $item->toArray()['category'];
- $item['category_id'] = $item['category'][0]['id'];
+ ])->each(function (&$item) use ($type, $tabKindMap, $kindSets) {
+ $row = $item->toArray();
+ if (!empty($row['category'])) {
+ $item['category'] = $row['category'];
+ $item['category_id'] = $row['category'][0]['id'] ?? 0;
+ }
+ $item['thumbnail'] = $row['thumbnail'] ?? null;
+ $item['url'] = $row['url'] ?? '';
+ $rawContent = $row['content'] ?? '';
+ // 与 ContentService / 常见问题页一致:先解码再交给模板 strip_tags
+ $item['content'] = $rawContent !== '' ? htmlspecialchars_decode((string)$rawContent) : '';
+ $item['sr_date'] = $this->formatSearchDate($row['publish_time'] ?? ($row['create_time'] ?? ''));
+
+ if (isset($tabKindMap[$type])) {
+ [$kind, $name] = $tabKindMap[$type];
+ } else {
+ [$kind, $name] = $this->resolveSearchKind((int)($row['id'] ?? 0), $kindSets);
+ if ($kind === 'content' && !empty($row['category'][0]['title'])) {
+ $name = $row['category'][0]['title'];
+ }
}
- $item['thumbnail'] = $item->toArray()['thumbnail'];
+ $item['sr_kind'] = $kind;
+ $item['sr_tab_name'] = $name;
});
$append = ['q' => $keywords];
if ($type !== 'all') {
diff --git a/public/themes/dist_static/static/css/search.css b/public/themes/dist_static/static/css/search.css
index 5b70d6e..60f3542 100644
--- a/public/themes/dist_static/static/css/search.css
+++ b/public/themes/dist_static/static/css/search.css
@@ -231,6 +231,70 @@
color: var(--color-primary);
}
+/* 分类型:统一左右布局,仅叠加交互样式(视频播放与视频教程页一致) */
+.sr-item--video .sr-item__media--video {
+ position: relative;
+ padding: 0;
+ border: 0;
+ background: transparent;
+ cursor: pointer;
+ overflow: hidden;
+ text-align: left;
+ appearance: none;
+ -webkit-appearance: none;
+}
+.sr-item--video .v-card__media {
+ width: 100%;
+ height: 100%;
+ aspect-ratio: auto;
+ border-radius: inherit;
+}
+.sr-item--video .v-card__cover {
+ object-fit: cover;
+}
+.sr-item--video .sr-item__media--video:hover .v-card__cover {
+ transform: scale(1.04);
+}
+.sr-item--video .sr-item__media--video:hover .v-card__play {
+ opacity: 1;
+ transform: translate(-50%, -50%) scale(1);
+}
+.sr-item--video .v-card__play img,
+.sr-item--video .sr-item__media:hover .v-card__play img {
+ width: 1rem;
+ height: auto;
+ margin-left: 0.125rem;
+ object-fit: contain;
+ transform: none;
+}
+.sr-item--faq .sr-item__faq {
+ margin: 0;
+ border-bottom: 0;
+ border-top: none;
+}
+.sr-item--faq .f-item__trigger {
+ padding-left: 0;
+ padding-right: 0;
+}
+.sr-item--faq .f-item__panel > .f-item__a {
+ min-height: 0;
+}
+.sr-item--faq .f-item.is-open .f-item__a {
+ padding-left: 1.25rem;
+ padding-right: 1.25rem;
+}
+.sr-item__more--download img,
+.sr-item__more--product .s-product-card__arrow {
+ display: block;
+ width: 1.25rem;
+ height: auto;
+ flex-shrink: 0;
+}
+.sr-item__more--download:hover,
+.sr-item__more--product:hover {
+ color: var(--color-primary);
+}
+
@media (max-width: 992px) {
.sr-page {
padding-top: calc(var(--header-h) + 32Px);
diff --git a/public/themes/dist_static/static/js/search.js b/public/themes/dist_static/static/js/search.js
index 674b72f..b32dcbc 100644
--- a/public/themes/dist_static/static/js/search.js
+++ b/public/themes/dist_static/static/js/search.js
@@ -1,13 +1,74 @@
import "./common.js";
import "./pagination.js";
//#region js/search.js
+function initHotTags() {
+ $(".sr-hot__tag").on("click", function() {
+ const keyword = ($(this).attr("data-keyword") || $(this).text() || "").trim();
+ if (!keyword) return;
+ const $form = $(".sr-search");
+ $form.find(".s-search__input").val(keyword);
+ const form = $form.get(0);
+ if (!form) return;
+ if (typeof form.requestSubmit === "function") form.requestSubmit();
+ else form.submit();
+ });
+}
+function initFaqAccordion() {
+ $(".sr-list .f-item__trigger").on("click", function() {
+ const $item = $(this).closest(".f-item");
+ const willOpen = !$item.hasClass("is-open");
+ $(".sr-list .f-item.is-open").not($item).removeClass("is-open").find(".f-item__trigger").attr("aria-expanded", "false");
+ $item.toggleClass("is-open", willOpen);
+ $(this).attr("aria-expanded", willOpen ? "true" : "false");
+ });
+}
+function initVideoModal() {
+ const modal = document.getElementById("v-modal");
+ const video = document.getElementById("v-modal-video");
+ const titleEl = document.getElementById("v-modal-title");
+ if (!modal || !video || !titleEl) return;
+ let lastFocus = null;
+ const openModal = (src, title) => {
+ if (!src) return;
+ lastFocus = document.activeElement;
+ titleEl.textContent = title || "视频播放";
+ if (video.getAttribute("src") !== src) video.src = src;
+ modal.hidden = false;
+ document.body.style.overflow = "hidden";
+ if (window.lenis?.stop) window.lenis.stop();
+ video.play().catch(() => {});
+ modal.querySelector(".v-modal__close")?.focus();
+ };
+ const closeModal = () => {
+ video.pause();
+ video.removeAttribute("src");
+ video.load();
+ modal.hidden = true;
+ document.body.style.overflow = "";
+ if (window.lenis?.start) window.lenis.start();
+ if (lastFocus && typeof lastFocus.focus === "function") lastFocus.focus();
+ };
+ document.querySelectorAll(".sr-item--video [data-video-src]").forEach((card) => {
+ card.addEventListener("click", () => {
+ const title =
+ card.closest(".sr-item")?.querySelector(".sr-item__title")?.textContent?.trim() ||
+ card.getAttribute("aria-label")?.replace(/^播放[::]/, "").trim() ||
+ "视频播放";
+ openModal(card.getAttribute("data-video-src") || "", title);
+ });
+ });
+ modal.querySelectorAll("[data-v-modal-close]").forEach((el) => {
+ el.addEventListener("click", closeModal);
+ });
+ document.addEventListener("keydown", (e) => {
+ if (e.key === "Escape" && !modal.hidden) closeModal();
+ });
+}
$(function() {
const q = new URLSearchParams(window.location.search).get("q")?.trim() || "";
if (q) $(".sr-search .s-search__input").val(q);
- $(".sr-hot__tag").on("click", function() {
- const keyword = $(this).attr("data-keyword") || $(this).text().trim();
- $(".sr-search .s-search__input").val(keyword);
- $(".sr-search").trigger("submit");
- });
+ initHotTags();
+ initFaqAccordion();
+ initVideoModal();
});
//#endregion
diff --git a/public/themes/website/1/zh/demo/download.html b/public/themes/website/1/zh/demo/download.html
index 5877cfa..2592364 100644
--- a/public/themes/website/1/zh/demo/download.html
+++ b/public/themes/website/1/zh/demo/download.html
@@ -105,7 +105,7 @@
{hcTaglib:contentPage mainField="*" subField="*" name="hc_content" limit="12" item="vo"}
-
+
{$vo.title}
diff --git a/public/themes/website/1/zh/demo/single_search.html b/public/themes/website/1/zh/demo/single_search.html
index ed1b0af..6c3f568 100644
--- a/public/themes/website/1/zh/demo/single_search.html
+++ b/public/themes/website/1/zh/demo/single_search.html
@@ -48,6 +48,8 @@
+
+
@@ -79,7 +81,7 @@
{/hcTaglib:category}
{volist name="descriptionArr" id="one"}
-
+
{/volist}
@@ -89,9 +91,9 @@
{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_counts = $sr_counts ?? ['all' => 0, 'product' => 0, 'news' => 0, 'faq' => 0, 'other' => 0];
$sr_tabs = [];
- foreach (['all', 'product', 'solution', 'news', 'faq'] as $tabType) {
+ foreach (['all', 'product', 'news', 'faq', 'other'] as $tabType) {
$query = ['type' => $tabType];
if ($sr_q !== '') {
$query['q'] = $sr_q;
@@ -108,10 +110,6 @@
href="{$sr_tabs.product}">
产品 ( {$sr_counts.product} )
-
- 解决方案 ( {$sr_counts.solution} )
-
新闻 ( {$sr_counts.news} )
@@ -120,45 +118,132 @@
href="{$sr_tabs.faq}">
常见问题 ( {$sr_counts.faq} )
+
+ 其他 ( {$sr_counts.other} )
+
{volist name="list" id="vo"}
{php}
- $sr_date = '';
- $sr_time = $vo['publish_time'] ?? ($vo['create_time'] ?? '');
- if (!empty($sr_time)) {
- $sr_date = is_numeric($sr_time) ? date('Y-m-d', (int)$sr_time) : $sr_time;
+ $sr_kind = $vo['sr_kind'] ?? 'content';
+ $sr_date = $vo['sr_date'] ?? '';
+ if ($sr_date === '') {
+ $sr_time = $vo['publish_time'] ?? ($vo['create_time'] ?? '');
+ if ($sr_time !== '' && $sr_time !== null) {
+ if (is_numeric($sr_time)) {
+ $sr_date = date('Y-m-d', (int)$sr_time);
+ } else {
+ $ts = strtotime((string)$sr_time);
+ $sr_date = $ts ? date('Y-m-d', $ts) : substr((string)$sr_time, 0, 10);
+ }
+ }
}
$sr_cid = $vo['category_id'] ?? ($vo['category'][0]['id'] ?? 0);
$sr_thumb = is_array($vo['thumbnail'] ?? null) ? ($vo['thumbnail']['url'] ?? '') : '';
$sr_url = hcUrl('detail/index', ['id' => $vo['id'], 'cid' => $sr_cid]);
+ $sr_file = $vo['url'] ?? '';
+ $sr_title = $vo['title'] ?? '';
+ $sr_type_name = $vo['sr_tab_name'] ?? '内容';
+ $sr_show_desc = ($sr_kind === 'news' || $sr_kind === 'content');
+ $sr_show_more = ($sr_kind === 'news' || $sr_kind === 'content');
{/php}
-
+
+ {if $sr_kind == 'video'}
+
+ {elseif $sr_kind == 'download' /}
+
+
+
+ {elseif $sr_kind == 'announce' /}
+
+
+
+ {else /}
+ {/if}
+
- {if !empty($vo['category'][0]['title'])}{$vo.category.0.title}{else /}内容{/if}
+ {$sr_type_name}
+
+ {if $sr_kind == 'faq'}
+
+
+
+
+ {$vo.content|strip_tags}
+
+
+
+ {else /}
- {$vo.title}
+ {if $sr_kind == 'video'}
+ {$sr_title}
+ {elseif $sr_kind == 'download' /}
+ {$sr_title}
+ {elseif $sr_kind == 'announce' /}
+ {$sr_title}
+ {else /}
+ {$sr_title}
+ {/if}
+ {/if}
+
+ {if $sr_show_desc}
{notempty name="vo.description"}
{$vo.description}
{/notempty}
+ {/if}
+
+ {if $sr_kind == 'download'}
+
+ 立即下载
+
+
+ {elseif $sr_kind == 'product' /}
+
+ 了解产品
+
+
+
+
+ {elseif $sr_kind == 'announce' /}
+
+ 探索更多
+
+
+ {elseif $sr_show_more /}
探索更多
-
-
+
+ {/if}
{/volist}
@@ -175,6 +260,21 @@
{else /}
{$list_page|raw}
{/if}
+
+
+
+
+
+
视频播放
+
+
+
+
+
+
+