New file |
| | |
| | | /** |
| | | * 新闻相关API |
| | | */ |
| | | import { http, Method } from "@/utils/request.js"; |
| | | |
| | | import api from "@/config/api.js"; |
| | | export function getNews(params){ |
| | | return http.request({ |
| | | url: "/lmk/news/page", |
| | | method: Method.GET, |
| | | needToken: true, |
| | | params: params, |
| | | }) |
| | | } |
| | | |
| | | export function detail(params){ |
| | | return http.request({ |
| | | url: "/lmk/news/" + params, |
| | | method: Method.GET, |
| | | needToken: true, |
| | | }) |
| | | } |
| | | |
| | | export function addNews(params){ |
| | | return http.request({ |
| | | url: "/lmk/news", |
| | | method: Method.POST, |
| | | needToken: true, |
| | | data: params, |
| | | }) |
| | | } |
| | | |
| | | export function editNews(params){ |
| | | return http.request({ |
| | | url: "/lmk/news", |
| | | method: Method.PUT, |
| | | needToken: true, |
| | | data: params, |
| | | }) |
| | | } |
| | | export function publish(params){ |
| | | return http.request({ |
| | | url: "/lmk/news/publish/" + params, |
| | | method: Method.PUT, |
| | | needToken: true, |
| | | }) |
| | | } |
| | | export function delById(params){ |
| | | return http.request({ |
| | | url: "/lmk/news/" + params, |
| | | method: Method.DELETE, |
| | | needToken: true, |
| | | }) |
| | | } |
| | | |
| | | |
| | |
| | | } |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/news/news", |
| | | "style": { |
| | | "navigationBarTitleText": "新闻", |
| | | "componentPlaceholder": { |
| | | "u-icon": "view", |
| | | "u-button": "view", |
| | | "u-form": "view", |
| | | "u-form-item": "view", |
| | | "u-input": "view", |
| | | "u-popup": "view", |
| | | "u-search": "view", |
| | | "u-loading": "view", |
| | | "u-navbar": "view", |
| | | "u-loadmore":"view" |
| | | } |
| | | } |
| | | }, |
| | | { |
| | | "path": "pages/news/detail", |
| | | "style": { |
| | | "navigationBarTitleText": "新闻详情", |
| | | "componentPlaceholder": { |
| | | "u-icon": "view", |
| | | "u-button": "view", |
| | | "u-form": "view", |
| | | "u-form-item": "view", |
| | | "u-input": "view", |
| | | "u-popup": "view", |
| | | "u-search": "view", |
| | | "u-loading": "view", |
| | | "u-navbar": "view", |
| | | "u-loadmore":"view" |
| | | } |
| | | } |
| | | }, |
| | | // { |
| | | // "path": "pages/customerManager/customerManager", |
| | | // "style": { |
| | |
| | | query: { |
| | | pageNumber: 1, |
| | | pageSize: 10, |
| | | publish:1, |
| | | }, |
| | | loading: false, // 是否正在加载 |
| | | noMore: false, // 是否没有更多数据 |
| | |
| | | this.getActivityList(); |
| | | }, |
| | | loadMore() { |
| | | |
| | | // 显示加载状态 |
| | | this.loading = true; |
| | | |
New file |
| | |
| | | <template> |
| | | <view class="detail-page"> |
| | | <!-- 加载状态 --> |
| | | <view v-if="loading" class="loading-container"> |
| | | <u-loading mode="circle" size="48"></u-loading> |
| | | <text class="loading-text">加载中...</text> |
| | | </view> |
| | | |
| | | <!-- 内容区域 --> |
| | | <template v-else> |
| | | <!-- 标题区 --> |
| | | <view class="header"> |
| | | <text class="title">{{ detailData.title }}</text> |
| | | <text class="meta"> |
| | | <text class="date">{{ formatDate(detailData.publishDate) }}</text> |
| | | </text> |
| | | </view> |
| | | |
| | | <!-- 富文本内容 --> |
| | | <scroll-view scroll-y class="content-wrapper"> |
| | | <rich-text |
| | | :nodes="formatContent(detailData.content)" |
| | | class="content" |
| | | ></rich-text> |
| | | </scroll-view> |
| | | </template> |
| | | |
| | | <!-- 错误提示 --> |
| | | <u-toast ref="uToast"></u-toast> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { detail } from '@/api/news.js' |
| | | |
| | | export default { |
| | | data() { |
| | | return { |
| | | detailId: '', |
| | | loading: true, |
| | | detailData: { |
| | | title: '', |
| | | content: '', |
| | | publishDate: '' |
| | | } |
| | | } |
| | | }, |
| | | onLoad(options) { |
| | | if (options.id) { |
| | | this.detailId = options.id |
| | | this.loadDetailData() |
| | | } |
| | | }, |
| | | methods: { |
| | | async loadDetailData() { |
| | | this.loading = true |
| | | try { |
| | | const res = await detail(this.detailId) |
| | | if (res.statusCode === 200) { |
| | | this.detailData = { |
| | | title: res.data.data.title || '无标题', |
| | | content: res.data.data.content || '', |
| | | publishDate: res.data.data.publishDate || '' |
| | | } |
| | | } else { |
| | | this.showError('加载失败: ' + (res.message || '未知错误')) |
| | | } |
| | | } catch (error) { |
| | | console.error('加载详情出错:', error) |
| | | this.showError('网络请求失败') |
| | | } finally { |
| | | this.loading = false |
| | | } |
| | | }, |
| | | |
| | | formatDate(dateString) { |
| | | if (!dateString) return '未知时间' |
| | | |
| | | try { |
| | | const date = new Date(dateString) |
| | | if (isNaN(date.getTime())) return dateString |
| | | |
| | | const year = date.getFullYear() |
| | | const month = (date.getMonth() + 1).toString().padStart(2, '0') |
| | | const day = date.getDate().toString().padStart(2, '0') |
| | | const hours = date.getHours().toString().padStart(2, '0') |
| | | const minutes = date.getMinutes().toString().padStart(2, '0') |
| | | |
| | | return `${year}-${month}-${day} ${hours}:${minutes}` |
| | | } catch (e) { |
| | | console.error('日期格式化错误:', e) |
| | | return dateString |
| | | } |
| | | }, |
| | | |
| | | formatContent(content) { |
| | | if (!content) return '' |
| | | |
| | | // 基础处理:确保图片自适应 |
| | | const processedContent = content |
| | | .replace(/<img/gi, '<img style="max-width:100%;height:auto;display:block;margin:20rpx auto;"') |
| | | .replace(/<table/gi, '<table style="width:100%;border-collapse:collapse;"') |
| | | .replace(/<td/gi, '<td style="border:1px solid #ddd;padding:8px;"') |
| | | |
| | | return processedContent |
| | | }, |
| | | |
| | | showError(message) { |
| | | this.$refs.uToast.show({ |
| | | type: 'error', |
| | | message: message, |
| | | duration: 2000 |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .detail-page { |
| | | padding: 30rpx; |
| | | min-height: 100vh; |
| | | background-color: #f8f8f8; |
| | | } |
| | | |
| | | .loading-container { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | height: 60vh; |
| | | |
| | | .loading-text { |
| | | margin-top: 20rpx; |
| | | font-size: 28rpx; |
| | | color: #999; |
| | | } |
| | | } |
| | | |
| | | .header { |
| | | background-color: #fff; |
| | | border-radius: 16rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 30rpx; |
| | | box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | |
| | | .title { |
| | | display: block; |
| | | font-size: 36rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | line-height: 1.5; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .meta { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | font-size: 24rpx; |
| | | color: #999; |
| | | |
| | | .date { |
| | | margin-left: 15rpx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .content-wrapper { |
| | | background-color: #fff; |
| | | border-radius: 16rpx; |
| | | padding: 30rpx; |
| | | box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | height: calc(100vh - 300rpx); |
| | | |
| | | .content { |
| | | font-size: 30rpx; |
| | | line-height: 1.8; |
| | | color: #333; |
| | | |
| | | // 覆盖rich-text内部元素样式 |
| | | /deep/ p { |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | /deep/ h1, /deep/ h2, /deep/ h3 { |
| | | margin: 40rpx 0 20rpx; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | /deep/ ul, /deep/ ol { |
| | | padding-left: 40rpx; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | /deep/ li { |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | /deep/ a { |
| | | color: #007aff; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | /deep/ blockquote { |
| | | border-left: 4rpx solid #ddd; |
| | | padding-left: 20rpx; |
| | | color: #666; |
| | | margin: 20rpx 0; |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <view class="wrapper"> |
| | | <view style="height: 100rpx"></view> |
| | | <!-- 内容区域 --> |
| | | <scroll-view scroll-y class="content" style="height: 40vh;" @scrolltolower="loadMore" :lower-threshold="100"> |
| | | <view class="waterfall"> |
| | | |
| | | <view class="item" v-for="(item, idx) in mockData" :key="item.id" @click="handleItemClick(item)"> |
| | | <text class="title">{{ item.title }}</text> |
| | | <text class="publishDate">发布时间:{{ item.publishDate }}</text> |
| | | </view> |
| | | |
| | | </view> |
| | | <!-- <view style="height: 150rpx;"></view> --> |
| | | <!-- 改进的加载更多提示 --> |
| | | <view class="load-more"> |
| | | <u-loadmore v-if="mockData.length > 0" :status="loading ? 'loading' : noMore ? 'nomore' : 'loadmore'" |
| | | :load-text="{ |
| | | loadmore: '上拉加载更多', |
| | | loading: '正在加载', |
| | | nomore: '没有更多了' |
| | | }" /> |
| | | </view> |
| | | <view style="height:150rpx"> |
| | | |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import '@/components/uview-components/uview-ui'; |
| | | import { getNews,addNews,editNews,publish,delById } from '@/api/news.js' |
| | | export default { |
| | | data() { |
| | | return { |
| | | query: { |
| | | pageNumber: 1, |
| | | pageSize: 10, |
| | | publish:1, |
| | | }, |
| | | loading:false, |
| | | total:0, |
| | | mockData:[], |
| | | noMore:false, |
| | | |
| | | } |
| | | }, |
| | | mounted() { |
| | | |
| | | }, |
| | | onLoad() { |
| | | this.getNewsPage(); |
| | | }, |
| | | methods: { |
| | | handleItemClick(item){ |
| | | uni.navigateTo({ |
| | | url: `/pages/news/detail?id=${item.id}` // 参数通过 URL 传递 |
| | | }); |
| | | }, |
| | | async getNewsPage(){ |
| | | try { |
| | | const res = await getNews(this.query); |
| | | this.loading = false; |
| | | if(res.statusCode === 200){ |
| | | const newData = res.data.data.map(value =>({ |
| | | id:value.id, |
| | | title:value.title, |
| | | publish:value.publish, |
| | | publishDate:value.publishDate |
| | | })); |
| | | // 更新总数据量 |
| | | this.total = res.data.total || 0; |
| | | // 追加或替换数据 |
| | | this.mockData = this.query.pageNumber === 1 ? |
| | | newData : |
| | | [...this.mockData, ...newData]; |
| | | |
| | | // 判断是否还有更多数据 |
| | | this.noMore = newData.length < this.query.pageSize || |
| | | this.mockData.length >= this.total; |
| | | |
| | | } |
| | | |
| | | } catch (error) { |
| | | console.error('加载失败:', error); |
| | | // 失败时回退页码 |
| | | if (this.query.pageNumber > 1) { |
| | | this.query.pageNumber -= 1; |
| | | } |
| | | } finally { |
| | | this.loading = false; |
| | | uni.hideLoading(); |
| | | uni.stopPullDownRefresh(); |
| | | } |
| | | |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .wrapper { |
| | | padding: 0 20rpx; |
| | | box-sizing: border-box; |
| | | background-color: #f5f5f5; |
| | | height: 100%; |
| | | |
| | | .content { |
| | | padding-bottom: 20rpx; |
| | | box-sizing: border-box; |
| | | |
| | | .waterfall { |
| | | display: flex; |
| | | flex-direction: column; |
| | | padding: 0 10rpx; |
| | | |
| | | .item { |
| | | background: #fff; |
| | | border-radius: 12rpx; |
| | | padding: 24rpx; |
| | | margin-bottom: 20rpx; |
| | | box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05); |
| | | transition: transform 0.2s ease, box-shadow 0.2s ease; |
| | | |
| | | &:active { |
| | | transform: scale(0.98); |
| | | box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .title { |
| | | display: block; |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | font-weight: 500; |
| | | line-height: 1.5; |
| | | margin-bottom: 12rpx; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | display: -webkit-box; |
| | | -webkit-line-clamp: 3; |
| | | -webkit-box-orient: vertical; |
| | | } |
| | | |
| | | .publishDate { |
| | | display: block; |
| | | font-size: 22rpx; |
| | | color: #999; |
| | | text-align: right; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .load-more { |
| | | padding: 30rpx 0; |
| | | background-color: transparent; |
| | | |
| | | .u-loadmore { |
| | | font-size: 26rpx !important; |
| | | color: #999 !important; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /* 平板及以上设备适配 */ |
| | | @media (min-width: 768px) { |
| | | .waterfall { |
| | | flex-direction: row !important; |
| | | flex-wrap: wrap; |
| | | |
| | | .item { |
| | | width: calc(50% - 10rpx); |
| | | margin: 0 5rpx 20rpx; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <image src="/static/mine/shensu.png" mode=""></image> |
| | | <view>用户权限</view> |
| | | </view> |
| | | <view class="interact-item" v-if="isStoreManger" @click="navigateTo('/pages/news/news')"> |
| | | <image src="/static/mine/shensu.png" mode=""></image> |
| | | <view>新闻</view> |
| | | </view> |
| | | |
| | | <!-- <view class="interact-item" v-if="isStoreManger" @click="navigateTo('/pages/customerManager/customerManager')"> |
| | | <image src="/static/mine/shensu.png" mode=""></image> |