Merge branch 'main' of https://gitlab.guxuan.icu/Leo_Ding/JinShan_uniapp
This commit is contained in:
commit
00a70f1c6c
@ -111,7 +111,7 @@
|
|||||||
<view class="btn-group">
|
<view class="btn-group">
|
||||||
<u-button
|
<u-button
|
||||||
@click="handleModify"
|
@click="handleModify"
|
||||||
:type="isEditing ? 'success' : 'primary'"
|
:type="isEditing ? 'primary' : 'primary'"
|
||||||
class="action-btn primary-btn"
|
class="action-btn primary-btn"
|
||||||
:loading="isLoading"
|
:loading="isLoading"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
@ -119,8 +119,9 @@
|
|||||||
{{isEditing ? '保存修改' : '修改'}}
|
{{isEditing ? '保存修改' : '修改'}}
|
||||||
</u-button>
|
</u-button>
|
||||||
<u-button
|
<u-button
|
||||||
|
v-if="showWithdrawButton"
|
||||||
@click="handleWithdraw"
|
@click="handleWithdraw"
|
||||||
:type="isEditing ? 'default' : 'warning'"
|
:type="isEditing ? 'error' : 'error'"
|
||||||
class="action-btn"
|
class="action-btn"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
>
|
>
|
||||||
@ -154,6 +155,12 @@
|
|||||||
tempImagePaths: [] // 临时存储上传的图片路径
|
tempImagePaths: [] // 临时存储上传的图片路径
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
showWithdrawButton() {
|
||||||
|
// 使用数组includes方法简化条件判断
|
||||||
|
return [1, 2, 99].includes(this.detailObj.status);
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
let obj = uni.getStorageSync("Detail");
|
let obj = uni.getStorageSync("Detail");
|
||||||
this.detailObj = {...obj};
|
this.detailObj = {...obj};
|
||||||
@ -249,6 +256,7 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
// 修改/保存操作
|
// 修改/保存操作
|
||||||
async handleModify() {
|
async handleModify() {
|
||||||
if (!this.isEditing) {
|
if (!this.isEditing) {
|
||||||
@ -300,7 +308,11 @@
|
|||||||
|
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '修改成功',
|
title: '修改成功',
|
||||||
icon: 'success'
|
icon: 'success',
|
||||||
|
success: () => {
|
||||||
|
// 修改成功后跳转到列表页
|
||||||
|
this.redirectToList();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.isEditing = false;
|
this.isEditing = false;
|
||||||
@ -343,7 +355,11 @@
|
|||||||
if (res && res.success) {
|
if (res && res.success) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '撤回成功',
|
title: '撤回成功',
|
||||||
icon: 'success'
|
icon: 'success',
|
||||||
|
success: () => {
|
||||||
|
// 撤回成功后跳转到列表页
|
||||||
|
this.redirectToList();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
@ -351,6 +367,12 @@
|
|||||||
icon: 'none'
|
icon: 'none'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
// 跳转到列表页
|
||||||
|
redirectToList() {
|
||||||
|
uni.redirectTo({
|
||||||
|
url: '/pages/myTickets/index'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,13 +5,29 @@
|
|||||||
<view class="content-wrapper">
|
<view class="content-wrapper">
|
||||||
<!-- 公告列表 -->
|
<!-- 公告列表 -->
|
||||||
<view class="notice-list">
|
<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"
|
<view class="notice-item"
|
||||||
v-for="(item,index) in noticesList"
|
v-for="(item,index) in noticesList"
|
||||||
:key="index"
|
:key="item.id"
|
||||||
@click="goNoticeDetail(item.id)">
|
@click="goNoticeDetail(item.id)">
|
||||||
<image :src="item.cover"
|
<image :src="item.cover"
|
||||||
class="notice-image"
|
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-content">
|
||||||
<view class="notice-title line-clamp-1">{{item.title}}</view>
|
<view class="notice-title line-clamp-1">{{item.title}}</view>
|
||||||
<view class="notice-description line-clamp-2" v-html="item.desc"></view>
|
<view class="notice-description line-clamp-2" v-html="item.desc"></view>
|
||||||
@ -22,8 +38,13 @@
|
|||||||
</view>
|
</view>
|
||||||
</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>
|
<image src="/static/images/empty-notice.png" class="empty-image"></image>
|
||||||
<text class="empty-text">暂无公告</text>
|
<text class="empty-text">暂无公告</text>
|
||||||
</view>
|
</view>
|
||||||
@ -49,45 +70,102 @@
|
|||||||
formatTime,
|
formatTime,
|
||||||
IMAGE_BASE_URL,
|
IMAGE_BASE_URL,
|
||||||
noticesList: [],
|
noticesList: [],
|
||||||
|
cachedNotices: uni.getStorageSync('cachedNotices') || [],
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
totalPages: 1
|
totalPages: 1,
|
||||||
|
lastRequestTime: 0
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getNotices();
|
this.getNotices();
|
||||||
|
this.initScrollListener();
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.removeScrollListener();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async getNotices() {
|
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;
|
this.isLoading = true;
|
||||||
|
uni.showLoading({ title: '加载中...', mask: true });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await get('/api/v1/apps/home/notices', {
|
const res = await get('/api/v1/apps/home/notices', {
|
||||||
current: this.currentPage,
|
current: this.currentPage,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
|
}, {
|
||||||
|
timeout: 5000 // 设置超时时间
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res?.success) {
|
if (res?.success) {
|
||||||
const processedData = res.data.map(item => ({
|
const processedData = res.data.map(item => ({
|
||||||
...item,
|
...item,
|
||||||
cover: item.cover.startsWith('http') ? item.cover : IMAGE_BASE_URL + item.cover
|
cover: this.processImageUrl(item.cover),
|
||||||
|
desc: this.filterHtmlTags(item.desc) // 过滤HTML标签防止XSS
|
||||||
}));
|
}));
|
||||||
|
|
||||||
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);
|
this.totalPages = Math.ceil(res.total / 10);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('获取公告失败:', err);
|
console.error('获取公告失败:', err);
|
||||||
uni.showToast({
|
if (this.currentPage === 1) {
|
||||||
title: '加载公告失败',
|
uni.showToast({
|
||||||
icon: 'none'
|
title: '加载公告失败',
|
||||||
});
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
uni.hideLoading();
|
||||||
this.isLoading = false;
|
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(/ /g, ' ');
|
||||||
|
},
|
||||||
|
|
||||||
|
handleImageError(index) {
|
||||||
|
this.$set(this.noticesList, index, {
|
||||||
|
...this.noticesList[index],
|
||||||
|
cover: '/static/images/default-cover.jpg'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
goNoticeDetail(id) {
|
goNoticeDetail(id) {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/serviceNoticeDetail/index?Id=${id}`,
|
url: `/pages/serviceNoticeDetail/index?Id=${id}`,
|
||||||
@ -95,10 +173,27 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
loadMore() {
|
initScrollListener() {
|
||||||
if (this.currentPage < this.totalPages) {
|
this.onScroll = () => {
|
||||||
this.currentPage++;
|
const scrollHeight = document.documentElement.scrollHeight;
|
||||||
this.getNotices();
|
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;
|
height: 220rpx;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
|
will-change: transform;
|
||||||
|
image-rendering: -webkit-optimize-contrast;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-content {
|
.notice-content {
|
||||||
@ -204,6 +301,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.load-more {
|
||||||
|
text-align: center;
|
||||||
|
padding: 30rpx 0;
|
||||||
|
color: var(--text-light);
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.empty-state {
|
.empty-state {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -223,6 +327,62 @@
|
|||||||
color: var(--text-light);
|
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%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
export const BASE_URL = 'https://jinshan.nantong.info';
|
export const BASE_URL = 'https://jinshan.nantong.info';
|
||||||
export const IMAGE_BASE_URL = `https://jinshan.nantong.info`
|
export const IMAGE_BASE_URL = `https://jinshan.nantong.info`
|
||||||
export const WS_BASE_URL = `wss://jinshan.nantong.info`
|
export const WS_BASE_URL = `wss://jinshan.nantong.info`
|
||||||
|
// export const BASE_URL = 'http://10.10.1.6:8071';
|
||||||
|
// export const IMAGE_BASE_URL = `http://10.10.1.6:8071`;
|
||||||
|
|
||||||
// http://36.212.197.253:8071
|
// http://36.212.197.253:8071
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user