# Conflicts:
#	property-uniapp-project/manifest.json
#	property-uniapp-project/pageSubPack/api/request.js
This commit is contained in:
wangyuxin
2026-04-30 08:33:11 +08:00
25 changed files with 1766 additions and 1217 deletions

View File

@@ -8,7 +8,9 @@
},
onHide: function() {
console.log('App Hide')
}
},
}
</script>

View File

@@ -54,11 +54,10 @@
<view v-for="activity in displayedActivities" :key="activity.id" class="activity-card"
@tap="viewActivityDetail(activity)">
<view class="status-badge">
进行中
{{activity.statusText}}
</view>
<view v-if="activity.hasSigned && activeTab === 'list'" class="signed-badge">
<uni-icons type="checkbox-filled" size="14" color="#fff"></uni-icons>
<view v-if="activity.applied && activeTab === 'list'" class="signed-badge">
<text>已报名</text>
</view>
@@ -67,7 +66,7 @@
<up-row justify="space-between" customStyle="margin-bottom: 10px">
<up-col span="4">
<view class="info-image-left">
<image src="http://47.104.199.163:9090/images/home/activity-img.png"
<image :src="activity.coverImage"
style="width: 100%;height: 100%;"></image>
</view>
</up-col>
@@ -82,11 +81,11 @@
</view>
<view class="activity-detail">
<text class="detail-text">地点{{ activity.location }}</text>
<text class="detail-text-address">地点{{ activity.address }}</text>
</view>
<view class="activity-detail">
<text class="detail-text-num">活动名额{{ activity.totalQuota }}已报名{{ activity.signedCount }}</text>
<text class="detail-text-num">活动名额{{ activity.quota }}已报名{{ activity.appliedCount }}</text>
</view>
</view>
</up-col>
@@ -94,10 +93,14 @@
</view>
<view class="action-buttons">
<button v-if="activeTab === 'list' && !activity.hasSigned" class="action-btn signup-btn"
<button v-if="activeTab === 'list' && activity.applied === false" class="action-btn signup-btn"
@tap.stop="signupActivity(activity)">
立即报名
</button>
<button v-if="activeTab === 'list' && activity.applied === true" class="action-btn signup-btn"
@tap.stop="cancelActivity(activity)">
取消报名
</button>
</view>
</view>
</view>
@@ -119,381 +122,263 @@
</template>
<script setup>
import {
ref,
computed,
onMounted,
watch
} from 'vue'
import moment from 'moment';
import { ref, watch } from 'vue'
import { onLoad, onUnload, onShow } from '@dcloudio/uni-app'
import moment from 'moment';
import { getActivityList } from '../api/api.js'
// 响应式数据
const activeTab = ref('list') // 'list' 或 'my'
const myFilter = ref('reviewing') // 我的活动筛选条件
const isLoading = ref(false)
const hasMoreData = ref(true)
const currentPage = ref(1)
const pageSize = 5
// ===================== 响应式数据 =====================
const activeTab = ref('list') // 'list' 或 'my'
const myFilter = ref('reviewing') // 我的活动筛选条件
const isLoading = ref(false)
const hasMoreData = ref(true)
const currentPage = ref(1)
const pageSize = ref(5)
const total = ref(0)
const displayedActivities = ref([]) // 当前显示的活动列表
// 筛选条件
const filters = ref([{
label: '审核中',
value: 'reviewing'
},
{
label: '已通过',
value: 'passed'
},
{
label: '未通过',
value: 'rejected'
},
{
label: '已结束',
value: 'ended'
}
])
// 筛选条件
const filters = ref([
{ label: '审核中', value: 'reviewing' },
{ label: '已通过', value: 'passed' },
{ label: '未通过', value: 'rejected' },
{ label: '已结束', value: 'ended' }
])
// 模拟活动数据
const allActivities = ref([{
id: 1,
title: '阳光海岸社区生活节活动',
startTime: '2023-06-19 08:00',
endTime: '2023-06-30 17:00',
location: '山东省日照市东港区莱顿二期花语墅西沿街',
totalQuota: 100,
signedCount: 68,
status: 'ongoing', // ongoing, upcoming, ended
hasSigned: true,
myStatus: 'passed', // reviewing, passed, rejected
coverImage: '/static/activity1.jpg',
description: '阳光海岸社区生活节是一个为期两周的社区文化活动,包括文艺表演、手工艺品展示、美食节等多种活动。'
},
{
id: 2,
title: '社区环保志愿者招募活动',
startTime: '2023-07-01 09:00',
endTime: '2023-07-31 18:00',
location: '社区公园及周边街道',
totalQuota: 50,
signedCount: 32,
status: 'upcoming',
hasSigned: false,
myStatus: 'reviewing',
coverImage: '/static/activity2.jpg',
description: '招募社区环保志愿者,参与垃圾分类宣传、社区清洁等环保活动。'
},
{
id: 3,
title: '端午节包粽子亲子活动',
startTime: '2023-06-10 14:00',
endTime: '2023-06-10 17:00',
location: '社区活动中心',
totalQuota: 30,
signedCount: 30,
status: 'ended',
hasSigned: true,
myStatus: 'passed',
coverImage: '/static/activity3.jpg',
description: '端午节亲子活动,教孩子们包粽子,了解传统文化。'
},
{
id: 4,
title: '老年人智能手机使用培训',
startTime: '2023-07-05 14:00',
endTime: '2023-07-12 16:00',
location: '社区图书馆二楼',
totalQuota: 20,
signedCount: 15,
status: 'upcoming',
hasSigned: false,
myStatus: null,
coverImage: '/static/activity4.jpg',
description: '为社区老年人提供智能手机使用培训包括微信、支付宝等常用APP的使用。'
},
{
id: 5,
title: '社区篮球友谊赛',
startTime: '2023-06-25 15:00',
endTime: '2023-06-25 18:00',
location: '社区体育馆',
totalQuota: 40,
signedCount: 25,
status: 'ongoing',
hasSigned: false,
myStatus: 'rejected',
coverImage: '/static/activity5.jpg',
description: '社区篮球友谊赛,欢迎篮球爱好者报名参加。'
},
{
id: 6,
title: '儿童暑期绘画班',
startTime: '2023-07-10 09:00',
endTime: '2023-08-20 11:00',
location: '社区艺术工作室',
totalQuota: 25,
signedCount: 18,
status: 'upcoming',
hasSigned: true,
myStatus: 'reviewing',
coverImage: '/static/activity6.jpg',
description: '暑期儿童绘画班,培养孩子的艺术兴趣和创造力。'
},
{
id: 7,
title: '社区消防安全演练',
startTime: '2023-05-15 10:00',
endTime: '2023-05-15 12:00',
location: '社区广场',
totalQuota: 100,
signedCount: 85,
status: 'ended',
hasSigned: true,
myStatus: 'passed',
coverImage: '/static/activity7.jpg',
description: '社区消防安全知识讲座和应急演练。'
},
{
id: 8,
title: '邻里美食分享会',
startTime: '2023-07-08 16:00',
endTime: '2023-07-08 19:00',
location: '社区花园',
totalQuota: 40,
signedCount: 22,
status: 'upcoming',
hasSigned: false,
myStatus: null,
coverImage: '/static/activity8.jpg',
description: '邻里美食分享会,每家带一道拿手菜,共享美食。'
}
])
// ===================== 核心方法 =====================
/**
* 重置并刷新第一页数据
*/
const resetAndLoad = () => {
currentPage.value = 1
displayedActivities.value = []
hasMoreData.value = true
getActivityListData(1)
}
// 当前显示的活动列表
const displayedActivities = ref([])
// 计算属性:根据当前标签和筛选条件过滤活动
const filteredActivities = computed(() => {
if (activeTab.value === 'list') {
// 活动列表页:显示所有活动
return allActivities.value
/**
* 返回上一级页面
*/
const narBack = () => {
const sourcePage = uni.getStorageSync('sourcePage')
if (sourcePage === "index") {
uni.switchTab({ url: "/pages/index/index" })
} else if (sourcePage === "serviceIndex") {
uni.switchTab({ url: "/pages/serviceIndex/serviceIndex" })
} else {
// 我的活动页:根据筛选条件过滤
if (myFilter.value === 'all') {
return allActivities.value.filter(activity => activity.hasSigned)
} else {
return allActivities.value.filter(activity =>
activity.hasSigned && activity.myStatus === myFilter.value
)
uni.navigateBack()
}
}
})
}
// 返回
const narBack = () => {
if (uni.getStorageSync('sourcePage') === "index") {
uni.switchTab({
url: "/pages/index/index"
})
} else if (uni.getStorageSync('sourcePage') === "serviceIndex") {
uni.switchTab({
url: "/pages/serviceIndex/serviceIndex"
})
}
}
// 切换选项卡
const switchTab = (tab) => {
/**
* 切换选项卡(活动列表/我的活动)
* @param {string} tab - 选项卡类型
*/
const switchTab = (tab) => {
activeTab.value = tab
currentPage.value = 1
displayedActivities.value = []
loadPageData(1)
}
resetAndLoad()
}
// 切换筛选条件
const switchFilter = (filter) => {
/**
* 切换我的活动筛选条件
* @param {string} filter - 筛选值
*/
const switchFilter = (filter) => {
myFilter.value = filter
currentPage.value = 1
displayedActivities.value = []
loadPageData(1)
}
resetAndLoad()
}
// 加载分页数据
const loadPageData = (pageNum) => {
/**
* 获取活动列表数据
* @param {number} pageNum - 页码
*/
const getActivityListData = async (pageNum = 1) => {
// 防止重复加载
if (isLoading.value) return
isLoading.value = true
// 模拟网络请求延迟
setTimeout(() => {
const startIndex = (pageNum - 1) * pageSize
const endIndex = startIndex + pageSize
const pageData = filteredActivities.value.slice(startIndex, endIndex)
try {
// 构造请求参数(区分活动列表/我的活动 + 筛选条件)
const params = {
pageNum: pageNum,
pageSize: pageSize.value,
//tabType: activeTab.value, // 传给后端list/my
//filterType: activeTab.value === 'my' ? myFilter.value : '' // 我的活动筛选条件
}
const res = await getActivityList(params)
if (res.code === 200) {
const list = res.rows || []
total.value = res.total || 0
// 第一页覆盖数据,下一页追加数据
if (pageNum === 1) {
displayedActivities.value = pageData
displayedActivities.value = list
} else {
displayedActivities.value = [...displayedActivities.value, ...pageData]
displayedActivities.value = [...displayedActivities.value, ...list]
}
// 判断是否还有更多数据
hasMoreData.value = displayedActivities.value.length < total.value
currentPage.value = pageNum
hasMoreData.value = displayedActivities.value.length < filteredActivities.value.length
} else {
uni.showToast({ title: res.msg || "加载失败", icon: 'error' })
if (pageNum === 1) displayedActivities.value = []
}
} catch (err) {
uni.showToast({ title: "网络异常", icon: 'none' })
if (pageNum === 1) displayedActivities.value = []
} finally {
isLoading.value = false
}, 500)
}
}
// 加载更多数据
const loadMoreData = () => {
/**
* 加载更多数据
*/
const loadMoreData = () => {
if (isLoading.value || !hasMoreData.value) return
loadPageData(currentPage.value + 1)
}
getActivityListData(currentPage.value + 1)
}
// 获取活动状态类名
const getStatusClass = (status) => {
const classMap = {
'ongoing': 'status-ongoing',
}
return classMap[status] || ''
}
/**
* 格式化日期
* @param {string} date - 原始日期字符串
* @returns {string} 格式化后的日期
*/
const formatDate = (date) => {
if (!date) return ''
return moment(date).format('YYYY-MM-DD HH:mm')
}
// 获取我的活动状态文本
const getMyStatusText = (status) => {
const statusMap = {
'reviewing': '审核中',
'passed': '已通过',
'rejected': '未通过',
'ended': '已结束'
}
return statusMap[status] || '未知'
}
// 获取我的活动状态类名
const getMyStatusClass = (status) => {
const classMap = {
'reviewing': 'my-status-reviewing',
'passed': 'my-status-passed',
'rejected': 'my-status-rejected',
'ended': 'my-status-ended'
}
return classMap[status] || ''
}
// 格式化日期
function formatDate(date) {
return moment(date).format('YYYY-MM-DD HH:mm:ss')
}
// 报名活动
const signupActivity = (activity) => {
/**
* 报名活动(跳转详情页)
* @param {object} activity - 活动对象
*/
const signupActivity = (activity) => {
uni.navigateTo({
url: `/pages/activity-list/activityListDetails?id=${activity.id}&title=${activity.title}`
url: `/pageSubPack/activity-list/activityListDetails?id=${activity.id}&title=${activity.title}&applied=${activity.applied}`
})
}
}
// 查看活动详情
const viewActivityDetail = (activity) => {
/**
* 取消报名(跳转详情页)
* @param {object} activity - 活动对象
*/
const cancelActivity = (activity) => {
uni.navigateTo({
url: `/pages/activityDetail/activityDetail?id=${activity.id}`,
fail: () => {
// 如果页面不存在,显示活动信息
uni.showModal({
title: activity.title,
content: `${activity.description}\n\n时间${formatDate(activity.startTime)}${formatDate(activity.endTime)}\n地点${activity.location}\n名额${activity.totalQuota}人,已报名:${activity.signedCount}`,
showCancel: false,
confirmText: '知道了'
url: `/pageSubPack/activity-list/activityListDetails?id=${activity.id}&title=${activity.title}&applied=${activity.applied}`
})
}
})
}
}
// 联系管理员
const contactAdmin = (activity) => {
uni.showModal({
title: '联系管理员',
content: `您报名"${activity.title}"未通过审核,是否联系管理员了解原因?`,
success: (res) => {
if (res.confirm) {
uni.makePhoneCall({
phoneNumber: '400-123-4567'
/**
* 查看活动详情(跳转详情页)
* @param {object} activity - 活动对象
*/
const viewActivityDetail = (activity) => {
uni.navigateTo({
url: `/pageSubPack/activity-list/activityListDetails?id=${activity.id}&title=${activity.title}&applied=${activity.applied}`
})
}
}
})
}
}
// 监听筛选条件变化
watch(() => myFilter.value, () => {
// ===================== 生命周期 & 监听 =====================
/**
* 页面显示时刷新数据
*/
onShow(() => {
getActivityListData(1) // 强制刷新第一页
})
/**
* 页面加载时监听刷新事件(来自详情页)
*/
onLoad(() => {
// 监听详情页报名/取消报名后的刷新通知
uni.$on('refreshActivityList', (data) => {
console.log('收到活动列表刷新通知:', data)
getActivityListData(1)
})
})
/**
* 页面卸载时移除事件监听(避免内存泄漏)
*/
onUnload(() => {
uni.$off('refreshActivityList')
})
/**
* 监听我的活动筛选条件变化
*/
watch(() => myFilter.value, () => {
if (activeTab.value === 'my') {
currentPage.value = 1
displayedActivities.value = []
loadPageData(1)
resetAndLoad()
}
})
// 初始化
onMounted(() => {
loadPageData(1)
})
})
</script>
<style>
/* 页面容器 */
.container {
<style scoped>
/* 页面容器 */
.container {
width: 100%;
height: 100vh;
background-color: #fff;
display: flex;
flex-direction: column;
padding: 0;
}
}
.status_bar {
.status_bar {
height: 46px;
width: 100%;
}
}
/* 顶部标题 固定 */
.top-bar {
/* 顶部标题 固定 */
.top-bar {
width: 100%;
flex-shrink: 0;
background: #fff;
}
}
/* 滚动区域 */
.scroll-container {
/* 滚动区域 */
.scroll-container {
flex: 1;
width: 100%;
overflow-y: auto;
padding: 0 20rpx;
padding: 0 40rpx;
box-sizing: border-box;
}
}
.topImg {
.topImg {
width: 100%;
height: 280rpx;
margin: 20rpx 0;
}
}
/* 顶部选项卡 */
.tabs-container {
/* 顶部选项卡 */
.tabs-container {
display: flex;
top: 0;
z-index: 10;
margin-bottom: 20rpx
}
}
.tab-item {
.tab-item {
flex: 1;
text-align: center;
padding: 20rpx 0;
font-size: 32rpx;
color: #666;
position: relative;
}
}
.tab-item.active {
.tab-item.active {
color: #007AFF;
font-weight: 500;
}
}
.tab-indicator {
.tab-indicator {
position: absolute;
bottom: 0;
left: 50%;
@@ -502,26 +387,26 @@
height: 6rpx;
background-color: #007AFF;
border-radius: 4rpx;
}
}
/* 筛选容器 */
.filter-container {
/* 筛选容器 */
.filter-container {
background-color: #fff;
padding: 20rpx 0;
border-bottom: 1px solid #eee;
}
}
.filter-scroll {
.filter-scroll {
width: 100%;
white-space: nowrap;
}
}
.filter-buttons {
.filter-buttons {
display: inline-flex;
padding: 0 30rpx;
}
}
.filter-btn {
.filter-btn {
display: inline-block;
padding: 12rpx 30rpx;
margin-right: 20rpx;
@@ -530,21 +415,21 @@
background-color: #f8f8f8;
border-radius: 25rpx;
border: 1px solid #eee;
}
}
.filter-btn.active {
.filter-btn.active {
color: #fff;
background-color: #2F77FD;
}
}
/* 活动列表 */
.activity-list {
/* 活动列表 */
.activity-list {
width: 100%;
padding-bottom: 40rpx;
}
}
/* 活动卡片 */
.activity-card {
/* 活动卡片 */
.activity-card {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
@@ -552,10 +437,10 @@
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
position: relative;
border: 1px solid #e1e1e1;
}
}
/* 活动状态标签 */
.status-badge {
/* 活动状态标签 */
.status-badge {
position: absolute;
top: 10rpx;
left: 20rpx;
@@ -565,105 +450,109 @@
color: #fff;
z-index: 1;
background: repeating-linear-gradient(to right, #FD7373, #FB3F3F);
}
}
/* 已报名标识 */
.signed-badge {
/* 已报名标识 */
.signed-badge {
position: absolute;
top: 20rpx;
right: 8rpx;
display: flex;
align-items: center;
padding: 6rpx 14rpx;
background-color: #007AFF;
color: #fff;
color: #007AFF;
font-size: 22rpx;
border-radius: 20rpx;
z-index: 1;
}
}
.signed-badge text {
.signed-badge text {
margin-left: 8rpx;
}
}
/* 活动信息 */
.activity-info {
/* 活动信息 */
.activity-info {
margin-top: 10rpx;
}
}
.info-image-left {
.info-image-left {
width: 170rpx;
height: 220rpx;
}
}
.activity-title {
.activity-title {
font-size: 30rpx;
font-weight: 500;
color: #222;
margin-bottom: 30rpx;
line-height: 1.4;
padding-right: 100rpx;
}
}
.activity-detail {
.activity-detail {
display: flex;
align-items: flex-start;
margin-bottom: 20rpx;
color: #999;
font-size: 24rpx;
line-height: 1.4;
}
}
.activity-detail .uni-icons {
.activity-detail .uni-icons {
margin-right: 15rpx;
margin-top: 4rpx;
flex-shrink: 0;
}
}
.detail-text-num {
.detail-text-num {
font-size: 28rpx;
color: #666666;
}
}
.detail-text {
.detail-text {
flex: 1;
}
font-size: 20rpx;
}
.detail-text-address{
flex: 1;
font-size: 22rpx;
}
/* 操作按钮 */
.action-buttons {
/* 操作按钮 */
.action-buttons {
margin-top: 30rpx;
display: flex;
justify-content: flex-end;
}
}
.action-btn {
.action-btn {
padding: 0 40rpx;
height: 60rpx;
line-height: 60rpx;
font-size: 24rpx;
border-radius: 35rpx;
margin: 0;
}
}
.signup-btn {
.signup-btn {
background-color: #007AFF;
color: #fff;
}
}
.signup-btn:active {
.signup-btn:active {
background-color: #0056cc;
}
}
/* 加载更多 */
.load-more-container {
/* 加载更多 */
.load-more-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 0;
color: #999;
}
}
.load-more-btn {
.load-more-btn {
display: flex;
align-items: center;
justify-content: center;
@@ -673,13 +562,13 @@
color: #007AFF;
font-size: 28rpx;
margin-bottom: 20rpx;
}
}
.load-more-btn text {
.load-more-btn text {
margin-right: 10rpx;
}
}
.loading-container {
.loading-container {
display: flex;
align-items: center;
justify-content: center;
@@ -689,14 +578,14 @@
color: #007AFF;
font-size: 28rpx;
margin-bottom: 20rpx;
}
}
.loading-icon {
.loading-icon {
margin-right: 10rpx;
animation: rotate 1s linear infinite;
}
}
@keyframes rotate {
@keyframes rotate {
from {
transform: rotate(0deg);
}
@@ -704,22 +593,22 @@
to {
transform: rotate(360deg);
}
}
}
/* 空状态 */
.empty-state {
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
color: #ccc;
}
}
.empty-text {
.empty-text {
font-size: 32rpx;
color: #999;
margin-top: 20rpx;
margin-bottom: 10rpx;
}
}
</style>

View File

@@ -1,5 +1,6 @@
<template>
<view class="container">
<view class="status_bar"></view>
<!-- 顶部导航栏 -->
<view class="nav-bar">
<view class="nav-left" @tap="goBack">
@@ -8,10 +9,10 @@
<view class="nav-title">{{navTitle}}</view>
<view class="nav-right"></view>
</view>
<view style="status_bar"></view>
<!-- 内容区域 -->
<scroll-view class="content" scroll-y>
<scroll-view class="scroll-content" scroll-y="true">
<!-- 选项卡 -->
<view class="tabs">
<view
@@ -36,51 +37,51 @@
<view class="info-section">
<view class="info-item">
<view class="info-label">报名截止时间</view>
<view class="info-value">2023-08-08 23:00</view>
<view class="info-value">{{formatTime(activityConent.applyDeadline)}}</view>
</view>
<view class="info-item">
<view class="info-label">活动时间</view>
<view class="info-value">
<text>2023-08-09 10:00 2023-08-20 16:00</text>
<text>{{formatTime(activityConent.startTime)}} {{formatTime(activityConent.endTime)}}</text>
</view>
</view>
<view class="info-item">
<view class="info-label">签到时间</view>
<view class="info-value">
<text>2023-08-09 10:00 2023-08-10 16:00</text>
<text>{{formatTime(activityConent.signinStartTime)}} {{formatTime(activityConent.signinEndTime)}}</text>
</view>
</view>
<view class="info-item">
<view class="info-label">活动地点</view>
<view class="info-value">山东省日照市高新区高新管委会大厦前高新公园</view>
<view class="info-value">{{activityConent.address}}</view>
</view>
<view class="info-item">
<view class="info-label">活动名额</view>
<view class="info-value">10</view>
<view class="info-value">{{activityConent.quota}}</view>
</view>
<view class="info-item">
<view class="info-label">主办方</view>
<view class="info-value">山海天社区</view>
<view class="info-value">{{activityConent.organizer}}</view>
</view>
<view class="info-item">
<view class="info-label">联系人</view>
<view class="info-value">赵璐</view>
<view class="info-value">{{activityConent.contactName}}</view>
</view>
<view class="info-item">
<view class="info-label">联系电话</view>
<view class="info-value">15799999999</view>
<view class="info-value">{{activityConent.contactPhone}}</view>
</view>
<view class="info-item">
<view class="info-label">积分奖励</view>
<view class="info-value">100</view>
<view class="info-value">{{activityConent.pointsReward}}</view>
</view>
</view>
@@ -88,7 +89,7 @@
<view class="section">
<view class="section-title">招募要求</view>
<view class="section-content">
要求就是要求就是要求就是
{{activityConent.requirements}}
</view>
</view>
@@ -96,8 +97,8 @@
<view class="section" style="margin-bottom: 40rpx;">
<view class="section-title">活动详情</view>
<view class="section-content">
<view>1这就是活动详情zhejiushihuodongxiangqing这就是活动详情zhejiushihuodongxiangqing</view>
<view>2活动详情zhejiushuodongxiangqing</view>
<view>{{activityConent.content || "暂无"}}</view>
<view></view>
</view>
</view>
</view>
@@ -106,7 +107,7 @@
<view v-else class="status-content">
<view class="status-header">
<view class="status-title">
<image src="http://47.104.199.163:9090/images/activity/liveScene.png" style="width: 40rpx;height: 30rpx;"></image>
<image src="http://47.104.199.163:9090/images/activity/liveScene.png" style="width: 38rpx;height: 23rpx;"></image>
现场实况
</view>
<view class="status-count">{{ liveRecords.length }}</view>
@@ -165,16 +166,9 @@
>
取消报名
</button>
<button class="action-btn add-status-btn" v-if="activeTab === 'status'" @click="liveClick">添加实况</button>
<!-- 已报名且在活动实况页显示添加实况按钮 -->
<!-- <button
v-if="hasSigned && activeTab === 'status'"
class="action-btn add-status-btn"
@tap="handleAddStatus"
>
添加实况
</button> -->
<!-- 活动实况页显示添加实况按钮 -->
<button class="action-btn add-status-btn" v-if="hasSigned &&activeTab === 'status'" @click="liveClick">添加实况</button>
</view>
<!-- 报名确认模态框 -->
@@ -182,7 +176,7 @@
<view class="modal-content">
<view class="modal-header">报名确认</view>
<view class="modal-body">
<view>确定要报名参加"社区'小槐花课堂'开课了"</view>
<view>{{navTitle}}</view>
<view class="modal-tip">报名后请按时参加活动</view>
</view>
<view class="modal-footer">
@@ -197,7 +191,7 @@
<view class="modal-content">
<view class="modal-header">取消报名</view>
<view class="modal-body">
<view>确定要取消报名"社区'小槐花课堂'开课了"</view>
<view>{{navTitle}}</view>
<view class="modal-tip">取消后如需参加需重新报名</view>
</view>
<view class="modal-footer">
@@ -216,44 +210,89 @@
<script setup>
import { ref, onMounted } from 'vue'
import {onLoad} from '@dcloudio/uni-app'
import {onLoad,onUnload} from '@dcloudio/uni-app'
import moment from 'moment'
import {getActivityDetails,confirmResident,cancelActivity,getActivityLive} from '../api/api.js'
// 响应式数据
const activeTab = ref('detail') // 'detail' 或 'status'
const hasSigned = ref(false) // 是否已报名
const showSignupModal = ref(false)
const showCancelModal = ref(false)
// const showAddStatusModal = ref(false)
const showToast = ref(false)
const toastMessage = ref('')
const newStatusContent = ref('')
// const newStatusImages = ref([])
const navTitle = ref('')
const curActivityId = ref('') // 当前活动的id
const activityConent = ref({}) // 获取详情数据
// 实况记录数据
const liveRecords = ref([
{
id: 1,
user: '微信用户',
time: '2023-08-20 16:00:02',
content: '参与活动打卡',
images: []
},
{
id: 2,
user: '微信用户',
time: '2023-08-20 16:20:02',
content: '目前现场的互动氛围很好',
images: ['http://47.104.199.163:9090/images/logo.png', 'http://47.104.199.163:9090/images/repair/house1.png']
}
// {
// id: 1,
// user: '微信用户',
// time: '2023-08-20 16:00:02',
// content: '参与活动打卡',
// images: []
// },
// {
// id: 2,
// user: '微信用户',
// time: '2023-08-20 16:20:02',
// content: '目前现场的互动氛围很好',
// images: ['http://47.104.199.163:9090/images/logo.png', 'http://47.104.199.163:9090/images/repair/house1.png']
// }
])
// 接受参数
onLoad((option) =>{
// console.log('dsdsds',option)
navTitle.value = option.title
// ========方法=====
// 时间格式化函数
const formatTime = (timeStr) => {
if (!timeStr) return ''
return moment(timeStr).format('YYYY-MM-DD HH:mm')
}
// ---接收参数---
onLoad((option) =>{
navTitle.value = option?.title
curActivityId.value = option?.id
// 处理applied参数可能是字符串类型需要转布尔
hasSigned.value = option?.applied === 'true' || option?.applied === true
// console.log('hasSigned.value',hasSigned.value)
// 2. 初始化数据
if (curActivityId.value) {
getActivityDetailsData(curActivityId.value) // 获取活动详情
getLiveActivityList() // 获取实况列表
}
// 3. 监听实况列表刷新事件
uni.$on('refreshLiveList', () => {
getLiveActivityList()
})
})
// 获取列表详情
const getActivityDetailsData = async (id) =>{
try{
const res = await getActivityDetails(id)
if(res.code === 200){
activityConent.value = res.data || {}
}else{
activityConent.value = {}
uni.showToast({
title: res.msg ||"详情获取失败",
icon:"error"
})
}
}catch(err){
uni.showToast({
title: "网络异常",
icon:"none"
})
// console.error('获取活动详情失败:', err)
}
}
// 切换选项卡
const switchTab = (tab) => {
activeTab.value = tab
@@ -276,12 +315,40 @@ const hideSignupModal = () => {
// 确认报名
const confirmSignup = () => {
hasSigned.value = true
confirmRegist()
hideSignupModal()
showToastMessage('报名成功!')
}
// 模拟更新名额
// 在实际应用中这里应该调用API更新后端数据
// 确认报名接口
const confirmRegist = async () =>{
try{
const res = await confirmResident(curActivityId.value)
if(res.code === 200){
hasSigned.value = true // 报名成功后更新状态
uni.showToast({
title: '报名成功',
icon:"success"
})
setTimeout(() =>{
uni.$emit('refreshActivityList', {
activityId: curActivityId.value,
isSigned: true
})
uni.navigateBack()
},1500)
}else{
uni.showToast({
title: res.msg || "报名失败",
icon:"error"
})
}
}catch(err){
uni.showToast({
title: "网络异常",
icon:"none"
})
// console.error('报名失败:', err)
}
}
// 处理取消报名
@@ -296,26 +363,77 @@ const hideCancelModal = () => {
// 确认取消报名
const confirmCancel = () => {
hasSigned.value = false
hasSigned.value = false // 取消报名后更新状态
cancelActivityData()
hideCancelModal()
showToastMessage('已取消报名')
// 模拟更新名额
// 在实际应用中这里应该调用API更新后端数据
// showToastMessage('已取消报名')
}
// 确认取消报名接口
const cancelActivityData = async () =>{
try{
const res = await cancelActivity(curActivityId.value)
if(res.code === 200){
hasSigned.value = false // 报名成功后更新状态
uni.showToast({
title: '取消报名成功',
icon:"success"
})
setTimeout(() =>{
uni.$emit('refreshActivityList', {
activityId: curActivityId.value,
isSigned: false
})
uni.navigateBack()
},1500)
}else{
uni.showToast({
title: res.msg || "取消报名失败",
icon:"error"
})
}
}catch(err){
uni.showToast({
title: "网络异常",
icon:"none"
})
}
}
// -----获取活动实况列表----
const getLiveActivityList = async () =>{
try{
const res = await getActivityLive(curActivityId.value)
if(res.code === 200){
liveRecords.value = res.rows || []
}else{
liveRecords.value = []
uni.showToast({
title: res.msg ||"实况列表获取失败",
icon:"error"
})
}
}catch(err){
uni.showToast({
title: "网络异常",
icon:"none"
})
}
}
// 添加实况跳转
const liveClick = () =>{
uni.navigateTo({
url:'/pageSubPack/activity-list/live-add'
url:`/pageSubPack/activity-list/live-add?id=${curActivityId.value}`
})
}
// 监听实况列表刷新
// 获取当前时间
const getCurrentTime = () => {
return moment(date).format('YYYY-MM-DD HH:mm:ss')
}
// ---------
onUnload(() => {
uni.$off('refreshLiveList')
})
// 显示提示信息
const showToastMessage = (message) => {
@@ -327,17 +445,11 @@ const showToastMessage = (message) => {
}, 2000)
}
// 初始化
onMounted(() => {
// 模拟检查用户是否已报名
// 实际项目中应该从接口获取
setTimeout(() => {
hasSigned.value = false // 默认未报名
}, 100)
})
</script>
<style>
<style scoped>
/* 页面容器 */
.container {
min-height: 100vh;
@@ -345,11 +457,15 @@ onMounted(() => {
flex-direction: column;
background: linear-gradient(to bottom left, #E2F0FF 0%, #ffffff 50%)
}
/* 状态栏 */
.status_bar {
height: 46px;
width: 100%;
}
/* 顶部导航栏 */
.nav-bar {
height: 90rpx;
/* background: linear-gradient(135deg, #007AFF 0%, #0056cc 100%); */
display: flex;
align-items: center;
padding: 0 30rpx;
@@ -357,7 +473,6 @@ onMounted(() => {
position: sticky;
top: 44px;
z-index: 100;
/* box-shadow: 0 4rpx 12rpx rgba(0, 122, 255, 0.2); */
}
.nav-left {
@@ -370,7 +485,7 @@ onMounted(() => {
.nav-title {
flex: 1;
text-align: left;
font-size: 36rpx;
font-size: 32rpx;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
@@ -383,28 +498,22 @@ onMounted(() => {
width: 60rpx;
}
/* 内容区域 */
.content {
/* 滚动内容区域 */
.scroll-content {
flex: 1;
margin-bottom: 120rpx;
}
.status_bar {
height: 46px;
width: 100%;
box-sizing: border-box;
overflow-y: auto;
margin-bottom: 120rpx;
}
/* 选项卡 */
.tabs {
display: flex;
/* background-color: #fff; */
border-bottom: 1rpx solid #e9ecef;
position: sticky;
top: 90rpx;
z-index: 90;
}
.tab-item {
/* flex: 1; */
text-align: center;
padding: 30rpx 35rpx;
font-size: 32rpx;
@@ -418,31 +527,15 @@ onMounted(() => {
font-weight: 600;
}
/* .tab-item.active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 80rpx;
height: 6rpx;
background: linear-gradient(90deg, #007AFF, #00aaff);
border-radius: 3rpx 3rpx 0 0;
} */
/* 活动详情内容 */
.detail-content {
/* padding: 30rpx; */
margin-top: 89rpx;
padding: 0;
}
/* 基本信息区域 */
.info-section {
/* background-color: #fff; */
border-radius: 16rpx;
padding: 30rpx;
/* margin-bottom: 30rpx; */
/* box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); */
padding: 30rpx 40rpx;
}
.info-item {
@@ -471,14 +564,13 @@ onMounted(() => {
color: #222;
font-weight: 500;
line-height: 1.4;
text-align: right;
}
/* 区块样式 */
.section {
/* background-color: #fff; */
border-radius: 16rpx;
padding: 30rpx;
/* margin-bottom: 30rpx; */
padding: 30rpx 40rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
@@ -515,12 +607,9 @@ onMounted(() => {
display: flex;
justify-content: space-between;
align-items: center;
/* background-color: #fff; */
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 30rpx;
margin-top: 80rpx;
/* box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); */
}
.status-title {
@@ -532,19 +621,16 @@ onMounted(() => {
.status-count {
padding: 8rpx 20rpx;
/* background-color: #007AFF; */
color: #666;
font-size: 24rpx;
font-size: 28rpx;
border-radius: 20rpx;
font-weight: 500;
}
/* 实况列表 */
.record-list {
/* background-color: #fff; */
border-radius: 16rpx;
overflow: hidden;
/* box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); */
}
.record-item {
@@ -552,10 +638,6 @@ onMounted(() => {
border-bottom: 1rpx solid #f0f0f0;
}
/* .record-item:last-child {
border-bottom: none;
} */
.record-user {
display: flex;
align-items: flex-start;
@@ -576,11 +658,9 @@ onMounted(() => {
}
.user-info {
/* flex: 1; */
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.user-name {
@@ -599,7 +679,6 @@ onMounted(() => {
font-size: 28rpx;
color: #333;
line-height: 1.5;
/* margin-bottom: 20rpx; */
margin: 25rpx 0rpx;
}
@@ -612,7 +691,7 @@ onMounted(() => {
.image-item {
width: 150rpx;
height: 170rpx;
height: 150rpx;
border-radius: 12rpx;
background-color: #f8f9fa;
background-size: cover;
@@ -774,97 +853,6 @@ onMounted(() => {
background-color: #f0f7ff;
}
/* 添加实况模态框 */
.add-status-modal {
width: 650rpx;
}
.status-input {
width: 100%;
min-height: 200rpx;
padding: 20rpx;
font-size: 28rpx;
color: #333;
background-color: #f8f9fa;
border-radius: 12rpx;
border: 1rpx solid #e9ecef;
margin-bottom: 20rpx;
}
.input-counter {
text-align: right;
font-size: 24rpx;
color: #999;
margin-bottom: 30rpx;
}
.image-upload-area {
margin-top: 30rpx;
}
.upload-tip {
font-size: 24rpx;
color: #999;
margin-bottom: 20rpx;
text-align: left;
}
.image-preview {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
}
.preview-item {
position: relative;
width: 150rpx;
height: 150rpx;
border-radius: 12rpx;
overflow: hidden;
}
.preview-image {
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
background-color: #f8f9fa;
}
.remove-btn {
position: absolute;
top: 0;
right: 0;
width: 40rpx;
height: 40rpx;
background-color: rgba(0, 0, 0, 0.6);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
border-radius: 0 0 0 12rpx;
}
.upload-btn {
width: 150rpx;
height: 150rpx;
border: 2rpx dashed #e9ecef;
border-radius: 12rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #999;
background-color: #f8f9fa;
}
.upload-text {
font-size: 24rpx;
margin-top: 10rpx;
}
/* 提示信息 */
.toast {
position: fixed;

View File

@@ -54,16 +54,27 @@
<script setup>
import { ref } from 'vue';
// import { chooseImage, uploadFile, getLocation } from '@dcloudio/uni-app';
import { onLoad } from '@dcloudio/uni-app';
import { addActivityLive, signinActivityLive, uploadImage } from '../api/api.js';
// 活动ID
const activityId = ref('');
// 实况描述内容
const statusDesc = ref('');
// 输入字符长度
const currentLength = ref(0);
// 图片列表
// 图片列表(本地临时路径)
const imgList = ref([]);
// 定位信息
const location = ref('山东省日照市东港区学院路');
// 经纬度
const longitude = ref('');
const latitude = ref('');
// 接收活动ID
onLoad((option) => {
activityId.value = option?.id || '';
});
// 监听输入框字数变化
const handleTextInput = (e) => {
@@ -73,11 +84,10 @@ const handleTextInput = (e) => {
// 选择图片
const chooseImg = () => {
uni.chooseImage({
count: 3 - imgList.value.length, // 最多可选择的数量
count: 3 - imgList.value.length,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
// 临时文件路径转存到图片列表
imgList.value = [...imgList.value, ...res.tempFilePaths];
},
fail: (err) => {
@@ -88,57 +98,104 @@ const chooseImg = () => {
};
// 删除图片
const deleteImg = (index) => {
const deleteImg = (index) => {
imgList.value.splice(index, 1);
};
};
// 重新定位
// 重新定位
// 定位并获取具体地点名称
const reLocate = () => {
uni.showLoading({ title: '定位中...' });
uni.getLocation({
type: 'gcj02',
// 调用微信原生选择位置接口(自带地址解析,无调用上限)
uni.chooseLocation({
success: (res) => {
uni.hideLoading();
// 实际项目中可调用逆地理编码接口转换为具体地址,这里模拟
location.value = '山东省日照市东港区学院路(新定位)';
uni.showToast({ title: '定位成功', icon: 'success' });
// res.address = 具体地址XX市XX区XX街道XX小区
// res.name = 地点名称XX小区
longitude.value = res.longitude;
latitude.value = res.latitude;
location.value = res.address; // 显示具体地址
uni.showToast({ title: `定位成功:${res.name}`, icon: 'success' });
},
fail: (err) => {
// 处理权限拒绝/定位失败
if (err.errMsg.includes('auth deny')) {
uni.showModal({
title: '需要定位权限',
content: '请允许获取位置信息以显示具体地点',
confirmText: '去设置',
success: (modalRes) => {
if (modalRes.confirm) {
uni.openSetting({
success: (settingRes) => {
// 用户开启权限后重新定位
if (settingRes.authSetting['scope.userLocation']) {
reLocate();
}
}
});
}
}
});
} else {
uni.hideLoading();
uni.showToast({ title: '定位失败,请检查权限', icon: 'none' });
uni.showToast({ title: '定位失败,请重试', icon: 'none' });
console.error('定位失败:', err);
}
}
});
};
// 上传所有图片
const uploadAllImages = async () => {
if (imgList.value.length === 0) return [];
const uploadPromises = imgList.value.map(img => uploadImage(img));
const results = await Promise.all(uploadPromises);
return results.map(res => res.data?.url || res.data);
};
// 提交实况
const submitStatus = () => {
// 简单校验
const submitStatus = async () => {
if (!statusDesc.value.trim()) {
uni.showToast({ title: '请输入实况描述', icon: 'none' });
return;
}
if (!activityId.value) {
uni.showToast({ title: '活动ID缺失', icon: 'none' });
return;
}
// 模拟提交逻辑(实际项目中可上传图片+提交表单)
uni.showLoading({ title: '提交中...' });
uni.showLoading({ title: '提交中...', mask: true });
// 如需上传图片可循环调用uploadFile
// 示例:
// const uploadPromises = imgList.value.map(img => {
// return uploadFile({
// url: '你的上传接口',
// filePath: img,
// name: 'file'
// });
// });
// Promise.all(uploadPromises).then(res => { /* 处理上传结果 */ });
try {
// 先上传图片
const imageUrls = await uploadAllImages();
// 提交实况
const liveData = {
content: statusDesc.value.trim(),
images: imageUrls,
location: location.value,
longitude: longitude.value,
latitude: latitude.value
};
await addActivityLive(activityId.value, liveData);
// 同时签到
await signinActivityLive(activityId.value, {
location: location.value,
longitude: longitude.value,
latitude: latitude.value
});
setTimeout(() => {
uni.hideLoading();
uni.showToast({ title: '添加成功', icon: 'success' });
// 提交成功后可返回上一页
uni.$emit('refreshLiveList');
setTimeout(() => {
uni.navigateBack();
}, 1000);
}, 1500);
} catch (err) {
uni.hideLoading();
uni.showToast({ title: err || '提交失败', icon: 'none' });
}
};
</script>

View File

@@ -1,4 +1,4 @@
import request,{get,post} from "./request.js"
import request,{get,post,upload} from "./request.js"
// =======账号密码登录========
@@ -39,7 +39,12 @@ export const getCurrentVillage = () =>{
export const getVillageSwitch = (data)=>{
return post('/api/resident/village/switch',data)
}
// ====公告列表======
// 首页最新活动列表
export const getActivitylatest = () =>{
return get('/api/resident/activity/latest')
}
// ========公告列表======
// 获取公告列表
export const getNoticeList = (data) =>{
return get('/api/resident/notice/list',data)
@@ -48,3 +53,72 @@ export const getNoticeList = (data) =>{
export const getNoticeDetails = (noticeId) =>{
return get(`/api/resident/notice/${noticeId}`)
}
// =========键开门===============
// 查询门禁列表
export const getAccessDevices = (data) =>{
return get("/api/resident/access/devices",data)
}
// 确认开门
export const getAccessOpen = (data) =>{
return post("/api/resident/access/open",data)
}
// ==========活动列表===========
// 获取活动列表
export const getActivityList = (data) =>{
return get("/api/resident/activity/list",data)
}
// 获取活动列表详情
export const getActivityDetails = (id) =>{
return get(`/api/resident/activity/${id}`)
}
// 确认报名
export const confirmResident = (id) =>{
return post(`/api/resident/activity/${id}/apply`)
}
// 取消报名
export const cancelActivity = (id) =>{
return post(`/api/resident/activity/${id}/cancel`)
}
// =========我的活动========
export const getMyActivityList = () =>{
return get("/api/resident/activity/my")
}
// ===========活动实况============
// 活动实况列表
export const getActivityLive = (id) =>{
return get(`/api/resident/activity/${id}/live`)
}
// 添加实况
export const addActivityLive = (id, data) =>{
return post(`/api/resident/activity/${id}/live`, data)
}
// 签到
export const signinActivityLive = (id, data) =>{
return post(`/api/resident/activity/${id}/signin`, data)
}
// ==============在线报修========
// 添加报修工单
export const addQueryRepair = (data) =>{
return post("/repairOrder/addRepair",data)
}
// 获取公共维修项目列表
export const getRepairProjectList = (data) =>{
return get("/repairOrder/list",data)
}
// 获取报修列表
export const getQueryRepair = (data) =>{
return get("/repairOrder/queryRepair",data)
}
// 获取房屋列表
export const getHouseList = (data) =>{
return get('/api/resident/my/house/list',data)
}
// 通用文件上传
export const uploadImage = () => {
return upload('');
}
// 上传单张图片(请根据后端实际地址修改)
export const uploadFile = (filePath) => {
return upload(filePath, '/common/upload');
}

View File

@@ -49,7 +49,8 @@
</view>
<!-- 协议勾选 -->
<view class="agreement-item" @click="toggleAgreement">
<view class="agreement-item" >
<view @click="toggleAgreement">
<uni-icons
type="checkbox-filled"
size="24"
@@ -62,7 +63,8 @@
color="#999"
v-else
></uni-icons>
<text class="agreement-text">我已阅读并同意用户隐私政策</text>
</view>
<text class="agreement-text" @click.stop="goToPrivacy">我已阅读并同意<text class="agreePrivacy">用户隐私政策</text></text>
</view>
<!-- 登录按钮 -->
@@ -93,11 +95,13 @@ const phoneNumber = ref('');
const verifyCode = ref('');
const placeholderStyle = ref('font-size:24rpx')
// 是否同意协议
const agreeAgreement = ref(true); // 默认勾选
const agreeAgreement = ref(false); // 默认勾选
// 倒计时(秒)
const countDown = ref(0);
// 定时器
let timer = null;
// 控制点击变色
// const isClicked = ref(false)
// ========== 计算属性 ==========
// 是否可以获取验证码(手机号格式正确)
@@ -124,6 +128,14 @@ const checkForm = () => {
const toggleAgreement = () => {
agreeAgreement.value = !agreeAgreement.value;
};
// 跳转隐私协议
const goToPrivacy = () => {
// 点击变色
// isClicked.value = true
uni.navigateTo({
url:'/pageSubPack/loginSub/privacy',
})
}
// 获取验证码
const getVerifyCode = () => {
@@ -209,6 +221,14 @@ const handleLogin = () => {
// uni.showLoading({
// title: '登录中...'
// });
// uni.showToast({
// title:"登录成功",
// icon:'success'
// })
// setTimeout(() =>{
// uni.switchTab({ url: '/pages/index/index' });
// },1500)
uni.hideLoading()
};
// ========== 生命周期 ==========
@@ -319,7 +339,12 @@ onUnmounted(() => {
color: #666;
margin-left: 10rpx;
}
.agreePrivacy{
color: #2F77FD;
}
/* .agreePrivacy.active{
color: #2F77FD;
} */
/* 登录按钮 */
.login-btn {
width: 100%;

View File

@@ -0,0 +1,169 @@
<template>
<view class="privacy-page">
<!-- 协议内容 -->
<scroll-view class="privacy-content" scroll-y>
<view class="content-wrap">
<view class="title">隐私政策</view>
<view class="section">
<!-- <view class="section-title">适用范围</view> -->
<view class="section-text">
欢迎使用我们的产品/服务以下统称服务我们高度重视用户隐私保护
致力于为您提供安全可靠的服务体验本隐私政策旨在向您说明我们如何收集使用存储保护您的个人信息
以及您享有的相关权利请您在使用我们的服务前仔细阅读并理解本政策全部内容
一旦您使用我们的服务即视为您同意本政策的所有条款
我们收集的个人信息 为了向您提供更优质个性化的服务我们会根据服务功能的需要收集您主动提供或在使用服务过程中产生的个人信息
具体如下 您主动提供的信息 账户注册信息当您注册我们的账户时您可能需要提供姓名手机号码电子邮箱密码等信息用于身份验证账户登录及找回
服务相关信息根据您使用的具体服务您可能需要提供相关补充信息如使用客服咨询服务时提供问题描述及相关凭证使用交易类服务时提供收货地址支付相关信息等
自愿提交的信息您主动向我们反馈的意见建议或参与我们的问卷调查活动时提交的相关信息
若您通过第三方平台如微信支付宝QQ等登录我们的服务我们会根据您的授权
从第三方获取您的相关账户信息如昵称头像用于完成登录及账户绑定
我们不会获取第三方平台的其他未授权信息
</view>
</view>
</view>
</scroll-view>
<!-- 底部同意按钮新增 -->
<!-- <view class="agree-btn-wrap" v-if="needAgree">
<button class="agree-btn" @click="agreePrivacy">同意隐私政策</button>
</view> -->
</view>
</template>
<script setup>
import { onLoad } from '@dcloudio/uni-app';
// 返回上一页
// const navigateBack = () => {
// uni.navigateBack({
// delta: 1
// });
// };
// 页面加载
// onLoad((options) => {
// // 判断是否需要同意
// if (options.needAgree === '1') {
// needAgree.value = true;
// }
// });
// 返回上一页
// const goToBack = () => {
// uni.navigateBack({
// delta: 1
// });
// };
// 同意隐私政策(新增)
// const agreePrivacy = () => {
// // #ifdef MP-WEIXIN
// wx.requirePrivacyAuthorize({
// success: () => {
// uni.showToast({
// title: '已同意隐私政策',
// icon: 'success'
// });
// // 记录授权状态(可选,存入本地缓存)
// uni.setStorageSync('privacyAgreed', true);
// navigateBack();
// },
// fail: () => {
// uni.showToast({
// title: '授权失败',
// icon: 'none'
// });
// }
// });
// // #endif
// // 非微信小程序直接标记同意
// // #ifndef MP-WEIXIN
// uni.setStorageSync('privacyAgreed', true);
// uni.showToast({
// title: '已同意隐私政策',
// icon: 'success'
// });
// navigateBack();
// // #endif
// };
</script>
<style scoped>
.privacy-page {
min-height: 100vh;
background-color: #f8f8f8;
}
/* 导航栏 */
.nav-bar {
display: flex;
align-items: center;
padding: 20rpx;
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.nav-back {
display: flex;
align-items: center;
}
.back-text {
font-size: 30rpx;
color: #333;
margin-left: 10rpx;
}
.nav-title {
font-size: 32rpx;
font-weight: 500;
margin-left: 60rpx;
}
/* 协议内容 */
.privacy-content {
height: calc(100vh - 88rpx);
}
.content-wrap {
padding: 30rpx;
}
.title {
font-size: 28rpx;
font-weight: 600;
text-align: center;
margin-bottom: 40rpx;
color: #333;
}
.section {
margin-bottom: 30rpx;
padding: 0 20rpx;
}
.section-title {
font-size: 30rpx;
font-weight: 500;
color: #333;
margin-bottom: 10rpx;
}
.section-text {
font-size: 28rpx;
line-height: 1.6;
color: #666;
}
/* 同意按钮样式(新增) */
.agree-btn-wrap {
padding: 20rpx;
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
border-top: 1rpx solid #eee;
}
.agree-btn {
background-color: #007aff;
color: #fff;
border-radius: 8rpx;
height: 88rpx;
font-size: 32rpx;
}
</style>

View File

@@ -29,7 +29,8 @@
<!-- 协议勾选 + 忘记密码 -->
<view class="agreement-wrap">
<view class="agreement-item" @click="toggleAgreement">
<view class="agreement-item">
<view @click="toggleAgreement">
<uni-icons
type="checkbox-filled"
size="24"
@@ -42,7 +43,11 @@
color="#999"
v-else
></uni-icons>
<text class="agreement-text">我已阅读并同意用户隐私政策</text>
</view>
<text class="agreement-text" @click.stop="goToPrivacy">我已阅读并同意
<text class="agreePrivacy" >用户隐私政策</text>
</text>
</view>
<navigator class="forget-pwd" url="/pageSubPack/loginSub/forget-pwd">忘记密码</navigator>
</view>
@@ -72,8 +77,10 @@ import {getLoginPassword} from '../api/api.js'
// 响应式数据
const phoneNumber = ref('');
const password = ref('');
const agreeAgreement = ref(true); // 默认勾选
const agreeAgreement = ref(false); // 默认勾选
const placeholderStyle = ref('font-size:24rpx')
// 控制点击变色
// const isClicked = ref(false)
// 计算属性:是否可登录(帐号+密码+协议都满足)
const canLogin = computed(() => {
@@ -87,6 +94,15 @@ const toggleAgreement = () => {
agreeAgreement.value = !agreeAgreement.value;
};
// 跳转隐私协议
const goToPrivacy = () => {
// 点击变色
// isClicked.value = true
uni.navigateTo({
url:'/pageSubPack/loginSub/privacy',
})
}
// 检查表单
const checkForm = () => {};
@@ -113,13 +129,13 @@ const handleLogin = () => {
uni.showToast({ title: '请勾选协议', icon: 'none' });
return;
}
uni.showLoading({ title: '登录中...', mask: true });
// uni.showLoading({ title: '登录中...', mask: true });
let params = {
username:phoneNumber.value,
password:password.value
}
getLoginPassword(params).then(res =>{
console.log('33333',res)
// console.log('33333',res)
if(res.code === 200){
uni.setStorageSync('token', res.data.token);
// 存用户ID
@@ -127,6 +143,7 @@ const handleLogin = () => {
// 存当前绑定的小区ID0 表示未绑定
const currentVillageId = res.data.currentVillageId || 0;
uni.setStorageSync('currentVillageId', currentVillageId);
uni.setStorageSync('userId',res.data.userId)
}
uni.hideLoading();
uni.showToast({ title: '登录成功', icon: 'success' });
@@ -220,6 +237,12 @@ const handleLogin = () => {
color: #666;
margin-left: 10rpx;
}
.agreePrivacy{
color: #2F77FD;
}
/* .agreePrivacy.active{
color: #2F77FD;
} */
.forget-pwd {
font-size: 28rpx;

View File

@@ -105,7 +105,7 @@
}
}catch(err){
uni.showToast({
title:"加载失败",
title: res.msg ||"网络异常",
icon:"none"
})
}

View File

@@ -28,42 +28,50 @@
<view
v-for="(item, index) in doorList"
:key="index"
:class="['door-item', { active: selectedDoor === index }]"
@click="selectDoor(index)"
:class="['door-item', { active: selectedDoor === item.id }]"
@click="selectDoor(item.id)"
>
<!-- 图标占位可替换为真实图标/图片 -->
<!-- 图标占位 -->
<view class="door-icon">
<image class="icon-text" :src="selectedDoor === index ? item.iconActive:item.iconText"></image>
<image class="icon-text" :src="selectedDoor === item.id ? item.iconActive:item.iconText"></image>
</view>
<!-- 门名称 -->
<text class="door-name">{{ item.name }}</text>
<text class="door-name" :class="{active :selectedDoor === item.id}">{{ item.deviceName }}</text>
</view>
</view>
<!-- 空状态 -->
<view v-if="doorList.length === 0" class="empty-state">
<uni-icons type="info" size="60" color="#ccc"></uni-icons>
<text class="empty-text">暂无可用门禁</text>
</view>
</view>
<!-- 确认开门按钮 -->
<button class="confirm-btn" @click="openDoor" :disabled="selectedDoor === -1">确认开门</button>
<button class="confirm-btn" @click="openDoorClick" :disabled="selectedDoor === -1">确认开门</button>
</view>
</template>
<script setup>
import { ref } from 'vue'
import {onLoad} from '@dcloudio/uni-app'
import {getAccessDevices,getAccessOpen} from '../api/api.js'
// 响应式数据:选中的门索引、开门成功提示
const selectedDoor = ref(-1) // -1表示未选中
const showSuccess = ref(false)
// 门列表数据
// 门列表数据
const doorList = ref([
{ iconText: 'http://47.104.199.163:9090/images/openDoor/westDoor.png',iconActive:"http://47.104.199.163:9090/images/openDoor/westDoorWhite.png", name: '西入门闸机' },
{ iconText: 'http://47.104.199.163:9090/images/openDoor/eastDoor.png',iconActive:"http://47.104.199.163:9090/images/openDoor/eastDoorWhite.png", name: '东入门闸机' },
{ iconText: 'http://47.104.199.163:9090/images/openDoor/unitDoor.png',iconActive:"http://47.104.199.163:9090/images/openDoor/unitDoorWhite.png", name: '1号楼1单元门' },
{ iconText: 'http://47.104.199.163:9090/images/openDoor/unitDoor.png',iconActive:"http://47.104.199.163:9090/images/openDoor/unitDoorWhite.png", name: '1号楼2单元门' }
// { iconText: 'http://47.104.199.163:9090/images/openDoor/westDoor.png',iconActive:"http://47.104.199.163:9090/images/openDoor/westDoorWhite.png", name: '西入门闸机' },
// { iconText: 'http://47.104.199.163:9090/images/openDoor/eastDoor.png',iconActive:"http://47.104.199.163:9090/images/openDoor/eastDoorWhite.png", name: '东入门闸机' },
// { iconText: 'http://47.104.199.163:9090/images/openDoor/unitDoor.png',iconActive:"http://47.104.199.163:9090/images/openDoor/unitDoorWhite.png", name: '1号楼1单元门' },
// { iconText: 'http://47.104.199.163:9090/images/openDoor/unitDoor.png',iconActive:"http://47.104.199.163:9090/images/openDoor/unitDoorWhite.png", name: '1号楼2单元门' }
])
// 选择门的方法
const selectDoor = (index) => {
selectedDoor.value = index
const selectDoor = (id) => {
selectedDoor.value = id
showSuccess.value = false // 选择新门时隐藏成功提示
}
// 返回
@@ -80,8 +88,38 @@ const narBack = () =>{
uni.removeStorageSync('sourcePage')
}
// 查询门禁列表
const getAccessControl = async () =>{
try{
const res = await getAccessDevices()
if(res.code === 200){
if(res.data){
doorList.value = res.data || []
}else{
doorList.value = []
}
}else{
doorList.value = []
uni.showToast({
title:'门禁查询失败',
icon:"error"
})
}
}catch{
doorList.value = []
uni.showToast({
title:res.msg,
icon:"error"
})
}
}
// 确认开门方法
const openDoor = () => {
const openDoorClick = () => {
// 校验:未选择门时提示
// if (selectedDoor.value === -1) {
// uni.showToast({
@@ -90,14 +128,42 @@ const openDoor = () => {
// })
// return
// }
// 模拟开门接口请求(实际项目替换为真实接口)
setTimeout(() => {
showSuccess.value = true
// 可选:添加成功提示后自动隐藏
// setTimeout(() => showSuccess.value = false, 2000)
}, 500)
getAccessOpenData()
}
// 开门接口
const getAccessOpenData = async () =>{
try{
let params = {deviceId:selectedDoor.value}
const res = await getAccessOpen(params)
if(res.code === 200){
if(res.data){
uni.showToast({
title:'开门成功',
icon:"success"
})
}else{
uni.showToast({
title:'开门失败',
icon:"error"
})
}
}else{
uni.showToast({
title:'开门失败',
icon:"error"
})
}
}catch{
uni.showToast({
title: res.msg,
icon:"error"
})
}
}
onLoad(() =>{
getAccessControl()
})
</script>
<style scoped>
@@ -229,6 +295,23 @@ const openDoor = () => {
font-size: 26rpx;
color: #333333;
}
.door-name.active {
color: #ffffff;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-text {
font-size: 32rpx;
color: #999;
margin-top: 20rpx;
margin-bottom: 10rpx;
}
/* 确认按钮 */
.confirm-btn {

View File

@@ -66,88 +66,103 @@
<view class="repair-item" v-for="(item, index) in repairList" :key="index">
<!-- 标题和状态 -->
<view class="item-header">
<text class="item-title">标题标题标题</text>
<text class="item-title">{{ item.title || '报修' }}</text>
<text class="item-status" :class="item.statusClass">{{ item.status }}</text>
</view>
<!-- 标签 -->
<view class="item-tags">
<text class="tag">公共报修</text>
<text class="tag">路灯</text>
<view class="item-tags" v-if="item.repairTypeName || item.category">
<text class="tag" v-if="item.repairTypeName">{{ item.repairTypeName }}</text>
<text class="tag" v-if="item.category">{{ item.category }}</text>
</view>
<!-- 内容 -->
<view class="item-content">
{{ item.content }}
{{ item.content || item.description || '' }}
</view>
<!-- 图片占位 -->
<view class="item-imgs">
<view class="img-placeholder" v-for="(i,index) in item.imageList" :key="index">
<!-- <uni-icons type="image" size="20" color="#ccc"></uni-icons> -->
<image :src="i.img" style="width: 100%;height: 100%;"></image>
<view class="item-imgs" v-if="item.imageList && item.imageList.length">
<view class="img-placeholder" v-for="(img, idx) in item.imageList" :key="idx">
<image :src="typeof img === 'string' ? img : img.img" style="width: 100%;height: 100%;"></image>
</view>
</view>
<!-- 时间和详情 -->
<view class="item-footer">
<text class="item-time">{{ item.time }}</text>
<text class="item-time">{{ item.time || item.createTime || '' }}</text>
<text class="detail-btn" @click="goDetail(item)">报修详情</text>
</view>
</view>
<!-- 空状态 -->
<view v-if="repairList.length === 0" class="empty-state">
<uni-icons type="info" size="30" color="#ccc"></uni-icons>
<text class="empty-text">暂无数据</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue';
import { ref, watch } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { getQueryRepair } from '../api/api.js';
// 报修类型public:公共报修personal:个人报修)
const activeType = ref('public');
// 报修状态pending:待派单assigned:已派单completed:已完成)
const activeStatus = ref('pending');
// 模拟报修列表数据根据activeStatus过滤
const repairList = computed(() => {
let statusText = ""
let statusClass = ""
if(activeStatus.value === 'pending'){
statusText = '待派单',
statusClass = 'pending'
} else if(activeStatus.value === 'assigned'){
statusText = '已派单',
statusClass = 'assigned'
}else{
statusText = '已完成',
statusClass = 'completed'
const repairList = ref([]);
const loading = ref(false);
// 状态映射
const statusMap = {
pending: { text: '待派单', class: 'pending', value: '0' },
assigned: { text: '已派单', class: 'assigned', value: '1' },
completed: { text: '已完成', class: 'completed', value: '2' }
};
// 获取报修列表
const fetchRepairList = async () => {
const villageId = uni.getStorageSync('currentVillageId');
if (!villageId || villageId === '0') {
uni.showToast({ title: '请先选择小区', icon: 'none' });
return;
}
// 原始数据
const mockData = [
{
status: statusText,
statusClass: statusClass,
content: '1栋楼下道路路灯不亮并且有的亮度不够请物业公司及时更换防止发生意外事故。',
time: '2023-08-01 10:05:46',
imageList:[
{img:'http://47.104.199.163:9090/images/repair/house1.png'},
{img:'http://47.104.199.163:9090/images/repair/house2.png'},
{img:'http://47.104.199.163:9090/images/repair/house3.png'},
]
},
{
status: statusText,
statusClass: statusClass,
content: '西门的门禁不好用了,草坪有好多的草,话题也应高擦了',
time: '2025-08-01 10:05:46',
imageList:[
{img:'http://47.104.199.163:9090/images/repair/house1.png'},
{img:'http://47.104.199.163:9090/images/repair/house2.png'},
{img:'http://47.104.199.163:9090/images/repair/house3.png'},
]
loading.value = true;
try {
const statusInfo = statusMap[activeStatus.value];
const params = {
villageId,
// 报修类型0-公共报修1-个人报修(按后端实际字段调整)
projectType: activeType.value === 'public' ? '0' : '1',
// 状态0-待派单1-已派单2-已完成(按后端实际字段调整)
problemStatus: statusInfo.value
};
const res = await getQueryRepair(params);
if (res.code === 200) {
// 按后端实际返回结构调整
const list = res.data|| [];
repairList.value = list.map(item => ({
...item,
status: statusInfo.text,
statusClass: statusInfo.class
}));
} else {
uni.showToast({ title: res.msg || '获取失败', icon: 'none' });
}
];
return mockData;
} catch (e) {
uni.showToast({ title: '网络异常', icon: 'none' });
} finally {
loading.value = false;
}
};
// 监听状态变化自动刷新
watch(activeStatus, () => {
fetchRepairList();
});
function publicClick(){
@@ -178,11 +193,15 @@ const goBack = () => {
}
// 跳转报修详情
const goDetail = (item) => {
// const itemStr = encodeURIComponent(JSON.stringify(item))
const itemStr = encodeURIComponent(JSON.stringify(item))
uni.navigateTo({
url: `/pageSubPack/online-repair/repairDetail?item = ${encodeURIComponent(JSON.stringify(item))}`
url: `/pageSubPack/online-repair/repairDetail?item = ${itemStr}`
});
};
onLoad(() => {
fetchRepairList();
});
</script>
<style scoped>
@@ -391,6 +410,20 @@ const goDetail = (item) => {
justify-content: center;
margin-right: 8px;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-text {
font-size: 32rpx;
color: #999;
margin-top: 20rpx;
margin-bottom: 10rpx;
}
/* 底部时间和详情 */
.item-footer {

View File

@@ -5,7 +5,7 @@
<text class="label">报修房屋</text>
<view class="value" @click="goToSelectHouse">
{{ houseName || '请选择房屋' }}
<text class="switch" v-if="houseName">切换</text>
<!-- <text class="switch" v-if="houseName">切换</text> -->
<uni-icons type="arrowright" size="16"></uni-icons>
</view>
</view>
@@ -148,10 +148,12 @@ import { ref,computed,onMounted} from 'vue'
import {
onLoad, onUnload
} from '@dcloudio/uni-app'
import { addQueryRepair, uploadFile } from '../api/api.js'
import moment from 'moment/moment'
// 选中的房屋/项目名称
const houseName = ref('')
const houseId = ref('')
const projectName = ref('')
// 表单数据
@@ -159,7 +161,7 @@ const form = ref({
title: '',
desc: '',
phone: '',
datetime: '2023-08-01 15:30', // 默认时间(匹配截图)
datetime: '', // 默认时间
images: []
})
@@ -167,7 +169,7 @@ const form = ref({
const timePopupRef = ref(null)
// 初始时间()
const selectedTime = ref(moment().hour(9).minute(30).second(0));
// 选择的时间(弹窗中预览)
// 选择的时间
const tempTime = ref(moment().hour(9).minute(30).second(0));
// ========== 时间选择列数据 ==========
@@ -240,6 +242,8 @@ const confirmTime = () => {
// 拼接选中的时间
const selectedDateTime = `${selectedDate.value} ${selectedHour.value}:${selectedMinute.value}`;
selectedTime.value = moment(selectedDateTime, 'YYYY-MM-DD HH:mm');
// 给表单赋值
form.value.datetime = selectedTime.value.format('YYYY-MM-DD HH:mm')
// 关闭弹窗
closeTimePopup();
@@ -332,6 +336,7 @@ onLoad(() => {
// 监听选择房屋事件
uni.$on('selectHouse', (data) => {
houseName.value = data.name
houseId.value = data.id
})
// 监听选择维修项目事件
uni.$on('selectProject', (data) => {
@@ -348,7 +353,7 @@ onUnload(() => {
// 跳转到选择房屋页
const goToSelectHouse = () => {
uni.navigateTo({
url: '/pageSubPack/online-repair/selectHouse'
url: `/pageSubPack/online-repair/selectHouse?houseId=${houseId.value}`
})
}
@@ -398,8 +403,26 @@ const deleteImage = (index) => {
form.value.images.splice(index, 1)
}
// 上传图片到服务器
const uploadImages = async () => {
const uploadedUrls = []
for (const path of form.value.images) {
if (path.startsWith('http')) {
uploadedUrls.push(path)
continue
}
try {
const res = await uploadFile(path)
uploadedUrls.push(res.data || res.msg || '')
} catch (e) {
console.error('图片上传失败', e)
}
}
return uploadedUrls
}
// 表单提交
const submitForm = () => {
const submitForm = async () => {
// 校验
if (!houseName.value) {
uni.showToast({ title: '请选择报修房屋', icon: 'none' })
@@ -426,16 +449,46 @@ const submitForm = () => {
return
}
// 提交逻辑(替换为你的接口)
const villageId = uni.getStorageSync('currentVillageId')
if (!villageId || villageId === '0') {
uni.showToast({ title: '请先选择小区', icon: 'none' })
return
}
uni.showLoading({ title: '提交中...' })
setTimeout(() => {
uni.hideLoading()
try {
const imageUrls = await uploadImages()
const userId = uni.getStorageSync('userId')
const params = {
villageId,
houseId: houseId.value,
maintenanceItemId: projectId.value,
title: form.value.title,
problem: form.value.desc,
residentId: userId,
orderDate: form.value.datetime,
phone: form.value.phone,
problemImageUrl: imageUrls.join(','),
projectType: '1',
problemStatus: ''
}
const res = await addQueryRepair(params)
if (res.code === 200) {
uni.showToast({ title: '提交成功', icon: 'success' })
// 重置表单
// form.value = { title: '', desc: '', phone: '', datetime: '', images: [] }
// houseName.value = ''
// projectName.value = ''
}, 1000)
form.value = { title: '', desc: '', phone: '', datetime: '', images: [] }
houseName.value = ''
projectName.value = ''
uni.navigateBack({ delta: 1 })
} else {
uni.showToast({ title: res.msg || '提交失败', icon: 'none' })
}
} catch (e) {
uni.showToast({ title: '网络异常', icon: 'none' })
} finally {
uni.hideLoading()
}
}
</script>

View File

@@ -17,7 +17,7 @@
v-for="item in categoryList"
:key="item.id"
>
{{ item.name }}
{{ item.projectName }}
</view>
</scroll-view>
@@ -30,7 +30,7 @@
v-for="item in currentProjectList"
:key="item.id"
>
{{ item.name }}
{{ item.projectName }}
</view>
</scroll-view>
</view>
@@ -42,39 +42,50 @@
<script setup>
import { ref, computed } from 'vue'
// import { uniShowToast, uniNavigateBack } from '@dcloudio/uni-app'
import { onLoad } from '@dcloudio/uni-app'
import { getRepairProjectList } from '../api/api.js'
// 分类列表(匹配截图:家具/电路/水暖/门频/电器)
const categoryList = ref([
{ id: 1, name: '家具' },
{ id: 2, name: '电路' },
{ id: 3, name: '水暖' },
{ id: 4, name: '门频' },
{ id: 5, name: '电器' }
])
// 分类列表
const categoryList = ref([])
// 项目列表(匹配截图:床/沙发/柜子/椅子等)
const projectList = ref([
{ id: 101, categoryId: 1, name: '床' },
{ id: 102, categoryId: 1, name: '沙发' },
{ id: 103, categoryId: 1, name: '柜子' },
{ id: 104, categoryId: 1, name: '椅子' },
{ id: 201, categoryId: 2, name: '插座' },
{ id: 202, categoryId: 2, name: '开关' },
{ id: 301, categoryId: 3, name: '水龙头' },
{ id: 302, categoryId: 3, name: '水管' }
])
// 项目列表
const projectList = ref([])
// 选中状态
const activeCategory = ref(1) // 默认选中家具
const activeCategory = ref(1)
const selectedProject = ref({}) // 选中的项目
const searchKey = ref('')
// 从接口获取个人报修项目列表
const fetchProjectList = async () => {
try {
const res = await getRepairProjectList({ projectType: '1' })
if (res.code === 200) {
const data = res.data || []
categoryList.value = data.map(item => ({ id: item.id, projectName: item.projectName }))
projectList.value = data.flatMap(item =>
(item.children || []).map(child => ({
id: child.id,
categoryId: item.id,
projectName: child.projectName
}))
)
activeCategory.value = categoryList.value[0]?.id || 1
}
} catch (e) {
uni.showToast({ title: '获取项目列表失败', icon: 'none' })
}
}
onLoad(() => {
fetchProjectList()
})
// 筛选当前分类的项目
const currentProjectList = computed(() => {
let list = projectList.value.filter(item => item.categoryId === activeCategory.value)
if (searchKey.value) {
list = list.filter(item => item.name.includes(searchKey.value))
list = list.filter(item => item.projectName.includes(searchKey.value))
}
return list
})
@@ -98,10 +109,10 @@ const confirmSelect = () => {
}
// 获取分类名称
const categoryName = categoryList.value.find(c => c.id === activeCategory.value).name
const categoryName = categoryList.value.find(c => c.id === activeCategory.value).projectName
// 传递给表单页
uni.$emit('selectProject', {
name: `${categoryName}-${selectedProject.value.name}`
name: `${categoryName}-${selectedProject.value.projectName}`
})
uni.navigateBack({ delta: 1 })

View File

@@ -57,10 +57,11 @@
<script setup>
import { ref } from 'vue'
import{onLoad} from "@dcloudio/uni-app"
// import { onLoad, uniShowToast, uniNavigateBack, uniChooseImage, uniUploadFile } from '@dcloudio/uni-app'
import { addQueryRepair, uploadFile } from '../api/api.js'
// 接收上个页面传的维修项目
const repairProject = ref('')
const projectId = ref('')
// 表单数据
const form = ref({
@@ -75,6 +76,9 @@ onLoad((options) => {
if (options.category && options.project) {
repairProject.value = `${options.category}-${options.project}`
}
if (options.projectId) {
projectId.value = options.projectId
}
})
// 返回选择项目页
@@ -98,17 +102,6 @@ const chooseImage = () => {
const tempFilePaths = res.tempFilePaths
form.value.images = [...form.value.images, ...tempFilePaths]
// 【可选】如果需要上传到服务器,取消下面注释
// tempFilePaths.forEach(path => {
// uni.uploadFile({
// url: '你的上传接口地址',
// filePath: path,
// name: 'file',
// success: (uploadRes) => {
// console.log('上传成功', uploadRes)
// }
// })
// })
}
})
}
@@ -118,8 +111,27 @@ const deleteImage = (index) => {
form.value.images.splice(index, 1)
}
// 上传图片到服务器
const uploadImages = async () => {
const uploadedUrls = []
for (const path of form.value.images) {
if (path.startsWith('http')) {
uploadedUrls.push(path)
continue
}
try {
const res = await uploadFile(path)
// 根据后端实际返回结构调整字段名
uploadedUrls.push(res.data || res.msg || '')
} catch (e) {
console.error('图片上传失败', e)
}
}
return uploadedUrls
}
// 表单提交
const submitForm = () => {
const submitForm = async () => {
// 表单校验
if (!repairProject.value) {
uni.showToast({ title: '请选择维修项目', icon: 'none' })
@@ -138,19 +150,49 @@ const submitForm = () => {
return
}
// 提交数据(替换成你的接口)
const villageId = uni.getStorageSync('currentVillageId')
if (!villageId || villageId === '0') {
uni.showToast({ title: '请先选择小区', icon: 'none' })
return
}
uni.showLoading({ title: '提交中...' })
// 模拟接口请求
setTimeout(() => {
uni.hideLoading()
uni.showToast({ title: '提交成功', icon: 'success' })
try {
// 先上传图片
const imageUrls = await uploadImages()
const userId = uni.getStorageSync('userId')
// 提交成功后重置表单/返回上一页
// form.value = { title: '', desc: '', phone: '', images: [] }
// 或返回上一页
uni.navigateBack({ delta: 3 })
}, 1000)
const params = {
villageId,
maintenanceItemId:projectId.value,
title: form.value.title,
problem: form.value.desc,
residentId:userId,//人员id
// repairTypeName: repairProject.value,
phone: form.value.phone,
problemImageUrl: imageUrls.join(','),
projectType: '0',
problemStatus:''
}
const res = await addQueryRepair(params)
if (res.code === 200) {
uni.showToast({ title: '提交成功', icon: 'success' })
form.value = { title: '', desc: '', phone: '', images: [] }
repairProject.value = ''
// uni.navigateBack({ delta: 2 })
uni.navigateTo({
url:'/pageSubPack/online-repair/onlineRepair'
})
} else {
uni.showToast({ title: res.msg || '提交失败', icon: 'none' })
}
} catch (e) {
uni.showToast({ title: '网络异常', icon: 'none' })
} finally {
uni.hideLoading()
}
}
</script>

View File

@@ -3,7 +3,6 @@
<!-- 维修流程模块 -->
<view class="info-section">
<!-- <uni-section title="维修流程" type="line" style="margin-bottom: 20rpx;"> -->
<view class="section-title">维修流程</view>
<uni-row class="demo-uni-row" :width="nvueWidth">
<uni-col :span="12">
@@ -19,13 +18,10 @@
<view class="step-time-3">2026-03-28 09:30:38</view>
</uni-col>
</uni-row>
<!-- </uni-section> -->
</view>
<!-- 报修信息模块 -->
<view class="info-section">
<!-- <uni-section title="报修信息" type="line" style="margin-bottom: 20rpx;"> -->
<view class="section-title">报修信息</view>
<view class="info-item">
<view class="info-label">报修房屋</view>
@@ -59,7 +55,6 @@
</view>
</view>
</view>
<!-- </uni-section> -->
</view>
<!-- 联系物业按钮 -->
@@ -70,15 +65,9 @@
</template>
<script setup>
import {
ref
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app'
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
// 步骤条当前激活的步骤0=待处理1=已派单2=已完成)
const currentStep = ref(2)
const nvueWidth = ref('300')
// 报修信息数据
@@ -91,21 +80,25 @@
time: '2023-08-01 15:30'
})
// 步骤状态
const currentStep = ref(1)
onLoad((option) => {
const decodeStr = decodeURIComponent(JSON.stringify(option))
// let item = JSON.parse(decodeStr)
// repairInfo.value = optionStr
console.log('item', decodeStr)
const status = decodeStr.statusClass
// console.log('status',status)
const optionstr = decodeURIComponent(JSON.stringify(option))
// console.log('接收参数:', optionstr)
// ✅ 正确解析参数
const status = optionstr.statusClass
// ✅ 正确赋值
switch (status) {
case 'pending': //待处理
currentStep.value = 0
break
case 'assignde':
case 'assignde': //已派单
currentStep.value = 1
break
case 'completed':
case 'completed': //已完成
currentStep.value = 2
break
default:
@@ -135,7 +128,6 @@
border-radius: 8px;
padding: 13px;
margin-bottom: 16px;
/* margin-top: 20rpx; */
}
.repair-steps-container {
@@ -153,21 +145,16 @@
.step-time {
height: 54px;
line-height: 54px;
font-size: 12px;
font-size: 24rpx;
color: #999;
/* font-size: 22rpx;
color: #999;
text-align: right;
margin-top: 5rpx; */
}
.step-time-3{
height: 38px;
line-height: 38px;
font-size: 12px;
font-size: 24rpx;
color: #999;
}
/* -------- */
.section-title {
font-size: 32rpx;
font-weight: bold;
@@ -184,32 +171,18 @@
.slot-desc {
font-size: 10px;
}
/* .step-time {
position: absolute;
right: 0;
top: 0;
font-size: 14px;
color: #999;
white-space: nowrap;
} */
.info-item {
display: flex;
padding: 12px 0;
/* border-bottom: 1px solid #f0f0f0; */
}
.info-item-desc {
/* display: flex; */
padding: 12px 0;
/* border-bottom: 1px solid #f0f0f0; */
}
.info-section.active {
color: #007AFF;
/* font-weight: 500; */
}
.info-item:last-child {
@@ -270,4 +243,14 @@
.contact-btn::after {
border: none;
}
/* ========== 颜色的代码 ========== */
/* u-text__value u-text__value--main */
/* 激活的步骤标题 + 描述 强制蓝色 */
::v-deep .u-text__value .u-text__value--main {
color: #007aff !important;
}
::v-deep .up-steps-item--active .up-steps-item__desc {
color: #007aff !important;
}
</style>

View File

@@ -17,7 +17,7 @@
v-for="item in categoryList"
:key="item.id"
>
{{ item.name }}
{{ item.projectName }}
</view>
</scroll-view>
@@ -29,7 +29,7 @@
v-for="item in currentProjectList"
:key="item.id"
>
{{ item.name }}
{{ item.projectName }}
</view>
</scroll-view>
</view>
@@ -38,24 +38,43 @@
<script setup>
import { ref, computed } from 'vue'
// import { uniNavigateTo } from '@dcloudio/uni-app'
import { onLoad } from '@dcloudio/uni-app'
import { getRepairProjectList } from '../api/api.js'
// 分类列表
const categoryList = ref([
{ id: 1, name: '基础设施' },
{ id: 2, name: '电子设施' }
])
const categoryList = ref([])
// 所有项目数据
const projectList = ref([
{ id: 101, categoryId: 1, name: '路灯' },
{ id: 102, categoryId: 1, name: '楼层指示灯' },
{ id: 103, categoryId: 2, name: '应急出口灯' },
{ id: 104, categoryId: 1, name: '草坪灯' }
])
const projectList = ref([])
// 选中的分类
const activeCategory = ref(1)
// 从接口获取维修项目列表
const fetchProjectList = async () => {
try {
const res = await getRepairProjectList({ projectType: '0' })
if (res.code === 200) {
const data = res.data || []
// 兼容后端嵌套格式 [{id, name, children: [{id, name}]}]
categoryList.value = data.map(item => ({ id: item.id, projectName: item.projectName }))
projectList.value = data.flatMap(item =>
(item.children || []).map(child => ({
id: child.id,
categoryId: item.id,
projectName: child.projectName
}))
)
activeCategory.value = categoryList.value[0]?.id || 1
}
} catch (e) {
uni.showToast({ title: '获取项目列表失败', icon: 'none' })
}
}
onLoad(() => {
fetchProjectList()
})
// 搜索关键词
const searchKey = ref('')
@@ -63,7 +82,7 @@ const searchKey = ref('')
const currentProjectList = computed(() => {
let list = projectList.value.filter(item => item.categoryId === activeCategory.value)
if (searchKey.value) {
list = list.filter(item => item.name.includes(searchKey.value))
list = list.filter(item => item.projectName.includes(searchKey.value))
}
return list
})
@@ -76,10 +95,10 @@ const selectCategory = (item) => {
// 选择项目并跳转到维修项目填写页
const selectProject = (item) => {
// 获取分类名称
const categoryName = categoryList.value.find(c => c.id === item.categoryId)?.name
const categoryName = categoryList.value.find(c => c.id === item.categoryId)?.projectName
// 跳转并传参
uni.navigateTo({
url: `/pageSubPack/online-repair/publicRepair?category=${categoryName}&project=${item.name}`
url: `/pageSubPack/online-repair/publicRepair?category=${categoryName}&project=${item.projectName}&projectId=${item.id}`
})
}
</script>

View File

@@ -9,10 +9,10 @@
v-for="item in houseList"
:key="item.id"
>
<image src="http://47.104.199.163:9090/images/repair/house.png" style="width: 60rpx;height: 60rpx;"></image>
<image src="http://47.104.199.163:9090/images/repair/house.png" style="width: 48rpx;height: 48rpx;"></image>
<view class="house-name">
{{ item.community }}
<view class="house-detail">{{ item.address }}</view>
{{ item.villageName }}
<view class="house-detail">{{ item.houseType }}</view>
</view>
<uni-icons type="checkbox-filled" size="20" color="#007aff" v-if="selectedHouse.id === item.id"></uni-icons>
@@ -26,17 +26,49 @@
<script setup>
import { ref } from 'vue'
// import { uniShowToast, uniNavigateBack } from '@dcloudio/uni-app'
import { onLoad } from '@dcloudio/uni-app'
import { getHouseList } from '../api/api.js'
// 房屋列表数据
const houseList = ref([
{ id: 1, community: '桂花园(别墅)', address: '2号楼2幢' },
{ id: 2, community: '安泰翡翠城', address: '11号楼5单元1801' },
{ id: 3, community: '碧桂园新城·时代之光', address: '3号楼' }
])
const houseList = ref([])
// 选中的房屋
const selectedHouse = ref(houseList.value[0]) // 默认选中第一个
const selectedHouse = ref({})
// 分页参数
const pageNum = ref(1)
const pageSize = ref(10)
// 预传入的房屋ID从表单页带过来
const preSelectedHouseId = ref('')
// 获取房屋列表
const fetchHouseList = async () => {
try {
const res = await getHouseList({ pageNum: pageNum.value, pageSize: pageSize.value })
if (res.code === 200) {
const list = res.rows || []
houseList.value = list
if (houseList.value.length > 0) {
if (preSelectedHouseId.value) {
const found = houseList.value.find(item => String(item.id) === String(preSelectedHouseId.value))
selectedHouse.value = found || houseList.value[0]
} else {
selectedHouse.value = houseList.value[0]
}
}
}
} catch (e) {
uni.showToast({ title: '获取房屋列表失败', icon: 'none' })
}
}
onLoad((options) => {
if (options.houseId) {
preSelectedHouseId.value = options.houseId
}
fetchHouseList()
})
// 选择房屋
const selectHouse = (item) => {
@@ -52,7 +84,8 @@ const confirmSelect = () => {
// 把选中的房屋信息传给表单页通过uni.$emit
uni.$emit('selectHouse', {
name: `${selectedHouse.value.community} ${selectedHouse.value.address}`
id: selectedHouse.value.id,
name: `${selectedHouse.value.villageName} ${selectedHouse.value.houseType}`
})
// 返回上一页
@@ -68,7 +101,7 @@ const confirmSelect = () => {
display: flex;
flex-direction: column;
justify-content: space-between;
background: #fff;
background: #f9f9f9;
}
.house-list {
flex: 1;
@@ -78,13 +111,14 @@ const confirmSelect = () => {
justify-content: flex-start;
align-items: center;
padding: 15px 10px;
border: 1px solid #eee;
background-color: #fff;
border-radius: 8px;
margin-bottom: 10px;
}
.house-item.active {
border-color: #007aff;
background: #f0f8ff;
border: 1px solid #2F77FD;
}
.house-name {
font-size: 16px;
@@ -108,7 +142,8 @@ const confirmSelect = () => {
background: #007aff;
color: #fff;
border-radius: 8px;
font-size: 16px;
font-size: 28rpx;
margin-top: 20px;
margin-bottom: 20rpx;
}
</style>

View File

@@ -112,6 +112,8 @@ const sendToFriend = () => {
color: #fff;
border-radius: 8rpx;
height: 88rpx;
font-size: 32rpx;
font-size: 28rpx;
line-height: 88rpx;
}
</style>

View File

@@ -1,11 +1,5 @@
<template>
<view class="visitor-invite">
<!-- 顶部导航 -->
<!-- <view class="nav-bar">
<uni-icons type="left" size="20" color="#000" @click="navigateBack"></uni-icons>
<view class="nav-title">访客邀请</view>
</view> -->
<!-- 标签切换车辆来访/人员来访 -->
<view class="tab-wrap">
<view
@@ -27,8 +21,8 @@
<!-- 表单区域 -->
<view class="form-wrap">
<!-- 房屋信息 -->
<view class="form-section">
<view class="section-title">房屋信息</view>
<view class="form-section">
<view class="form-item">
<view class="label">
<text class="required">*</text>来访地址
@@ -41,8 +35,9 @@
</view>
<!-- 访客信息 -->
<view class="form-section">
<view class="section-title">访客信息</view>
<view class="form-section">
<view class="form-item">
<view class="label">访客姓名</view>
<input
@@ -103,14 +98,15 @@
</view>
<!-- 有效期 -->
<view class="form-section">
<view class="section-title">有效期</view>
<view class="form-section">
<view class="form-item">
<view class="label">
<text class="required">*</text>来访时间
</view>
<view class="value">
<uni-datetime-picker type="datetime" v-model="visitTime" @change="changeLog" :border="false"/>
<uni-datetime-picker type="datetime" v-model="form.visitTime" @change="handleTimeChange" :border="false"/>
</view>
</view>
<view class="form-item">
@@ -118,11 +114,11 @@
<text class="required">*</text>离开时间
</view>
<view class="value">
<uni-datetime-picker type="datetime" v-model="leaveTime" @change="changeLog" :border="false"/>
<uni-datetime-picker type="datetime" v-model="form.leaveTime" @change="handleTimeChange" :border="false"/>
</view>
</view>
<view class="tips">预计来访时间和预计离开时间间隔不能超过24小时</view>
<view class="tips">车辆停留请勿超出有效期需及时驶出小区</view>
<view class="tips-2">车辆停留请勿超出有效期需及时驶出小区</view>
</view>
</view>
@@ -176,31 +172,30 @@
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import { ref, reactive} from 'vue';
import{onLoad } from "@dcloudio/uni-app"
import moment from 'moment';
// 标签激活状态
const activeTab = ref('car');
// 表单数据
const form = reactive({
address: '桂花园别墅2号楼2层', // 来访地址默认值
name: '', // 访客姓名
gender: '', // 性别默认值
phone: '', // 联系方式
carNumber: ['', '', '', '', '', '', ''], // 车牌7位
peopleNum: '', // 随行人数
visitTime: '', // 来访时间默认值
leaveTime: '' // 离开时间
address: '',
name: '',
gender: '',
phone: '',
carNumber: ['', '', '', '', '', '', '', ''], // 修复8位车牌
peopleNum: '',
visitTime: '',
leaveTime: ''
});
// 地址列表(模拟数据)
// 地址列表
const addressList = ref([
'桂花园别墅1号楼1层',
'桂花园别墅2号楼2层'
@@ -209,31 +204,18 @@ const addressList = ref([
// 性别列表
const genderList = ref(['男', '女']);
// 弹窗/选择器实例
// 弹窗实例
const addressModalRef = ref(null);
const genderModalRef = ref(null);
const timePickerRef = ref(null);
// 标记当前选择的是来访/离开时间
const currentTimeType = ref('');
// 页面初始化
onLoad(() => {
// 可在这里请求接口获取地址/默认时间等数据
});
// 切换tab
const switchTab = (tab) => {
activeTab.value = tab;
// 切换Tab后重置表单
resetForm();
// nextTick(() =>{
// })
};
// 返回上一页
const navigateBack = () => {
uni.navigateBack();
};
// 打开地址弹窗
@@ -263,32 +245,51 @@ const selectGender = (item) => {
genderModalRef.value.close();
};
// 打开时间选择器
// const openTimePicker = (type) => {
// currentTimeType.value = type;
// timePickerRef.value.open();
// };
// 时间校验
const handleTimeChange = () => {
if (!form.visitTime || !form.leaveTime) return true;
const visitMoment = moment(form.visitTime);
const leaveMoment = moment(form.leaveTime);
// 离开时间不能早于来访时间
if (leaveMoment.isBefore(visitMoment)) {
uni.showToast({
title: '离开时间不能早于来访时间',
icon: 'none'
});
form.leaveTime = visitMoment.clone().add(1, 'hours').format('YYYY-MM-DD HH:mm');
return false;
}
// 校验来访/离开时间间隔
const checkTimeInterval = () => {
if (!form.visitTime || !form.leaveTime) return;
const visitTime = new Date(form.visitTime).getTime();
const leaveTime = new Date(form.leaveTime).getTime();
const diffHours = (leaveTime - visitTime) / (1000 * 60 * 60);
// 不能超过24小时
const diffHours = leaveMoment.diff(visitMoment, 'hours', true);
if (diffHours > 24) {
uni.showToast({
title: '时间间隔不能超过24小时',
icon: 'none'
});
form.leaveTime = '';
form.leaveTime = visitMoment.clone().add(24, 'hours').format('YYYY-MM-DD HH:mm');
return false;
}
return true;
};
// 表单提交
// 提交表单
const submitForm = () => {
// 必填项校验
// 1. 必填项校验
if (!form.address) {
uni.showToast({ title: '请选择来访地址', icon: 'none' });
return;
}
if (!form.name) {
uni.showToast({ title: '请输入访客姓名', icon: 'none' });
return;
}
if (!form.gender) {
uni.showToast({ title: '请选择性别', icon: 'none' });
return;
}
if (!form.phone) {
uni.showToast({ title: '请输入访客联系方式', icon: 'none' });
return;
@@ -297,22 +298,27 @@ const submitForm = () => {
uni.showToast({ title: '请输入正确的手机号', icon: 'none' });
return;
}
if (activeTab === 'car' && form.carNumber.join('').length < 8) {
if (activeTab === 'car' && form.carNumber.join('').length < 7) {
uni.showToast({ title: '请填写完整车牌号', icon: 'none' });
return;
}
if (!form.peopleNum || form.peopleNum < 0) {
if (!form.peopleNum || form.peopleNum <= 0) {
uni.showToast({ title: '请输入正确的随行人数', icon: 'none' });
return;
}
if (!form.visitTime) {
uni.showToast({ title: '请选择来访时间', icon: 'none' });
return;
}
if (!form.leaveTime) {
uni.showToast({ title: '请选择离开时间', icon: 'none' });
return;
}
checkTimeInterval();
if (!form.leaveTime) return; // 时间校验失败则终止提交
// 提交逻辑(替换为实际接口请求)
// 2. 时间规则校验
if (!handleTimeChange()) return;
// 3. 提交成功
uni.showLoading({ title: '提交中...' });
setTimeout(() => {
uni.hideLoading();
@@ -321,22 +327,28 @@ const submitForm = () => {
icon: 'success'
});
}, 1000);
setTimeout(() =>{
uni.navigateTo({
url:"/pageSubPack/visitor/qrcode-page"
})
},1500)
};
// 重置表单
const resetForm = () => {
// 重置表单
const resetForm = () => {
form.address = '';
form.name = '';
form.gender = '';
form.phone = '';
form.carNumber = '';
form.escortNum = '';
form.carNumber = ['', '', '', '', '', '', '', ''];
form.peopleNum = '';
form.visitTime = '';
form.leaveTime = '';
form.carNumber = ['', '', '', '', '', '', '']; // 重置车牌号输入
};
};
</script>
<style scoped>
.visitor-invite {
background-color: #f5f5f5;
@@ -385,22 +397,23 @@ const submitForm = () => {
padding: 20rpx 40rpx;
}
.section-title {
font-size: 32rpx;
font-size: 28rpx;
font-weight: 500;
margin-bottom: 20rpx;
/* margin-bottom: 20rpx; */
margin: 30rpx 40rpx 10rpx;
}
.form-item {
display: flex;
align-items: center;
padding: 20rpx 0;
padding: 30rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.form-item:last-child {
border-bottom: none;
}
.label {
width: 200rpx;
font-size: 30rpx;
width: 205rpx;
font-size: 32rpx;
color: #333;
}
.required {
@@ -440,18 +453,24 @@ const submitForm = () => {
.tips {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
margin-top: 44rpx;
text-align: center;
}
.tips-2{
font-size: 24rpx;
color: #999;
margin: 20rpx;
text-align: center;
}
/* 底部按钮 */
.btn-wrap {
padding: 20rpx;
padding: 20rpx 40rpx 50rpx;
}
.submit-btn {
background-color: #007aff;
color: #fff;
font-size: 32rpx;
font-size: 28rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 8rpx;

View File

@@ -167,6 +167,7 @@ const goToQrcode = () => {
color: #fff;
border-radius: 8rpx;
height: 88rpx;
font-size: 32rpx;
font-size: 28rpx;
line-height: 88rpx
}
</style>

View File

@@ -73,8 +73,8 @@
<!-- 新增邀请按钮 -->
<view class="add-btn" @click="addInvitation">
<uni-icons type="plus" size="18" color="#fff"></uni-icons>
<text class="add-text">新增邀请</text>
<!-- <uni-icons type="plus" size="18" color="#fff"></uni-icons> -->
<text class="add-text">新增邀请+</text>
</view>
</view>
</template>
@@ -332,5 +332,6 @@ const addInvitation = () => {
}
.add-text {
margin-left: 5px;
font-size: 28rpx;
}
</style>

View File

@@ -68,6 +68,13 @@
"navigationBarTitleText": "修改密码",
"navigationStyle": "custom"
}
},
{
"path": "privacy",
"style": {
"navigationBarTitleText": "用户隐私政策"
// "navigationStyle": "custom"
}
}
]
},

View File

@@ -50,7 +50,7 @@
<up-col span="3">
<view class="demo-layout bg-purple openClass" @click="openDoorClick">
<view class="iconClass">
<image src="http://47.104.199.163:9090/images/home/openDoor.png" mode="" style="width: 70px;height: 70px;border-radius: 8px;"></image>
<image src="http://47.104.199.163:9090/images/home/openDoor.png" class="iconClass-img"></image>
</view>
<view class="title-dome-calss">一键开门</view>
</view>
@@ -58,7 +58,7 @@
<up-col span="3">
<view class="demo-layout bg-purple-light openClass" @click="payMentClick">
<view class="iconClass">
<image src="http://47.104.199.163:9090/images/home/lifePay.png" mode="" style="width: 70px;height: 70px;border-radius: 8px;"></image>
<image src="http://47.104.199.163:9090/images/home/lifePay.png" class="iconClass-img"></image>
</view>
<view class="title-dome-calss">生活缴费</view>
</view>
@@ -66,7 +66,7 @@
<up-col span="3">
<view class="demo-layout bg-purple openClass" @click="onlineRepairClick">
<view class="iconClass">
<image src="http://47.104.199.163:9090/images/home/repair.png" mode="" style="width: 70px;height: 70px;border-radius: 8px;"></image>
<image src="http://47.104.199.163:9090/images/home/repair.png" class="iconClass-img"></image>
</view>
<view class="title-dome-calss">在线报修</view>
</view>
@@ -74,7 +74,7 @@
<up-col span="3">
<view class="demo-layout bg-purple-light openClass" @click="visitorClick">
<view class="iconClass">
<image src="http://47.104.199.163:9090/images/home/visitorInvite.png" mode="" style="width: 70px;height: 70px;border-radius: 8px;"></image>
<image src="http://47.104.199.163:9090/images/home/visitorInvite.png" class="iconClass-img"></image>
</view>
<view class="title-dome-calss">访客邀请</view>
</view>
@@ -126,18 +126,18 @@
<up-row justify="space-between" customStyle="margin-bottom: 10px">
<up-col span="4">
<view class="demo-layout bg-purple-light activity-img">
<image src="http://47.104.199.163:9090/images/home/activity-img.png" style="width: 100%;height: 100%;"></image>
<image :src="activity.coverImage" style="width: 100%;height: 100%;"></image>
</view>
</up-col>
<up-col span="10">
<up-col span="8">
<view class="demo-layout bg-purple activity-info">
<view class="activity-title">{{activity.title}}</view>
<view class="activity-time">{{activity.time}}</view>
<view class="activity-time">{{formatDate(activity.startTime)}}-{{formatDate(activity.endTime)}}</view>
<view class="activity-organizer">
<text>{{activity.organizer}}</text>
<text>{{activity.createName}}</text>
</view>
<view class="activity-enrollment">
<text>活动名额100已报名{{activity.enrollment}}</text>
<text>活动名额{{activity.quota}}已报名{{activity.appliedCount}}</text>
</view>
</view>
</up-col>
@@ -147,6 +147,13 @@
</view>
</scroll-view>
<!-- 空状态 -->
<view v-if="activityList.length === 0" class="empty-state">
<uni-icons type="info" size="30" color="#ccc"></uni-icons>
<text class="empty-text">暂无数据</text>
</view>
</uni-card>
@@ -158,34 +165,14 @@
<script setup>
import { ref } from 'vue'
import {onLoad,onUnload,onShow} from '@dcloudio/uni-app'
import {getNoticeLatest,getCurrentVillage} from "/pageSubPack/api/api.js"
import moment from 'moment'
import {getNoticeLatest,getCurrentVillage,getActivitylatest} from "/pageSubPack/api/api.js"
// 列表数据状态
const isDropdownOpen = ref(false)
const scrollTop = ref(0)
const activityList = ref([
{
id:'0',
statusText:'进行中',
img:'/static/logo.png',
title:'社区活动节活动',
time:'2026.3.5-2026.4.30',
organizer:'物业中心',
enrollment:'87'
},
// {
// id:'1',
// statusText:'进行中',
// img:'',
// title:'222',
// time:'2026.3.5-2026.4.30',
// organizer:'物业中心',
// enrollment:'87'
// },
])
const activityList = ref([]) // 活动列表数据
const currentCommunity = ref('请认证小区')
const currentVillageId = ref('')
const bannerList = ref([
@@ -196,6 +183,11 @@ const noticeText = ref('暂无公告')
const noticeId = ref('')
// =======方法=======
// 格式化时间
const formatDate = (date) => {
if (!date) return ''
return moment(date).format('YYYY-MM-DD HH:mm')
}
// 公告跳转
const getNoticeMore = () =>{
uni.setStorageSync('sourcePage',"index")
@@ -205,7 +197,7 @@ const getNoticeMore = () =>{
}
// 接收选择小区返回的参数
const slelctHomeData = (data) =>{
console.log('收到B页面返回的数据', data)
// console.log('收到B页面返回的数据', data)
if(data.name){
currentCommunity.value = data.name
}
@@ -214,6 +206,7 @@ const slelctHomeData = (data) =>{
}
getCurrentCommunityData()
getNoticeData()
getNewActivityList()
}
// 获取当前认证小区的方法
const getCurrentCommunityData = () => {
@@ -235,7 +228,7 @@ const getCurrentCommunityData = () => {
}
// 获取小区公告
// 获取首页最新小区公告
const getNoticeData = async() =>{
try{
const res = await getNoticeLatest()
@@ -282,6 +275,7 @@ onLoad(() => {
getCurrentCommunityData(); // 加载时获取认证小区
}
getNoticeData()
getNewActivityList()
})
// 页面销毁时移除监听(防止内存泄漏)
@@ -305,10 +299,10 @@ const payMentClick =() =>{
}
// 点击小区选择
const navigateToPage = () => {
const token = uni.getStorageSync('token') || '';
// const token = uni.getStorageSync('token') || '';
const cachedVillageId = uni.getStorageSync('currentVillageId');
// 未登录 或 未绑定小区 → 去我的页面认证
if (!token || !cachedVillageId || cachedVillageId === '0' || cachedVillageId === 0) {
if ( !cachedVillageId || cachedVillageId === '0' || cachedVillageId === 0) {
currentCommunity.value = '请去认证小区';
uni.setStorageSync('currentCommunity', '请去认证小区');
uni.showToast({
@@ -328,6 +322,33 @@ const navigateToPage = () => {
});
}
};
// 获取首页最新活动
const getNewActivityList = async() =>{
try{
const res = await getActivitylatest()
if(res.code === 200){
if(res.data){
activityList.value = res.data || []
}else{
activityList.value = []
}
}else{
activityList.value = []
uni.showToast({
title: err.msg,
icon: 'error',
duration: 1000
})
}
}catch{
uni.showToast({
title: err.msg,
icon: 'error',
duration: 1000
})
}
}
const onlineRepairClick = () =>{
@@ -352,15 +373,10 @@ const moreClick = () =>{
}
</script>
<style lang="scss" scoped>
.content-class{
// margin: 0 20rpx;
// padding: 2rpx;
background-color: #f9f9f9;
padding-bottom: 100rpx;
box-sizing: border-box; /* 保证padding不会撑大容器 */
@@ -369,7 +385,6 @@ const moreClick = () =>{
.nar-top{
height: 400rpx;
width: 100%;
// background-color: #C0E4FB;
background: repeating-linear-gradient(to right, #EDF4FA , #C0E4FB);
position: absolute;
@@ -468,15 +483,20 @@ const moreClick = () =>{
align-items: center;
margin-top: 20rpx;
.iconClass{
width: 130rpx;
height: 130rpx;
width: 88rpx;
height: 88rpx;
border-radius: 10rpx;
// opacity: 0.4;
}
.iconClass-img{
width: 100%;
height: 100%;
border-radius: 8px
}
.title-dome-calss{
margin-top: 10rpx;
font-size: 13px;
font-size: 28rpx;
color: #000;
}
}
@@ -573,7 +593,20 @@ const moreClick = () =>{
margin-top: 20rpx;
color: #666666;
}
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-text {
font-size: 32rpx;
color: #999;
margin-top: 20rpx;
margin-bottom: 10rpx;
}
.look-more-title{
color: #3E8EFF;

View File

@@ -2,7 +2,7 @@ const version = '3'
// 开发环境才提示,生产环境不会提示
if (process.env.NODE_ENV === 'development') {
console.log(`\n %c uview-plus V${version} %c https://uview-plus.jiangruyi.com/ \n\n`, 'color: #ffffff; background: #3c9cff; padding:5px 0;', 'color: #3c9cff;background: #ffffff; padding:5px 0;');
//console.log(`\n %c uview-plus V${version} %c https://uview-plus.jiangruyi.com/ \n\n`, 'color: #ffffff; background: #3c9cff; padding:5px 0;', 'color: #3c9cff;background: #ffffff; padding:5px 0;');
}
export default {