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

442 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="hasSearched && ['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,
// 排序参数0代表无排序(全灰)1/2发布时间3/4浏览量
orderType: 0,
// 排序图标资源
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;
this.orderType = 0; // 清除搜索时重置排序
},
// 切换排序
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.orderType = 0;
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 };
// 仅在有排序选择时orderType !== 0才传入 order_type 参数
if (['brand', 'skill', 'collection_brand', 'collection_course'].includes(this.searchType)) {
if (this.orderType !== 0) {
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 || [];
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 || '',
time: item.create_time ? item.create_time.split(' ')[0] : '',
video: item.video || ''
}));
}
if (isReset) {
this.resultList = formattedList;
} else {
this.resultList = [...this.resultList, ...formattedList];
}
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: #fff;
}
.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: 100rpx;
.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: 13rpx;
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: 300rpx;
height: 300rpx;
margin-bottom: 20rpx;
}
text {
font-size: 28rpx;
color: #999;
}
}
.load-more-text {
text-align: center;
padding: 30rpx 0;
font-size: 24rpx;
color: #999;
}
</style>