This commit is contained in:
qiuyuan 2025-08-08 14:30:26 +08:00
parent 98e9cf72e7
commit 79e74d84db
3 changed files with 206 additions and 21 deletions

View File

@ -111,7 +111,7 @@
<view class="btn-group">
<u-button
@click="handleModify"
:type="isEditing ? 'success' : 'primary'"
:type="isEditing ? 'primary' : 'primary'"
class="action-btn primary-btn"
:loading="isLoading"
shape="circle"
@ -119,8 +119,9 @@
{{isEditing ? '保存修改' : '修改'}}
</u-button>
<u-button
v-if="showWithdrawButton"
@click="handleWithdraw"
:type="isEditing ? 'default' : 'warning'"
:type="isEditing ? 'error' : 'error'"
class="action-btn"
shape="circle"
>
@ -154,6 +155,12 @@
tempImagePaths: [] //
};
},
computed: {
showWithdrawButton() {
// 使includes
return [1, 2, 99].includes(this.detailObj.status);
}
},
mounted() {
let obj = uni.getStorageSync("Detail");
this.detailObj = {...obj};
@ -249,6 +256,7 @@
});
},
// /
async handleModify() {
if (!this.isEditing) {
@ -300,7 +308,11 @@
uni.showToast({
title: '修改成功',
icon: 'success'
icon: 'success',
success: () => {
//
this.redirectToList();
}
});
this.isEditing = false;
@ -343,7 +355,11 @@
if (res && res.success) {
uni.showToast({
title: '撤回成功',
icon: 'success'
icon: 'success',
success: () => {
//
this.redirectToList();
}
});
} else {
uni.showToast({
@ -351,6 +367,12 @@
icon: 'none'
});
}
},
//
redirectToList() {
uni.redirectTo({
url: '/pages/myTickets/index'
});
}
}
};

View File

@ -5,13 +5,29 @@
<view class="content-wrapper">
<!-- 公告列表 -->
<view class="notice-list">
<!-- 骨架屏 -->
<view class="skeleton-wrapper" v-if="isLoading && noticesList.length === 0">
<view class="skeleton-item" v-for="i in 5" :key="'skeleton-'+i">
<view class="skeleton-image"></view>
<view class="skeleton-content">
<view class="skeleton-line"></view>
<view class="skeleton-line short"></view>
<view class="skeleton-line shorter"></view>
</view>
</view>
</view>
<!-- 实际列表 -->
<view class="notice-item"
v-for="(item,index) in noticesList"
:key="index"
:key="item.id"
@click="goNoticeDetail(item.id)">
<image :src="item.cover"
class="notice-image"
mode="aspectFill"></image>
mode="aspectFill"
lazy-load
:show-menu-by-longpress="true"
@error="handleImageError(index)"></image>
<view class="notice-content">
<view class="notice-title line-clamp-1">{{item.title}}</view>
<view class="notice-description line-clamp-2" v-html="item.desc"></view>
@ -22,8 +38,13 @@
</view>
</view>
<!-- 加载更多提示 -->
<view class="load-more" v-if="noticesList.length > 0 && currentPage < totalPages">
<text>{{isLoading ? '加载中...' : '上拉加载更多'}}</text>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="noticesList.length === 0">
<view class="empty-state" v-if="noticesList.length === 0 && !isLoading">
<image src="/static/images/empty-notice.png" class="empty-image"></image>
<text class="empty-text">暂无公告</text>
</view>
@ -49,45 +70,102 @@
formatTime,
IMAGE_BASE_URL,
noticesList: [],
cachedNotices: uni.getStorageSync('cachedNotices') || [],
isLoading: false,
currentPage: 1,
totalPages: 1
totalPages: 1,
lastRequestTime: 0
};
},
mounted() {
this.getNotices();
this.initScrollListener();
},
beforeDestroy() {
this.removeScrollListener();
},
methods: {
async getNotices() {
if (this.isLoading) return;
//
const now = Date.now();
if (this.isLoading || (now - this.lastRequestTime < 1000)) return;
this.lastRequestTime = now;
// 使
if (this.currentPage === 1 && this.cachedNotices.length > 0) {
this.noticesList = [...this.cachedNotices];
//
setTimeout(() => this.fetchNotices(), 300);
return;
}
await this.fetchNotices();
},
async fetchNotices() {
this.isLoading = true;
uni.showLoading({ title: '加载中...', mask: true });
try {
const res = await get('/api/v1/apps/home/notices', {
current: this.currentPage,
pageSize: 10,
}, {
timeout: 5000 //
});
if (res?.success) {
const processedData = res.data.map(item => ({
...item,
cover: item.cover.startsWith('http') ? item.cover : IMAGE_BASE_URL + item.cover
cover: this.processImageUrl(item.cover),
desc: this.filterHtmlTags(item.desc) // HTMLXSS
}));
this.noticesList = [...this.noticesList, ...processedData];
if (this.currentPage === 1) {
this.noticesList = processedData;
//
uni.setStorage({
key: 'cachedNotices',
data: processedData,
expire: 3600 // 1
});
} else {
this.noticesList = this.noticesList.concat(processedData);
}
this.totalPages = Math.ceil(res.total / 10);
}
} catch (err) {
console.error('获取公告失败:', err);
uni.showToast({
title: '加载公告失败',
icon: 'none'
});
if (this.currentPage === 1) {
uni.showToast({
title: '加载公告失败',
icon: 'none'
});
}
} finally {
uni.hideLoading();
this.isLoading = false;
}
},
processImageUrl(url) {
if (!url) return '/static/images/default-cover.jpg';
return url.startsWith('http') ? url : IMAGE_BASE_URL + url;
},
filterHtmlTags(str) {
if (!str) return '';
return str.replace(/<[^>]+>/g, '').replace(/&nbsp;/g, ' ');
},
handleImageError(index) {
this.$set(this.noticesList, index, {
...this.noticesList[index],
cover: '/static/images/default-cover.jpg'
});
},
goNoticeDetail(id) {
uni.navigateTo({
url: `/pages/serviceNoticeDetail/index?Id=${id}`,
@ -95,10 +173,27 @@
});
},
loadMore() {
if (this.currentPage < this.totalPages) {
this.currentPage++;
this.getNotices();
initScrollListener() {
this.onScroll = () => {
const scrollHeight = document.documentElement.scrollHeight;
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const clientHeight = document.documentElement.clientHeight;
// 200px
if (scrollTop + clientHeight >= scrollHeight - 200 &&
this.currentPage < this.totalPages &&
!this.isLoading) {
this.currentPage++;
this.getNotices();
}
};
window.addEventListener('scroll', this.onScroll);
},
removeScrollListener() {
if (this.onScroll) {
window.removeEventListener('scroll', this.onScroll);
}
}
}
@ -163,6 +258,8 @@
height: 220rpx;
flex-shrink: 0;
background-color: #f5f5f5;
will-change: transform;
image-rendering: -webkit-optimize-contrast;
}
.notice-content {
@ -204,6 +301,13 @@
}
}
.load-more {
text-align: center;
padding: 30rpx 0;
color: var(--text-light);
font-size: 26rpx;
}
.empty-state {
display: flex;
flex-direction: column;
@ -223,6 +327,62 @@
color: var(--text-light);
}
}
/* 骨架屏样式 */
.skeleton-wrapper {
display: flex;
flex-direction: column;
gap: 24rpx;
.skeleton-item {
display: flex;
background: #fff;
border-radius: var(--radius);
overflow: hidden;
height: 220rpx;
.skeleton-image {
width: 220rpx;
height: 220rpx;
background: linear-gradient(90deg, #f2f2f2 25%, #e6e6e6 50%, #f2f2f2 75%);
background-size: 400% 100%;
animation: loading 1.5s ease infinite;
}
.skeleton-content {
flex: 1;
padding: 24rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
.skeleton-line {
height: 32rpx;
background: #f2f2f2;
border-radius: 8rpx;
margin-bottom: 16rpx;
animation: loading 1.5s ease infinite;
&.short {
width: 60%;
}
&.shorter {
width: 40%;
}
}
}
}
}
@keyframes loading {
0% {
background-position: 100% 50%;
}
100% {
background-position: 0 50%;
}
}
}
}
}

View File

@ -1,5 +1,8 @@
export const BASE_URL = 'http://10.10.1.6:8071';
export const IMAGE_BASE_URL = `http://10.10.1.6:8071`;
// export const BASE_URL = 'http://10.10.1.6:8071';
// export const IMAGE_BASE_URL = `http://10.10.1.6:8071`;
export const BASE_URL = 'https://jinshan.nantong.info';
export const IMAGE_BASE_URL = `https://jinshan.nantong.info`
// http://36.212.197.253:8071