mrr.sj.front/pages/search_common/search.vue

450 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="search-page">
<view class="search-header" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="header-content">
<image class="back-icon" src="/static/images/back.png" @tap="goBack"></image>
<view class="search-box">
<input
class="search-input"
v-model="keyword"
:placeholder="placeholderText"
placeholder-class="search-placeholder"
focus
confirm-type="search"
@confirm="handleSearch"
/>
<text class="search-btn" @tap="handleSearch">搜索</text>
</view>
</view>
</view>
<view class="nav-placeholder" :style="{ height: navHeight + 'px' }"></view>
<view class="result-container">
<view class="sort-bar" v-if="['brand', 'skill', 'collection_brand', 'collection_course'].includes(searchType)">
<view class="sort-item" @tap="toggleSort('time')">
<text class="sort-text" :class="{ active: orderType === 1 || orderType === 2 }">发布时间</text>
<view class="arrows">
<image class="arrow-icon" :src="orderType === 1 ? iconPinkUp : iconGreyUp"></image>
<image class="arrow-icon" :src="orderType === 2 ? iconPinkDown : iconGreyDown"></image>
</view>
</view>
<view class="sort-item" @tap="toggleSort('view')">
<text class="sort-text" :class="{ active: orderType === 3 || orderType === 4 }">浏览量</text>
<view class="arrows">
<image class="arrow-icon" :src="orderType === 3 ? iconPinkUp : iconGreyUp"></image>
<image class="arrow-icon" :src="orderType === 4 ? iconPinkDown : iconGreyDown"></image>
</view>
</view>
</view>
<view v-if="loading && page === 1" class="loading-tips">加载中...</view>
<view v-else-if="searchType === 'notice' && resultList.length > 0">
<notice-card
v-for="item in resultList"
:key="item.id"
:item="item"
@click="goToDetail(item)"
/>
</view>
<view v-else-if="(searchType === 'brand' || searchType === 'collection_brand') && resultList.length > 0" class="brand-grid">
<brand-card
class="brand-card-wrap"
v-for="item in resultList"
:key="item.id"
:item="item"
:tagKeys="item.tagKeys || []"
@click="goToDetail(item)"
/>
</view>
<view v-else-if="(searchType === 'skill' || searchType === 'collection_course') && resultList.length > 0" class="course-list">
<skill-card
v-for="item in resultList"
:key="item.id"
:item="item"
@click="goToDetail(item)"
/>
</view>
<view v-else-if="!loading && hasSearched && resultList.length === 0" class="empty-state">
<image class="empty-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/4e4320d1-cc5c-4312-890c-f1eb8381fdd2.png"></image>
<text>未搜索到相关内容哦~</text>
</view>
<view v-if="resultList.length > 0" class="load-more-text">
<text v-if="loading && page > 1">正在加载更多...</text>
<text v-else-if="!hasMore">没有更多了</text>
</view>
</view>
</view>
</template>
<script>
import BrandCard from "@/pages/home/components/brand-card.vue";
import SkillCard from "@/pages/home/components/skill-card.vue";
import NoticeCard from "@/pages/home/components/notice-card.vue";
import request from '@/utils/request';
export default {
components: {
BrandCard,
SkillCard,
NoticeCard
},
data() {
return {
statusBarHeight: 0,
navHeight: 0,
searchType: '',
keyword: '',
resultList: [],
loading: false,
hasSearched: false,
// 分页相关
page: 1,
limit: 10,
hasMore: true,
// 排序参数 1/2发布时间 3/4浏览量
orderType: 1,
// 排序图标资源
iconGreyUp: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e00ab2ac-bccf-42d8-820c-65727327e279.png',
iconGreyDown: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/dfffb601-6877-4608-a3eb-88695b05c6c2.png',
iconPinkUp: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/5bc84dff-e120-4d4b-a33e-25d68e065654.png',
iconPinkDown: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/ff2c2c9d-694a-4adf-b737-66b07f4a63ae.png',
};
},
computed: {
placeholderText() {
const map = {
'notice': '搜索公告通知',
'brand': '搜索精选品牌',
'skill': '搜索技能课程',
'collection_brand': '搜索收藏品牌的内容',
'collection_course': '搜索收藏课程的内容'
};
return map[this.searchType] || '请输入搜索内容';
}
},
onLoad(options) {
const systemInfo = uni.getSystemInfoSync();
this.statusBarHeight = systemInfo.statusBarHeight;
this.navHeight = this.statusBarHeight + 44;
if (options.type) {
this.searchType = options.type;
}
},
onReachBottom() {
if (this.hasMore && !this.loading) {
this.page++;
this.loadData(false);
}
},
methods: {
goBack() {
uni.navigateBack();
},
clearSearch() {
this.keyword = '';
this.resultList = [];
this.hasSearched = false;
this.page = 1;
this.hasMore = true;
},
// 切换排序
toggleSort(type) {
if (type === 'time') {
this.orderType = this.orderType === 1 ? 2 : 1;
} else if (type === 'view') {
this.orderType = this.orderType === 3 ? 4 : 3;
}
// 如果已经输入了关键词并搜索过,切换排序直接重新加载当前结果
if (this.hasSearched) {
this.page = 1;
this.hasMore = true;
this.loadData(true);
}
},
handleSearch() {
if (!this.keyword.trim()) {
return uni.showToast({ title: '请输入搜索内容', icon: 'none' });
}
this.hasSearched = true;
this.page = 1;
this.hasMore = true;
this.loadData(true);
},
async loadData(isReset) {
this.loading = true;
try {
let apiUrl = '';
let params = { page: this.page, limit: this.limit };
// 给支持排序的接口加上排序参数
if (['brand', 'skill', 'collection_brand', 'collection_course'].includes(this.searchType)) {
params.order_type = this.orderType;
}
switch (this.searchType) {
case 'notice':
apiUrl = '/sj/notice/list';
params.title = this.keyword;
break;
case 'brand':
apiUrl = '/sj/brand/list';
params.name = this.keyword;
break;
case 'skill':
apiUrl = '/sj/skill/list';
params.name = this.keyword;
break;
case 'collection_brand':
apiUrl = '/sj/brand/collectList';
params.name = this.keyword;
break;
case 'collection_course':
apiUrl = '/sj/skill/collectList';
params.name = this.keyword;
break;
default:
throw new Error('未知的搜索类型');
}
const res = await request.post(apiUrl, params);
if (res.code === 200 && res.data) {
const list = res.data.list || [];
// 文档里总数叫 count不是 total
const totalCount = res.data.count || 0;
let formattedList = list;
if (this.searchType === 'brand' || this.searchType === 'collection_brand') {
formattedList = list.map(item => {
let tags = [];
if (item.franchise_state === 1) tags.push('franchise');
if (item.student_state === 1) tags.push('recruit');
if (tags.length === 0) tags.push('more');
return {
...item,
name: item.name,
bgSrc: item.cover_img || 'https://dummyimage.com/300x200/ccc/fff.png',
avatar: item.logo_img || 'https://dummyimage.com/100x100/ccc/fff.png',
tagKeys: tags
};
});
} else if (this.searchType === 'skill' || this.searchType === 'collection_course') {
formattedList = list.map(item => ({
...item,
title: item.name,
coverSrc: item.cover_img || 'https://dummyimage.com/300x200/ccc/fff.png',
type: item.link_name || (item.link_type === 1 ? '平台课程' : '品牌课程'),
views: item.browse_num || 0,
typeIcon: item.link_logo || ''
}));
}
if (isReset) {
this.resultList = formattedList;
} else {
this.resultList = [...this.resultList, ...formattedList];
}
// 根据接口文档返回的 count 来判断是否还有更多
this.hasMore = this.resultList.length < totalCount;
} else {
if (isReset) this.resultList = [];
this.hasMore = false;
}
} catch (e) {
console.error('搜索异常:', e);
uni.showToast({ title: '搜索失败,请重试', icon: 'none' });
} finally {
this.loading = false;
}
},
goToDetail(item) {
if (!item.id) return;
if (this.searchType === 'notice') {
uni.navigateTo({ url: `/pages/notice/detail?id=${item.id}` });
} else if (this.searchType === 'brand' || this.searchType === 'collection_brand') {
uni.navigateTo({ url: `/pages/brand/detail?id=${item.id}` });
} else if (this.searchType === 'skill' || this.searchType === 'collection_course') {
uni.navigateTo({ url: `/pages/skill/detail?id=${item.id}` });
}
}
}
};
</script>
<style lang="scss" scoped>
.search-page {
min-height: 100vh;
background: #F5F5F5;
}
.search-header {
position: fixed;
top: 0;
left: 0;
right: 0;
background-color: #Fff;
z-index: 999;
.header-content {
height: 44px;
display: flex;
align-items: center;
padding: 0 24rpx;
.back-icon {
width: 28rpx;
height: 28rpx;
padding-right: 20rpx;
}
.search-box {
flex: 1;
height: 74rpx;
background: #f5f5f5;
border-radius: 37rpx;
display: flex;
align-items: center;
padding: 0 6rpx 0 24rpx;
.search-input {
flex: 1;
font-size: 28rpx;
}
.search-placeholder {
color: #CCCCCC;
}
.clear-icon {
font-size: 28rpx;
color: #999;
padding: 10rpx 20rpx;
}
}
.search-btn {
width: 110rpx;
height: 62rpx;
line-height: 62rpx;
background: #FF4767;
border-radius: 31rpx;
font-size: 28rpx;
color: #fff;
text-align: center;
}
}
}
.result-container {
padding: 24rpx;
}
/* ================= 排序栏新增样式 ================= */
.sort-bar {
display: flex;
align-items: center;
padding: 10rpx 0 24rpx; /* 根据结果页面适当微调顶部间距 */
gap: 60rpx;
.sort-item {
display: flex;
align-items: center;
.sort-text {
font-weight: 400;
font-size: 26rpx;
color: #666666;
line-height: 37rpx;
margin-right: 6rpx;
&.active {
color: #FF4767;
font-weight: 500;
}
}
.arrows {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.arrow-icon {
width: 14rpx;
height: 8rpx;
margin: 2rpx 0;
}
}
}
}
/* ============================================== */
.loading-tips {
text-align: center;
padding: 100rpx 0;
color: #999;
font-size: 28rpx;
}
.brand-grid {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.brand-card-wrap { width: 48.5%; margin-bottom: 24rpx; }
}
.course-list {
display: flex;
flex-direction: column;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 200rpx 0;
.empty-icon {
width: 200rpx;
height: 200rpx;
margin-bottom: 20rpx;
}
text {
font-size: 28rpx;
color: #999;
}
}
.load-more-text {
text-align: center;
padding: 30rpx 0;
font-size: 24rpx;
color: #999;
}
</style>