首页、公告列表与详情、两个精选、我的收藏、总搜索组件

This commit is contained in:
BAKEYi 2026-05-28 10:41:27 +08:00
parent a9455ce98d
commit b6945acf9b
9 changed files with 880 additions and 210 deletions

View File

@ -227,6 +227,12 @@
"navigationStyle": "custom"
}
},
{
"path": "pages/search_common/search",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/settings/index",
"style": {

View File

@ -0,0 +1,101 @@
<template>
<view class="notice-item" @tap="handleClick">
<view class="item-main">
<text class="item-title line-2">{{ item.title }}</text>
<view class="item-time">
<text class="time-text">发布时间{{ formattedTime }}</text>
</view>
</view>
</view>
</template>
<script>
export default {
name: "NoticeCard",
props: {
//
item: {
type: Object,
default: () => ({})
}
},
computed: {
//
formattedTime() {
const timeStr = this.item.create_time;
if (!timeStr) return '--:--:--';
let formatted = timeStr;
if (timeStr.includes('.') && !timeStr.includes('-')) {
formatted = timeStr.replace(/\./g, '-');
if (formatted.match(/\d{4}-\d{2}-\d{2}\d{2}:\d{2}:\d{2}/)) {
formatted = formatted.replace(/(\d{4}-\d{2}-\d{2})(\d{2}:\d{2}:\d{2})/, '$1 $2');
}
}
if (formatted.includes('-') && formatted.includes(':')) {
return formatted;
}
return timeStr;
}
},
methods: {
handleClick() {
// click
this.$emit('click', this.item);
}
}
};
</script>
<style lang="scss" scoped>
.notice-item {
display: flex;
align-items: center;
justify-content: space-between;
background: #ffffff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
transition: all 0.2s ease;
.item-main {
flex: 1;
min-width: 0; //
.item-title {
font-size: 30rpx;
font-weight: 500;
color: #101010;
line-height: 42rpx;
margin-bottom: 16rpx;
}
.item-time {
display: flex;
align-items: center;
.time-icon {
width: 24rpx;
height: 24rpx;
margin-right: 8rpx;
opacity: 0.6;
}
.time-text {
font-size: 22rpx;
color: #999;
line-height: 30rpx;
}
}
}
}
//
.line-2 {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
word-break: break-all;
}
</style>

View File

@ -3,7 +3,7 @@
<image class="page-bg" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/0901dc51-ff5a-427f-aab1-993d80aced79.png" mode="widthFix"></image>
<view class="header-section">
<view class="user-info">
<view class="user-info" @tap="handleLoginRedirect">
<image class="avatar" :src="currentAvatar"></image>
<view class="info-text">
<view class="name">{{ currentName }}</view>
@ -17,6 +17,70 @@
</view>
</view>
<view class="setup-modal-mask" v-if="showSetupModal" catchtouchmove="true">
<view class="setup-modal-content">
<image class="modal-bg" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/ac2cb4aa-a7c5-4776-98f4-b77e9c0cfa85.png"></image>
<view class="modal-header">
<text>温馨提示</text>
</view>
<view class="modal-body">
<text class="modal-subtitle">发布服务请完善以下资料</text>
<view class="missing-list">
<view class="missing-item" v-if="!hasBusinessTime">
<view class="item-left">
<image class="item-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/21b26b44-5e36-4291-8900-705a3d5bfa90.png"></image>
<view class="item-texts">
<text class="item-title">营业时间未配置</text>
<text class="item-desc">请完善店铺的营业时间</text>
</view>
</view>
<view class="item-right orange" @tap="goToBusinessTime">去配置</view>
</view>
<view class="missing-item" v-if="!hasServiceSkill">
<view class="item-left">
<image class="item-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/21b26b44-5e36-4291-8900-705a3d5bfa90.png"></image>
<view class="item-texts">
<text class="item-title">服务技能未配置</text>
<text class="item-desc">请完善服务技能</text>
</view>
</view>
<view class="item-right orange" @tap="goToServiceSkill">去配置</view>
</view>
<view class="missing-item" v-if="!isCertified">
<view class="item-left">
<image class="item-icon" :src="userInfo.id_type === -1 ? 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/25c444be-a66e-45d6-b50d-922149ff059f.png' : 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/21b26b44-5e36-4291-8900-705a3d5bfa90.png'"></image>
<view class="item-texts">
<text class="item-title">{{ userInfo.id_type === -1 ? '资质认证中' : '资质未认证' }}</text>
<text class="item-desc">请完成资质认证</text>
</view>
</view>
<view class="item-right" :class="userInfo.id_type === -1 ? 'grey' : 'orange'" @tap="goToAuth">
{{ userInfo.id_type === -1 ? '查看进度' : '去认证' }}
</view>
</view>
</view>
</view>
<view class="modal-footer">
<view class="btn-know" @tap="closeSetupModal">知道了</view>
</view>
</view>
</view>
<view class="settlement-guide card-box" v-if="isLogin && isUserInfoLoaded && !isCertified">
<view class="guide-header" :style="{ backgroundImage: 'url(' + (isSettled ? 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/c3a6cdd8-0447-4b50-a8cc-7bab1eedb502.png' : 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/138ccbc3-486d-4287-b67f-f809de99e408.png') + ')' }">
<view class="guide-body">
<text class="guide-text">{{ isSettled ? '完成认证,开启您的接单之路' : '入驻美融融,生意更轻松' }}</text>
<view class="guide-btn" @tap="goToSettle">{{ isSettled ? '去认证' : '去入驻' }}</view>
</view>
</view>
</view>
<view class="quick-actions card-box" v-if="isLogin && isSettled">
<view class="action-item" v-for="(item, index) in quickActions" :key="index" @tap="handleQuickAction(item)">
<image class="action-icon" :src="item.icon"></image>
@ -87,15 +151,6 @@
</view>
</view>
<view class="settlement-guide card-box" v-if="isLogin && !isCertified">
<view class="guide-header" :style="{ backgroundImage: 'url(' + (isSettled ? 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/c3a6cdd8-0447-4b50-a8cc-7bab1eedb502.png' : 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/138ccbc3-486d-4287-b67f-f809de99e408.png') + ')' }">
<view class="guide-body">
<text class="guide-text">{{ isSettled ? '完成认证,开启您的接单之路' : '入驻美融融,生意更轻松' }}</text>
<view class="guide-btn" @tap="goToSettle">{{ isSettled ? '去认证' : '去入驻' }}</view>
</view>
</view>
</view>
<view class="notice-bar card-box">
<image class="notice-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/75f624b8-a6ed-4787-9380-1a087062805b.png"></image>
<swiper class="notice-swiper" vertical autoplay circular interval="3000">
@ -143,6 +198,9 @@
</template>
<script>
//
let hasShownSetupModal = false;
import TabBar from "@/components/tabbar/tabbar.vue"
import projectTabs from "./components/project-tabs.vue";
import request from "@/utils/request";
@ -163,15 +221,17 @@ export default {
data() {
return {
isLogin: false,
isUserInfoLoaded: false, //
userInfo: {},
authDetails: null, //
sjId: null,
showSetupModal: false, //
displayScore: 0,
animationTimer: null,
noticeList: [
{ id: 0, title: '暂无最新公告', url: '' }
],
noticeList: [{ id: 0, title: '', url: '' }],
quickActions: [
{ name: '扫码验券', icon: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/b128e423-e4b5-4ad6-bdf5-522de7a9bc68.png' , path: '/pages/shop/verify/verify-order'},
@ -187,24 +247,43 @@ export default {
};
},
computed: {
// true() / false()
isOpenStore() {
return this.userInfo.order_taking === 1;
//
canOpenStore() {
return this.hasBusinessTime && this.hasServiceSkill && this.isCertified;
},
// 使 authDetails
hasServiceSkill() {
// authDetails servers_kill
return this.authDetails && Array.isArray(this.authDetails.servers_kill) && this.authDetails.servers_kill.length > 0;
},
//
isOpenStore() {
//
if (!this.canOpenStore) {
return false;
}
// (使 == "1")
return this.userInfo.order_taking == 1;
},
//
// (0: , -1: , >0: ) 0
isSettled() {
if (!this.isLogin || !this.userInfo) return false;
// typeof undefined !== 0
if (!this.isLogin || typeof this.userInfo.id_type === 'undefined') return false;
return this.userInfo.id_type !== 0;
},
// (> 0 )
isCertified() {
if (!this.isLogin || !this.userInfo) return false;
if (!this.isLogin || typeof this.userInfo.id_type === 'undefined') return false;
return this.userInfo.id_type > 0;
},
//
// userInfo authDetails
hasBusinessTime() {
return this.isLogin && !!this.userInfo.business_time;
const timeFromUser = this.userInfo && this.userInfo.business_time;
const timeFromAuth = this.authDetails && this.authDetails.business_time;
return this.isLogin && !!(timeFromUser || timeFromAuth);
},
//
@ -253,34 +332,51 @@ export default {
}
},
methods: {
goToBrandList() {
uni.navigateTo({
url: '/pages/jingxuan/selected-brands'
//
handleLoginRedirect() {
// isLogin false
if (!this.isLogin) {
uni.navigateTo({
url: '/pages/blogPopup/blogPopup'
});
}
},
//
closeSetupModal() {
this.showSetupModal = false;
},
goToBusinessTime() {
this.showSetupModal = false;
uni.navigateTo({ url: '/pages/shop/business-time' });
},
goToServiceSkill() {
this.showSetupModal = false;
uni.navigateTo({ url: '/pages/shop/service-skills' });
},
goToAuth() {
this.showSetupModal = false;
uni.navigateTo({ url: '/pages/ruzhu/ruzhu' });
},
goToBrandList() {
uni.navigateTo({ url: '/pages/jingxuan/selected-brands' });
},
goToSkillList() {
uni.navigateTo({
url: '/pages/jingxuan/selected-skills'
});
uni.navigateTo({ url: '/pages/jingxuan/selected-skills' });
},
// ()
handleQuickAction(item) {
if (item.name === '扫码验券') {
console.log('准备跳转到自定义扫码页...');
uni.navigateTo({
url: '/pages/shop/verify/verify-order',
fail: (err) => {
console.error('跳转失败:', err);
}
});
return;
uni.navigateTo({
url: '/pages/shop/verify/verify-order',
fail: (err) => { console.error('跳转失败:', err); }
});
return;
} // 2.
else if (item.path) {
else if (item.path) {
uni.navigateTo({
url: item.path,
fail: () => {
uni.showToast({ title: '页面开发中...', icon: 'none' });
}
fail: () => { uni.showToast({ title: '页面开发中...', icon: 'none' }); }
});
}
},
@ -297,31 +393,51 @@ export default {
getUserInfo() {
request.post("/sj/user/getUser").then((result) => {
if(result.code === 200 && result.data) {
if(result.code == 200 && result.data) {
this.userInfo = result.data;
this.isUserInfoLoaded = true; // UI
uni.setStorageSync("sjId", result.data.id);
this.sjId = result.data.id;
//
this.initStoreStatus();
//
// 使
if (this.isSettled) {
this.fetchOrderNum();
this.fetchRealTimeData();
//
this.fetchAuthDetails();
}
}
});
},
//
//
fetchAuthDetails() {
request.post("/sj/userSjAuth/details", {
type: 1, // 1
id_type: '1' //
}).then(res => {
if (res.code == 200 && res.data) {
this.authDetails = res.data;
}
//
// 退
if (!this.canOpenStore && !hasShownSetupModal) {
this.showSetupModal = true;
hasShownSetupModal = true;
}
});
},
fetchOrderNum() {
request.post("/sj/poster/orderNum").then(res => {
if(res.code === 200 && res.data) {
if(res.code == 200 && res.data) {
this.orderBasic = {
updateTime: this.formatTime(res.data.now_date),
waitStart: res.data.dfw || 0,
waitFinish: res.data.dwc || 0,
finished: res.data.yqx || 0 // dfw, dwc, yqx()yqx
finished: res.data.yqx || 0
};
}
});
@ -330,7 +446,7 @@ export default {
//
fetchRealTimeData() {
request.post("/sj/poster/realTimeData").then(res => {
if(res.code === 200 && res.data) {
if(res.code == 200 && res.data) {
const today = res.data.today || {};
const yesterday = res.data.yesterday || {};
@ -351,7 +467,7 @@ export default {
//
fetchNoticeList() {
request.post('/sj/notice/list', { page: 1, limit: 6 }).then(res => {
if(res.code === 200 && res.data && res.data.list.length > 0) {
if(res.code == 200 && res.data && res.data.list.length > 0) {
this.noticeList = res.data.list;
}
});
@ -359,12 +475,10 @@ export default {
//
fetchBrandList() {
// 4
request.post('/sj/brand/list', { page: 1, limit: 4 }).then(res => {
if(res.code === 200 && res.data && res.data.list) {
if(res.code == 200 && res.data && res.data.list) {
this.brands = res.data.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');
@ -384,14 +498,14 @@ export default {
//
fetchSkillList() {
request.post('/sj/skill/list', { page: 1, limit: 4 }).then(res => {
if(res.code === 200 && res.data && res.data.list) {
if(res.code == 200 && res.data && res.data.list) {
this.skills = res.data.list.map(item => {
return {
id: item.id,
title: item.name,
type: item.link_name || '平台课程',
views: item.browse_num || 0,
time: item.create_time ? item.create_time.split(' ')[0] : '', //
time: item.create_time ? item.create_time.split(' ')[0] : '',
coverSrc: item.cover_img || 'https://dummyimage.com/200x150/ccc/fff.png&text=Video',
typeIcon: item.link_logo || ''
};
@ -402,51 +516,51 @@ export default {
//
goToNotice(msg) {
if(msg.id) {
uni.navigateTo({ url: `/pages/notice/detail?id=${msg.id}` });
}
if(msg.id) { uni.navigateTo({ url: `/pages/notice/detail?id=${msg.id}` }); }
},
//
//
goToNoticeList() {
uni.navigateTo({ url: '/pages/notice/notice_list' });
},
//
goToSettle() {
uni.navigateTo({ url: '/pages/ruzhu/ruzhu' });
},
//
initStoreStatus() {
//
if (!this.isCertified || (this.isCertified && !this.hasBusinessTime)) {
this.isOpenStore = false;
} else {
// (self_state 1, 0)
this.isOpenStore = this.userInfo.self_state === 1;
}
},
// /
toggleStatus() {
if (!this.isCertified) {
uni.showModal({ title: '提示', content: '请先完善认证信息', confirmText: '去完善' });
const currentStatus = this.userInfo.order_taking;
const isCurrentlyResting = currentStatus != 1; //
//
if (isCurrentlyResting && !this.canOpenStore) {
this.showSetupModal = true;
return;
}
if (this.isCertified && !this.hasBusinessTime) {
uni.showToast({ title: '请先配置营业时间', icon: 'none' });
return;
}
let newStatus = this.userInfo.order_taking == 1 ? 2 : 1;
//
let newStatus = currentStatus == 1 ? 2 : 1;
// Loading
uni.showLoading({ title: '切换中...', mask: true });
//
request.post("/sj/sjordertaking", {
order_taking: newStatus,
sjid: this.sjId
}).then((res) => {
this.getUserInfo();
uni.hideLoading();
//
this.userInfo.order_taking = newStatus; //
this.getUserInfo(); //
uni.showToast({ title: newStatus == 1 ? '已开启营业' : '已休息', icon: 'none' });
}).catch(() => {
//
uni.hideLoading();
uni.showToast({ title: '网络异常,切换失败', icon: 'none' });
});
},
animateScore() {
const target = parseFloat(this.orderDetail.score) || 0;
if (this.animationTimer) clearInterval(this.animationTimer);
@ -485,6 +599,134 @@ export default {
background: #f5f6f8;
}
/* ============ 提示弹窗样式 ============ */
.setup-modal-mask {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
.setup-modal-content {
width: 630rpx;
height: 775rpx; /* 根据要求绝对固定宽高 */
background: #FFFFFF;
border-radius: 32rpx;
position: relative;
display: flex;
flex-direction: column;
.modal-bg {
position: absolute;
top: 0; left: 0;
width: 630rpx;
height: 265rpx;
z-index: 0;
}
.modal-header {
position: relative;
z-index: 1;
height: 150rpx;
display: flex;
align-items: flex-end;
justify-content: center;
padding-bottom: 20rpx;
text {
font-size: 36rpx;
font-weight: bold;
color: #333333;
letter-spacing: 2rpx;
}
}
.modal-body {
position: relative;
z-index: 1;
flex: 1;
padding: 50rpx 40rpx;
.modal-subtitle {
font-size: 28rpx;
color: #333333;
margin-bottom: 40rpx;
display: block;
}
.missing-list {
display: flex;
flex-direction: column;
gap: 46rpx; /* 间距拉开显得不那么拥挤 */
.missing-item {
display: flex;
justify-content: space-between;
align-items: center;
.item-left {
display: flex;
align-items: center;
.item-icon {
width: 36rpx;
height: 36rpx;
margin-right: 16rpx;
}
.item-texts {
display: flex;
flex-direction: column;
.item-title {
font-size: 30rpx;
color: #333333;
font-weight: 500;
margin-bottom: 8rpx;
}
.item-desc {
font-size: 24rpx;
color: #999999;
}
}
}
.item-right {
font-size: 26rpx;
font-weight: 400;
&.orange { color: #FF7B00; }
&.grey { color: #999999; }
}
}
}
}
.modal-footer {
position: relative;
z-index: 1;
padding: 0 50rpx 50rpx;
.btn-know {
width: 100%;
height: 88rpx;
background: #FF4767;
border-radius: 44rpx;
color: #FFFFFF;
font-size: 32rpx;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
}
}
}
}
/* ==================================== */
.homePage {
position: relative;
min-height: 100vh;
@ -589,7 +831,6 @@ export default {
}
}
// /
.status-toggle {
position: relative;
width: 173rpx;
@ -664,7 +905,6 @@ export default {
width: 100%;
height: 248rpx;
background-size: cover;
/* 背景图由动态 style 接管 */
}
.guide-body {

View File

@ -9,7 +9,7 @@
borderBottom="none"
:show-headle="true"
headleSrc="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/51ebca5d-95c5-4fb8-8aa3-560a79100d25.png"
@onHeadleClick="">
@onHeadleClick="goToSearch">
</custom-navbar>
<serviecFirstTab
@ -132,6 +132,46 @@ export default {
this.loadFilterCategories();
},
methods: {
//
goToSearch() {
uni.navigateTo({
url: '/pages/search_common/search?type=brand' //
});
},
// state === 1
async loadFilterCategories() {
try {
const syrId = uni.getStorageSync('syrId') || 1;
const res = await request.post('/sj/firstclass', { id: syrId })
if ((res.code === 200 || res.state === 1) && res.data) {
const firstClasses = res.data;
//
const promises = firstClasses.map(async (first) => {
try {
const subRes = await request.post('/sj/secondclass', { id: first.id })
return {
id: first.id,
title: first.title,
children: ((subRes.code === 200 || subRes.state === 1) && subRes.data) ? subRes.data : []
}
} catch(e) {
return { id: first.id, title: first.title, children: [] }
}
});
const filterData = await Promise.all(promises);
//
this.brandFilterData = filterData.filter(group => group.children.length > 0);
} else {
this.brandFilterData = []
}
} catch (error) {
console.error('获取分类字典失败', error)
}
},
// ============ ============
toggleSort(type) {
if (type === 'time') {

View File

@ -137,7 +137,9 @@ export default {
},
methods: {
goToSearch() {
uni.showToast({ title: '搜索功能开发中', icon: 'none' })
uni.navigateTo({
url: '/pages/search_common/search?type=skill' //
});
},
//

View File

@ -2,7 +2,7 @@
<view class="collection-page">
<custom-navbar title="我的收藏" :showBack="true" backgroundColor="#FFFFFF" :show-headle="true" borderBottom="none"
headleSrc="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/51ebca5d-95c5-4fb8-8aa3-560a79100d25.png" @onHeadleClick="addAddress">
headleSrc="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/51ebca5d-95c5-4fb8-8aa3-560a79100d25.png" @onHeadleClick="goToSearch">
</custom-navbar>
<view class="nav-placeholder" :style="{ height: navHeight + 'px' }"></view>
@ -167,6 +167,13 @@ export default {
this.loadData()
},
methods: {
//
goToSearch() {
// this.currentTab 'brand' 'course'
uni.navigateTo({
url: `/pages/search_common/search?type=collection_${this.currentTab}`
});
},
goBack() {
uni.navigateBack()
},

View File

@ -7,9 +7,6 @@
backgroundColor="linear-gradient(180deg, #E6ECFB 0%, #FFFFFF 100%)"
:showHeadle="true"
borderBottom="none"
headleSrc="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/51ebca5d-95c5-4fb8-8aa3-560a79100d25.png"
@onHeadleClick="handleShare"
/>
<!-- 页面主要内容 -->

View File

@ -1,34 +1,30 @@
<template>
<view class="notice-page">
<!-- 自定义顶部导航组件 -->
<custom-navbar title="公告通知" :showBack="true" backgroundColor="#FFFFFF" :show-headle="true"
headleSrc="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/51ebca5d-95c5-4fb8-8aa3-560a79100d25.png" @onHeadleClick="addAddress">
</custom-navbar>
<!-- 主要内容区域 -->
<custom-navbar
title="公告通知"
:showBack="true"
backgroundColor="#FFFFFF"
:show-headle="true"
headleSrc="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/51ebca5d-95c5-4fb8-8aa3-560a79100d25.png"
@onHeadleClick="goToSearch"
/>
<view class="page-content">
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<view class="loading-icon"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 公告列表 -->
<view v-else-if="noticeList.length > 0" class="notice-list">
<view
class="notice-item"
v-for="(item, index) in noticeList"
<NoticeCard
v-for="item in noticeList"
:key="item.id"
@tap="goToDetail(item)"
>
<view class="item-main">
<text class="item-title line-2">{{ item.title }}</text>
<view class="item-time">
<text class="time-text">发布时间{{ formatTime(item.create_time) }}</text>
</view>
</view>
<!-- <image class="arrow-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/ee119e24-1cb2-43df-86e6-61af8e9fd0ad.png" mode="aspectFit"></image> -->
</view>
:item="item"
@click="goToDetail"
/>
<!-- 加载更多状态 -->
<view v-if="loadingMore" class="load-more">
@ -38,7 +34,6 @@
<text>没有更多了</text>
</view>
</view>
<!-- 空状态 -->
<view v-else class="empty-container">
<image class="empty-img" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/8c5e7b2a-3d4f-4b1e-9a7c-6e8f2d1b4a5c.png" mode="aspectFit"></image>
@ -50,37 +45,44 @@
<script>
import CustomNavbar from '@/components/custom-navbar/custom-navbar.vue';
import NoticeCard from '@/pages/home/components/notice-card.vue';
import request from '@/utils/request';
export default {
components: {
CustomNavbar
CustomNavbar,
NoticeCard //
},
data() {
return {
loading: false, //
loadingMore: false, //
noticeList: [], //
page: 1, //
limit: 10, //
total: 0, //
hasMore: true //
loading: false,
loadingMore: false,
noticeList: [],
page: 1,
limit: 10,
total: 0,
hasMore: true
};
},
onLoad() {
this.fetchNoticeList(true);
},
//
onPullDownRefresh() {
this.fetchNoticeList(true);
},
//
onReachBottom() {
if (!this.loadingMore && this.hasMore && !this.loading) {
this.fetchNoticeList(false);
}
},
methods: {
//
goToSearch() {
uni.navigateTo({
url: '/pages/search_common/search?type=notice'
});
},
//
fetchNoticeList(reset = true) {
if (reset) {
@ -90,8 +92,10 @@ export default {
} else {
if (!this.hasMore) return;
this.loadingMore = true;
// +1
this.page++;
}
request.post('/sj/notice/list', {
page: this.page,
limit: this.limit
@ -109,18 +113,15 @@ export default {
//
this.hasMore = this.noticeList.length < total;
// 1
if (this.hasMore && !reset) {
this.page++;
}
} else {
if (reset) this.noticeList = [];
this.hasMore = false;
//
if (!reset && this.page > 1) this.page--;
}
}).catch(err => {
console.error('获取公告列表失败:', err);
if (reset) this.noticeList = [];
if (!reset && this.page > 1) this.page--; //
uni.showToast({
title: '加载失败,请稍后重试',
icon: 'none'
@ -128,34 +129,13 @@ export default {
}).finally(() => {
this.loading = false;
this.loadingMore = false;
//
if (reset) {
uni.stopPullDownRefresh();
}
});
},
//
formatTime(timeStr) {
if (!timeStr) return '--:--:--';
// 2026-06-10 15:00:00 2026.06.1015:00:00
let formatted = timeStr;
//
if (timeStr.includes('.') && !timeStr.includes('-')) {
formatted = timeStr.replace(/\./g, '-');
// "2026.06.1015:00:00" -> "2026-06-10 15:00:00"
if (formatted.match(/\d{4}-\d{2}-\d{2}\d{2}:\d{2}:\d{2}/)) {
formatted = formatted.replace(/(\d{4}-\d{2}-\d{2})(\d{2}:\d{2}:\d{2})/, '$1 $2');
}
}
//
if (formatted.includes('-') && formatted.includes(':')) {
return formatted;
}
return timeStr;
},
//
// NoticeCard $emit item
goToDetail(item) {
if (item.id) {
uni.navigateTo({
@ -177,7 +157,6 @@ export default {
padding: 20rpx 20rpx 40rpx;
}
//
.loading-container {
display: flex;
flex-direction: column;
@ -204,54 +183,6 @@ export default {
@keyframes spin {
to { transform: rotate(360deg); }
}
//
.notice-list {
.notice-item {
display: flex;
align-items: center;
justify-content: space-between;
background: #ffffff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
// box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
transition: all 0.2s ease;
.item-main {
flex: 1;
min-width: 0; //
.item-title {
font-size: 30rpx;
font-weight: 500;
color: #101010;
line-height: 42rpx;
margin-bottom: 16rpx;
}
.item-time {
display: flex;
align-items: center;
.time-icon {
width: 24rpx;
height: 24rpx;
margin-right: 8rpx;
opacity: 0.6;
}
.time-text {
font-size: 22rpx;
color: #999;
line-height: 30rpx;
}
}
}
}
}
//
.load-more, .no-more {
text-align: center;
padding: 30rpx 0;
@ -259,7 +190,6 @@ export default {
color: #999;
}
//
.empty-container {
display: flex;
flex-direction: column;
@ -279,20 +209,4 @@ export default {
color: #999;
}
}
//
.line-2 {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
word-break: break-all;
}
::v-deep .icon-headle {
width: 32rpx !important;
height: 32rpx !important;
}
</style>

View File

@ -0,0 +1,363 @@
<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"
/>
<!-- <view v-if="keyword" class="clear-icon" @tap="clearSearch"></view> -->
<text class="search-btn" @tap="handleSearch">搜索</text>
</view>
</view>
</view>
<view class="nav-placeholder" :style="{ height: navHeight + 'px' }"></view>
<view class="result-container">
<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
};
},
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;
},
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 };
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: #F5F5F5;
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: #fff;
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;
}
.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>