论坛调整

This commit is contained in:
hebo 2025-10-13 20:28:12 +08:00
parent 743f8aeae7
commit 05e789ebb4
15 changed files with 5553 additions and 113 deletions

View File

@ -5,3 +5,8 @@ import { get, post } from "@/utils/request";
export const findNjJsListApi = async (params: any) => {
return await get("/api/bj/findNjJsList", params);
};
// 根据教师ID列表查询教师信息
export const findByIdsApi = async (params: any) => {
return await get("/api/js/findByIds", params);
};

201
src/api/forum/index.ts Normal file
View File

@ -0,0 +1,201 @@
import { get, post } from "@/utils/request";
// ==================== 社区相关 ====================
/**
*
*/
export function communityFindPageApi(params: any) {
return get('/api/forumCommunity/findPage', params);
}
/**
* ID查询社区详情
*/
export function communityFindByIdApi(params: any) {
return get('/api/forumCommunity/findById', params);
}
/**
* /
*/
export function communitySaveApi(params: any) {
return post('/api/forumCommunity/save', params);
}
/**
*
*/
export function communityDeleteApi(params: any) {
return post('/api/forumCommunity/logicDelete', params);
}
// ==================== 社区成员相关 ====================
/**
*
*/
export function communityMemberJoinApi(params: any) {
return post('/api/forumCommunityMember/save', params);
}
/**
* 退
*/
export function communityMemberQuitApi(params: any) {
return post('/api/forumCommunityMember/logicDelete', params);
}
/**
*
*/
export function communityMemberFindPageApi(params: any) {
return get('/api/forumCommunityMember/findPage', params);
}
/**
*
*/
export function getMyCommunityListApi(params: any) {
return get('/api/forumCommunityMember/findPage', params);
}
// ==================== 帖子相关 ====================
/**
*
*/
export function postFindPageApi(params: any) {
return get('/api/forumPost/findPage', params);
}
/**
* ID查询帖子详情
*/
export function postFindByIdApi(params: any) {
return get('/api/forumPost/findById', params);
}
/**
* /
*/
export function postSaveApi(params: any) {
return post('/api/forumPost/save', params);
}
/**
*
*/
export function postDeleteApi(params: any) {
return post('/api/forumPost/logicDelete', params);
}
// ==================== 评论相关 ====================
/**
*
*/
export function commentFindPageApi(params: any) {
return get('/api/forumComment/findPage', params);
}
/**
* ID查询评论详情
*/
export function commentFindByIdApi(params: any) {
return get('/api/forumComment/findById', params);
}
/**
* /
*/
export function commentSaveApi(params: any) {
return post('/api/forumComment/save', params);
}
/**
*
*/
export function commentDeleteApi(params: any) {
return post('/api/forumComment/logicDelete', params);
}
// ==================== 点赞相关 ====================
/**
* /
*/
export function likeSaveApi(params: any) {
return post('/api/forumLike/save', params);
}
/**
*
*/
export function likeDeleteApi(params: any) {
return post('/api/forumLike/logicDelete', params);
}
/**
*
*/
export function likeFindByIdApi(params: any) {
return get('/api/forumLike/findById', params);
}
// ==================== 收藏相关 ====================
/**
* /
*/
export function collectSaveApi(params: any) {
return post('/api/forumCollect/save', params);
}
/**
*
*/
export function collectDeleteApi(params: any) {
return post('/api/forumCollect/logicDelete', params);
}
/**
*
*/
export function collectFindPageApi(params: any) {
return get('/api/forumCollect/findPage', params);
}
// ==================== 消息相关 ====================
/**
*
*/
export function messageFindPageApi(params: any) {
return get('/api/forumMessage/findPage', params);
}
/**
*
*/
export function messageReadApi(params: any) {
return post('/api/forumMessage/save', params);
}
/**
*
*/
export function messageUnreadCountApi(params: any) {
return get('/api/forumMessage/unreadCount', params);
}
// ==================== 文件上传相关 ====================
/**
*
*/
export function fileUploadApi(params: any) {
return post('/api/upload/uploadFile', params);
}

View File

@ -339,6 +339,51 @@
"enablePullDownRefresh": false
}
},
{
"path": "pages/view/routine/lt/index",
"style": {
"navigationBarTitleText": "学校论坛",
"enablePullDownRefresh": true,
"backgroundColor": "#f5f7fa"
}
},
{
"path": "pages/view/routine/lt/detail",
"style": {
"navigationBarTitleText": "论坛详情",
"enablePullDownRefresh": true,
"backgroundColor": "#f5f7fa"
}
},
{
"path": "pages/view/routine/lt/create",
"style": {
"navigationBarTitleText": "创建论坛",
"backgroundColor": "#f5f7fa"
}
},
{
"path": "pages/view/routine/lt/edit",
"style": {
"navigationBarTitleText": "修改论坛",
"backgroundColor": "#f5f7fa"
}
},
{
"path": "pages/view/routine/lt/postDetail",
"style": {
"navigationBarTitleText": "帖子详情",
"enablePullDownRefresh": true,
"backgroundColor": "#f5f7fa"
}
},
{
"path": "pages/view/routine/lt/publish",
"style": {
"navigationBarTitleText": "发布帖子",
"backgroundColor": "#f5f7fa"
}
},
{
"path": "pages/view/routine/ShiTangXunCha/index",
"style": {

View File

@ -294,7 +294,7 @@ const sections = reactive<Section[]>([
text: "交流讨论",
show: true,
permissionKey: "ysyc-jltl",
path: "/pages/view/rw/index",
path: "/pages/view/routine/lt/index",
},
{
id: "gnyy7",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,703 @@
<template>
<view class="forum-community-page">
<!-- 顶部搜索和筛选区域 -->
<view class="top-section">
<view class="search-card">
<!-- 搜索框 -->
<view class="search-item">
<view class="search-container">
<BasicSearch
placeholder="搜索交流群名称"
@search="handleSearch"
class="search-input"
v-model="searchKeyword"
/>
<u-button
text="查询"
type="primary"
size="small"
class="search-button"
@click="handleSearch(searchKeyword)"
:custom-style="{ flexShrink: 0, width: 'auto', minWidth: '120rpx' }"
/>
</view>
</view>
<!-- Tab筛选 -->
<scroll-view scroll-x class="filter-tabs-scroll">
<view class="filter-tabs">
<view
v-for="tab in tabs"
:key="tab.value"
class="filter-tab"
:class="{ active: activeTab === tab.value }"
@click="switchTab(tab.value)"
>
{{ tab.label }}
</view>
</view>
</scroll-view>
</view>
</view>
<!-- 中间内容区域 -->
<view class="middle-section">
<scroll-view
scroll-y
class="list-scroll-view"
@scrolltolower="loadMore"
lower-threshold="100"
>
<view v-if="isLoading && dataList.length === 0" class="loading-indicator">
<text class="loading-icon"></text>
<text class="loading-text">加载中...</text>
</view>
<template v-else-if="dataList.length > 0">
<view
v-for="item in dataList"
:key="item.id"
class="community-item"
:class="{ 'no-image': !item.fileUrl }"
@click="goDetail(item)"
>
<!-- 社区封面 -->
<image
v-if="item.fileUrl"
:src="item.fileUrl"
class="community-cover"
mode="aspectFill"
/>
<!-- 社区信息 -->
<view class="community-info" :class="{ 'full-width': !item.fileUrl }">
<view class="info-header">
<text class="community-name">{{ item.communityName }}</text>
<u-tag
v-if="item.communityType == 2"
text="私密"
type="warning"
size="mini"
/>
</view>
<text class="community-desc">{{ item.communityDesc || '暂无描述' }}</text>
<view class="community-meta">
<view class="meta-item">
<u-icon name="account" size="14" color="#999" />
<text>{{ item.memberCount || 0 }} 成员</text>
</view>
<view class="meta-item">
<u-icon name="edit-pen" size="14" color="#999" />
<text>{{ item.postCount || 0 }} 帖子</text>
</view>
<view class="meta-item">
<u-icon name="calendar" size="14" color="#999" />
<text>{{ item.createdUserName || '未知' }}</text>
</view>
</view>
<!-- 分类标签和修改按钮 -->
<view class="category-and-edit">
<view class="category-tag" v-if="item.category">
<u-tag
:text="getCategoryText(item.category)"
plain
size="mini"
type="info"
/>
</view>
<view class="edit-icon" @click.stop="goEdit(item)">
<u-icon name="edit-pen" size="20" color="#007aff" />
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="action-btn">
<u-icon name="arrow-right" size="18" color="#999" />
</view>
</view>
</template>
<view v-else class="empty-state">
<text class="empty-icon">📚</text>
<text class="empty-text">暂无数据</text>
</view>
<!-- 加载更多 -->
<view v-if="isLoading && dataList.length > 0" class="loading-more">
<text class="loading-more-icon"></text>
<text class="loading-more-text">加载中...</text>
</view>
<!-- 没有更多数据 -->
<view v-if="!hasMore && dataList.length > 0" class="no-more">
<text class="no-more-icon"></text>
<text class="no-more-text">已加载全部社区</text>
</view>
</scroll-view>
</view>
<!-- 底部创建按钮 -->
<view class="bottom-section">
<u-button
type="primary"
text="创建交流群"
@click="goCreate"
block
shape="circle"
:custom-style="{ height: '88rpx', fontSize: '32rpx' }"
/>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, onUnmounted } from 'vue';
import { communityFindPageApi } from '@/api/forum';
import { useDicStore } from '@/store/modules/dic';
import BasicSearch from '@/components/BasicSearch/Search.vue';
const { findByPid } = useDicStore();
//
const searchKeyword = ref('');
// Tab -
const tabs = ref([
{ label: '全部', value: 'all', dictionaryCode: 'all' }
]);
const activeTab = ref('all');
//
const dataList = ref([]);
const isLoading = ref(false);
const hasMore = ref(true);
const currentPage = ref(1);
const pageSize = ref(20);
// Tab
const loadTabData = async () => {
try {
const res = await findByPid({ pid: 2146632765 });
console.log('Tab分类API返回数据:', res);
// res.result
let data = [];
if (res && res.result && Array.isArray(res.result) && res.result.length > 0) {
data = res.result;
} else if (res && Array.isArray(res) && res.length > 0) {
data = res;
}
if (data.length > 0) {
// Tab""
const tabOptions = [
{ label: '全部', value: 'all', dictionaryCode: 'all' }
];
//
data.forEach(item => {
if (item.dictionaryValue && item.dictionaryCode) {
tabOptions.push({
label: item.dictionaryValue,
value: item.dictionaryCode,
dictionaryCode: item.dictionaryCode
});
}
});
tabs.value = tabOptions;
console.log('加载Tab数据成功:', tabs.value);
}
} catch (error) {
console.error('加载Tab数据失败:', error);
// 使Tab
tabs.value = [
{ label: '全部', value: 'all', dictionaryCode: 'all' }
];
}
};
//
const queryData = async (isLoadMore = false) => {
if (isLoading.value) return;
isLoading.value = true;
try {
const params: any = {
page: isLoadMore ? currentPage.value + 1 : 1,
rows: pageSize.value,
communityName: searchKeyword.value,
status: 'A'
};
// Tab
if (activeTab.value !== 'all' && activeTab.value !== 'my') {
params.communityType = parseInt(activeTab.value);
}
console.log('queryData中的searchKeyword.value:', searchKeyword.value);
console.log('请求参数:', params);
const res = await communityFindPageApi(params);
console.log('API返回数据:', res);
//
let list = [];
if (res?.rows && Array.isArray(res.rows)) {
// : { total: 1, page: 1, records: 1, rows: [...] }
list = res.rows;
} else if (res?.records && Array.isArray(res.records)) {
// : { records: [...], total: 1 }
list = res.records;
} else if (res?.data) {
// : { data: { records: [...] } }
if (Array.isArray(res.data)) {
list = res.data;
} else if (res.data.rows && Array.isArray(res.data.rows)) {
list = res.data.rows;
} else if (res.data.records && Array.isArray(res.data.records)) {
list = res.data.records;
}
} else if (Array.isArray(res)) {
//
list = res;
}
console.log('解析后的列表数据:', list);
if (isLoadMore) {
dataList.value.push(...list);
currentPage.value++;
} else {
dataList.value = list;
currentPage.value = 1;
}
hasMore.value = list.length === pageSize.value;
} catch (error) {
console.error('查询社区列表失败:', error);
uni.showToast({
title: '查询失败',
icon: 'none'
});
} finally {
isLoading.value = false;
}
};
// Tab
const switchTab = (value: string) => {
activeTab.value = value;
queryData(false);
};
//
const handleSearch = (event: any) => {
console.log('搜索事件对象:', event);
let keyword = '';
//
if (typeof event === 'string') {
//
keyword = event;
} else if (event && typeof event === 'object') {
//
keyword = event.detail?.value ||
event.target?.value ||
event.value ||
'';
}
// searchKeyword
searchKeyword.value = keyword;
console.log('搜索关键词:', searchKeyword.value);
// searchKeyword
setTimeout(() => {
console.log('延迟查询时的searchKeyword:', searchKeyword.value);
queryData(false);
}, 100);
};
//
const loadMore = () => {
if (!isLoading.value && hasMore.value) {
queryData(true);
}
};
//
const getCategoryText = (category: string) => {
const categoryMap: Record<string, string> = {
'study': '学习交流',
'club': '社团活动',
'lost': '失物招领',
'news': '校园动态'
};
return categoryMap[category] || category || '未分类';
};
//
const goDetail = (item: any) => {
uni.navigateTo({
url: `/pages/view/routine/lt/detail?id=${item.id}`
});
};
//
const goEdit = (item: any) => {
uni.navigateTo({
url: `/pages/view/routine/lt/edit?id=${item.id}`
});
};
//
const goCreate = () => {
uni.navigateTo({
url: '/pages/view/routine/lt/create'
});
};
onMounted(async () => {
// Tab
await loadTabData();
queryData(false);
//
uni.$on('refreshCommunityList', () => {
console.log('收到刷新事件,重新加载数据');
queryData(false);
});
});
//
onUnmounted(() => {
uni.$off('refreshCommunityList');
});
</script>
<style lang="scss" scoped>
.forum-community-page {
display: flex;
flex-direction: column;
height: 100vh;
background: #f5f7fa;
.top-section {
flex-shrink: 0;
background: #fff;
padding: 20rpx;
border-bottom: 1rpx solid #eee;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
z-index: 100;
.search-card {
.search-item {
margin-bottom: 20rpx;
.search-container {
display: flex;
align-items: center;
gap: 12rpx;
.search-input {
flex: 1;
min-width: 0;
}
.search-button {
flex-shrink: 0 !important;
width: auto !important;
min-width: 120rpx !important;
height: 80rpx !important;
}
}
}
.filter-tabs-scroll {
width: 100%;
white-space: nowrap;
.filter-tabs {
display: flex;
gap: 20rpx;
padding: 0 4rpx; // Tab
min-width: 100%;
.filter-tab {
flex-shrink: 0; // Tab
height: 64rpx;
line-height: 64rpx;
text-align: center;
background: #f5f7fa;
border-radius: 12rpx;
font-size: 28rpx;
color: #666;
transition: all 0.3s;
padding: 0 24rpx; // Tab
white-space: nowrap; //
&.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
font-weight: 500;
}
}
}
}
}
}
.middle-section {
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
}
.list-scroll-view {
flex: 1;
padding: 20rpx;
box-sizing: border-box;
height: 0; // flex
}
//
.loading-indicator {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60rpx 20rpx;
.loading-icon {
font-size: 32rpx;
margin-bottom: 12rpx;
animation: pulse 1.5s ease-in-out infinite;
}
.loading-text {
color: #667eea;
font-size: 28rpx;
}
}
@keyframes pulse {
0%, 100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.6;
transform: scale(1.1);
}
}
.community-item {
display: flex;
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
transition: all 0.3s;
&:active {
transform: scale(0.98);
}
//
&.no-image {
.community-info {
margin-left: 0;
}
}
.community-cover {
width: 160rpx;
height: 160rpx;
border-radius: 12rpx;
flex-shrink: 0;
background: #f5f7fa;
}
.community-info {
flex: 1;
margin-left: 24rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
//
&.full-width {
margin-left: 0;
width: 100%;
}
.info-header {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 12rpx;
.community-name {
font-size: 32rpx;
font-weight: 600;
color: #333;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.community-desc {
font-size: 26rpx;
color: #999;
line-height: 1.5;
margin-bottom: 16rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.community-meta {
display: flex;
gap: 32rpx;
.meta-item {
display: flex;
align-items: center;
gap: 8rpx;
font-size: 24rpx;
color: #999;
}
}
.category-and-edit {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 12rpx;
}
.category-tag {
flex: 1;
}
.edit-icon {
display: flex;
align-items: center;
justify-content: center;
width: 60rpx;
height: 60rpx;
border-radius: 50%;
background-color: rgba(0, 122, 255, 0.1);
transition: background-color 0.2s;
&:active {
background-color: rgba(0, 122, 255, 0.2);
}
}
}
.action-btn {
display: flex;
align-items: center;
margin-left: 12rpx;
}
}
//
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx 20rpx;
.empty-icon {
font-size: 64rpx;
margin-bottom: 16rpx;
opacity: 0.6;
}
.empty-text {
font-size: 32rpx;
color: #4a5568;
font-weight: 600;
margin-bottom: 16rpx;
}
.empty-hint {
font-size: 26rpx;
color: #a0aec0;
}
}
//
.loading-more {
display: flex;
align-items: center;
justify-content: center;
gap: 16rpx;
padding: 40rpx;
.loading-more-icon {
font-size: 36rpx;
animation: pulse 1.5s ease-in-out infinite;
}
.loading-more-text {
color: #667eea;
font-size: 28rpx;
font-weight: 500;
}
}
//
.no-more {
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
padding: 48rpx 40rpx;
margin-top: 16rpx;
background: linear-gradient(135deg, #f7f9fc 0%, #e8f0fe 100%);
border-radius: 24rpx;
margin-left: 32rpx;
margin-right: 32rpx;
.no-more-icon {
font-size: 32rpx;
color: #52c41a;
}
.no-more-text {
color: #718096;
font-size: 26rpx;
font-weight: 500;
}
}
.bottom-section {
flex-shrink: 0;
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
padding: 20rpx;
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.05);
z-index: 100;
border-top: 1rpx solid #eee;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,259 @@
<template>
<view class="publish-post-page">
<view class="form-container">
<!-- 标题输入 -->
<view class="form-item">
<view class="item-label">标题 <text class="required">*</text></view>
<u--input
v-model="form.title"
placeholder="请输入标题最多50字"
:maxlength="50"
:showWordLimit="true"
/>
</view>
<!-- 内容输入 -->
<view class="form-item">
<view class="item-label">内容</view>
<u--textarea
v-model="form.content"
placeholder="分享你的想法..."
:maxlength="2000"
:count="true"
:auto-height="true"
:height="300"
/>
</view>
<!-- 图片视频文档上传 -->
<view class="form-item">
<view class="item-label">图片视频文档</view>
<view class="upload-section">
<ImageVideoUpload
v-model:image-list="imageList"
v-model:video-list="videoList"
v-model:file-list="fileList"
:max-image-count="5"
:max-video-count="3"
:max-file-count="5"
:enable-file="true"
:allowed-file-types="['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt']"
:compress-config="compressConfig"
:upload-api="attachmentUpload"
@image-upload-success="onImageUploadSuccess"
@video-upload-success="onVideoUploadSuccess"
@file-upload-success="onFileUploadSuccess"
/>
</view>
</view>
</view>
<!-- 底部按钮 -->
<view class="bottom-buttons">
<u-button
text="发布"
type="primary"
@click="publishPost"
:loading="submitting"
block
shape="circle"
/>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { postSaveApi } from '@/api/forum';
import { attachmentUpload } from '@/api/system/upload';
import { useUserStore } from '@/store/modules/user';
import { ImageVideoUpload, type ImageItem, type VideoItem, type FileItem, COMPRESS_PRESETS } from '@/components/ImageVideoUpload';
// store
const userStore = useUserStore();
// ID
const communityId = ref('');
// ID
const getCurrentTeacherId = () => {
const jsData = userStore.getJs;
return jsData?.id || null;
};
//
const form = ref({
communityId: '',
title: '',
content: '',
fileUrl: '',
fileName: '',
fileFormat: '',
userId: ''
});
//
const compressConfig = ref(COMPRESS_PRESETS.medium);
//
const imageList = ref<ImageItem[]>([]);
//
const videoList = ref<VideoItem[]>([]);
//
const fileList = ref<FileItem[]>([]);
//
const submitting = ref(false);
//
onLoad((options: any) => {
if (options.communityId) {
communityId.value = options.communityId;
form.value.communityId = options.communityId;
}
});
//
const onImageUploadSuccess = (image: ImageItem, index: number) => {
console.log('图片上传成功:', image, index);
};
const onVideoUploadSuccess = (video: VideoItem, index: number) => {
console.log('视频上传成功:', video, index);
};
const onFileUploadSuccess = (file: FileItem, index: number) => {
console.log('文档上传成功:', file, index);
};
//
const publishPost = async () => {
//
if (!form.value.title) {
uni.showToast({
title: '请输入标题',
icon: 'none'
});
return;
}
submitting.value = true;
try {
// ID
const currentUserId = getCurrentTeacherId();
if (!currentUserId) {
uni.showToast({
title: '无法获取用户信息',
icon: 'none'
});
submitting.value = false;
return;
}
// ID
form.value.userId = currentUserId;
//
const imageUrls = imageList.value
.filter(img => img.url)
.map(img => img.url)
.filter((url): url is string => !!url);
const videoUrls = videoList.value
.filter(video => video.url)
.map(video => video.url)
.filter((url): url is string => !!url);
const fileUrls = fileList.value
.filter(file => file.url)
.map(file => file.url)
.filter((url): url is string => !!url);
// URL
const allUrls = [...imageUrls, ...videoUrls, ...fileUrls];
form.value.fileUrl = JSON.stringify(allUrls);
form.value.fileName = JSON.stringify(allUrls.map(url => {
return url.split('/').pop() || '';
}));
form.value.fileFormat = JSON.stringify(allUrls.map(url => {
return url.split('.').pop() || 'jpg';
}));
await postSaveApi(form.value);
uni.showToast({
title: '发布成功',
icon: 'success'
});
setTimeout(() => {
uni.navigateBack({
success: () => {
// detail
uni.$emit('refreshPostList');
}
});
}, 1500);
} catch (error: any) {
uni.showToast({
title: error.message || '发布失败',
icon: 'none'
});
} finally {
submitting.value = false;
}
};
</script>
<style lang="scss" scoped>
.publish-post-page {
min-height: 100vh;
background: #f5f7fa;
.form-container {
padding: 20rpx;
padding-bottom: 180rpx;
.form-item {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 20rpx;
.item-label {
font-size: 28rpx;
font-weight: 500;
color: #333;
margin-bottom: 20rpx;
.required {
color: #f56c6c;
margin-left: 4rpx;
}
}
.upload-section {
width: 100%;
}
}
}
.bottom-buttons {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
padding: 20rpx;
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.05);
z-index: 100;
}
}
</style>

View File

@ -17,46 +17,26 @@
</view>
</view>
<!-- 查询组件 -->
<view class="query-component">
<view class="search-card">
<view class="search-item">
<uni-search-bar
v-model="searchForm.kcmc"
placeholder="搜索课程名称、教师..."
bgColor="#ffffff"
radius="100"
cancelButton="none"
@confirm="handleSearch"
@input="handleSearchInput"
></uni-search-bar>
</view>
<view class="search-actions">
<u-button
text="查询"
type="primary"
size="small"
@click="handleSearch"
class="search-btn"
/>
<u-button
text="重置"
type="info"
size="small"
@click="handleReset"
class="reset-btn"
/>
</view>
<!-- 统计和学期选择 -->
<view class="stats-bar">
<view class="stats-left">
<text class="stats-icon">📖</text>
<text class="stats-text">所有课程</text>
<text class="stats-count">{{ courseList.length }}</text>
</view>
<view class="stats-right">
<uni-data-select
v-model="searchForm.xqId"
:localdata="xqList"
placeholder="选择学期"
@change="handleXqChange"
class="xq-select"
></uni-data-select>
</view>
</view>
<!-- 列表组件 -->
<view class="list-component">
<view class="list-header">
<text class="list-title-icon">📖</text>
<text class="list-title">所有课程</text>
<text class="list-subtitle"> {{ courseList.length }} 门课程</text>
</view>
<scroll-view
scroll-y
@ -133,7 +113,7 @@
<script lang="ts" setup>
import { ref, reactive, onMounted } from "vue";
import { onShow } from "@dcloudio/uni-app";
import { kcjbFindPageApi, kcjbBannerApi, kcjbRegisterApi } from "@/api/base/kcjbApi";
import { kcjbFindPageApi, kcjbBannerApi, kcjbRegisterApi, getXqList } from "@/api/base/kcjbApi";
import { useUserStore } from "@/store/modules/user";
const { getJs, getUser } = useUserStore();
@ -153,9 +133,12 @@ interface CourseItem {
//
const searchForm = reactive({
kcmc: '' //
xqId: '' // ID
});
//
const xqList = ref<Array<{ value: string; text: string }>>([]);
//
const courseList = ref<CourseItem[]>([]);
const isLoading = ref(false);
@ -163,20 +146,37 @@ const hasMore = ref(true);
const currentPage = ref(1);
const pageSize = ref(10);
//
const handleSearchInput = (value: string) => {
searchForm.kcmc = value;
//
const loadXqList = async () => {
try {
const response: any = await getXqList();
console.log('学期列表原始数据:', response);
// uni-data-select
const data = Array.isArray(response) ? response : (response?.result || response?.data || response?.rows || []);
console.log('提取的学期数据:', data);
xqList.value = data.map((item: any) => ({
value: item.id,
text: item.xqmc || item.name
}));
console.log('转换后的下拉选项:', xqList.value);
//
if (xqList.value.length > 0) {
searchForm.xqId = xqList.value[0].value;
console.log('默认选择学期:', searchForm.xqId);
//
getCourseList(false);
}
} catch (error) {
console.error('获取学期列表失败:', error);
xqList.value = [];
}
};
//
const handleSearch = () => {
console.log('搜索课程名称:', searchForm.kcmc);
getCourseList(false);
};
//
const handleReset = () => {
searchForm.kcmc = '';
//
const handleXqChange = (value: string) => {
console.log('选择的学期ID:', value);
//
getCourseList(false);
};
@ -190,7 +190,7 @@ const getCourseList = async (isLoadMore = false) => {
const params: any = {
page: isLoadMore ? currentPage.value + 1 : 1,
rows: pageSize.value,
kcmc: searchForm.kcmc
xqId: searchForm.xqId
};
// admin ID
@ -298,12 +298,15 @@ const loadMore = () => {
//
onShow(() => {
getCourseList(false);
//
if (searchForm.xqId) {
getCourseList(false);
}
});
//
onMounted(() => {
getCourseList(false);
loadXqList(); //
});
</script>
@ -384,33 +387,46 @@ onMounted(() => {
}
}
//
.query-component {
padding: 16px;
//
.stats-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
background-color: #ffffff;
border-bottom: 1px solid #e8ecf1;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.search-card {
.search-item {
margin-bottom: 12px;
border-bottom: 1px solid #e9ecef;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
gap: 12px;
.stats-left {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
:deep(.uni-searchbar) {
border: 1px solid #e8ecf1;
border-radius: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
.stats-icon {
font-size: 20px;
}
.stats-text {
font-size: 16px;
font-weight: 600;
color: #2c3e50;
}
.stats-count {
font-size: 15px;
font-weight: bold;
color: #4e73df;
}
}
.search-actions {
display: flex;
gap: 10px;
justify-content: flex-end;
.stats-right {
flex: 1;
max-width: 200px;
.search-btn, .reset-btn {
min-width: 70px;
border-radius: 20px;
.xq-select {
width: 100%;
}
}
}
@ -422,37 +438,11 @@ onMounted(() => {
display: flex;
flex-direction: column;
.list-header {
display: flex;
align-items: center;
gap: 8px;
padding: 16px 16px 12px 16px;
background-color: transparent;
.list-title-icon {
font-size: 20px;
line-height: 1;
flex-shrink: 0;
}
.list-title {
font-size: 18px;
font-weight: bold;
color: #2c3e50;
flex-shrink: 0;
}
.list-subtitle {
font-size: 13px;
color: #7f8c9a;
margin-left: 4px;
}
}
.list-scroll-view {
flex: 1;
padding: 0 16px 16px 16px;
padding: 12px 16px 16px 16px;
box-sizing: border-box;
height: 0; // flex
}
}
@ -719,16 +709,29 @@ onMounted(() => {
}
//
:deep(.uni-searchbar__box) {
border-radius: 20px !important;
background-color: #ffffff !important;
}
:deep(.uni-searchbar__text-placeholder) {
color: #a0aec0 !important;
}
:deep(.uni-searchbar__box-icon-search) {
color: #4e73df !important;
:deep(.stats-right .uni-data-select) {
width: 100%;
.uni-select {
background-color: #f8f9fa;
border-radius: 8px;
border: 1px solid #e9ecef;
height: 36px;
min-height: 36px;
}
.uni-select__input-text {
color: #212529;
font-size: 14px;
}
.uni-select__input-placeholder {
color: #adb5bd;
font-size: 13px;
}
.uni-select__selector {
padding: 0 12px;
}
}
</style>

View File

@ -364,6 +364,7 @@ onMounted(() => {
flex: 1;
padding: 12px;
box-sizing: border-box;
height: 0; // flex
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB