diff --git a/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml b/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml index 7605a84..acf322c 100644 --- a/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml +++ b/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml @@ -209,7 +209,7 @@ id, resource_id, title, content, summary, cover_image, tag_id, author, source, is_audited, publish_time, status,source_url, creator,create_time ) VALUES ( - #{id}, #{resourceID}, #{title}, #{content}, #{summary}, #{coverImage}, #{tagID}, #{author}, #{source}, #{isAudited}, #{publistTime} + #{id}, #{resourceID}, #{title}, #{content}, #{summary}, #{coverImage}, #{tagID}, #{author}, #{source}, #{isAudited}, #{publishTime} #{status}, #{sourceUrl}, #{creator}, #{createTime} ) diff --git a/schoolNewsWeb/src/views/admin/manage/content/BannerManagementView.vue b/schoolNewsWeb/src/views/admin/manage/content/BannerManagementView.vue index 5d17e12..6945d4c 100644 --- a/schoolNewsWeb/src/views/admin/manage/content/BannerManagementView.vue +++ b/schoolNewsWeb/src/views/admin/manage/content/BannerManagementView.vue @@ -1,15 +1,13 @@ @@ -303,7 +226,7 @@ import { resourceApi } from '@/apis/resource/resource'; import { courseApi } from '@/apis/study/course'; import { FileUpload } from '@/components'; import { FILE_DOWNLOAD_URL } from '@/config'; -import { AdminLayout } from '@/views/admin'; +import { AdminLayout } from '@/views/admin'; // 数据状态 const loading = ref(false); @@ -352,7 +275,7 @@ async function loadBanners() { loading.value = true; const result = await bannerApi.getBannerPage(pageParam.value); banners.value = result.pageDomain?.dataList || []; - + // 更新分页信息 if (result.pageParam) { pageParam.value = { @@ -397,7 +320,7 @@ async function handleEdit(banner: Banner) { isEdit.value = true; currentBanner.value = { ...banner }; dialogVisible.value = true; - + // 如果有linkID,预加载该项的信息以便显示标题 if (banner.linkID && (banner.linkType === 1 || banner.linkType === 2)) { await loadSelectedItem(banner.linkID, banner.linkType); @@ -464,9 +387,24 @@ async function handleSubmit() { return; } + // 校验链接内容:资源、课程或外部链接必须有一个有值 + if (currentBanner.value.linkType === 1 || currentBanner.value.linkType === 2) { + // 选择资源或课程时,必须选择具体的资源/课程 + if (!currentBanner.value.linkID) { + ElMessage.warning(`请选择${currentBanner.value.linkType === 1 ? '资源' : '课程'}`); + return; + } + } else if (currentBanner.value.linkType === 3) { + // 选择外部链接时,必须输入链接地址 + if (!currentBanner.value.linkUrl || currentBanner.value.linkUrl.trim() === '') { + ElMessage.warning('请输入外部链接地址'); + return; + } + } + try { submitting.value = true; - + if (isEdit.value) { await bannerApi.updateBanner(currentBanner.value); ElMessage.success('更新成功'); @@ -474,7 +412,7 @@ async function handleSubmit() { await bannerApi.createBanner(currentBanner.value); ElMessage.success('创建成功'); } - + handleCloseDialog(); await loadBanners(); } catch (error) { @@ -510,7 +448,7 @@ async function handleDelete(banner: Banner) { type: 'warning' } ); - + await bannerApi.deleteBannerById(banner.id!); ElMessage.success('删除成功'); await loadBanners(); @@ -550,12 +488,12 @@ function getBannerImageUrl(banner: Banner): string { if (!banner.imageUrl) { return '/img/article-default.png'; } - + // 如果已经是完整URL(http或https开头),直接返回 if (banner.imageUrl.startsWith('http://') || banner.imageUrl.startsWith('https://')) { return banner.imageUrl; } - + // 否则拼接文件下载URL return FILE_DOWNLOAD_URL + banner.imageUrl; } @@ -572,16 +510,16 @@ function handleImageError(event: Event) { async function loadSelectOptions(reset = false) { if (loadingMore.value) return; if (noMoreData.value && !reset) return; - + if (reset) { selectPageParam.value.pageNumber = 1; selectOptions.value = []; noMoreData.value = false; } - + try { loadingMore.value = true; - + let result; if (currentBanner.value.linkType === 1) { // 加载资源列表 @@ -596,34 +534,34 @@ async function loadSelectOptions(reset = false) { searchKeyword.value ? { title: searchKeyword.value } as any : undefined ); } - + if (result && result.pageDomain?.dataList) { const newOptions = result.pageDomain.dataList.map((item: any) => { // 根据linkType选择正确的ID字段 let itemId = ''; let itemTitle = ''; - + if (currentBanner.value.linkType === 1) { // 资源使用resourceID - itemId = item.resourceID ; - itemTitle = item.title ; + itemId = item.resourceID; + itemTitle = item.title; } else if (currentBanner.value.linkType === 2) { // 课程使用courseID - itemId = item.courseID ; - itemTitle = item.name ; + itemId = item.courseID; + itemTitle = item.name; } - + return { id: itemId, title: itemTitle }; }); - + // 去重合并数据 const existingIds = new Set(selectOptions.value.map(opt => opt.id)); const uniqueNewOptions = newOptions.filter((opt: any) => !existingIds.has(opt.id)); selectOptions.value = [...selectOptions.value, ...uniqueNewOptions]; - + // 更新分页信息 if (result.pageParam) { selectPageParam.value = { @@ -631,7 +569,7 @@ async function loadSelectOptions(reset = false) { ...result.pageParam }; } - + // 检查是否还有更多数据 noMoreData.value = selectPageParam.value.pageNumber >= selectPageParam.value.totalPages; } @@ -646,15 +584,15 @@ async function loadSelectOptions(reset = false) { // 切换下拉框显示 function toggleDropdown() { dropdownVisible.value = !dropdownVisible.value; - + if (dropdownVisible.value) { // 打开时加载数据(不重置已选项) searchKeyword.value = ''; - + // 总是加载第一页数据(保留已选项) - const hasPreloadedItem = selectOptions.value.length === 1 && - selectOptions.value[0].id === currentBanner.value.linkID; - + const hasPreloadedItem = selectOptions.value.length === 1 && + selectOptions.value[0].id === currentBanner.value.linkID; + if (hasPreloadedItem) { // 如果只有预加载的一项,重置并加载完整列表(会保留预加载项因为去重逻辑) selectPageParam.value.pageNumber = 1; @@ -664,7 +602,7 @@ function toggleDropdown() { // 列表为空时,重置并加载 loadSelectOptions(true); } - + // 点击外部关闭下拉框 nextTick(() => { document.addEventListener('click', closeDropdown); @@ -685,7 +623,7 @@ function handleSearchChange() { if (searchTimer) { clearTimeout(searchTimer); } - + searchTimer = setTimeout(() => { loadSelectOptions(true); }, 500); // 500ms防抖 @@ -697,7 +635,7 @@ function handleScroll(event: Event) { const scrollTop = target.scrollTop; const scrollHeight = target.scrollHeight; const clientHeight = target.clientHeight; - + // 滚动到距离底部50px时加载更多 if (scrollHeight - scrollTop - clientHeight < 50) { if (!loadingMore.value && !noMoreData.value) { @@ -718,10 +656,10 @@ function handleSelectItem(item: any) { // 获取已选择项的标签 function getSelectedItemLabel(): string { if (!currentBanner.value.linkID) return ''; - + const selected = selectOptions.value.find(opt => opt.id === currentBanner.value.linkID); const label = selected ? selected.title : ''; - + return label; } @@ -732,7 +670,7 @@ watch(() => currentBanner.value.linkType, (newType, oldType) => { selectOptions.value = []; noMoreData.value = false; selectPageParam.value.pageNumber = 1; - + // 如果下拉框是打开的,重新加载数据 if (dropdownVisible.value) { loadSelectOptions(true); @@ -885,7 +823,9 @@ onMounted(() => { } @keyframes spin { - to { transform: rotate(360deg); } + to { + transform: rotate(360deg); + } } // Banner 列表 @@ -896,21 +836,21 @@ onMounted(() => { gap: 16px; overflow-y: auto; min-height: 0; - + // 美化滚动条 &::-webkit-scrollbar { width: 8px; } - + &::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 4px; } - + &::-webkit-scrollbar-thumb { background: #c1c1c1; border-radius: 4px; - + &:hover { background: #a8a8a8; } @@ -1077,7 +1017,7 @@ onMounted(() => { &.btn-delete { color: #E7000B; - + &:hover { background: rgba(231, 0, 11, 0.1); } @@ -1124,8 +1064,13 @@ onMounted(() => { } @keyframes fadeIn { - from { opacity: 0; } - to { opacity: 1; } + from { + opacity: 0; + } + + to { + opacity: 1; + } } .dialog-container { @@ -1145,6 +1090,7 @@ onMounted(() => { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); @@ -1253,10 +1199,10 @@ onMounted(() => { border: 1px solid #e4e7ed; img { - width: 100%; + width: 100%; height: auto; max-height: 200px; - object-fit: cover; + object-fit: cover; } } @@ -1286,12 +1232,12 @@ onMounted(() => { } // 分页组件 -.pagination-container { - margin-top: 24px; - display: flex; - justify-content: center; - padding: 20px 0; -} +// .pagination-container { +// margin-top: 24px; +// display: flex; +// justify-content: center; +// padding: 20px 0; +// } // ==================== 下拉选择器样式 ==================== .select-container {