@ -0,0 +1 @@ |
|||||||
|
C:/Users/kola/project/school-prompt/backend/mini小程序端接口设计文档.md |
||||||
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 988 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 86 KiB |
@ -1,89 +1,101 @@ |
|||||||
<view class="page"> |
<view class="page"> |
||||||
<van-tabs |
<view class="page-header"> |
||||||
class="tabs" |
<van-tabs |
||||||
color="rgba(74, 184, 253, 1)" |
class="tabs" |
||||||
title-inactive-color="rgba(71, 85, 105, 1)" |
color="rgba(74, 184, 253, 1)" |
||||||
title-active-color="rgba(74, 184, 253, 1)" |
title-inactive-color="rgba(71, 85, 105, 1)" |
||||||
active="{{ active }}" |
title-active-color="rgba(74, 184, 253, 1)" |
||||||
bind:change="onChange" |
active="{{ active }}" |
||||||
sticky |
bind:change="onChange" |
||||||
> |
> |
||||||
<!-- 公告 --> |
<van-tab title="公告"></van-tab> |
||||||
<van-tab title="公告"> |
<van-tab title="重要通知"></van-tab> |
||||||
<view class="page0"> |
<van-tab title="活动通知"></van-tab> |
||||||
<view |
<van-tab title="智能体通知"></van-tab> |
||||||
class="module" |
</van-tabs> |
||||||
wx:for="{{announcementList}}" |
<view class="search-wrap"> |
||||||
wx:key="id" |
<view class="date-range"> |
||||||
data-id="{{item.id}}" |
<image class="icon" src="{{imageUrl}}icon4.png?t={{Timestamp}}"></image> |
||||||
bind:tap="handleAnnouncementDetail" |
<picker class="picker" mode="date"> |
||||||
> |
<view class="date">2026-05-28</view> |
||||||
<view class="date">{{item.createdAt}}</view> |
</picker> |
||||||
<view class="card"> |
至 |
||||||
<view class="title">{{item.title}}</view> |
<picker class="picker" mode="date"> |
||||||
<mp-html class="content" content="{{item.content}}"></mp-html> |
<view class="date">2026-05-28</view> |
||||||
<view class="c-footer"> |
</picker> |
||||||
<view class="detail"> |
<van-icon class="arrow" name="arrow-down" /> |
||||||
查看详情 |
</view> |
||||||
<van-icon name="arrow" /> |
<image class="clear" src="{{imageUrl}}icon101.png?t={{Timestamp}}" bindtap="handleClearNotice"></image> |
||||||
</view> |
</view> |
||||||
</view> |
</view> |
||||||
|
|
||||||
|
<!-- 公告 --> |
||||||
|
<view class="page0" wx:if="{{active==0}}"> |
||||||
|
<view |
||||||
|
class="module" |
||||||
|
wx:for="{{announcementList}}" |
||||||
|
wx:key="id" |
||||||
|
data-id="{{item.id}}" |
||||||
|
bind:tap="handleAnnouncementDetail" |
||||||
|
> |
||||||
|
<view class="date">{{item.createdAt}}</view> |
||||||
|
<view class="card"> |
||||||
|
<view class="title">{{item.title}}</view> |
||||||
|
<mp-html class="content" content="{{item.content}}"></mp-html> |
||||||
|
<view class="c-footer"> |
||||||
|
<view class="detail"> |
||||||
|
查看详情 |
||||||
|
<van-icon name="arrow" /> |
||||||
</view> |
</view> |
||||||
</view> |
</view> |
||||||
<pagination pagination="{{announcementPagination}}"></pagination> |
|
||||||
</view> |
</view> |
||||||
</van-tab> |
</view> |
||||||
|
<pagination pagination="{{announcementPagination}}"></pagination> |
||||||
|
</view> |
||||||
|
|
||||||
<!-- 重要通知 --> |
<!-- 重要通知 --> |
||||||
<van-tab title="重要通知"> |
<view class="page0" wx:elif="{{active==1}}"> |
||||||
<view class="page0"> |
<view |
||||||
<view |
class="module" |
||||||
class="module" |
wx:for="{{importantList}}" |
||||||
wx:for="{{importantList}}" |
wx:key="id" |
||||||
wx:key="id" |
data-task-id="{{item.taskId}}" |
||||||
data-task-id="{{item.taskId}}" |
bind:tap="handleNoticeDetail" |
||||||
bind:tap="handleNoticeDetail" |
> |
||||||
> |
<view class="date">{{item.scheduleAt}}</view> |
||||||
<view class="date">{{item.scheduleAt}}</view> |
<view class="card"> |
||||||
<view class="card"> |
<view class="title">{{item.title}}</view> |
||||||
<view class="title">{{item.title}}</view> |
<mp-html class="content" content="{{item.content}}"></mp-html> |
||||||
<mp-html class="content" content="{{item.content}}"></mp-html> |
<view class="c-footer"> |
||||||
<view class="c-footer"> |
<view class="detail"> |
||||||
<view class="detail"> |
查看详情 |
||||||
查看详情 |
<van-icon name="arrow" /> |
||||||
<van-icon name="arrow" /> |
|
||||||
</view> |
|
||||||
</view> |
|
||||||
</view> |
</view> |
||||||
</view> |
</view> |
||||||
<pagination pagination="{{importantPagination}}"></pagination> |
|
||||||
</view> |
</view> |
||||||
</van-tab> |
</view> |
||||||
|
<pagination pagination="{{importantPagination}}"></pagination> |
||||||
|
</view> |
||||||
|
|
||||||
<!-- 活动通知 --> |
<!-- 活动通知 --> |
||||||
<van-tab title="活动通知"> |
<view class="page2" wx:elif="{{active==2}}"> |
||||||
<view class="page2"> |
<view class="module" wx:for="{{activityList}}" wx:key="id" data-index="{{index}}" bind:tap="handleActNotice"> |
||||||
<view class="module" wx:for="{{activityList}}" wx:key="id" data-index="{{index}}" bind:tap="handleActNotice"> |
<view class="date">{{item.scheduleAt}}</view> |
||||||
<view class="date">{{item.scheduleAt}}</view> |
<view class="card"> |
||||||
<view class="card"> |
<view class="title">{{item.title}}</view> |
||||||
<view class="title">{{item.title}}</view> |
<view class="content">{{item.subtitle}}</view> |
||||||
<view class="content">{{item.subtitle}}</view> |
<view class="c-footer"> |
||||||
<view class="c-footer"> |
<view class="tag {{item.subType}}">{{item.subTypeName}}</view> |
||||||
<view class="tag {{item.subType}}">{{item.subTypeName}}</view> |
<view class="detail"> |
||||||
<view class="detail"> |
查看详情 |
||||||
查看详情 |
<van-icon name="arrow" /> |
||||||
<van-icon name="arrow" /> |
|
||||||
</view> |
|
||||||
</view> |
|
||||||
</view> |
</view> |
||||||
</view> |
</view> |
||||||
<pagination pagination="{{activityPagination}}"></pagination> |
|
||||||
</view> |
</view> |
||||||
</van-tab> |
</view> |
||||||
|
<pagination pagination="{{activityPagination}}"></pagination> |
||||||
|
</view> |
||||||
|
|
||||||
<!-- 智能体通知 --> |
<!-- 智能体通知 --> |
||||||
<van-tab title="智能体通知"> |
<view class="page-empty" wx:elif="{{active==3}}">暂未开放</view> |
||||||
<view class="page-empty">暂未开放</view> |
|
||||||
</van-tab> |
|
||||||
</van-tabs> |
|
||||||
</view> |
</view> |
||||||
|
|||||||
@ -1,8 +1,230 @@ |
|||||||
const _app = getApp<IAppOption>(); |
const app = getApp<IAppOption>() |
||||||
|
|
||||||
Page({ |
Page({ |
||||||
data: {}, |
data: { |
||||||
onLoad() {}, |
imageUrl: app.globalData.imageUrl, |
||||||
}); |
Timestamp: app.globalData.Timestamp, |
||||||
|
keyword: '', |
||||||
|
active: 0, |
||||||
|
// 搜索历史
|
||||||
|
historyList: [] as any[], |
||||||
|
// 是否已搜索
|
||||||
|
searched: false, |
||||||
|
// 头部高度(用于 tabs 吸顶定位)
|
||||||
|
headerHeight: 0, |
||||||
|
// 活动搜索结果
|
||||||
|
activityList: [] as any[], |
||||||
|
activityPagination: { page: 1, pages: 1, count: 0 }, |
||||||
|
activityLoading: false, |
||||||
|
// 智能体搜索结果
|
||||||
|
agentList: [] as any[], |
||||||
|
agentPagination: { page: 1, pages: 1, count: 0 }, |
||||||
|
agentLoading: false, |
||||||
|
}, |
||||||
|
|
||||||
|
onLoad() { |
||||||
|
app.waitLogin().then(() => { |
||||||
|
this.fetchHistory() |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
onReady() { |
||||||
|
this.measureHeader() |
||||||
|
}, |
||||||
|
|
||||||
|
onReachBottom() { |
||||||
|
if (this.data.active === 0) { |
||||||
|
this.loadMoreActivities() |
||||||
|
} |
||||||
|
else if (this.data.active === 1) { |
||||||
|
this.loadMoreAgents() |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
// 获取搜索历史
|
||||||
|
fetchHistory() { |
||||||
|
wx.ajax({ |
||||||
|
url: '/search/history', |
||||||
|
method: 'GET', |
||||||
|
}) |
||||||
|
.then((res: any) => { |
||||||
|
if (res?.list?.length) { |
||||||
|
this.setData({ historyList: res.list }) |
||||||
|
} |
||||||
|
}) |
||||||
|
.catch((err) => { |
||||||
|
console.error('获取搜索历史失败', err) |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
// 清除搜索历史
|
||||||
|
handleClearHistory() { |
||||||
|
wx.ajax({ |
||||||
|
url: '/search/clear-history', |
||||||
|
method: 'POST', |
||||||
|
}) |
||||||
|
.then(() => { |
||||||
|
this.setData({ historyList: [] }) |
||||||
|
}) |
||||||
|
.catch((err) => { |
||||||
|
console.error('清除搜索历史失败', err) |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
// 测量头部高度
|
||||||
|
measureHeader() { |
||||||
|
this.createSelectorQuery() |
||||||
|
.select('.header') |
||||||
|
.boundingClientRect((rect) => { |
||||||
|
if (rect) { |
||||||
|
this.setData({ headerHeight: rect.height }) |
||||||
|
} |
||||||
|
}) |
||||||
|
.exec() |
||||||
|
}, |
||||||
|
|
||||||
|
// 点击历史标签搜索
|
||||||
|
handleTagTap(e: WechatMiniprogram.TouchEvent) { |
||||||
|
const keyword = e.currentTarget.dataset.keyword |
||||||
|
this.setData({ keyword }, () => { |
||||||
|
this.doSearch() |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
// 输入关键词
|
||||||
|
handleInput(e: WechatMiniprogram.Input) { |
||||||
|
this.setData({ keyword: e.detail.value }) |
||||||
|
}, |
||||||
|
|
||||||
|
// 点击搜索按钮
|
||||||
|
handleSearch() { |
||||||
|
this.doSearch() |
||||||
|
}, |
||||||
|
|
||||||
|
// 执行搜索
|
||||||
|
doSearch() { |
||||||
|
const { keyword } = this.data |
||||||
|
if (!keyword.trim()) return |
||||||
|
|
||||||
|
this.setData({ |
||||||
|
searched: true, |
||||||
|
activityList: [], |
||||||
|
agentList: [], |
||||||
|
activityPagination: { page: 1, pages: 1, count: 0 }, |
||||||
|
agentPagination: { page: 1, pages: 1, count: 0 }, |
||||||
|
}, () => { |
||||||
|
this.measureHeader() |
||||||
|
}) |
||||||
|
|
||||||
|
// 根据当前 tab 决定搜索类型
|
||||||
|
const tabToType: Record<number, string> = { 0: '2', 1: '3' } |
||||||
|
const type = tabToType[this.data.active] || '1' |
||||||
|
this.fetchSearch(keyword, type, 1) |
||||||
|
|
||||||
|
// 刷新搜索历史
|
||||||
|
this.fetchHistory() |
||||||
|
}, |
||||||
|
|
||||||
|
// 切换 tab
|
||||||
|
onChange(e: WechatMiniprogram.CustomEvent) { |
||||||
|
const active = e.detail.index ?? 0 |
||||||
|
this.setData({ active }) |
||||||
|
|
||||||
|
if (!this.data.searched) return |
||||||
|
|
||||||
|
const tabToType: Record<number, string> = { 0: '2', 1: '3' } |
||||||
|
const type = tabToType[active] || '1' |
||||||
|
const list = active === 0 ? this.data.activityList : this.data.agentList |
||||||
|
if (list.length === 0) { |
||||||
|
this.fetchSearch(this.data.keyword, type, 1) |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
// 搜索接口
|
||||||
|
fetchSearch(keyword: string, type: string, page: number) { |
||||||
|
const loadingKey = type === '2' ? 'activityLoading' : 'agentLoading' |
||||||
|
this.setData({ [loadingKey]: true } as any) |
||||||
|
|
||||||
|
wx.ajax({ |
||||||
|
url: '/search/search', |
||||||
|
method: 'GET', |
||||||
|
data: { keyword, type, page, pageSize: 20 }, |
||||||
|
}) |
||||||
|
.then((res: any) => { |
||||||
|
const data: any = {} |
||||||
|
|
||||||
|
if (type === '1' || type === '2') { |
||||||
|
const acts = res?.activities?.list || [] |
||||||
|
const pagination = res?.activities?.pagination || {} |
||||||
|
// 处理 mainImages
|
||||||
|
const activityList = acts.map((item: any) => { |
||||||
|
let mainImages = item.mainImages |
||||||
|
if (typeof mainImages === 'string') { |
||||||
|
try { |
||||||
|
mainImages = JSON.parse(mainImages) |
||||||
|
} |
||||||
|
catch (_) { |
||||||
|
mainImages = [] |
||||||
|
} |
||||||
|
} |
||||||
|
return { ...item, mainImages } |
||||||
|
}) |
||||||
|
|
||||||
|
const newList = page === 1 ? activityList : [...this.data.activityList, ...activityList] |
||||||
|
data.activityList = newList |
||||||
|
data.activityPagination = { |
||||||
|
page: pagination.page || page, |
||||||
|
pages: pagination.totalPages || 1, |
||||||
|
count: pagination.total || 0, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (type === '1' || type === '3') { |
||||||
|
const agents = res?.agents?.list || [] |
||||||
|
const pagination = res?.agents?.pagination || {} |
||||||
|
const newList = page === 1 ? agents : [...this.data.agentList, ...agents] |
||||||
|
data.agentList = newList |
||||||
|
data.agentPagination = { |
||||||
|
page: pagination.page || page, |
||||||
|
pages: pagination.totalPages || 1, |
||||||
|
count: pagination.total || 0, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
data[loadingKey] = false |
||||||
|
this.setData(data) |
||||||
|
}) |
||||||
|
.catch((err) => { |
||||||
|
console.error('搜索失败', err) |
||||||
|
this.setData({ [loadingKey]: false } as any) |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
// 活动触底加载
|
||||||
|
loadMoreActivities() { |
||||||
|
const { activityPagination, activityLoading } = this.data |
||||||
|
if (activityLoading || activityPagination.page >= activityPagination.pages) return |
||||||
|
this.fetchSearch(this.data.keyword, '2', activityPagination.page + 1) |
||||||
|
}, |
||||||
|
|
||||||
|
// 智能体触底加载
|
||||||
|
loadMoreAgents() { |
||||||
|
const { agentPagination, agentLoading } = this.data |
||||||
|
if (agentLoading || agentPagination.page >= agentPagination.pages) return |
||||||
|
this.fetchSearch(this.data.keyword, '3', agentPagination.page + 1) |
||||||
|
}, |
||||||
|
|
||||||
|
// 跳转活动详情
|
||||||
|
handleActivityTap(e: WechatMiniprogram.TouchEvent) { |
||||||
|
const id = e.currentTarget.dataset.id |
||||||
|
wx.navigateTo({ url: `/pages/actDetail/index?id=${id}` }) |
||||||
|
}, |
||||||
|
|
||||||
|
// 跳转智能体详情
|
||||||
|
handleAgentTap(e: WechatMiniprogram.TouchEvent) { |
||||||
|
const id = e.currentTarget.dataset.id |
||||||
|
wx.navigateTo({ url: `/pages/agent/index?id=${id}` }) |
||||||
|
}, |
||||||
|
}) |
||||||
|
|
||||||
export {} |
export {} |
||||||
|
|||||||