替换图片并删掉本地图片,代码分包

This commit is contained in:
zhouyanli
2026-04-21 15:48:52 +08:00
parent b0d90c27db
commit 65e6e5bf30
70 changed files with 316 additions and 212 deletions

View File

@@ -1,871 +0,0 @@
<template>
<view class="container">
<view class="status_bar"></view>
<!-- 顶部标题栏 -->
<view class="top-bar">
<uni-nav-bar left-icon="left" title="活动列表" backgroundColor ="none" :border="false" @clickLeft="narBack" />
</view>
<!-- 顶部图片 -->
<view class="topImg">
<image src="/static/首页/banner.png" style="width: 100%;height:100%;"></image>
</view>
<!-- 选项卡 -->
<view class="tabs-container">
<view class="tab-item" :class="{ 'active': activeTab === 'list' }" @tap="switchTab('list')">
活动列表
<view v-if="activeTab === 'list'" class="tab-indicator"></view>
</view>
<view class="tab-item" :class="{ 'active': activeTab === 'my' }" @tap="switchTab('my')">
我的活动
<view v-if="activeTab === 'my'" class="tab-indicator"></view>
</view>
</view>
<!-- 我的活动状态筛选 -->
<view v-if="activeTab === 'my'" class="filter-container">
<scroll-view class="filter-scroll" scroll-x>
<view class="filter-buttons">
<view v-for="filter in filters" :key="filter.value" class="filter-btn"
:class="{ 'active': myFilter === filter.value }" @tap="switchFilter(filter.value)">
{{ filter.label }}
</view>
</view>
</scroll-view>
</view>
<!-- 活动列表 -->
<scroll-view class="activity-list" scroll-y @scrolltolower="loadMoreData" :lower-threshold="100">
<!-- 空状态 -->
<view v-if="displayedActivities.length === 0" class="empty-state">
<uni-icons type="info" size="60" color="#ccc"></uni-icons>
<text class="empty-text">
{{ activeTab === 'list' ? '暂无活动' : '暂无参与的活动' }}
</text>
</view>
<!-- 活动卡片 -->
<view v-for="activity in displayedActivities" :key="activity.id" class="activity-card"
@tap="viewActivityDetail(activity)">
<!-- 活动状态标签 -->
<!-- <view class="status-badge" :class="getStatusClass(activity.status)">
{{ getStatusText(activity.status) }}
</view> -->
<view class="status-badge">
进行中
</view>
<!-- 已报名标识 -->
<view v-if="activity.hasSigned && activeTab === 'list'" class="signed-badge">
<uni-icons type="checkbox-filled" size="14" color="#fff"></uni-icons>
<text>已报名</text>
</view>
<!-- 活动信息 -->
<view class="activity-info">
<view class="u-demo-block__content ">
<up-row justify="space-between" customStyle="margin-bottom: 10px">
<up-col span="4">
<view class="info-image-left">
<image src="/static/首页/activity-img.png" style="width: 100%;height: 100%;"></image>
</view>
</up-col>
<up-col span="8">
<view class="demo-layout bg-purple">
<view class="activity-title">
{{ activity.title }}
</view>
<view class="activity-detail">
<!-- <uni-icons type="calendar" size="16" color="#999"></uni-icons> -->
<text class="detail-text">时间{{ formatDate(activity.startTime) }}
{{ formatDate(activity.endTime) }}</text>
</view>
<view class="activity-detail">
<!-- <uni-icons type="location-filled" size="16" color="#999"></uni-icons> -->
<text class="detail-text">地点{{ activity.location }}</text>
</view>
<view class="activity-detail">
<!-- <uni-icons type="person" size="16" color="#999"></uni-icons> -->
<text class="detail-text-num">活动名额{{ activity.totalQuota }}已报名{{ activity.signedCount }}</text>
</view>
</view>
</up-col>
</up-row>
</view>
<!-- 我的活动额外信息 -->
<!-- <view v-if="activeTab === 'my'" class="my-activity-info">
<view class="my-status" :class="getMyStatusClass(activity.myStatus)">
{{ getMyStatusText(activity.myStatus) }}
</view>
<view v-if="activity.myStatus === 'reviewing'" class="review-tips">
<text>审核中请耐心等待</text>
</view>
<view v-if="activity.myStatus === 'rejected'" class="review-tips">
<text>未通过审核如有疑问请联系管理员</text>
</view>
</view> -->
<!-- 操作按钮 -->
<view class="action-buttons">
<button v-if="activeTab === 'list' && !activity.hasSigned" class="action-btn signup-btn"
@tap.stop="signupActivity(activity)">
立即报名
</button>
<!-- <button v-if="activeTab === 'list' && activity.hasSigned" class="action-btn signed-btn"
disabled>
已报名
</button> -->
<!-- <button
v-if="activeTab === 'my' && activity.myStatus === 'passed' && activity.status === 'ongoing'"
class="action-btn signup-btn" @tap.stop="viewActivityDetail(activity)">
查看详情
</button> -->
<!-- <button
v-if="activeTab === 'my' && activity.myStatus === 'passed' && activity.status === 'ended'"
class="action-btn ended-btn" disabled>
已结束
</button> -->
<!-- <button v-if="activeTab === 'my' && activity.myStatus === 'rejected'"
class="action-btn reject-btn" @tap.stop="contactAdmin(activity)">
联系管理员
</button> -->
</view>
</view>
</view>
<!-- 加载更多 -->
<view v-if="hasMoreData && displayedActivities.length > 0" class="load-more-container">
<view v-if="!isLoading" class="load-more-btn" @tap="loadMoreData">
<text>加载更多</text>
<uni-icons type="arrow-down" size="16" color="#007AFF"></uni-icons>
</view>
<view v-else class="loading-container">
<uni-icons type="spinner-cycle" size="20" color="#007AFF" class="loading-icon"></uni-icons>
<text>正在加载...</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup>
import {
ref,
computed,
onMounted,
watch
} from 'vue'
import moment from 'moment';
// 响应式数据
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 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 displayedActivities = ref([])
// 计算属性:根据当前标签和筛选条件过滤活动
const filteredActivities = computed(() => {
if (activeTab.value === 'list') {
// 活动列表页:显示所有活动
return allActivities.value
} else {
// 我的活动页:根据筛选条件过滤
if (myFilter.value === 'all') {
return allActivities.value.filter(activity => activity.hasSigned)
} else {
return allActivities.value.filter(activity =>
activity.hasSigned && activity.myStatus === myFilter.value
)
}
}
})
// 返回
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) => {
activeTab.value = tab
currentPage.value = 1
displayedActivities.value = []
loadPageData(1)
}
// 切换筛选条件
const switchFilter = (filter) => {
myFilter.value = filter
currentPage.value = 1
displayedActivities.value = []
loadPageData(1)
}
// 加载分页数据
const loadPageData = (pageNum) => {
isLoading.value = true
// 模拟网络请求延迟
setTimeout(() => {
const startIndex = (pageNum - 1) * pageSize
const endIndex = startIndex + pageSize
const pageData = filteredActivities.value.slice(startIndex, endIndex)
if (pageNum === 1) {
displayedActivities.value = pageData
} else {
displayedActivities.value = [...displayedActivities.value, ...pageData]
}
currentPage.value = pageNum
hasMoreData.value = displayedActivities.value.length < filteredActivities.value.length
isLoading.value = false
}, 500)
}
// 加载更多数据
const loadMoreData = () => {
if (isLoading.value || !hasMoreData.value) return
loadPageData(currentPage.value + 1)
}
// 获取活动状态文本
// const getStatusText = (status) => {
// const statusMap = {
// 'ongoing': '进行中',
// 'upcoming': '即将开始',
// 'ended': '已结束'
// }
// return statusMap[status] || '未知'
// }
// 获取活动状态类名
const getStatusClass = (status) => {
const classMap = {
'ongoing': 'status-ongoing',
// 'upcoming': 'status-upcoming',
// 'ended': 'status-ended'
}
return classMap[status] || ''
}
// 获取我的活动状态文本
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) => {
// console.log('fffff', activity)
uni.navigateTo({
url: `/pages/activity-list/activityListDetails?id=${activity.id}&title=${activity.title}`
})
}
// const signupActivity = (activity) => {
// uni.showModal({
// title: '报名确认',
// content: `确定要报名参加"${activity.title}"吗?`,
// success: (res) => {
// if (res.confirm) {
// // 模拟报名成功
// activity.hasSigned = true
// activity.myStatus = 'reviewing'
// activity.signedCount += 1
// uni.showToast({
// title: '报名成功,等待审核',
// icon: 'success',
// duration: 2000
// })
// // 如果是我的活动页,刷新列表
// if (activeTab.value === 'my') {
// currentPage.value = 1
// displayedActivities.value = []
// loadPageData(1)
// }
// }
// }
// })
// }
// 查看活动详情
const viewActivityDetail = (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: '知道了'
})
}
})
}
// 联系管理员
const contactAdmin = (activity) => {
uni.showModal({
title: '联系管理员',
content: `您报名"${activity.title}"未通过审核,是否联系管理员了解原因?`,
success: (res) => {
if (res.confirm) {
uni.makePhoneCall({
phoneNumber: '400-123-4567'
})
}
}
})
}
// 监听筛选条件变化
watch(() => myFilter.value, () => {
if (activeTab.value === 'my') {
currentPage.value = 1
displayedActivities.value = []
loadPageData(1)
}
})
// 初始化
onMounted(() => {
loadPageData(1)
})
</script>
<style>
/* 页面容器 */
.container {
min-height: 100vh;
background-color: #f9f9f9;
display: flex;
flex-direction: column;
padding: 20rpx;
}
.status_bar {
height: 32px;
width: 100%;
}
/* 顶部标题 */
.top-bar {
text-align: center;
margin-bottom: 20rpx;
}
.topImg {
width: 100%;
height: 280rpx;
margin-bottom: 20rpx;
}
/* 顶部选项卡 */
.tabs-container {
display: flex;
/* background-color: #fff; */
/* border-bottom: 1px solid #eee; */
/* position: sticky; */
top: 0;
z-index: 10;
margin-bottom: 20rpx
}
.tab-item {
flex: 1;
text-align: center;
padding: 20rpx 0;
font-size: 32rpx;
color: #666;
position: relative;
}
.tab-item.active {
color: #007AFF;
font-weight: 500;
}
.tab-indicator {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 120rpx;
height: 6rpx;
background-color: #007AFF;
border-radius: 4rpx;
}
/* 筛选容器 */
.filter-container {
background-color: #fff;
padding: 20rpx 0;
border-bottom: 1px solid #eee;
}
.filter-scroll {
width: 100%;
white-space: nowrap;
}
.filter-buttons {
display: inline-flex;
padding: 0 30rpx;
}
.filter-btn {
display: inline-block;
padding: 12rpx 30rpx;
margin-right: 20rpx;
font-size: 28rpx;
color: #666;
background-color: #f8f8f8;
border-radius: 25rpx;
border: 1px solid #eee;
}
.filter-btn.active {
color: #fff;
background-color: #2F77FD;
/* border-color: #007AFF; */
}
/* 活动列表 */
.activity-list {
flex: 1;
/* padding: 30rpx; */
margin-bottom: 20rpx;
}
/* 活动卡片 */
.activity-card {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
position: relative;
}
/* 活动状态标签 */
.status-badge {
position: absolute;
top: 10rpx;
left: 20rpx;
padding: 6rpx 20rpx;
font-size: 22rpx;
border-radius: 15rpx 0rpx 15rpx 0rpx;
color: #fff;
z-index: 1;
background: repeating-linear-gradient(to right, #FD7373 , #FB3F3F);
}
/* .status-ongoing {
background: repeating-linear-gradient(to right, #FD7373 , #FB3F3F);
} */
/* .status-upcoming {
background-color: #ff9d00;
}
.status-ended {
background-color: #999;
} */
/* 已报名标识 */
.signed-badge {
position: absolute;
top: 20rpx;
right: 8rpx;
display: flex;
align-items: center;
padding: 6rpx 14rpx;
background-color: #007AFF;
color: #fff;
font-size: 22rpx;
border-radius: 20rpx;
z-index: 1;
}
.signed-badge text {
margin-left: 8rpx;
}
/* 活动信息 */
.activity-info {
margin-top: 10rpx;
}
.info-image-left {
width: 170rpx;
height: 220rpx;
}
.activity-title {
font-size: 30rpx;
font-weight: 500;
color: #222;
margin-bottom: 30rpx;
line-height: 1.4;
padding-right: 100rpx;
}
.activity-detail {
display: flex;
align-items: flex-start;
margin-bottom: 20rpx;
color: #999;
font-size: 24rpx;
line-height: 1.4;
}
.activity-detail .uni-icons {
margin-right: 15rpx;
margin-top: 4rpx;
flex-shrink: 0;
}
.detail-text-num{
font-size: 28rpx;
color: #666666;
}
.detail-text {
flex: 1;
}
/* 我的活动信息 */
.my-activity-info {
margin-top: 20rpx;
padding: 20rpx;
background-color: #f8f8f8;
border-radius: 12rpx;
}
.my-status {
display: inline-block;
padding: 8rpx 24rpx;
font-size: 24rpx;
border-radius: 20rpx;
margin-bottom: 10rpx;
}
.my-status-reviewing {
background-color: #e6f2ff;
color: #007AFF;
}
.my-status-passed {
background-color: #e6f7f0;
color: #07c160;
}
.my-status-rejected {
background-color: #fdeeee;
color: #f5222d;
}
.my-status-ended {
background-color: #f5f5f5;
color: #999;
}
.review-tips {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
/* 操作按钮 */
.action-buttons {
margin-top: 30rpx;
display: flex;
justify-content: flex-end;
}
.action-btn {
padding: 0 40rpx;
height: 60rpx;
line-height: 60rpx;
font-size: 24rpx;
border-radius: 35rpx;
margin: 0;
}
.signup-btn {
background-color: #007AFF;
color: #fff;
}
.signup-btn:active {
background-color: #0056cc;
}
.signed-btn {
background-color: #f0f0f0;
color: #999;
}
.reject-btn {
background-color: #fff;
color: #f5222d;
border: 1px solid #f5222d;
}
.ended-btn {
background-color: #f5f5f5;
color: #999;
}
/* 加载更多 */
.load-more-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 0;
color: #999;
}
.load-more-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 40rpx;
background-color: #f8f8f8;
border-radius: 50rpx;
color: #007AFF;
font-size: 28rpx;
margin-bottom: 20rpx;
}
.load-more-btn text {
margin-right: 10rpx;
}
.loading-container {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 40rpx;
background-color: #f8f8f8;
border-radius: 50rpx;
color: #007AFF;
font-size: 28rpx;
margin-bottom: 20rpx;
}
.loading-icon {
margin-right: 10rpx;
animation: rotate 1s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
color: #ccc;
}
.empty-text {
font-size: 32rpx;
color: #999;
margin-top: 20rpx;
margin-bottom: 10rpx;
}
/* 适配不同屏幕 */
@media (min-width: 768px) {
.container {
max-width: 500px;
margin: 0 auto;
}
}
</style>

View File

@@ -1,903 +0,0 @@
<template>
<view class="container">
<!-- 顶部导航栏 -->
<view class="nav-bar">
<view class="nav-left" @tap="goBack">
<uni-icons type="left" size="24" color="#000"></uni-icons>
</view>
<view class="nav-title">{{navTitle}}</view>
<view class="nav-right"></view>
</view>
<view style="status_bar"></view>
<!-- 内容区域 -->
<scroll-view class="content" scroll-y>
<!-- 选项卡 -->
<view class="tabs">
<view
class="tab-item"
:class="{ 'active': activeTab === 'detail' }"
@tap="switchTab('detail')"
>
活动详情
</view>
<view
class="tab-item"
:class="{ 'active': activeTab === 'status' }"
@tap="switchTab('status')"
>
活动实况
</view>
</view>
<!-- 活动详情内容 -->
<view v-if="activeTab === 'detail'" class="detail-content">
<!-- 基本信息 -->
<view class="info-section">
<view class="info-item">
<view class="info-label">报名截止时间</view>
<view class="info-value">2023-08-08 23:00</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>
</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>
</view>
</view>
<view class="info-item">
<view class="info-label">活动地点</view>
<view class="info-value">山东省日照市高新区高新管委会大厦前高新公园</view>
</view>
<view class="info-item">
<view class="info-label">活动名额</view>
<view class="info-value">10</view>
</view>
<view class="info-item">
<view class="info-label">主办方</view>
<view class="info-value">山海天社区</view>
</view>
<view class="info-item">
<view class="info-label">联系人</view>
<view class="info-value">赵璐</view>
</view>
<view class="info-item">
<view class="info-label">联系电话</view>
<view class="info-value">15799999999</view>
</view>
<view class="info-item">
<view class="info-label">积分奖励</view>
<view class="info-value">100</view>
</view>
</view>
<!-- 招募要求 -->
<view class="section">
<view class="section-title">招募要求</view>
<view class="section-content">
要求就是要求就是要求就是
</view>
</view>
<!-- 活动详情 -->
<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>
</view>
</view>
<!-- 活动实况内容 -->
<view v-else class="status-content">
<view class="status-header">
<view class="status-title">
<image src="/static/活动/现场实况.png" style="width: 40rpx;height: 30rpx;"></image>
现场实况
</view>
<view class="status-count">{{ liveRecords.length }}</view>
</view>
<!-- 实况列表 -->
<view class="record-list">
<view v-for="(record, index) in liveRecords" :key="index" class="record-item">
<view class="record-user">
<view class="user-avatar">
<uni-icons type="person" size="20" color="#999"></uni-icons>
</view>
<view class="user-info">
<view class="user-name">微信用户</view>
<view class="record-time">{{ record.time }}</view>
<view class="record-content">{{ record.content }}</view>
<!-- 图片展示 -->
<view v-if="record.images && record.images.length > 0" class="record-images">
<view
v-for="(img, imgIndex) in record.images"
:key="imgIndex"
class="image-item"
:style="{ backgroundImage: `url(${img})` }"
>
<image :src="img" style="width: 100%;height: 100%;"></image>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 底部操作栏 -->
<view class="bottom-bar">
<!-- 未报名状态显示报名按钮 -->
<button
v-if="!hasSigned && activeTab === 'detail'"
class="action-btn signup-btn"
@tap="handleSignup"
>
报名
</button>
<!-- 已报名状态显示取消报名按钮 -->
<button
v-if="hasSigned && activeTab === 'detail'"
class="action-btn cancel-btn"
@tap="handleCancelSignup"
>
取消报名
</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> -->
</view>
<!-- 报名确认模态框 -->
<view class="modal" :class="{ 'show': showSignupModal }">
<view class="modal-content">
<view class="modal-header">报名确认</view>
<view class="modal-body">
<view>确定要报名参加"社区'小槐花课堂'开课了"</view>
<view class="modal-tip">报名后请按时参加活动</view>
</view>
<view class="modal-footer">
<view class="modal-btn cancel" @tap="hideSignupModal">取消</view>
<view class="modal-btn confirm" @tap="confirmSignup">确定</view>
</view>
</view>
</view>
<!-- 取消报名确认模态框 -->
<view class="modal" :class="{ 'show': showCancelModal }">
<view class="modal-content">
<view class="modal-header">取消报名</view>
<view class="modal-body">
<view>确定要取消报名"社区'小槐花课堂'开课了"</view>
<view class="modal-tip">取消后如需参加需重新报名</view>
</view>
<view class="modal-footer">
<view class="modal-btn cancel" @tap="hideCancelModal">再想想</view>
<view class="modal-btn confirm" @tap="confirmCancel">确定取消</view>
</view>
</view>
</view>
<!-- 提示信息 -->
<view class="toast" :class="{ 'show': showToast }">
{{ toastMessage }}
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import {onLoad} from '@dcloudio/uni-app'
import moment from 'moment'
// 响应式数据
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 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: ['/static/logo.png', '/static/引导1.png']
}
])
// 接受参数
onLoad((option) =>{
// console.log('dsdsds',option)
navTitle.value = option.title
})
// 切换选项卡
const switchTab = (tab) => {
activeTab.value = tab
}
// 返回上一页
const goBack = () => {
uni.navigateBack()
}
// 处理报名
const handleSignup = () => {
showSignupModal.value = true
}
// 隐藏报名模态框
const hideSignupModal = () => {
showSignupModal.value = false
}
// 确认报名
const confirmSignup = () => {
hasSigned.value = true
hideSignupModal()
showToastMessage('报名成功!')
// 模拟更新名额
// 在实际应用中这里应该调用API更新后端数据
}
// 处理取消报名
const handleCancelSignup = () => {
showCancelModal.value = true
}
// 隐藏取消报名模态框
const hideCancelModal = () => {
showCancelModal.value = false
}
// 确认取消报名
const confirmCancel = () => {
hasSigned.value = false
hideCancelModal()
showToastMessage('已取消报名')
// 模拟更新名额
// 在实际应用中这里应该调用API更新后端数据
}
// 添加实况跳转
const liveClick = () =>{
uni.navigateTo({
url:'/pages/activity-list/live-add'
})
}
// 获取当前时间
const getCurrentTime = () => {
return moment(date).format('YYYY-MM-DD HH:mm:ss')
}
// 显示提示信息
const showToastMessage = (message) => {
toastMessage.value = message
showToast.value = true
setTimeout(() => {
showToast.value = false
}, 2000)
}
// 初始化
onMounted(() => {
// 模拟检查用户是否已报名
// 实际项目中应该从接口获取
setTimeout(() => {
hasSigned.value = false // 默认未报名
}, 100)
})
</script>
<style>
/* 页面容器 */
.container {
min-height: 100vh;
display: flex;
flex-direction: column;
background: linear-gradient(to bottom left, #E2F0FF 0%, #ffffff 50%)
}
/* 顶部导航栏 */
.nav-bar {
height: 90rpx;
/* background: linear-gradient(135deg, #007AFF 0%, #0056cc 100%); */
display: flex;
align-items: center;
padding: 0 30rpx;
color: #fff;
position: sticky;
top: 44px;
z-index: 100;
/* box-shadow: 0 4rpx 12rpx rgba(0, 122, 255, 0.2); */
}
.nav-left {
width: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.nav-title {
flex: 1;
text-align: left;
font-size: 36rpx;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 20rpx;
color: #000;
}
.nav-right {
width: 60rpx;
}
/* 内容区域 */
.content {
flex: 1;
margin-bottom: 120rpx;
}
.status_bar {
height: 46px;
width: 100%;
}
/* 选项卡 */
.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;
color: #666;
position: relative;
transition: all 0.3s;
}
.tab-item.active {
color: #007AFF;
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;
}
/* 基本信息区域 */
.info-section {
/* background-color: #fff; */
border-radius: 16rpx;
padding: 30rpx;
/* margin-bottom: 30rpx; */
/* box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); */
}
.info-item {
display: flex;
margin-bottom: 30rpx;
padding-bottom: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.info-item:last-child {
margin-bottom: 0;
padding-bottom: 0;
border-bottom: none;
}
.info-label {
width: 220rpx;
font-size: 28rpx;
color: #222;
flex-shrink: 0;
}
.info-value {
flex: 1;
font-size: 28rpx;
color: #222;
font-weight: 500;
line-height: 1.4;
}
/* 区块样式 */
.section {
/* background-color: #fff; */
border-radius: 16rpx;
padding: 30rpx;
/* margin-bottom: 30rpx; */
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.section-title {
font-size: 32rpx;
color: #333;
font-weight: 600;
margin-bottom: 20rpx;
padding-bottom: 20rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.section-content {
font-size: 28rpx;
color: #666;
line-height: 1.6;
}
.section-content view {
margin-bottom: 20rpx;
}
.section-content view:last-child {
margin-bottom: 0;
}
/* 活动实况内容 */
.status-content {
padding: 30rpx;
}
/* 实况头部 */
.status-header {
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 {
font-size: 32rpx;
color: #333;
font-weight: 600;
border-bottom: 1rpx solid #f0f0f0;
}
.status-count {
padding: 8rpx 20rpx;
/* background-color: #007AFF; */
color: #666;
font-size: 24rpx;
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 {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
/* .record-item:last-child {
border-bottom: none;
} */
.record-user {
display: flex;
align-items: flex-start;
justify-content: flex-start;
margin-bottom: 20rpx;
}
.user-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
background-color: #f8f9fa;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
border: 1rpx solid #e9ecef;
}
.user-info {
/* flex: 1; */
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.user-name {
font-size: 28rpx;
color: #333;
font-weight: 500;
margin-bottom: 8rpx;
}
.record-time {
font-size: 24rpx;
color: #999;
}
.record-content {
font-size: 28rpx;
color: #333;
line-height: 1.5;
/* margin-bottom: 20rpx; */
margin: 25rpx 0rpx;
}
/* 实况图片 */
.record-images {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
}
.image-item {
width: 150rpx;
height: 170rpx;
border-radius: 12rpx;
background-color: #f8f9fa;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border: 1rpx solid #e9ecef;
}
/* 底部操作栏 */
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx 30rpx 40rpx;
background-color: #fff;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
z-index: 100;
}
.action-btn {
height: 100rpx;
line-height: 100rpx;
border-radius: 12rpx;
font-size: 34rpx;
font-weight: 600;
width: 100%;
border: none;
transition: all 0.3s;
}
.signup-btn {
background: linear-gradient(135deg, #007AFF 0%, #0056cc 100%);
color: #fff;
box-shadow: 0 8rpx 30rpx rgba(0, 122, 255, 0.3);
}
.signup-btn:active {
background: linear-gradient(135deg, #0056cc 0%, #004199 100%);
transform: translateY(2rpx);
box-shadow: 0 4rpx 20rpx rgba(0, 122, 255, 0.2);
}
.cancel-btn {
background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%);
color: #666;
border: 1rpx solid #e9ecef;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.cancel-btn:active {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
transform: translateY(2rpx);
}
.add-status-btn {
background: linear-gradient(135deg, #007AFF 0%, #0056cc 100%);
color: #fff;
box-shadow: 0 8rpx 30rpx rgba(0, 122, 255, 0.3);
}
.add-status-btn:active {
background: linear-gradient(135deg, #0056cc 0%, #004199 100%);
transform: translateY(2rpx);
box-shadow: 0 4rpx 20rpx rgba(0, 122, 255, 0.2);
}
/* 模态框 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
align-items: center;
justify-content: center;
backdrop-filter: blur(5rpx);
}
.modal.show {
display: flex;
animation: fadeIn 0.3s;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.modal-content {
background-color: #fff;
border-radius: 20rpx;
width: 600rpx;
overflow: hidden;
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.2);
animation: slideUp 0.3s;
}
@keyframes slideUp {
from { transform: translateY(100rpx); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.modal-header {
padding: 40rpx 30rpx 20rpx;
text-align: center;
font-size: 36rpx;
font-weight: 600;
color: #333;
border-bottom: 1rpx solid #f0f0f0;
}
.modal-body {
padding: 40rpx 30rpx;
text-align: center;
font-size: 30rpx;
color: #666;
line-height: 1.5;
}
.modal-tip {
font-size: 24rpx;
color: #999;
margin-top: 20rpx;
}
.modal-footer {
display: flex;
border-top: 1rpx solid #f0f0f0;
}
.modal-btn {
flex: 1;
padding: 30rpx 0;
text-align: center;
font-size: 32rpx;
font-weight: 500;
transition: all 0.2s;
}
.modal-btn.cancel {
color: #666;
border-right: 1rpx solid #f0f0f0;
}
.modal-btn.cancel:active {
background-color: #f8f9fa;
}
.modal-btn.confirm {
color: #007AFF;
font-weight: 600;
}
.modal-btn.confirm:active {
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;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.8);
color: #fff;
padding: 20rpx 40rpx;
border-radius: 10rpx;
font-size: 28rpx;
z-index: 1100;
display: none;
backdrop-filter: blur(10rpx);
}
.toast.show {
display: block;
animation: toastFade 2s;
}
@keyframes toastFade {
0% { opacity: 0; transform: translate(-50%, -40%); }
20% { opacity: 1; transform: translate(-50%, -50%); }
80% { opacity: 1; transform: translate(-50%, -50%); }
100% { opacity: 0; transform: translate(-50%, -60%); }
}
/* 适配不同屏幕 */
@media (min-width: 768px) {
.container {
max-width: 500px;
margin: 0 auto;
}
}
</style>

View File

@@ -1,285 +0,0 @@
<template>
<view class="add-status-page">
<!-- 主体内容 -->
<scroll-view class="content" scroll-y>
<!-- 实况描述输入框 -->
<view class="form-item">
<text class="label">实况描述</text>
<textarea
class="textarea"
placeholder="请输入活动实况内容,也可打卡签到"
v-model="statusDesc"
maxlength="500"
@input="handleTextInput"
></textarea>
<text class="count">{{ currentLength }}/500</text>
</view>
<!-- 活动照片上传 -->
<view class="form-item">
<text class="label">活动照片</text>
<view class="upload-area">
<!-- 已上传图片 -->
<view class="uploaded-img" v-for="(img, index) in imgList" :key="index">
<image :src="img" mode="aspectFill"></image>
<text class="del-img" @click="deleteImg(index)">×</text>
</view>
<!-- 上传按钮最多3张 -->
<view class="upload-btn" @click="chooseImg" v-if="imgList.length < 3">
<text class="plus-icon">+</text>
<text class="tips">最多3张图片</text>
</view>
</view>
</view>
<!-- 签到地点 -->
<view class="form-item">
<text class="label">签到地点</text>
<view class="location-area">
<text class="location-text">{{ location }}</text>
<view class="re-locate" @click="reLocate">
<uni-icons type="refreshempty" size="14" color="#2196f3" class="re-icon"></uni-icons>
重新定位
</view>
</view>
</view>
</scroll-view>
<!-- 底部提交按钮 -->
<view class="submit-btn-area">
<button class="submit-btn" @click="submitStatus">提交</button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
// import { chooseImage, uploadFile, getLocation } from '@dcloudio/uni-app';
// 实况描述内容
const statusDesc = ref('');
// 输入字符长度
const currentLength = ref(0);
// 图片列表
const imgList = ref([]);
// 定位信息
const location = ref('山东省日照市东港区学院路');
// 监听输入框字数变化
const handleTextInput = (e) => {
currentLength.value = e.detail.value.length;
};
// 选择图片
const chooseImg = () => {
uni.chooseImage({
count: 3 - imgList.value.length, // 最多可选择的数量
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
// 临时文件路径转存到图片列表
imgList.value = [...imgList.value, ...res.tempFilePaths];
},
fail: (err) => {
uni.showToast({ title: '选择图片失败', icon: 'none' });
console.error('选择图片失败:', err);
}
});
};
// 删除图片
const deleteImg = (index) => {
imgList.value.splice(index, 1);
};
// 重新定位
const reLocate = () => {
uni.showLoading({ title: '定位中...' });
uni.getLocation({
type: 'gcj02',
success: (res) => {
uni.hideLoading();
// 实际项目中可调用逆地理编码接口转换为具体地址,这里模拟
location.value = '山东省日照市东港区学院路(新定位)';
uni.showToast({ title: '定位成功', icon: 'success' });
},
fail: (err) => {
uni.hideLoading();
uni.showToast({ title: '定位失败,请检查权限', icon: 'none' });
console.error('定位失败:', err);
}
});
};
// 提交实况
const submitStatus = () => {
// 简单校验
if (!statusDesc.value.trim()) {
uni.showToast({ title: '请输入实况描述', icon: 'none' });
return;
}
// 模拟提交逻辑(实际项目中可上传图片+提交表单)
uni.showLoading({ title: '提交中...' });
// 如需上传图片可循环调用uploadFile
// 示例:
// const uploadPromises = imgList.value.map(img => {
// return uploadFile({
// url: '你的上传接口',
// filePath: img,
// name: 'file'
// });
// });
// Promise.all(uploadPromises).then(res => { /* 处理上传结果 */ });
setTimeout(() => {
uni.hideLoading();
uni.showToast({ title: '添加成功', icon: 'success' });
// 提交成功后可返回上一页
uni.navigateBack();
}, 1000);
};
</script>
<style scoped>
/* 页面整体样式 */
.add-status-page {
width: 100%;
height: 100vh;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
}
/* 主体内容 */
.content {
flex: 1;
padding: 15px 0px;
}
/* 表单项通用样式 */
.form-item {
background-color: #fff;
padding: 15px;
border-radius: 8px;
margin-bottom: 15px;
}
.label {
font-size: 16px;
font-weight: 500;
margin-bottom: 10px;
display: block;
}
/* 输入框样式 */
.textarea {
width: 100%;
min-height: 120px;
padding: 10px;
border: 1px solid #eee;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
}
.count {
text-align: right;
font-size: 12px;
color: #999;
margin-top: 5px;
display: block;
}
/* 图片上传区域 */
.upload-area {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.uploaded-img {
width: 80px;
height: 80px;
position: relative;
}
.uploaded-img image {
width: 100%;
height: 100%;
border-radius: 4px;
}
.del-img {
position: absolute;
top: -5px;
right: -5px;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
background-color: #ff4444;
color: #fff;
border-radius: 50%;
font-size: 16px;
}
.upload-btn {
width: 80px;
height: 80px;
border: 1px dashed #ddd;
border-radius: 4px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.plus-icon {
font-size: 24px;
color: #999;
margin-bottom: 5px;
}
.tips {
font-size: 12px;
color: #999;
}
/* 定位区域 */
.location-area {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
}
.location-text {
font-size: 14px;
color: #333;
}
.re-locate {
font-size: 14px;
color: #007aff;
display: flex;
align-items: center;
}
.re-icon{
color: #007aff;
}
/* .re-locate::before {
content: '';
width: 16px;
height: 16px;
background: url(/static/logo.png) no-repeat center;
background-size: 100%;
margin-right: 5px;
} */
/* 提交按钮 */
.submit-btn-area {
padding: 15px;
}
.submit-btn {
width: 100%;
height: 48px;
line-height: 48px;
background-color: #007aff;
color: #fff;
border: none;
border-radius: 8px;
font-size: 16px;
}
</style>

View File

@@ -22,7 +22,7 @@ export const getResetPassword = (data) =>{
return post('/api/resident/auth/resetPassword',data)
}
// =========首页=======
// 查询小区列表
export const getVillageList = () =>{
return get('/api/resident/village/list')
// 首页最新公告
export const getNoticeList = () =>{
return get('/api/resident/notice/latest')
}

View File

@@ -16,9 +16,14 @@ export default function request(url, data = {}, method = "GET") {
data: data,
method: method,
header: {
"token": uni.getStorageSync("token") || '',
'content-type': 'application/json'
},
"Authorization": "Bearer " + (uni.getStorageSync("token") || ''),
"clientid": "resident",
'content-type': 'application/json'
},
// header: {
// "token": uni.getStorageSync("token") || '',
// 'content-type': 'application/json'
// },
success: (res) => {
uni.hideLoading(); // 关闭加载提示
@@ -44,16 +49,16 @@ export default function request(url, data = {}, method = "GET") {
return;
}
// 业务成功code=1
// 业务成功code=200
if (resData.code === 200) {
resolve(resData.data); // 只返回核心数据,简化页面调用
}
// 业务失败code=2
// 业务失败code=500
else if (resData.code === 500) {
const errMsg = resData.msg || '请求失败';
uni.showToast({ title: errMsg, icon: "none", duration: 2000 });
resolve(null)
// reject(errMsg);
// resolve(null)
reject(errMsg);
}
else {
const errMsg = resData.msg || '业务异常';

View File

@@ -14,7 +14,7 @@
<!-- 定位图标 -->
<view class="location-icon">
<!-- <uni-icons type="location-filled" size="14" color="#333333"></uni-icons> -->
<image src="/static/首页/adress.png" style="height: 100%;width: 100%;"></image>
<image src="http://47.104.199.163:9090/images/home/adress.png" style="height: 100%;width: 100%;"></image>
</view>
<!-- 小区名称和下拉箭头 -->
<view class="community-selector">
@@ -37,7 +37,7 @@
<up-swiper :list="bannerList" indicator indicatorMode="dot" ></up-swiper>
</view>
<view class="noticeClass" @click="getNoticeMore">
<image src="/static/首页/公告.png" style="width: 38px;height: 38px;position: absolute;top: 21px;left: 17px;"></image>
<image src="http://47.104.199.163:9090/images/home/notice.png" style="width: 38px;height: 38px;position: absolute;top: 21px;left: 17px;"></image>
<view class="col-line"></view>
<uni-notice-bar show-icon :text="noticeText" type="line"
:showIcon="false" show-get-more scrollable color="#000" background-color="#fff" moreColor="#000">
@@ -50,7 +50,7 @@
<up-col span="3">
<view class="demo-layout bg-purple openClass" @click="openDoorClick">
<view class="iconClass">
<image src="/static/首页/一键开门.png" mode="" style="width: 70px;height: 70px;border-radius: 8px;"></image>
<image src="http://47.104.199.163:9090/images/home/openDoor.png" mode="" style="width: 70px;height: 70px;border-radius: 8px;"></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="/static/首页/生活缴费.png" mode="" style="width: 70px;height: 70px;border-radius: 8px;"></image>
<image src="http://47.104.199.163:9090/images/home/lifePay.png" mode="" style="width: 70px;height: 70px;border-radius: 8px;"></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="/static/首页/在线报修.png" mode="" style="width: 70px;height: 70px;border-radius: 8px;"></image>
<image src="http://47.104.199.163:9090/images/home/repair.png" mode="" style="width: 70px;height: 70px;border-radius: 8px;"></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="/static/首页/访客邀请.png" mode="" style="width: 70px;height: 70px;border-radius: 8px;"></image>
<image src="http://47.104.199.163:9090/images/home/visitorInvite.png" mode="" style="width: 70px;height: 70px;border-radius: 8px;"></image>
</view>
<view class="title-dome-calss">访客邀请</view>
</view>
@@ -88,7 +88,7 @@
<up-row justify="space-between" customStyle="margin-bottom: 10px">
<up-col span="3">
<view class="demo-layout bg-purple iconRightClass">
<image src="/static/首页/广告1.png" mode="" style="width: 100%;height: 100%;"></image>
<image src="http://47.104.199.163:9090/images/home/ad1.png" mode="" style="width: 100%;height: 100%;"></image>
</view>
</up-col>
<up-col span="8">
@@ -126,7 +126,7 @@
<up-row justify="space-between" customStyle="margin-bottom: 10px">
<up-col span="4">
<view class="demo-layout bg-purple-light activity-img">
<image src="/static/首页/activity-img.png" style="width: 100%;height: 100%;"></image>
<image src="http://47.104.199.163:9090/images/home/activity-img.png" style="width: 100%;height: 100%;"></image>
</view>
</up-col>
<up-col span="10">
@@ -158,6 +158,7 @@
<script setup>
import { ref } from 'vue'
import {onLoad,onUnload} from '@dcloudio/uni-app'
import {getNoticeList} from "/pages/api/api.js"
@@ -188,7 +189,7 @@ const activityList = ref([
])
const currentCommunity = ref('碧桂园小区')
const bannerList = ref([
'/static/首页/banner.png',
'http://47.104.199.163:9090/images/home/banner.png',
])
const noticeText = ref('公告公告本小区今天安装了摄像头')
@@ -205,10 +206,27 @@ const slelctHomeData = (data) =>{
// console.log('收到B页面返回的数据', data)
currentCommunity.value = data.name
}
// 获取小区公告
// const getNoticeData = () =>{
// getNoticeList().then(res =>{
// if(res.code === 200){
// noticeText.value = res.data.noticeTitle
// }else{
// noticeText.value = ''
// }
// }).catch(err =>{
// uni.showToast({
// title: '网络异常',
// icon: 'error'
// });
// })
// }
onLoad(() => {
// 绑定全局事件监听
uni.$on('confirm', slelctHomeData)
// getNoticeData()
})
@@ -221,7 +239,7 @@ onUnload(() => {
const openDoorClick = () =>{
uni.setStorageSync('sourcePage',"index")
uni.navigateTo({
url:"/pages/notice-list/oneClickOpen"
url:"/pageSubPack/notice-list/oneClickOpen"
})
}
// 生活缴费
@@ -234,27 +252,27 @@ const payMentClick =() =>{
const navigateToPage = () => {
uni.navigateTo({
url: '/pages/notice-list/selectHome'
url: '/pageSubPack/notice-list/selectHome'
})
}
const onlineRepairClick = () =>{
uni.setStorageSync('sourcePage',"index")
uni.navigateTo({
url:'/pages/online-repair/onlineRepair'
url:'/pageSubPack/online-repair/onlineRepair'
})
}
// 访客邀请
const visitorClick = () =>{
uni.setStorageSync('sourcePage',"index")
uni.navigateTo({
url:"/pages/visitor/visitor-list"
url:"/pageSubPack/visitor/visitor-list"
})
}
// 活动列表的查看更多
const moreClick = () =>{
uni.setStorageSync('sourcePage',"index")
uni.navigateTo({
url:"/pages/activity-list/activityList"
url:"/pageSubPack/activity-list/activityList"
})
}

View File

@@ -1,189 +0,0 @@
<template>
<view class="reset-pwd-page">
<view class="status_bar"></view>
<!-- 顶部导航栏 -->
<view class="nav-bar">
<uni-icons type="left" size="28" color="#333" @click="goBack"></uni-icons>
<view class="nav-title">修改密码</view>
</view>
<!-- 新密码输入框 -->
<view class="input-item">
<uni-easyinput class="input-content" type="password" v-model="newPassword"
placeholder="请输入密码" :inputBorder="false" :placeholderStyle="placeholderStyle">
</uni-easyinput>
</view>
<!-- 确认密码输入框 -->
<view class="input-item">
<uni-easyinput class="input-content" type="password" v-model="confirmPassword"
placeholder="请再次输入密码" :inputBorder="false" :placeholderStyle="placeholderStyle">
</uni-easyinput>
</view>
<!-- 确定按钮 -->
<button
class="confirm-btn"
:disabled="!canConfirm"
@click="handleConfirm"
>
确定
</button>
</view>
</template>
<script setup>
import { ref, computed } from 'vue';
import {onLoad,onShow} from '@dcloudio/uni-app'
import{getResetPassword} from '/pages/api/api.js'
// ========== 响应式数据 ==========
// 新密码
const newPassword = ref('');
// 确认密码
const confirmPassword = ref('');
const placeholderStyle = ref('font-size:24rpx')
// 定义接收上一页传来的参数
const phone = ref('');
const smsCode = ref('');
// ========== 计算属性 ==========
// 是否可点击确定按钮(密码格式正确 + 两次输入一致)
const canConfirm = computed(() => {
// 密码格式规则6-16位任意字符可根据需求调整
const pwdReg = /^(?=.*[a-zA-Z])(?=.*\d).{6,16}$/;
// 校验条件:新密码符合格式 + 确认密码和新密码一致 + 均不为空
return pwdReg.test(newPassword.value)
&& newPassword.value === confirmPassword.value
&& newPassword.value && confirmPassword.value;
});
// ========== 方法 ==========
// 返回上一页
const goBack = () => {
uni.navigateBack();
};
onShow(() => {
// 从缓存读取参数
const params = uni.getStorageSync('tempChangePwdParams') || {};
// 赋值
phone.value = params.phoneNumber
smsCode.value = params.verifyCode
// 用完删除缓存(避免下次跳转读取旧值)
uni.removeStorageSync('tempChangePwdParams');
});
// 检查表单状态(实时更新按钮状态)
const checkForm = () => {};
// 处理确定按钮点击(修改密码逻辑)
const handleConfirm = () => {
if (!canConfirm.value) return
let params = {
phone:phone.value,
smsCode:smsCode.value,
newPassword:newPassword.value,
confirmPassword:confirmPassword.value,
scene:'reset'
}
getResetPassword(params).then(res =>{
if(res.code === 200){
//uni.setStorageSync('token', res.token); // 假设后端返回token字段
uni.showToast({ title: '修改成功', icon: 'success' });
uni.navigateTo({ url: '/pages/login/pwd-login' });
}else{
uni.showToast({ title: "修改失败", icon: 'error' });
}
})
// 模拟修改密码请求
// uni.showLoading({
// title: '修改中...'
// });
// setTimeout(() => {
// uni.hideLoading();
// uni.showToast({
// title: '密码修改成功',
// icon: 'success'
// });
// // 密码修改成功后返回上一页(或跳转到登录页)
// uni.navigateTo({
// url:"/pages/login/pwd-login"
// });
// }, 1500);
};
</script>
<style scoped>
/* 页面容器 */
.reset-pwd-page {
padding: 20rpx 40rpx 40rpx;
min-height: 100vh;
background: repeating-linear-gradient(180deg, #E2F0FF, #F6FAFF );
}
.status_bar{
width: 100%;
height: 46px;
}
/* 顶部导航栏 */
.nav-bar {
display: flex;
align-items: center;
height: 88rpx;
margin-bottom: 60rpx;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 36rpx;
font-weight: 500;
color: #333;
}
/* 输入框通用样式 */
.input-item {
background-color: #fff;
border-radius: 40rpx;
padding: 0 30rpx;
height: 88rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
display: flex;
align-items: center;
}
/* 输入框内容 */
.input-content {
flex: 1;
font-size: 32rpx;
color: #333;
height: 100%;
}
/* 确定按钮 */
.confirm-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #007aff;
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
margin-top: 20rpx;
}
/* 确定按钮禁用状态 */
.confirm-btn:disabled {
background-color: #99c8ff;
color: #fff;
}
/* 禁用按钮边框 */
button:after{
border-radius: 50px;
}
</style>

View File

@@ -1,223 +0,0 @@
<template>
<view class="forget-page">
<view class="status_bar"></view>
<view class="nav-bar">
<uni-icons type="left" size="28" color="#333" @click="goBack"></uni-icons>
<view class="nav-title">忘记密码</view>
</view>
<!-- 手机号输入框 -->
<view class="input-item">
<view class="input-icon">
<image src="/static/登录/手机号.png" mode="" style="height: 100%;width: 100%;"></image>
</view>
<uni-easyinput class="input-content" type="number" v-model="phoneNumber" trim="all"
placeholder="请输入手机号" :inputBorder="false" maxlength="11" :placeholderStyle="placeholderStyle">
</uni-easyinput>
</view>
<!-- 验证码输入 + 获取验证码按钮 -->
<view class="input-item verify-item">
<view class="input-icon">
<image src="/static/登录/验证码.png" mode="" style="height: 100%;width: 100%;"></image>
<!-- <uni-icons type="locked" size="24" color="#999"></uni-icons> -->
</view>
<uni-easyinput class="input-content verify-input" type="number" v-model="verifyCode" trim="all"
placeholder="请输入验证码" :inputBorder="false" maxlength="6" :placeholderStyle="placeholderStyle">
</uni-easyinput>
<button
class="get-verify-btn"
:disabled="!canGetVerify || countDown > 0"
@click="getVerifyCode"
>
{{ countDown > 0 ? `${countDown}s后重新获取` : '获取验证码' }}
</button>
</view>
<!-- 下一步 -->
<button
class="forget-btn"
:disabled="!canRegister"
@click="handleNext"
>
下一步
</button>
</view>
</template>
<script setup>
import { ref, computed, onUnmounted } from 'vue';
import {getSendCode} from '/pages/api/api.js'
// ====响应式数据======
const phoneNumber = ref('')
const verifyCode = ref('')
const countDown = ref(0)
let timer = null;
const placeholderStyle = ref('font-size:24rpx')
// ========== 计算属性 ==========
// 是否可获取验证码(手机号格式正确)
const canGetVerify = computed(() => {
const phoneReg = /^1[3-9]\d{9}$/;
return phoneReg.test(phoneNumber.value);
});
// 获取验证码
const getVerifyCode = () => {
if (!canGetVerify.value) return;
let params = {
phone:phoneNumber.value,
scene:'reset'
}
getSendCode(params).then(res =>{
if(res.code === 200){
uni.showToast({
title: '验证码已发送',
icon: 'success'
});
}else{
uni.showToast({
title: '验证码发送失败',
icon: 'error'
});
}
})
// 开始60秒倒计时
countDown.value = 60;
timer = setInterval(() => {
countDown.value--;
if (countDown.value <= 0) {
clearInterval(timer);
timer = null;
}
}, 1000);
};
// 是否下一步
const canRegister = computed(() => {
const phoneReg = /^1[3-9]\d{9}$/; // 手机号11位
const verifyReg = /^\d{6}$/; // 验证码6位
return phoneReg.test(phoneNumber.value)
&& verifyReg.test(verifyCode.value)
});
const handleNext = () =>{
// 把参数存到本地缓存
uni.setStorageSync('tempChangePwdParams', {
phoneNumber:phoneNumber.value,
verifyCode: verifyCode.value
});
uni.navigateTo({
url: '/pages/loginSub/change-pwd'
});
}
// 返回上一页
const goBack = () => {
uni.navigateBack();
};
</script>
<style scoped>
.forget-page{
padding: 20rpx 40rpx 40rpx;
min-height: 100vh;
background: repeating-linear-gradient(180deg, #E2F0FF, #F6FAFF );
}
.status_bar{
width: 100%;
height: 46px;
}
/* 顶部导航栏 */
.nav-bar {
display: flex;
align-items: center;
/* height: 88rpx; */
margin-bottom: 70rpx;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 36rpx;
font-weight: 500;
color: #333;
}
/* 输入框通用样式 */
.input-item {
display: flex;
align-items: center;
background-color: #fff;
border-radius: 40rpx;
padding: 0 30rpx;
height: 88rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
/* 验证码输入项特殊布局 */
.verify-item {
position: relative;
padding-right: 220rpx;
}
/* 输入框图标 */
.input-icon {
margin-right: 20rpx;
color: #999;
width: 50rpx;
height: 50rpx;
}
/* 输入框内容 */
.input-content {
flex: 1;
font-size: 32rpx;
color: #333;
height: 100%;
}
.verify-input {
padding-right: 20rpx;
}
/* 获取验证码按钮 */
.get-verify-btn {
position: absolute;
right: 10rpx;
top: 50%;
transform: translateY(-50%);
width: 200rpx;
height: 70rpx;
line-height: 70rpx;
background-color: #007aff;
color: #fff;
border-radius: 35rpx;
font-size: 28rpx;
padding: 0;
margin: 0;
}
.get-verify-btn:disabled {
background-color: #ccc;
color: #fff;
}
/* 下一步按钮 */
.forget-btn{
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #007aff;
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
margin-bottom: 40rpx;
}
.forget-btn:disabled{
background-color: #99c8ff;
color: #fff;
}
/* 禁用按钮边框 */
button:after{
border-radius: 50px;
}
</style>

View File

@@ -1,331 +0,0 @@
<template>
<view class="login-page">
<view class="status_bar"></view>
<!-- 登录标题 -->
<view class="login-title">手机验证码登录</view>
<!-- 手机号输入框 -->
<view class="input-item">
<view class="input-icon">
<!-- <uni-icons type="phone" size="24" color="#999"></uni-icons> -->
<image src="/static/登录/手机号.png" mode="" style="height: 100%;width: 100%;"></image>
</view>
<uni-easyinput class="input-content" type="number" v-model="phoneNumber"
placeholder="请输入手机号" :inputBorder="false" maxlength="11" :placeholderStyle="placeholderStyle">
</uni-easyinput>
<!-- <input
class="input-content"
type="number"
placeholder="请输入手机号"
v-model="phoneNumber"
maxlength="11"
@input="checkForm"
/> -->
</view>
<!-- 验证码输入框 + 获取验证码按钮 -->
<view class="input-item verify-item">
<view class="input-icon">
<!-- <uni-icons type="locked" size="24" color="#999"></uni-icons> -->
<image src="/static/登录/验证码.png" mode="" style="height: 100%;width: 100%;"></image>
</view>
<uni-easyinput class="input-content verify-input" type="number" v-model="verifyCode"
placeholder="请输入验证码" :inputBorder="false" maxlength="6" :placeholderStyle="placeholderStyle">
</uni-easyinput>
<!-- <input
class="input-content verify-input"
type="number"
placeholder="请输入验证码"
v-model="verifyCode"
maxlength="6"
@input="checkForm"
/> -->
<button
class="get-verify-btn"
:disabled="!canGetVerify || countDown > 0"
@click="getVerifyCode"
>
{{ countDown > 0 ? `${countDown}s后重新获取` : '获取验证码' }}
</button>
</view>
<!-- 协议勾选 -->
<view class="agreement-item" @click="toggleAgreement">
<uni-icons
type="checkbox-filled"
size="24"
color="#007aff"
v-if="agreeAgreement"
></uni-icons>
<uni-icons
type="checkbox"
size="24"
color="#999"
v-else
></uni-icons>
<text class="agreement-text">我已阅读并同意用户隐私政策</text>
</view>
<!-- 登录按钮 -->
<button
class="login-btn"
:disabled="!canLogin"
@click="handleLogin"
>
登录
</button>
<!-- 底部链接 -->
<view class="bottom-link">
<navigator class="link-item" url="/pages/loginSub/pwd-login">账号密码登录</navigator>
<navigator class="link-item" url="/pages/loginSub/register-new-user">注册新用户</navigator>
</view>
</view>
</template>
<script setup>
import { ref, computed, onUnmounted } from 'vue';
import {getLoginBySms,getSendCode} from '/pages/api/api.js'
// ========== 响应式数据 ==========
// 手机号
const phoneNumber = ref('');
// 验证码
const verifyCode = ref('');
const placeholderStyle = ref('font-size:24rpx')
// 是否同意协议
const agreeAgreement = ref(true); // 默认勾选
// 倒计时(秒)
const countDown = ref(0);
// 定时器
let timer = null;
// ========== 计算属性 ==========
// 是否可以获取验证码(手机号格式正确)
const canGetVerify = computed(() => {
// 手机号正则11位数字以1开头
const phoneReg = /^1[3-9]\d{9}$/;
return phoneReg.test(phoneNumber.value);
});
// 是否可以登录(手机号+验证码+协议都满足)
const canLogin = computed(() => {
const phoneReg = /^1[3-9]\d{9}$/;
const verifyReg = /^\d{6}$/;
return phoneReg.test(phoneNumber.value) && verifyReg.test(verifyCode.value) && agreeAgreement.value;
});
// ========== 方法 ==========
// 检查表单状态
const checkForm = () => {
// 无需额外逻辑,计算属性已实时监听
};
// 切换协议勾选状态
const toggleAgreement = () => {
agreeAgreement.value = !agreeAgreement.value;
};
// 获取验证码
const getVerifyCode = () => {
if (!canGetVerify.value) return;
let params = {
phone:phoneNumber.value,
scene:'login'
}
getSendCode(params).then(res =>{
if(res.code === 200){
uni.showToast({
title: '验证码已发送',
icon: 'success'
});
}else{
uni.showToast({
title: '验证码发送失败',
icon: 'error'
});
}
})
// 开始60秒倒计时
countDown.value = 60;
timer = setInterval(() => {
countDown.value--;
if (countDown.value <= 0) {
clearInterval(timer);
timer = null;
}
}, 1000);
};
// 登录处理
const handleLogin = () => {
if (!canLogin.value) return;
let params = {
phone:phoneNumber.value,
smsCode:'123456',
scene:'login',
}
// getLoginBySms(params).then(res =>{
// if(res.code === 200){
// //uni.setStorageSync('token', res.token); // 假设后端返回token字段
// uni.showToast({ title: '登录成功', icon: 'success' });
// }else{
// uni.showToast({ title:'登录失败', icon: 'error' });
// }
// })
uni.switchTab({ url: '/pages/index/index' });
// 模拟登录请求
// uni.showLoading({
// title: '登录中...'
// });
};
// ========== 生命周期 ==========
// 页面卸载时清除定时器
onUnmounted(() => {
if (timer) {
clearInterval(timer);
}
});
</script>
<style scoped>
/* 页面容器 */
.login-page {
padding: 80rpx 40rpx 40rpx;
/* background-color: #f8f9fa; */
min-height: 100vh;
background: repeating-linear-gradient(to bottom, #E2F0FF, #F6FAFF );
}
.status_bar {
height: 160px;
width: 100%;
}
/* 登录标题 */
.login-title {
font-size: 48rpx;
font-weight: 600;
color: #333;
text-align: center;
margin-bottom: 80rpx;
}
/* 输入框项 */
.input-item {
display: flex;
align-items: center;
background-color: #fff;
border-radius: 40rpx;
padding: 0 30rpx;
height: 88rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
/* 验证码输入项(特殊布局) */
.verify-item {
position: relative;
padding-right: 220rpx;
}
/* 输入框图标 */
.input-icon {
width: 50rpx;
height: 50rpx;
margin-right: 20rpx;
color: #999;
}
/* 输入框内容 */
.input-content {
flex: 1;
font-size: 32rpx;
color: #333;
height: 100%;
}
/* 验证码输入框 */
.verify-input {
padding-right: 20rpx;
}
/* 获取验证码按钮 */
.get-verify-btn {
position: absolute;
right: 10rpx;
top: 50%;
transform: translateY(-50%);
width: 200rpx;
height: 70rpx;
line-height: 70rpx;
background-color: #007aff;
color: #fff;
border-radius: 35rpx;
font-size: 28rpx;
padding: 0;
margin: 0;
}
/* 获取验证码按钮禁用状态 */
.get-verify-btn:disabled {
background-color: #ccc;
color: #fff;
border-radius: 50px;
}
/* 协议勾选项 */
.agreement-item {
display: flex;
align-items: center;
margin: 40rpx 0;
padding-left: 10rpx;
}
/* 协议文本 */
.agreement-text {
font-size: 28rpx;
color: #666;
margin-left: 10rpx;
}
/* 登录按钮 */
.login-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #007aff;
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
margin-bottom: 60rpx;
}
/* 登录按钮禁用状态 */
.login-btn:disabled {
background-color: #99c8ff;
color: #fff;
/* border-radius: 50px; */
}
/* 禁用按钮边框 */
button:after{
border-radius: 50px;
}
/* 底部链接 */
.bottom-link {
display: flex;
justify-content: space-between;
padding: 0 20rpx;
}
/* 链接项 */
.link-item {
font-size: 28rpx;
color: #007aff;
text-decoration: none;
}
</style>

View File

@@ -1,234 +0,0 @@
<template>
<view class="login-page">
<view class="status_bar"></view>
<!-- 标题 -->
<view class="login-title">账号密码登录</view>
<!-- 手机号输入框 -->
<view class="input-item">
<view class="input-icon">
<!-- <uni-icons type="phone" size="24" color="#999"></uni-icons> -->
<image src="/static/登录/手机号.png" mode="" style="height: 100%;width: 100%;"></image>
</view>
<uni-easyinput class="input-content" type="text" v-model="phoneNumber"
placeholder="请输入帐号" :inputBorder="false" :placeholderStyle="placeholderStyle">
</uni-easyinput>
</view>
<!-- 密码输入框 -->
<view class="input-item">
<view class="input-icon">
<!-- <uni-icons type="locked" size="24" color="#999"></uni-icons> -->
<image src="/static/登录/密码.png" mode="" style="height: 100%;width: 100%;"></image>
</view>
<uni-easyinput class="input-content" type="password" v-model="password"
placeholder="请输入密码" :inputBorder="false" :placeholderStyle="placeholderStyle">
</uni-easyinput>
</view>
<!-- 协议勾选 + 忘记密码 -->
<view class="agreement-wrap">
<view class="agreement-item" @click="toggleAgreement">
<uni-icons
type="checkbox-filled"
size="24"
color="#007aff"
v-if="agreeAgreement"
></uni-icons>
<uni-icons
type="checkbox"
size="24"
color="#999"
v-else
></uni-icons>
<text class="agreement-text">我已阅读并同意用户隐私政策</text>
</view>
<navigator class="forget-pwd" url="/pages/loginSub/forget-pwd">忘记密码</navigator>
</view>
<!-- 登录按钮 -->
<button
class="login-btn"
@click="handleLogin"
>
登录
</button>
<!-- 底部链接 -->
<view class="bottom-link">
<navigator class="link-item" url="/pages/loginSub/login">验证码登录</navigator>
<navigator class="link-item" url="/pages/loginSub/register-new-user">注册新用户</navigator>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue';
import {onLoad} from "@dcloudio/uni-app"
import {getLoginPassword} from '/pages/api/api.js'
// 响应式数据
const phoneNumber = ref('');
const password = ref('');
const agreeAgreement = ref(true); // 默认勾选
const placeholderStyle = ref('font-size:24rpx')
// 计算属性:是否可登录(帐号+密码+协议都满足)
const canLogin = computed(() => {
const phoneReg = /^[a-zA-Z0-9_-]{4,16}$/; // 4-16位
const pwdReg = /^.{6,16}$/; // 密码6-16位
return phoneReg.test(phoneNumber.value) && pwdReg.test(password.value) && agreeAgreement.value;
});
// 切换协议勾选
const toggleAgreement = () => {
agreeAgreement.value = !agreeAgreement.value;
};
// 检查表单
const checkForm = () => {};
onLoad(() =>{
});
// 登录逻辑
const handleLogin = () => {
if (!canLogin.value) return
let params = {
username:phoneNumber.value,
password:password.value
}
// if (!phoneNumber.value) {
// uni.showToast({ title: '请输入手机号', icon: 'none' });
// // canLogin.value = true;
// return;
// }
// if (!password.value) {
// uni.showToast({ title: '请输入密码', icon: 'none' });
// // canLogin.value = true;
// return;
// }
// getLoginPassword(params).then(res =>{
// // console.log('33333',res)
// if(res.code === 200){
// // console.log('33333',res)
// //uni.setStorageSync('token', res.token); // 假设后端返回token字段
// uni.showToast({ title: '登录成功', icon: 'success' });
// uni.switchTab({ url: '/pages/index/index' });
// // uni.reLaunch({ url: '/pages/index/index' });
// }else{
// uni.showToast({ title: `${res.msg},请先注册账号`, icon: 'error' });
// }
// })
uni.showLoading({
title: '登录中...'
});
setTimeout(() => {
uni.switchTab({ url: '/pages/index/index' });
}, 1500);
};
</script>
<style scoped>
.login-page {
padding: 80rpx 40rpx 40rpx;
/* background-color: #f8f9fa; */
min-height: 100vh;
background: repeating-linear-gradient(180deg, #E2F0FF, #F6FAFF );
}
.status_bar {
height: 160px;
width: 100%;
}
.login-title {
font-size: 48rpx;
font-weight: 600;
color: #333;
text-align: center;
margin-bottom: 80rpx;
}
.input-item {
display: flex;
align-items: center;
background-color: #fff;
border-radius: 40rpx;
padding: 0 30rpx;
height: 88rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.input-icon {
margin-right: 20rpx;
width: 50rpx;
height: 50rpx;
}
.input-content {
flex: 1;
font-size: 28rpx;
color: #333;
height: 100%;
}
.agreement-wrap {
display: flex;
justify-content: space-between;
align-items: center;
margin: 40rpx 0;
padding: 0 10rpx;
}
.agreement-item {
display: flex;
align-items: center;
}
.agreement-text {
font-size: 28rpx;
color: #666;
margin-left: 10rpx;
}
.forget-pwd {
font-size: 28rpx;
color: #007aff;
text-decoration: none;
}
.login-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #007aff;
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
margin-bottom: 60rpx;
}
.login-btn:disabled {
background-color: #99c8ff;
color: #fff;
border-radius: 50px;
}
.bottom-link {
display: flex;
justify-content: space-between;
padding: 0 20rpx;
}
.link-item {
font-size: 28rpx;
color: #007aff;
text-decoration: none;
}
</style>

View File

@@ -1,367 +0,0 @@
<template>
<view class="register-page">
<view class="status_bar"></view>
<!-- 顶部导航 -->
<view class="nav-bar">
<uni-icons type="left" size="28" color="#333" @click="goBack"></uni-icons>
<view class="nav-title">注册</view>
</view>
<!-- 注册标题 -->
<view class="register-title">注册用户</view>
<!-- 手机号输入框 -->
<view class="input-item">
<view class="input-icon">
<image src="/static/登录/手机号.png" mode="" style="height: 100%;width: 100%;"></image>
</view>
<uni-easyinput class="input-content" type="number" v-model="phoneNumber"
placeholder="请输入手机号" :inputBorder="false" maxlength="11" :placeholderStyle="placeholderStyle">
</uni-easyinput>
</view>
<!-- 验证码输入 + 获取验证码按钮 -->
<view class="input-item verify-item">
<view class="input-icon">
<image src="/static/登录/验证码.png" mode="" style="height: 100%;width: 100%;"></image>
<!-- <uni-icons type="locked" size="24" color="#999"></uni-icons> -->
</view>
<uni-easyinput class="input-content verify-input" type="number" v-model="verifyCode"
placeholder="请输入验证码" :inputBorder="false" maxlength="6" :placeholderStyle="placeholderStyle">
</uni-easyinput>
<button
class="get-verify-btn"
:disabled="!canGetVerify || countDown > 0"
@click="getVerifyCode"
>
{{ countDown > 0 ? `${countDown}s后重新获取` : '获取验证码' }}
</button>
</view>
<!-- 账号输入框 -->
<view class="input-item">
<view class="input-icon">
<image src="/static/登录/账号.png" mode="" style="height: 100%;width: 100%;"></image>
<!-- <uni-icons type="person" size="24" color="#999"></uni-icons> -->
</view>
<uni-easyinput class="input-content" type="text" v-model="account"
placeholder="请输入账号" :inputBorder="false" :placeholderStyle="placeholderStyle">
</uni-easyinput>
</view>
<!-- 密码输入框 -->
<view class="input-item">
<view class="input-icon">
<image src="/static/登录/密码.png" mode="" style="height: 100%;width: 100%;"></image>
<!-- <uni-icons type="locked" size="24" color="#999"></uni-icons> -->
</view>
<uni-easyinput class="input-content" type="password" v-model="password"
placeholder="请输入密码" :inputBorder="false" :placeholderStyle="placeholderStyle" >
</uni-easyinput>
</view>
<!-- 协议勾选 -->
<view class="agreement-item" @click="toggleAgreement">
<uni-icons
type="checkbox-filled"
size="24"
color="#007aff"
v-if="agreeAgreement"
></uni-icons>
<uni-icons
type="checkbox"
size="24"
color="#999"
v-else
></uni-icons>
<text class="agreement-text">我已阅读并同意用户隐私政策</text>
</view>
<!-- 立即注册按钮 -->
<button
class="register-btn"
:disabled="!canRegister"
@click="handleRegister"
>
立即注册
</button>
<!-- 跳转登录链接 -->
<view class="login-link">
<navigator class="link-item" url="/pages/loginSub/login">已有账号去登录</navigator>
</view>
</view>
</template>
<script setup>
import { ref, computed, onUnmounted } from 'vue';
import {getRegisterUser,getSendCode} from '/pages/api/api.js'
// ========== 响应式数据 ==========
// 表单数据
const phoneNumber = ref('');
const verifyCode = ref('');
const account = ref('');
const password = ref('');
const placeholderStyle = ref('font-size:24rpx')
// 协议勾选(默认勾选,和截图一致)
const agreeAgreement = ref(true);
// 验证码倒计时
const countDown = ref(0);
let timer = null;
// ========== 计算属性 ==========
// 是否可获取验证码(手机号格式正确)
const canGetVerify = computed(() => {
const phoneReg = /^1[3-9]\d{9}$/;
return phoneReg.test(phoneNumber.value);
});
// 是否可注册(所有表单项+协议都满足)
const canRegister = computed(() => {
const phoneReg = /^1[3-9]\d{9}$/; // 手机号11位
const verifyReg = /^\d{6}$/; // 验证码6位
const accountReg = /^.{4,20}$/; // 账号4-20位
const pwdReg = /^.{6,16}$/; // 密码6-16位
return phoneReg.test(phoneNumber.value)
&& verifyReg.test(verifyCode.value)
&& accountReg.test(account.value)
&& pwdReg.test(password.value)
&& agreeAgreement.value;
});
// ========== 方法 ==========
// 返回上一页
const goBack = () => {
uni.navigateBack();
};
// 切换协议勾选状态
const toggleAgreement = () => {
agreeAgreement.value = !agreeAgreement.value;
};
// 检查表单状态(实时更新按钮状态)
const checkForm = () => {};
// 获取验证码
const getVerifyCode = () => {
if (!canGetVerify.value) return;
let params = {
phone:phoneNumber.value,
scene:'register'
}
getSendCode(params).then(res =>{
if(res.code === 200){
uni.showToast({
title: '验证码已发送',
icon: 'success'
});
}else{
uni.showToast({
title: '验证码发送失败',
icon: 'error'
});
}
})
// 开始60秒倒计时
countDown.value = 60;
timer = setInterval(() => {
countDown.value--;
if (countDown.value <= 0) {
clearInterval(timer);
timer = null;
}
}, 1000);
};
// 处理注册逻辑
const handleRegister = () => {
if (!canRegister.value) return;
let params = {
phone:phoneNumber.value,
smsCode:verifyCode.value,
username:account.value,
password:password.value
}
getRegisterUser(params).then(res =>{
if(res.code === 200){
uni.hideLoading();
uni.showToast({
title: '注册成功',
icon: 'success'
});
}else{
uni.showToast({
title: '注册失败,请重新注册',
icon: 'error'
});
}
})
// 注册成功后跳转到登录页
uni.redirectTo({
url: '/pages/login/pwd-login'
});
// setTimeout(() => {
// }, 1500);
};
// ========== 生命周期 ==========
// 页面卸载时清除定时器
onUnmounted(() => {
if (timer) {
clearInterval(timer);
}
});
</script>
<style scoped>
/* 页面容器 */
.register-page {
padding: 20rpx 40rpx 40rpx;
/* background-color: #f8f9fa; */
min-height: 100vh;
background: repeating-linear-gradient(180deg, #E2F0FF, #F6FAFF );
}
.status_bar {
height: 46px;
width: 100%;
}
/* 顶部导航栏 */
.nav-bar {
display: flex;
align-items: center;
/* height: 88rpx; */
margin-bottom: 70rpx;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 36rpx;
font-weight: 500;
color: #333;
}
/* 注册标题 */
.register-title {
font-size: 48rpx;
font-weight: 600;
color: #333;
margin-bottom: 60rpx;
}
/* 输入框通用样式 */
.input-item {
display: flex;
align-items: center;
background-color: #fff;
border-radius: 40rpx;
padding: 0 30rpx;
height: 88rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
/* 验证码输入项特殊布局 */
.verify-item {
position: relative;
padding-right: 220rpx;
}
/* 输入框图标 */
.input-icon {
margin-right: 20rpx;
color: #999;
width: 50rpx;
height: 50rpx;
}
/* 输入框内容 */
.input-content {
flex: 1;
font-size: 32rpx;
color: #333;
height: 100%;
}
.verify-input {
padding-right: 20rpx;
}
/* 获取验证码按钮 */
.get-verify-btn {
position: absolute;
right: 10rpx;
top: 50%;
transform: translateY(-50%);
width: 200rpx;
height: 70rpx;
line-height: 70rpx;
background-color: #007aff;
color: #fff;
border-radius: 35rpx;
font-size: 28rpx;
padding: 0;
margin: 0;
}
.get-verify-btn:disabled {
background-color: #ccc;
color: #fff;
}
/* 协议勾选项 */
.agreement-item {
display: flex;
align-items: center;
margin: 40rpx 0;
padding-left: 10rpx;
}
.agreement-text {
font-size: 28rpx;
color: #666;
margin-left: 10rpx;
}
/* 立即注册按钮 */
.register-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #007aff;
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
margin-bottom: 40rpx;
}
button:after{
border-radius: 50px;
}
.register-btn:disabled {
background-color: #99c8ff;
color: #fff;
}
/* 跳转登录链接 */
.login-link {
text-align: center;
}
.link-item {
font-size: 28rpx;
color: #007aff;
text-decoration: none;
}
</style>

View File

@@ -14,7 +14,7 @@
<swiper-item>
<view class="guide-item">
<view class="guide-img">
<image class="" src="/static/引导1.png" style="width: 100%;height: 100%;"></image>
<image src="http://47.104.199.163:9090/images/guide1.png" style="width: 100%;height: 100%;"></image>
</view>
<view style="height: 200rpx;width: 100%;"></view>
<view class="guide-title">便捷服务</view>
@@ -26,7 +26,7 @@
<swiper-item>
<view class="guide-item">
<view class="guide-img">
<image src="/static/引导2.png" style="width: 100%;height: 100%;"></image>
<image src="http://47.104.199.163:9090/images/guide2.png" style="width: 100%;height: 100%;"></image>
</view>
<view style="height: 200rpx;width: 100%;"></view>
<view class="guide-title">高效管理</view>
@@ -38,7 +38,7 @@
<swiper-item>
<view class="guide-item">
<view class="guide-img">
<image src="/static/引导3.png" style="width: 100%;height: 100%;"></image>
<image src="http://47.104.199.163:9090/images/guide3.png" style="width: 100%;height: 100%;"></image>
</view>
<view style="height: 200rpx;width: 100%;"></view>
<view class="guide-title">安全保障</view>
@@ -92,7 +92,7 @@ const handleBtnClick = () => {
uni.setStorageSync('hasShowGuide', true);
// 2. 跳转首页
uni.navigateTo({
url:"/pages/loginSub/pwd-login"
url:"/pageSubPack/loginSub/pwd-login"
})
// uni.switchTab({
// url: '/pages/index/index'

View File

@@ -1,383 +0,0 @@
<template>
<!-- 通知列表 -->
<scroll-view
class="notice-list"
scroll-y
:refresher-enabled="true"
:refresher-triggered="refreshing"
:style="{ height: `calc(100vh - ${statusBarHeight + navBarHeight}px)` }"
>
<view class="status_bar"></view>
<!-- 顶部标题栏 -->
<view class="top-bar">
<uni-nav-bar left-icon="left" title="通知公告" backgroundColor ="none" :border="false" @clickLeft="narBack" />
</view>
<!-- 下拉刷新 -->
<view class="refresh-container" v-if="refreshing">
<uni-icons type="spinner-cycle" size="20" color="#999" class="loading"></uni-icons>
<text class="refresh-text">刷新中...</text>
</view>
<!-- 通知列表项 -->
<view
v-for="(notice, index) in noticeList"
:key="notice.id"
class="notice-item"
@click="goToDetail(notice)"
>
<!-- 通知标题 -->
<view class="notice-header">
<view class="notice-title">
{{notice.title}}
</view>
<view class="unread-dot" v-if="notice.hasRead"></view>
</view>
<!-- 红点 -->
<!-- 发布者和时间 -->
<view class="notice-meta">
<view class="notice-author">
<text class="author-name">{{notice.author}}</text>
<text class="separator">·</text>
<text class="publish-time">{{notice.publishTime}}</text>
</view>
</view>
<!-- 通知摘要 -->
<view class="notice-summary">
{{ notice.summary }}
</view>
<!-- 详情 -->
<view class="notice-actions" @click.stop="goToDetail(notice)">
<!-- <view class="read-status" :class="{ unread: !notice.hasRead }">
{{ notice.hasRead ? '已读' : '未读' }}
</view> -->
<view class="" >
<text style="color: #2F77FD;font-size:14px">详情 </text>
</view>
<!-- <button class="detail-btn" @click.stop="goToDetail(notice)"> -->
<!-- <text>详情 </text> -->
<uni-icons type="right" size="13" color="#007AFF"></uni-icons>
<!-- </button> -->
</view>
</view>
<!-- 加载更多 -->
<view v-if="loadingMore" class="load-more">
<uni-icons type="spinner-cycle" size="18" color="#999" class="loading"></uni-icons>
<text>加载中...</text>
</view>
<!-- 没有更多数据 -->
<view v-if="noMoreData && noticeList.length > 0" class="no-more">
没有更多通知了
</view>
<!-- 空状态 -->
<view v-if="noticeList.length === 0 && !loading" class="empty-state">
<image src="/static/empty-notice.png" mode="aspectFit" class="empty-image"></image>
<text class="empty-text">暂无通知</text>
<button class="btn-refresh" @click="loadNotices">刷新试试</button>
</view>
</scroll-view>
</template>
<script setup>
import { ref } from 'vue';
const noticeList = ref([
{
id:'1',
title:'我那我是咖啡牛奶附件说的都是vv的v给v梵蒂冈v辅导报告发布',
author:'张三',
publishTime:'2026.3.5',
summary:'为保障社区居民生命财产安全,维护社区和谐稳定,根据上级部门要求,结合我社区实际情况,现就进一步加强社区安全管理工作通知如下',
hasRead:true
},
{
id:'2',
title:'今天停水了',
author:'张三',
publishTime:'2026.3.5',
summary:'为保障社区居民生命财产安全,维护社区和谐稳定,根据上级部门要求,结合我社区实际情况,现就进一步加强社区安全管理工作通知如下',
hasRead:true
}
])
const refreshing = ref(false)
const loadingMore = ref(false)
const noMoreData = ref(false)
const pageSize = 10
const statusBarHeight = ref(0)
const navBarHeight = 44
// --方法----
const goToDetail = () =>{
uni.navigateTo({
url:'/pages/notice-list/noticeListDetails'
})
}
// 返回
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"
})
}
uni.removeStorageSync('sourcePage')
}
</script>
<style lang="scss">
/* 通知列表容器 */
.notice-list {
padding: 20px;
box-sizing: border-box;
background-color: #f9f9f9;
}
.status_bar {
height: 32px;
width: 100%;
}
/* 顶部标题 */
.top-bar {
text-align: center;
margin-bottom: 20rpx;
}
/* 下拉刷新 */
.refresh-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px 0;
color: #999;
font-size: 14px;
.loading {
animation: rotate 1s linear infinite;
margin-bottom: 8px;
}
.refresh-text {
font-size: 12px;
}
}
/* 通知项样式 */
.notice-item {
background-color: #fff;
border-radius: 8px;
padding: 16px;
margin-bottom: 12px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
position: relative;
&:active {
background-color: #f9f9f9;
}
/* 重要通知标记 */
&.important::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background-color: #ff6b6b;
border-radius: 2px 0 0 2px;
}
}
.notice-header{
display: flex;
justify-content: space-between;
align-items: center;
}
/* 通知标题 */
.notice-title {
font-size: 16px;
font-weight: bold;
color: #333;
line-height: 1.4;
margin-bottom: 12px;
// display: -webkit-box;
// -webkit-box-orient: vertical;
// -webkit-line-clamp: 2;
width: 290px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// 未读红点
.unread-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #ff3b30;
}
/* 元信息 */
.notice-meta {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
color: #666;
font-size: 12px;
.notice-author {
display: flex;
align-items: center;
.author-name {
color: #666;
}
.separator {
margin: 0 6px;
color: #ccc;
}
.publish-time {
color: #999;
}
}
.notice-time {
display: flex;
align-items: center;
.date-text {
margin-left: 4px;
color: #999;
}
}
}
/* 通知摘要 */
.notice-summary {
font-size: 14px;
color: #666;
background-color: #f9f9f9;
line-height: 1.5;
margin-bottom: 16px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
text-overflow: ellipsis;
}
/* 操作区域 */
.notice-actions {
display: flex;
align-items: center;
justify-content: space-between;
.read-status {
font-size: 12px;
padding: 2px 8px;
border-radius: 10px;
background-color: #f0f0f0;
color: #999;
&.unread {
background-color: #e6f7ff;
color: #1890ff;
}
}
.detail-btn {
display: flex;
align-items: flex-end;
background: transparent;
border: none;
color: #007AFF;
font-size: 14px;
padding: 0;
margin-right: 10rpx;
&::after {
border: none;
}
text {
margin-right: 4px;
}
}
}
/* 分割线 */
// .divider {
// height: 1px;
// background-color: #f0f0f0;
// margin-top: 16px;
// }
/* 加载更多 */
.load-more {
display: flex;
align-items: center;
justify-content: center;
padding: 20px 0;
color: #999;
font-size: 14px;
.loading {
animation: rotate 1s linear infinite;
margin-right: 8px;
}
}
.no-more {
text-align: center;
padding: 20px 0;
color: #999;
font-size: 14px;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
.empty-image {
width: 200px;
height: 150px;
margin-bottom: 20px;
}
.empty-text {
font-size: 16px;
color: #999;
margin-bottom: 20px;
}
.btn-refresh {
background-color: #007AFF;
color: #fff;
padding: 8px 20px;
border-radius: 20px;
font-size: 14px;
&::after {
border: none;
}
}
}
</style>

View File

@@ -1,495 +0,0 @@
<template>
<view class="container">
<!-- 内容区域 -->
<!-- <scroll-view class="content-scroll" scroll-y> -->
<!-- 文章标题 -->
<view class="article-header">
<view class="article-title">
以实干担当不断开创中国特色大国外交新局面
</view>
</view>
<!-- 作者和日期 -->
<view class="notice-meta">
<view class="notice-author">
<text class="author-name">张三</text>
<text class="separator">·</text>
<text class="publish-time">2026.3.6</text>
</view>
</view>
<!-- 文章内容 -->
<view class="article-content">
<view class="paragraph">
学习贯彻习近平新时代中国特色社会主义思想主题教育开展以来外交外事战线认真学习贯彻习近平总书记重要讲话精神和党中央部署要求
全面准确把握目标要求紧密联系实际精心组织周密部署紧密结合外交中心工作开展主题教育
坚持学思用贯通知信行统一把习近平新时代中国特色社会主义思想转化为坚定理想锤炼党性和指导实践
推动工作的强大力量以实干担当不断开创中国特色大国外交新局面
</view>
<view class="articleimg">
<image src="/static/首页/notice-1.png" mode="" style="width: 100%;height: 100%;border-radius: 20rpx;"></image>
<image src="/static/首页/notice-2.png" mode="" style="width: 100%;height: 100%;border-radius: 20rpx;"></image>
</view>
</view>
<!-- 附件区域 -->
<!-- <view class="attachments-section">
<view class="section-title">
<view class="title-text">相关附件</view>
<view class="title-line"></view>
</view>
<view class="attachments-list"> -->
<!-- DOC文件 -->
<!-- <view class="attachment-item" @click="downloadFile('filename.DOC')">
<view class="attachment-left">
<view class="file-icon doc-icon">📄</view>
<view class="file-info">
<view class="file-name">文件名.DOC</view>
<view class="file-size">299kb</view>
</view>
</view>
<view class="download-btn">
<view class="download-icon"></view>
</view>
</view> -->
<!-- PDF文件 -->
<!-- <view class="attachment-item" @click="downloadFile('filename.PDF')">
<view class="attachment-left">
<view class="file-icon pdf-icon">📄</view>
<view class="file-info">
<view class="file-name">文件名.PDF</view>
<view class="file-size">299kb</view>
</view>
</view>
<view class="download-btn">
<view class="download-icon"></view>
</view>
</view>
</view>
</view> -->
<!-- </scroll-view> -->
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
// import { onLoad, onShow } from '@dcloudio/uni-app'
const isLoading = ref(false)
// 下载文件
const downloadFile = (fileName) => {
uni.showModal({
title: '下载确认',
content: `确定要下载 "${fileName}" 吗?`,
success: (res) => {
if (res.confirm) {
isLoading.value = true
// 显示下载进度
uni.showLoading({
title: '下载中...',
mask: true
})
// 模拟下载过程
setTimeout(() => {
uni.hideLoading()
isLoading.value = false
// 根据文件类型打开文件
if (fileName.endsWith('.PDF')) {
uni.showToast({
title: 'PDF文件已保存到下载目录',
icon: 'success',
duration: 2000
})
// 在真实项目中这里应该调用下载API
// uni.downloadFile({
// url: 'https://example.com/files/filename.PDF',
// success: (res) => {
// if (res.statusCode === 200) {
// uni.saveFile({
// tempFilePath: res.tempFilePath,
// success: (saveRes) => {
// uni.showToast({ title: '下载成功', icon: 'success' })
// }
// })
// }
// }
// })
} else if (fileName.endsWith('.DOC')) {
uni.showToast({
title: 'DOC文件已保存到下载目录',
icon: 'success',
duration: 2000
})
}
}, 1500)
}
}
})
}
</script>
<style lang="scss" scoped>
.container {
min-height: 100vh;
background-color: #ffffff;
padding: 20px;
// font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
/* 内容滚动区域 */
.content-scroll {
padding-top: 15px;
box-sizing: border-box;
background-color: #ffffff;
}
/* 文章头部 */
.article-header {
// padding: 24px 20px 20px;
// border-bottom: 1px solid #f0f0f0;
.article-title {
font-size: 32rpx;
font-weight: bold;
color: #333333;
line-height: 1.4;
margin-bottom: 16px;
text-align: center;
}
.article-meta {
display: flex;
align-items: center;
justify-content: center;
color: #666666;
font-size: 14px;
.meta-item {
display: flex;
align-items: center;
.meta-icon {
font-size: 14px;
margin-right: 4px;
}
.meta-text {
font-size: 14px;
}
}
.meta-divider {
margin: 0 12px;
color: #cccccc;
}
}
}
/* 元信息 */
.notice-meta {
display: flex;
align-items: center;
justify-content: center;
// margin-bottom: 12px;
color: #666;
font-size: 16px;
margin: 0 30rpx;
.notice-author {
display: flex;
align-items: center;
.author-name {
color: #666;
}
.separator {
margin: 0 6px;
color: #ccc;
}
.publish-time {
color: #999;
}
}
.notice-time {
display: flex;
align-items: center;
.date-text {
margin-left: 4px;
color: #999;
}
}
}
/* 文章内容 */
.article-content {
padding: 20px;
color: #333333;
line-height: 1.8;
font-size: 16px;
.articleimg{
width: 100%;
height: 249rpx;
}
.paragraph {
margin-bottom: 20px;
text-align: justify;
text-indent: 2em;
}
.bullet-list {
margin: 20px 0 20px 2em;
.bullet-item {
display: flex;
margin-bottom: 12px;
.bullet-dot {
color: #666666;
margin-right: 8px;
flex-shrink: 0;
}
.bullet-text {
flex: 1;
color: #333333;
}
}
}
}
/* 附件区域 */
.attachments-section {
padding: 20px;
background-color: #f9f9f9;
border-top: 1px solid #f0f0f0;
border-bottom: 1px solid #f0f0f0;
margin-top: 20px;
.section-title {
display: flex;
align-items: center;
margin-bottom: 20px;
.title-text {
font-size: 18px;
font-weight: 600;
color: #333333;
white-space: nowrap;
margin-right: 12px;
}
.title-line {
flex: 1;
height: 1px;
background-color: #e0e0e0;
}
}
.attachments-list {
.attachment-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
background-color: #ffffff;
border-radius: 8px;
margin-bottom: 12px;
border: 1px solid #e8e8e8;
&:active {
background-color: #f5f5f5;
}
&:last-child {
margin-bottom: 0;
}
.attachment-left {
display: flex;
align-items: center;
flex: 1;
.file-icon {
font-size: 24px;
margin-right: 12px;
&.doc-icon {
color: #2b579a; /* Word文档蓝色 */
}
&.pdf-icon {
color: #e74c3c; /* PDF红色 */
}
}
.file-info {
.file-name {
font-size: 16px;
color: #333333;
margin-bottom: 4px;
font-weight: 500;
}
.file-size {
font-size: 13px;
color: #666666;
}
}
}
.download-btn {
width: 40px;
height: 40px;
border-radius: 20px;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
.download-icon {
font-size: 18px;
color: #333333;
}
}
}
}
}
/* 页面底部 */
.page-footer {
padding: 30px 20px 40px;
display: flex;
flex-direction: column;
align-items: center;
.footer-text {
font-size: 16px;
color: #999999;
margin-bottom: 20px;
}
.footer-info {
display: flex;
align-items: center;
font-size: 13px;
color: #999999;
.info-item {
padding: 0 8px;
}
.info-divider {
color: #dddddd;
}
}
}
/* 响应式调整 */
@media (min-width: 768px) {
.container {
max-width: 768px;
margin: 0 auto;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
.article-header,
.article-content,
.attachments-section,
.page-footer {
padding-left: 40px;
padding-right: 40px;
}
}
/* 暗色模式适配 */
@media (prefers-color-scheme: dark) {
.container {
background-color: #1a1a1a;
}
.custom-navbar {
background-color: #1a1a1a;
border-bottom-color: #333333;
.nav-back,
.nav-title {
color: #ffffff;
}
.nav-actions .action-icon {
color: #ffffff;
}
}
.content-scroll {
background-color: #1a1a1a;
}
.article-header {
border-bottom-color: #333333;
.article-title {
color: #ffffff;
}
.article-meta {
color: #999999;
}
}
.article-content {
color: #e0e0e0;
.bullet-item .bullet-text {
color: #e0e0e0;
}
}
.attachments-section {
background-color: #2a2a2a;
border-top-color: #333333;
border-bottom-color: #333333;
.section-title .title-text {
color: #ffffff;
}
.attachment-item {
background-color: #2a2a2a;
border-color: #333333;
&:active {
background-color: #333333;
}
.file-info .file-name {
color: #ffffff;
}
.file-info .file-size {
color: #999999;
}
}
}
.page-footer {
.footer-text,
.footer-info {
color: #666666;
}
}
}
</style>

View File

@@ -1,247 +0,0 @@
<template>
<!-- 页面主容器 -->
<view class="door-open-page">
<view class="status_bar"></view>
<!-- 顶部标题栏 -->
<view class="top-bar">
<uni-nav-bar left-icon="left" title="远程开门" backgroundColor ="none" :border="false" @clickLeft="narBack" />
</view>
<view class="top-img">
<image src="/static/一键开门/远程开门.png" mode="" style="width: 100%;height: 100%;"></image>
</view>
<!-- 提示文案 -->
<view class="tips-text">
远程开门
<text class="tips-desc">请选择要开的门并保持距离在10米以内单次仅开一门</text>
</view>
<!-- 开门成功提示默认隐藏 -->
<view v-if="showSuccess" class="success-toast">开门成功</view>
<!-- 开门授权区域 -->
<view class="auth-container">
<view class="auth-title">开门授权</view>
<!-- 网格布局2行2列 -->
<view class="grid-container">
<!-- 每个门选项 -->
<view
v-for="(item, index) in doorList"
:key="index"
:class="['door-item', { active: selectedDoor === index }]"
@click="selectDoor(index)"
>
<!-- 图标占位可替换为真实图标/图片 -->
<view class="door-icon">
<image class="icon-text" :src="selectedDoor === index ? item.iconActive:item.iconText"></image>
</view>
<!-- 门名称 -->
<text class="door-name">{{ item.name }}</text>
</view>
</view>
</view>
<!-- 确认开门按钮 -->
<button class="confirm-btn" @click="openDoor" :disabled="selectedDoor === -1">确认开门</button>
</view>
</template>
<script setup>
import { ref } from 'vue'
// 响应式数据:选中的门索引、开门成功提示
const selectedDoor = ref(-1) // -1表示未选中
const showSuccess = ref(false)
// 门列表数据(和设计图一致)
const doorList = ref([
{ iconText: '/static/一键开门/西入门.png',iconActive:"/static/一键开门/西入门-白.png", name: '西入门闸机' },
{ iconText: '/static/一键开门/东入门.png',iconActive:"/static/一键开门/东入门-白.png", name: '东入门闸机' },
{ iconText: '/static/一键开门/单元门.png',iconActive:"/static/一键开门/单元门-白.png", name: '1号楼1单元门' },
{ iconText: '/static/一键开门/单元门.png',iconActive:"/static/一键开门/单元门-白.png", name: '1号楼2单元门' }
])
// 选择门的方法
const selectDoor = (index) => {
selectedDoor.value = index
showSuccess.value = false // 选择新门时隐藏成功提示
}
// 返回
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"
})
}
uni.removeStorageSync('sourcePage')
}
// 确认开门方法
const openDoor = () => {
// 校验:未选择门时提示
// if (selectedDoor.value === -1) {
// uni.showToast({
// title: '请先选择要开的门',
// icon: 'none'
// })
// return
// }
// 模拟开门接口请求(实际项目替换为真实接口)
setTimeout(() => {
showSuccess.value = true
// 可选:添加成功提示后自动隐藏
// setTimeout(() => showSuccess.value = false, 2000)
}, 500)
}
</script>
<style scoped>
/* 页面全局样式 */
.door-open-page {
background: linear-gradient(180deg, #E2F0FF, #F6FAFF);
min-height: 100vh;
padding: 30rpx 40rpx;
box-sizing: border-box;
}
.status_bar {
height: 32px;
width: 100%;
}
/* 顶部标题 */
.top-bar {
text-align: center;
margin-bottom: 20rpx;
}
.title-text {
font-size: 48rpx;
color: #ffffff;
font-weight: bold;
}
.top-img{
width: 460rpx;
height: 290rpx;
float: right;
}
/* 提示文案 */
.tips-text {
color: #000;
margin-bottom: 30rpx;
font-size: 68rpx;
line-height: 1.5;
font-weight: 700;
font-style: italic;
clear: both;
position: relative;
top: -86px;
left: 0;
}
.tips-desc {
display: block;
font-size: 24rpx;
opacity: 0.9;
color: #666666;
}
/* 开门成功提示 */
.success-toast {
background: rgba(0, 0, 0, 0.7);
color: #ffffff;
padding: 10rpx 20rpx;
border-radius: 30rpx;
font-size: 24rpx;
position: absolute;
top: 200rpx;
left: 50%;
transform: translateX(-50%);
z-index: 99;
}
/* 开门授权容器 */
.auth-container {
background: #ffffff;
border-radius: 20rpx;
padding: 30rpx 20rpx;
margin-bottom: 60rpx;
position: relative;
top: -86px;
left: 0;
}
.auth-title {
text-align: center;
font-size: 32rpx;
color: #333333;
padding-bottom: 20rpx;
border-bottom: 1px solid #e5e5e5;
margin-bottom: 30rpx;
}
/* 网格布局 */
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30rpx;
}
/* 门选项样式 */
.door-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 30rpx 20rpx;
border-radius: 10rpx;
border: 2px solid #2F77FD;
background-color: #F6F9FE;
transition: all 0.3s;
}
/* 选中状态 */
.door-item.active {
/* border-color: #2c6bc7; */
background-color: #2F77FD;
color: #ffffff;
}
/* 门图标 */
.door-icon {
width: 80rpx;
height: 80rpx;
/* background-color: #f5f5f5; */
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 15rpx;
}
.icon-text {
/* font-size: 40rpx; */
width: 100%;
height: 100%;
}
/* 门名称 */
.door-name {
font-size: 26rpx;
color: #333333;
}
/* 确认按钮 */
.confirm-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #2F77FD;
color: #fff;
font-size: 32rpx;
border-radius: 20rpx;
border: none;
}
.confirm-btn::after {
border: none; /* 去除uni-app默认按钮边框 */
}
</style>

View File

@@ -1,564 +0,0 @@
<template>
<view class="container">
<!-- 搜索区域 -->
<view class="search-container">
<view class="search-input-wrapper">
<uni-icons class="search-icon" type="search" size="20" color="#999"></uni-icons>
<input
class="search-input"
placeholder="请输入关键字进行搜索"
placeholder-class="placeholder"
v-model="searchText"
@input="handleSearchInput"
/>
<view v-if="searchText" class="clear-icon" @tap="clearSearch">
<uni-icons type="clear" size="18" color="#999"></uni-icons>
</view>
</view>
<button class="search-btn" @tap="handleSearch">搜索</button>
</view>
<!-- 小区列表 -->
<scroll-view
class="community-list"
scroll-y
:scroll-into-view="scrollIntoViewId"
@scrolltolower="loadMoreData"
:lower-threshold="100"
>
<view
v-for="item in displayedCommunities"
:key="item.id"
class="community-item"
:id="'item-' + item.id"
@tap="selectCommunity(item)"
>
<view class="community-item-left">
<image src="/static/首页/小区.png" style="width: 60rpx;height: 60rpx;"></image>
<!-- <uni-icons class="house-icon" type="home" size="24" color="#666"></uni-icons> -->
<view class="community-info">
<text class="community-name">{{ item.name }}</text>
<!-- <text class="community-address">{{ item.address }}</text> -->
<!-- <text class="community-id">ID: {{ item.id }}</text> -->
</view>
</view>
<view v-if="selectedItem && selectedItem.id === item.id" class="selected-icon">
<uni-icons type="checkbox-filled" size="24" color="#007AFF"></uni-icons>
</view>
</view>
<!-- 加载更多 -->
<view v-if="hasMoreData" class="load-more-container">
<view v-if="!isLoading" class="load-more-btn" @tap="loadMoreData">
<text>加载更多</text>
<uni-icons type="arrow-down" size="16" color="#007AFF"></uni-icons>
</view>
<view v-else class="loading-container">
<uni-icons type="spinner-cycle" size="20" color="#007AFF" class="loading-icon"></uni-icons>
<text>正在加载...</text>
</view>
<!-- <text class="page-info"> {{ currentPage }} / {{ totalPages }} </text> -->
</view>
<!-- 没有更多数据 -->
<view v-if="!hasMoreData && displayedCommunities.length > 0" class="no-more-data">
<text>没有更多数据了</text>
</view>
<!-- 空状态 -->
<view v-if="displayedCommunities.length === 0 && !isSearching" class="empty-state">
<uni-icons type="info" size="60" color="#ccc"></uni-icons>
<text class="empty-text">暂无小区数据</text>
</view>
<!-- 搜索无结果 -->
<view v-if="displayedCommunities.length === 0 && isSearching" class="empty-state">
<uni-icons type="search" size="60" color="#ccc"></uni-icons>
<text class="empty-text">未找到相关小区</text>
<text class="empty-tip">请尝试其他关键词</text>
</view>
</scroll-view>
<!-- 确认按钮 -->
<view class="confirm-area">
<button
class="confirm-btn"
:disabled="!selectedItem"
:class="{ 'disabled-btn': !selectedItem }"
@tap="handleConfirm"
>
确认选择
</button>
<view v-if="selectedItem" class="selected-info">
已选: {{ selectedItem.name }}
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import {onLoad} from '@dcloudio/uni-app'
import {getVillageList} from "/pages/api/api.js"
// 响应式数据
const searchText = ref('')
const selectedItem = ref(null)
const scrollIntoViewId = ref('')
// 分页相关
const currentPage = ref(1)
const pageSize = 5
const totalCommunities = ref([])
const displayedCommunities = ref([])
const hasMoreData = ref(true)
const isLoading = ref(false)
// 搜索状态
const isSearching = ref(false)
let searchTimeout = null
// 所有小区数据
const allCommunities = ref([
{ id: 1, name: '北境碧桂园小区', address: '北境市光明区碧桂路1号' },
{ id: 2, name: '南湖花园小区', address: '南湖市滨湖区湖滨路28号' },
{ id: 3, name: '东城国际社区', address: '东城市新城区人民路156号' },
{ id: 4, name: '西湖雅苑', address: '西湖市风景区梅灵路88号' },
{ id: 5, name: '北境锦绣家园', address: '北境市朝阳区朝阳路56号' },
{ id: 6, name: '阳光100小区', address: '阳光市高新区创业路100号' },
{ id: 7, name: '绿城玫瑰园', address: '绿城市西湖区学院路188号' },
{ id: 8, name: '金地自在城', address: '金城市江干区金沙大道200号' },
{ id: 9, name: '万科城市花园', address: '万城市余杭区文一西路299号' },
{ id: 10, name: '保利中央公园', address: '保利市拱墅区莫干山路500号' },
{ id: 11, name: '龙湖天街小区', address: '龙湖市滨江区江南大道100号' },
])
// 计算属性
const totalPages = computed(() => {
return Math.ceil(totalCommunities.value.length / pageSize)
})
// 方法
// 查询小区列表
const getCommunityList = () =>{
let params = {
keyword:searchText.value
}
getVillageList(params).then(res =>{
console.log('1111',res)
if(res.code === 200){
allCommunities.value = res.data
uni.showToast({
title:"查询成功",
icon:'success'
})
}else{
allCommunities.value = []
uni.showToast({
title:"查询失败",
icon:'error'
})
}
})
}
onLoad(() =>{
// getCommunityList()
})
// 加载分页数据
const loadPageData = (pageNum) => {
isLoading.value = true
// 模拟网络请求延迟
setTimeout(() => {
const startIndex = (pageNum - 1) * pageSize
const endIndex = startIndex + pageSize
const pageData = totalCommunities.value.slice(startIndex, endIndex)
if (pageNum === 1) {
displayedCommunities.value = pageData
} else {
displayedCommunities.value = [...displayedCommunities.value, ...pageData]
}
currentPage.value = pageNum
hasMoreData.value = displayedCommunities.value.length < totalCommunities.value.length
isLoading.value = false
// 滚动到新加载的内容
if (pageNum > 1 && pageData.length > 0) {
setTimeout(() => {
scrollIntoViewId.value = 'item-' + pageData[0].id
}, 100)
}
}, 500)
}
// 加载更多数据
const loadMoreData = () => {
if (isLoading.value || !hasMoreData.value) return
loadPageData(currentPage.value + 1)
}
// 处理搜索输入
const handleSearchInput = () => {
// 防抖处理
if (searchTimeout) {
clearTimeout(searchTimeout)
}
searchTimeout = setTimeout(() => {
handleSearch()
}, 300)
}
// 处理搜索
const handleSearch = () => {
const keyword = searchText.value.trim().toLowerCase()
if (!keyword) {
isSearching.value = false
totalCommunities.value = [...allCommunities.value]
loadPageData(1)
return
}
isSearching.value = true
totalCommunities.value = allCommunities.value.filter(community =>
community.name.toLowerCase().includes(keyword) ||
community.address.toLowerCase().includes(keyword)
)
loadPageData(1)
}
// 清空搜索
const clearSearch = () => {
searchText.value = ''
isSearching.value = false
totalCommunities.value = [...allCommunities.value]
loadPageData(1)
}
// 选择小区
const selectCommunity = (item) => {
selectedItem.value = item
}
// 确认选择
const handleConfirm = () => {
if (!selectedItem.value) {
uni.showToast({
title: '请先选择小区',
icon: 'none',
duration: 2000
})
return
}
uni.showModal({
title: '确认选择',
content: `您已选择: ${selectedItem.value.name}\n地址: ${selectedItem.value.address}`,
success: (res) => {
if (res.confirm) {
uni.showToast({
title: '选择成功',
icon: 'success',
duration: 1500
})
// 在实际应用中,这里可以触发回调或页面跳转
// uni.navigateBack() 或 emit('confirm', selectedItem.value)
uni.$emit('confirm', selectedItem.value)
// 模拟2秒后返回上一页
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
}
})
}
// 监听搜索文本变化
watch(searchText, (newVal) => {
if (!newVal.trim()) {
clearSearch()
}
})
// 初始化
onMounted(() => {
// 初始化数据
totalCommunities.value = [...allCommunities.value]
loadPageData(1)
// 默认选中第一项
if (displayedCommunities.value.length > 0) {
selectedItem.value = displayedCommunities.value[0]
}
})
</script>
<style scoped>
/* 页面容器 */
.container {
min-height: 100vh;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
padding: 40rpx;
}
/* 搜索区域 */
.search-container {
padding: 20rpx 30rpx;
background-color: #ffffff;
display: flex;
align-items: center;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
z-index: 10;
position: sticky;
top: 0;
}
.search-input-wrapper {
flex: 1;
background-color: #f8f8f8;
border-radius: 10rpx;
padding: 0 20rpx;
display: flex;
align-items: center;
height: 80rpx;
margin-right: 20rpx;
}
.search-icon {
margin-right: 15rpx;
}
.search-input {
flex: 1;
height: 100%;
font-size: 28rpx;
color: #333;
}
.placeholder {
color: #999;
font-size: 28rpx;
}
.clear-icon {
padding: 10rpx;
display: flex;
align-items: center;
justify-content: center;
}
.search-btn {
background-color: #007AFF;
color: #fff;
font-size: 28rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 10rpx;
padding: 0 30rpx;
margin: 0;
white-space: nowrap;
}
/* 小区列表 */
.community-list {
flex: 1;
/* padding: 0 30rpx; */
margin-top: 20rpx;
margin-bottom: 180rpx;
}
.community-item {
background-color: #fff;
border-radius: 12rpx;
padding: 30rpx;
margin-bottom: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
transition: all 0.2s;
}
.community-item:active {
background-color: #f9f9f9;
transform: translateY(2rpx);
}
.community-item-left {
display: flex;
align-items: center;
flex: 1;
}
.house-icon {
margin-right: 20rpx;
}
.community-info {
display: flex;
flex-direction: column;
flex: 1;
}
.community-name {
font-size: 32rpx;
color: #333;
font-weight: 500;
margin-bottom: 8rpx;
}
.community-address {
font-size: 24rpx;
color: #999;
margin-bottom: 5rpx;
}
.community-id {
font-size: 22rpx;
color: #ccc;
}
.selected-icon {
margin-left: 20rpx;
}
/* 加载更多容器 */
.load-more-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 0;
color: #999;
}
.load-more-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 40rpx;
background-color: #f8f8f8;
border-radius: 50rpx;
color: #007AFF;
font-size: 28rpx;
margin-bottom: 20rpx;
}
.load-more-btn text {
margin-right: 10rpx;
}
.loading-container {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 40rpx;
background-color: #f8f8f8;
border-radius: 50rpx;
color: #007AFF;
font-size: 28rpx;
margin-bottom: 20rpx;
}
.loading-icon {
margin-right: 10rpx;
animation: rotate 1s linear infinite;
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.page-info {
font-size: 24rpx;
color: #999;
}
/* 没有更多数据 */
.no-more-data {
text-align: center;
padding: 40rpx 0;
color: #999;
font-size: 28rpx;
}
/* 空状态 */
.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;
}
.empty-tip {
font-size: 24rpx;
color: #ccc;
}
/* 确认区域 */
.confirm-area {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx 30rpx 40rpx;
background-color: #fff;
box-shadow: 0 -2rpx 20rpx rgba(0, 0, 0, 0.05);
}
.confirm-btn {
background-color: #007AFF;
color: #fff;
height: 100rpx;
line-height: 100rpx;
border-radius: 12rpx;
font-size: 34rpx;
width: 100%;
margin: 0;
}
.disabled-btn {
background-color: #ccc;
color: #fff;
}
.selected-info {
text-align: center;
font-size: 24rpx;
color: #666;
margin-top: 20rpx;
padding: 10rpx;
background-color: #f8f8f8;
border-radius: 8rpx;
}
/* 适配不同屏幕 */
@media (min-width: 768px) {
.container {
max-width: 500px;
margin: 0 auto;
}
}
</style>

View File

@@ -1,400 +0,0 @@
<template>
<view class="repair-page">
<view class="status_bar"></view>
<!-- 顶部导航栏 -->
<view class="nav-bar">
<view class="nav-left" @tap="goBack">
<image src="/static/首页/arr-left.png" style="width: 48rpx; height: 48rpx;"></image>
</view>
<view class="nav-title">在线报修</view>
<view class="nav-right"></view>
</view>
<!-- 报修类型Tab -->
<view class="repair-type-tabs">
<view
class="type-tab"
:class="{ active: activeType === 'public' }"
@click="publicClick"
style="margin-right: 20rpx;"
>
<image src="/static/在线报修/公共报修.png" style="width: 100%;height: 100%;"></image>
<!-- <uni-icons type="staff" size="24" color="#fff"></uni-icons> -->
<!-- <text class="type-text">公共报修</text> -->
</view>
<view
class="type-tab"
:class="{ active: activeType === 'personal' }"
@click="personalClick"
>
<image src="/static/在线报修/个人报修.png" style="width: 100%;height: 100%;"></image>
<!-- <uni-icons type="person" size="24" color="#fff"></uni-icons> -->
<!-- <text class="type-text">个人报修</text> -->
</view>
</view>
<view style="margin: 14px 0px;">
<text class="repair-title">报修记录</text>
</view>
<!-- 报修记录状态Tab -->
<view class="record-status-tabs">
<text
class="status-tab"
:class="{ active: activeStatus === 'pending' }"
@click="activeStatus = 'pending'" >
待派单
</text>
<text
class="status-tab"
:class="{ active: activeStatus === 'assigned' }"
@click="activeStatus = 'assigned'"
>
已派单
</text>
<text
class="status-tab"
:class="{ active: activeStatus === 'completed' }"
@click="activeStatus = 'completed'"
>
已完成
</text>
</view>
<!-- 报修列表 -->
<view class="repair-list">
<view class="repair-item" v-for="(item, index) in repairList" :key="index">
<!-- 标题和状态 -->
<view class="item-header">
<text class="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>
<!-- 内容 -->
<view class="item-content">
{{ item.content }}
</view>
<!-- 图片占位 -->
<view class="item-imgs">
<view class="img-placeholder" v-for="i in 3" :key="i">
<!-- <uni-icons type="image" size="20" color="#ccc"></uni-icons> -->
<image src="/static/logo.png" style="width: 100%;height: 100%;"></image>
</view>
</view>
<!-- 时间和详情 -->
<view class="item-footer">
<text class="item-time">{{ item.time }}</text>
<text class="detail-btn" @click="goDetail(item)">报修详情</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue';
// 报修类型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 imgList = ref([
// {img:'/static/shouye'},
// ])
// 原始数据
const mockData = [
{
status: statusText,
statusClass: statusClass,
content: '1栋楼下道路路灯不亮并且有的亮度不够请物业公司及时更换防止发生意外事故。',
time: '2023-08-01 10:05:46'
},
{
status: statusText,
statusClass: statusClass,
content: '西门的门禁不好用了,草坪有好多的草,话题也应高擦了',
time: '2025-08-01 10:05:46'
}
];
return mockData;
});
function publicClick(){
activeType.value = 'public'
uni.navigateTo({
url:"/pages/online-repair/publicRepair"
})
};
function personalClick(){
activeType.value = 'personal'
uni.navigateTo({
url:"/pages/online-repair/personalRepair"
})
}
// 返回上一页
const goBack = () => {
if(uni.getStorageSync('sourcePage') === "index"){
uni.switchTab({
url:"/pages/index/index"
})
}else if(uni.getStorageSync('sourcePage') === "serviceIndex"){
uni.switchTab({
url:"/pages/serviceIndex/serviceIndex"
})
}
uni.removeStorageSync('sourcePage')
}
// 跳转报修详情
const goDetail = (item) => {
// const itemStr = encodeURIComponent(JSON.stringify(item))
uni.navigateTo({
url: `/pages/online-repair/repairDetail?item = ${encodeURIComponent(JSON.stringify(item))}`
});
};
</script>
<style scoped>
/* 页面整体样式 */
.repair-page {
background: linear-gradient(to bottom left, #E2F0FF 0%, #f9f9f9 50%);
min-height: 100vh;
padding: 0 40rpx;
}
.status_bar {
height: 32px;
width: 100%;
}
/* 顶部导航栏 */
.nav-bar {
height: 90rpx;
display: flex;
align-items: center;
/* padding: 0 30rpx; */
color: #fff;
position: sticky;
top: 44px;
z-index: 100;
margin-bottom: 20px;
/* box-shadow: 0 4rpx 12rpx rgba(0, 122, 255, 0.2); */
}
.nav-left {
width: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 20rpx;
color: #000;
}
.nav-right {
width: 60rpx;
}
/* 报修类型Tab */
.repair-type-tabs {
display: flex;
/* background-color: #fff; */
/* padding: 10px; */
}
.type-tab {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100px;
border-radius: 8px;
color: #fff;
font-size: 14px;
}
.type-tab.active {
background: linear-gradient(135deg, #1E88E5 0%, #64B5F6 100%);
}
.type-tab:not(.active) {
background: linear-gradient(135deg, #4DB6AC 0%, #80CBC4 100%);
}
.type-text {
margin-top: 5px;
}
.repair-title{
color: #222222;
font-size: 16px;
font-weight: 600;
}
/* 报修状态Tab */
.record-status-tabs {
display: flex;
/* background-color: #fff; */
/* margin-top: 10px; */
/* padding: 0 15px; */
}
.status-tab {
flex: 1;
text-align: left;
height: 40px;
line-height: 40px;
font-size: 15px;
color: #666;
position: relative;
}
.status-tab.active {
color: #2F77FD;
font-weight: bold;
}
.status-tab.active::after {
content: '';
position: absolute;
bottom: 0;
left: 20%;
transform: translateX(-50%);
width: 45px;
height: 4px;
border-radius: 4rpx;
background-color: #1E88E5;
}
/* 报修列表 */
.repair-list {
padding: 28rpx 0rpx;
}
.repair-item {
background-color: #fff;
border-radius: 8px;
padding: 15px;
margin-bottom: 10px;
}
/* 列表项头部 */
.item-header {
display: flex;
justify-content: flex-start;
align-items: center;
margin-bottom: 10px;
}
.item-title {
font-size: 16px;
font-weight: bold;
color: #333;
padding-right: 20rpx;
}
.item-status {
font-size: 14px;
}
.item-status.pending {
/* 待派单-红色 */
color: #ffffff;
background-color: #E34D59;
border-radius: 20rpx;
font-size: 12px;
line-height: 22px;
padding: 0px 10px;
}
.item-status.assigned {
/* 已派单-绿色 */
color: #ffffff;
background-color: #09C2BD;
border-radius: 20rpx;
font-size: 12px;
line-height: 22px;
padding: 0px 10px;
}
.item-status.completed{
color: #ffffff;
background-color: #2F77FD;
border-radius: 20rpx;
font-size: 12px;
line-height: 22px;
padding: 0px 10px;
}
/* 标签 */
.item-tags {
/* display: flex; */
/* gap: 8px; */
margin-bottom: 10px;
}
.tag {
/* background-color: #e3f2fd; */
color: #999999;
font-size: 12px;
padding: 2px 8px;
border-radius: 4px;
border: 1px solid #999999;
margin-right: 8px;
}
/* 内容 */
.item-content {
font-size: 14px;
color: #666;
line-height: 1.5;
margin-bottom: 10px;
/* background-color: #d7e4f5; */
}
/* 图片占位 */
.item-imgs {
display: flex;
/* gap: 12px; */
margin-bottom: 10px;
}
.img-placeholder {
width: 80px;
height: 80px;
background-color: #f5f5f5;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 8px;
}
/* 底部时间和详情 */
.item-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28rpx;
}
.item-time {
color: #999;
}
.detail-btn {
color: #1E88E5;
}
</style>

View File

@@ -1,639 +0,0 @@
<template>
<view class="form-container">
<!-- 报修房屋 -->
<view class="form-item">
<text class="label">报修房屋</text>
<view class="value" @click="goToSelectHouse">
{{ houseName || '请选择房屋' }}
<text class="switch" v-if="houseName">切换</text>
<uni-icons type="arrowright" size="16"></uni-icons>
</view>
</view>
<!-- 维修项目 -->
<view class="form-item">
<text class="label">维修项目</text>
<view class="value" @click="goToSelectProject">
{{ projectName || '请选择维修项目' }}
<uni-icons type="arrowright" size="16"></uni-icons>
</view>
</view>
<!-- 标题 -->
<view class="form-item">
<text class="label">标题</text>
<uni-easyinput v-model="form.title" placeholder="请输入内容" :inputBorder="false"></uni-easyinput>
</view>
<!-- 问题描述 -->
<view class="form-item-wenti">
<text class="label">问题描述</text>
<uni-easyinput v-model="form.desc" placeholder="请描述你的问题" type="textarea"></uni-easyinput>
</view>
<!-- 手机号 -->
<view class="form-item">
<text class="label">手机号</text>
<uni-easyinput type="number" v-model="form.phone" placeholder="请输入手机号" :inputBorder="false"></uni-easyinput>
</view>
<!-- 预约时间 -->
<view class="form-item time-item" @click="openTimePopup">
<text class="label">预约时间</text>
<text class="value time-value">{{ showTimeText }}</text>
</view>
<!-- <view class="form-item">
<text class="label">预约时间</text>
<view class="value" @click="">
<uni-datetime-picker type="datetime" v-model="form.datetime" @change="changeTime" />
</view>
</view> -->
<!-- 问题照片 -->
<view class="form-item-wenti">
<text class="label">问题照片</text>
<view class="upload-wrap">
<!-- 已上传图片 -->
<view class="upload-item" v-for="(img, index) in form.images" :key="index">
<image :src="img" mode="aspectFill" @click="previewImage(img)"></image>
<uni-icons type="close" size="16" @click="deleteImage(index)"></uni-icons>
</view>
<!-- 上传按钮 -->
<view class="upload-btn" @click="chooseImage">
<uni-icons type="plus" size="24"></uni-icons>
<text>上传照片</text>
</view>
</view>
</view>
<!-- 提交按钮 -->
<button class="submit-btn" @click="submitForm">提交</button>
<!-- 时间选择弹窗 -->
<uni-popup ref="timePopupRef" type="bottom" :mask-click="false" border-radius="16rpx">
<view class="time-popup-container">
<!-- 弹窗头部取消 + 标题 + 确认 -->
<view class="time-popup-header">
<text class="cancel-btn" @click="closeTimePopup">取消</text>
<text class="popup-title">预约时间({{ tempTime.format('YYYY-MM-DD HH:mm') }})</text>
<text class="confirm-btn" @click="confirmTime">确认</text>
</view>
<!-- 时间选择列日期 | 小时 | 分钟 -->
<view class="time-selector">
<!-- 日期列 -->
<scroll-view
class="time-column"
scroll-y
:scroll-top="dateScrollTop"
@scroll="onDateScroll"
scroll-with-animation
>
<view
class="time-item"
:class="{ active: dateItem.value === selectedDate }"
v-for="(dateItem, index) in dateList"
:key="index"
@click="selectDate(index)"
>
{{ dateItem.value }}{{dateItem.label}}
</view>
</scroll-view>
<!-- 小时列 -->
<scroll-view
class="time-column"
scroll-y
:scroll-top="hourScrollTop"
@scroll="onHourScroll"
scroll-with-animation
>
<view
class="time-item"
:class="{ active: hourItem.time === selectedHour }"
v-for="(hourItem, index) in hourList"
:key="index"
@click="selectHour(index)"
>
{{ hourItem.time }}
</view>
</scroll-view>
<!-- 分钟列 -->
<scroll-view
class="time-column"
scroll-y
:scroll-top="minuteScrollTop"
@scroll="onMinuteScroll"
scroll-with-animation
>
<view
class="time-item"
:class="{ active: minuteItem.minute === selectedMinute }"
v-for="(minuteItem, index) in minuteList"
:key="index"
@click="selectMinute(index)"
>
{{ minuteItem.minute }}
</view>
</scroll-view>
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import { ref,computed,onMounted} from 'vue'
import {
onLoad, onUnload
} from '@dcloudio/uni-app'
import moment from 'moment/moment'
// 选中的房屋/项目名称
const houseName = ref('')
const projectName = ref('')
// 表单数据
const form = ref({
title: '',
desc: '',
phone: '',
datetime: '2023-08-01 15:30', // 默认时间(匹配截图)
images: []
})
// ========== 基础数据 ==========
const timePopupRef = ref(null)
// 初始时间()
const selectedTime = ref(moment().hour(9).minute(30).second(0));
// 选择的时间(弹窗中预览)
const tempTime = ref(moment().hour(9).minute(30).second(0));
// ========== 时间选择列数据 ==========
// 日期列表(今天、明天、后天)
const dateList = ref([
{ label: '(今天)', value: moment().format('YYYY-MM-DD') },
{ label: '(明天)', value: moment().add(1, 'day').format('YYYY-MM-DD') },
{ label: '(后天)', value: moment().add(2, 'day').format('YYYY-MM-DD') }
]);
// 小时列表()
const hourList = ref([
{time: moment().hours(8).format('HH')},
{time:moment().hours(9).format('HH')},
{time:moment().hours(10).format('HH')},
{time:moment().hours(11).format('HH')},
{time:moment().hours(13).format('HH')},
{time:moment().hours(14).format('HH')},
{time:moment().hours(15).format('HH')},
{time:moment().hours(16).format('HH')},
{time:moment().hours(17).format('HH')},
{time:moment().hours(18).format('HH')},
]);
// 分钟列表()
const minuteList = ref([
{minute:moment().minute(0).format('mm')},
{minute:moment().minute(30).format('mm')}
]);
// ========== 选中状态 ==========
const selectedDate = ref(dateList.value[0].value);
const selectedHour = ref(hourList.value[1].time); // 默认选中8点
const selectedMinute = ref(minuteList.value[1].minute); // 默认选中00分
// ========== 滚动配置 ==========
const itemHeight = 80; // 每个时间项的高度rpx
const dateScrollTop = ref(0 * itemHeight); // 初始选中明天
const hourScrollTop = ref(1 * itemHeight); // 初始选中9点
const minuteScrollTop = ref(1 * itemHeight); // 初始选中00分
// ========== 显示文本 ==========
const showTimeText = computed(() => {
return selectedTime.value.format('YYYY-MM-DD HH:mm');
});
// ========== Popup 操作 ==========
// 打开时间弹窗
const openTimePopup = () => {
// 打开前同步临时时间为当前选中时间
tempTime.value = moment(selectedTime.value);
selectedDate.value = tempTime.value.format('YYYY-MM-DD');
selectedHour.value = tempTime.value.format('HH');
selectedMinute.value = tempTime.value.format('mm');
// 同步滚动位置
syncScrollPosition();
// 打开uni-popup
if(timePopupRef.value){
timePopupRef.value.open();
}
};
// 关闭时间弹窗
const closeTimePopup = () => {
timePopupRef.value.close();
};
// 确认选择时间
const confirmTime = () => {
// 拼接选中的时间
const selectedDateTime = `${selectedDate.value} ${selectedHour.value}:${selectedMinute.value}`;
selectedTime.value = moment(selectedDateTime, 'YYYY-MM-DD HH:mm');
// 关闭弹窗
closeTimePopup();
// 提示
uni.showToast({
title: `已选择 ${selectedTime.value.format('MM月DD日 HH:mm')}`,
icon: 'none'
});
};
// ========== 滚动/点击选择逻辑 ==========
// 同步滚动位置
const syncScrollPosition = () => {
// 日期滚动位置
const dateIndex = dateList.value.findIndex(item => item.value === selectedDate.value);
dateScrollTop.value = dateIndex * itemHeight;
// 小时滚动位置
const hourIndex = hourList.value.findIndex(item => item.time === selectedHour.value);
hourScrollTop.value = hourIndex * itemHeight;
// 分钟滚动位置
const minuteIndex = minuteList.value.findIndex(item => item.minute === selectedMinute.value);
minuteScrollTop.value = minuteIndex * itemHeight;
};
// 选择日期(点击)
const selectDate = (index) => {
selectedDate.value = dateList.value[index].value;
dateScrollTop.value = index * itemHeight;
updateTempTime();
};
// 选择小时(点击)
const selectHour = (index) => {
selectedHour.value = hourList.value[index].time;
hourScrollTop.value = index * itemHeight;
updateTempTime();
};
// 选择分钟(点击)
const selectMinute = (index) => {
selectedMinute.value = minuteList.value[index].minute;
minuteScrollTop.value = index * itemHeight;
updateTempTime();
};
// 滚动日期列(滑动)
const onDateScroll = (e) => {
const index = Math.round(e.detail.scrollTop / itemHeight);
if (index >= 0 && index < dateList.value.length) {
selectedDate.value = dateList.value[index].value;
updateTempTime();
}
};
// 滚动小时列(滑动)
const onHourScroll = (e) => {
const index = Math.round(e.detail.scrollTop / itemHeight);
if (index >= 0 && index < hourList.value.length) {
selectedHour.value = hourList.value[index].time;
updateTempTime();
}
};
// 滚动分钟列(滑动)
const onMinuteScroll = (e) => {
const index = Math.round(e.detail.scrollTop / itemHeight);
minuteScrollTop.value = index * itemHeight;
if (index >= 0 && index < minuteList.value.length) {
selectedMinute.value = minuteList.value[index].minute;
updateTempTime();
}
};
// 更新临时时间(弹窗预览)
const updateTempTime = () => {
tempTime.value = moment(`${selectedDate.value} ${selectedHour.value}:${selectedMinute.value}`, 'YYYY-MM-DD HH:mm');
};
// ========== 初始化 ==========
onMounted(() => {
syncScrollPosition();
});
// 页面加载时监听事件
onLoad(() => {
// 监听选择房屋事件
uni.$on('selectHouse', (data) => {
houseName.value = data.name
})
// 监听选择维修项目事件
uni.$on('selectProject', (data) => {
projectName.value = data.name
})
})
// 页面销毁移除监听
onUnload(() => {
uni.$off('selectHouse')
uni.$off('selectProject')
})
// 跳转到选择房屋页
const goToSelectHouse = () => {
uni.navigateTo({
url: '/pages/online-repair/selectHouse'
})
}
// 跳转到选择维修项目页
const goToSelectProject = () => {
uni.navigateTo({
url: '/pages/online-repair/personalSelectPoject'
})
}
// 选择预约时间
const changeTime = () =>{
}
// const chooseDateTime = () => {
// uni.datePicker({
// type: 'datetime',
// value: form.value.datetime,
// success: (res) => {
// form.value.datetime = res.value
// }
// })
// }
// 选择图片
const chooseImage = () => {
uni.chooseImage({
count: 3,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
form.value.images = [...form.value.images, ...res.tempFilePaths]
}
})
}
// 预览图片
const previewImage = (url) => {
uni.previewImage({
urls: form.value.images,
current: url
})
}
// 删除图片
const deleteImage = (index) => {
form.value.images.splice(index, 1)
}
// 表单提交
const submitForm = () => {
// 校验
if (!houseName.value) {
uni.showToast({ title: '请选择报修房屋', icon: 'none' })
return
}
if (!projectName.value) {
uni.showToast({ title: '请选择维修项目', icon: 'none' })
return
}
if (!form.value.title) {
uni.showToast({ title: '请输入问题标题', icon: 'none' })
return
}
if (!form.value.desc) {
uni.showToast({ title: '请输入问题描述', icon: 'none' })
return
}
if (!/^1[3-9]\d{9}$/.test(form.value.phone)) {
uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
return
}
if (!form.value.datetime) {
uni.showToast({ title: '请选择预约时间', icon: 'none' })
return
}
// 提交逻辑(替换为你的接口)
uni.showLoading({ title: '提交中...' })
setTimeout(() => {
uni.hideLoading()
uni.showToast({ title: '提交成功', icon: 'success' })
// 重置表单
// form.value = { title: '', desc: '', phone: '', datetime: '', images: [] }
// houseName.value = ''
// projectName.value = ''
}, 1000)
}
</script>
<style scoped>
.form-container {
padding: 15px;
background: #fff;
min-height: 100vh;
}
.form-item {
display: flex;
margin-bottom: 30rpx;
align-items: flex-start;
border-bottom: 1px solid #eee;
}
.label {
width: 140rpx;
font-size: 28rpx;
color: #333;
line-height: 80rpx;
}
.value {
flex: 1;
font-size: 28rpx;
color: #666;
line-height: 80rpx;
text-align: right;
display: flex;
justify-content: space-between;
}
.time-value {
color: #000;
}
.input, .textarea {
flex: 1;
padding: 20rpx;
font-size: 28rpx;
border: 1px solid #eee;
border-radius: 8rpx;
}
.textarea {
min-height: 160rpx;
line-height: 40rpx;
}
.form-item-wenti {
margin-bottom: 20px;
/* border-bottom: 1px solid #eee; */
padding-bottom: 10px;
}
/* .label {
font-size: 14px;
color: #333;
margin-bottom: 8px;
display: block;
}
.value {
font-size: 14px;
color: #666;
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
} */
.switch {
font-size: 12px;
color: #007aff;
margin-right: 5px;
}
input, textarea {
font-size: 14px;
width: 100%;
padding: 8px 0;
box-sizing: border-box;
}
textarea {
resize: none;
}
.upload-wrap {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 8px;
}
.upload-item {
position: relative;
width: 80px;
height: 80px;
}
.upload-item image {
width: 100%;
height: 100%;
border-radius: 4px;
}
.upload-item uni-icons {
position: absolute;
top: -5px;
right: -5px;
background: #ff3b30;
color: #fff;
border-radius: 50%;
padding: 2px;
}
.upload-btn {
width: 80px;
height: 80px;
border: 1px dashed #ccc;
border-radius: 4px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #999;
font-size: 12px;
}
.submit-btn {
width: 100%;
background: #007aff;
color: #fff;
border-radius: 8px;
height: 44px;
line-height: 44px;
font-size: 16px;
margin-top: 20px;
}
/* ========== uni-popup 时间选择器样式 ========== */
.time-popup-container {
background-color: #fff;
border-top-left-radius: 16rpx;
border-top-right-radius: 16rpx;
}
/* 弹窗头部 */
.time-popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 30rpx;
border-bottom: 1px solid #eee;
}
.cancel-btn {
font-size: 28rpx;
color: #666;
}
.popup-title {
font-size: 28rpx;
color: #333;
}
.confirm-btn {
font-size: 28rpx;
color: #007aff;
font-weight: 600;
}
/* 时间选择器容器 */
.time-selector {
display: flex;
height: 400rpx;
padding: 20rpx 0;
}
/* 单个时间列 */
.time-column {
flex: 1;
height: 100%;
text-align: center;
}
/* 时间项 */
.time-item {
height: 80rpx;
line-height: 80rpx;
font-size: 28rpx;
color: #666;
position: relative;
}
/* 选中项样式(高亮) */
.time-item.active {
color: #007aff;
font-weight: 600;
background-color: #d0d0d0;
}
.time-item.active::after {
content: '';
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
/* width: 80%; */
/* height: 1px; */
background-color: #d0d0d0;
}
</style>

View File

@@ -1,179 +0,0 @@
<template>
<view class="project-container">
<!-- 搜索框 -->
<view class="search-bar">
<input type="text" placeholder="请输入关键字搜索" v-model="searchKey" />
<uni-icons type="search" size="18"></uni-icons>
</view>
<!-- 分类+项目 -->
<view class="category-wrap">
<!-- 左侧分类 -->
<scroll-view scroll-y class="category-left">
<view
class="category-item"
:class="{ active: activeCategory === item.id }"
@click="selectCategory(item)"
v-for="item in categoryList"
:key="item.id"
>
{{ item.name }}
</view>
</scroll-view>
<!-- 右侧项目 -->
<scroll-view scroll-y class="category-right">
<view
class="project-item"
:class="{ active: selectedProject.id === item.id }"
@click="selectProject(item)"
v-for="item in currentProjectList"
:key="item.id"
>
{{ item.name }}
</view>
</scroll-view>
</view>
<!-- 确认按钮 -->
<button class="confirm-btn" @click="confirmSelect">确认</button>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
// import { uniShowToast, uniNavigateBack } from '@dcloudio/uni-app'
// 分类列表(匹配截图:家具/电路/水暖/门频/电器)
const categoryList = ref([
{ id: 1, name: '家具' },
{ id: 2, name: '电路' },
{ id: 3, name: '水暖' },
{ id: 4, name: '门频' },
{ id: 5, name: '电器' }
])
// 项目列表(匹配截图:床/沙发/柜子/椅子等)
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 activeCategory = ref(1) // 默认选中家具
const selectedProject = ref({}) // 选中的项目
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))
}
return list
})
// 选择分类
const selectCategory = (item) => {
activeCategory.value = item.id
selectedProject.value = {} // 切换分类清空选中项目
}
// 选择项目
const selectProject = (item) => {
selectedProject.value = item
}
// 确认选择
const confirmSelect = () => {
if (!selectedProject.value.id) {
uni.showToast({ title: '请选择维修项目', icon: 'none' })
return
}
// 获取分类名称
const categoryName = categoryList.value.find(c => c.id === activeCategory.value).name
// 传递给表单页
uni.$emit('selectProject', {
name: `${categoryName}-${selectedProject.value.name}`
})
uni.navigateBack({ delta: 1 })
}
</script>
<style scoped>
.project-container {
padding: 15px;
height: 100vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
background: #fff;
}
.search-bar {
display: flex;
align-items: center;
padding: 8px 10px;
background: #f5f5f5;
border-radius: 6px;
margin-bottom: 10px;
}
.search-bar input {
flex: 1;
font-size: 14px;
}
.category-wrap {
display: flex;
flex: 1;
margin-bottom: 20px;
}
.category-left {
width: 80px;
height: 100%;
background: #f8f8f8;
border-radius: 6px 0 0 6px;
}
.category-item {
padding: 12px 10px;
text-align: center;
font-size: 14px;
border-bottom: 1px solid #eee;
}
.category-item.active {
background: #fff;
color: #007aff;
font-weight: bold;
}
.category-right {
flex: 1;
height: 100%;
background: #fff;
border-radius: 0 6px 6px 0;
padding: 10px;
}
.project-item {
padding: 12px 10px;
font-size: 14px;
border-bottom: 1px solid #eee;
}
.project-item.active {
color: #007aff;
font-weight: bold;
}
.confirm-btn {
width: 100%;
height: 44px;
line-height: 44px;
background: #007aff;
color: #fff;
border-radius: 8px;
font-size: 16px;
}
</style>

View File

@@ -1,245 +0,0 @@
<template>
<view class="form-container">
<!-- 维修项目展示 -->
<view class="form-item">
<text class="label">维修项目</text>
<view class="value" @click="goBackSelect">
<view>
<text v-if="!repairProject">请选择项目</text>
{{ repairProject }}
</view>
<uni-icons type="arrowright" size="16"></uni-icons>
</view>
</view>
<!-- 标题输入 -->
<view class="form-item">
<text class="label">标题</text>
<!-- <input type="text" placeholder="请输入问题标题" v-model="form.title" /> -->
<uni-easyinput v-model="form.title" placeholder="请输入内容" :inputBorder="false"></uni-easyinput>
</view>
<!-- 问题描述 -->
<view class="form-item-wenti">
<text class="label">问题描述</text>
<!-- <textarea placeholder="请输入问题内容" v-model="form.desc" rows="5" ></textarea> -->
<uni-easyinput v-model="form.desc" placeholder="请描述你的问题" type="textarea"></uni-easyinput>
</view>
<!-- 手机号 -->
<view class="form-item">
<text class="label">手机号</text>
<uni-easyinput type="number" v-model="form.phone" placeholder="请输入手机号" :inputBorder="false"></uni-easyinput>
</view>
<!-- 图片上传 -->
<view class="form-item-wenti">
<text class="label">问题照片</text>
<view class="upload-wrap">
<!-- 已上传图片 -->
<view class="upload-item" v-for="(img, index) in form.images" :key="index">
<image :src="img" mode="aspectFill"></image>
<uni-icons type="close" size="16" @click="deleteImage(index)"></uni-icons>
</view>
<!-- 上传按钮 -->
<view class="upload-btn" @click="chooseImage">
<uni-icons type="plus" size="24"></uni-icons>
<text>上传照片</text>
</view>
</view>
</view>
<!-- 提交按钮 -->
<button class="submit-btn" @click="submitForm">提交</button>
</view>
</template>
<script setup>
import { ref } from 'vue'
import{onLoad} from "@dcloudio/uni-app"
// import { onLoad, uniShowToast, uniNavigateBack, uniChooseImage, uniUploadFile } from '@dcloudio/uni-app'
// 接收上个页面传的维修项目
const repairProject = ref('')
// 表单数据
const form = ref({
title: '',
desc: '',
phone: '',
images: [] // 图片列表
})
// 页面加载时获取传参
onLoad((options) => {
if (options.category && options.project) {
repairProject.value = `${options.category}-${options.project}`
}
})
// 返回选择项目页
const goBackSelect = () => {
uni.navigateTo({
url:"/pages/online-repair/repairSelect"
})
// uni.navigateBack({
// delta: 1
// })
}
// 选择图片
const chooseImage = () => {
uni.chooseImage({
count: 3, // 最多选3张
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
// 临时文件路径
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)
// }
// })
// })
}
})
}
// 删除图片
const deleteImage = (index) => {
form.value.images.splice(index, 1)
}
// 表单提交
const submitForm = () => {
// 表单校验
if (!repairProject.value) {
uni.showToast({ title: '请选择维修项目', icon: 'none' })
return
}
if (!form.value.title) {
uni.showToast({ title: '请输入问题标题', icon: 'none' })
return
}
if (!form.value.desc) {
uni.showToast({ title: '请输入问题描述', icon: 'none' })
return
}
if (!/^1[3-9]\d{9}$/.test(form.value.phone)) {
uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
return
}
// 提交数据(替换成你的接口)
uni.showLoading({ title: '提交中...' })
// 模拟接口请求
setTimeout(() => {
uni.hideLoading()
uni.showToast({ title: '提交成功', icon: 'success' })
// 提交成功后重置表单/返回上一页
// form.value = { title: '', desc: '', phone: '', images: [] }
// 或返回上一页
uni.navigateBack({ delta: 3 })
}, 1000)
}
</script>
<style scoped>
.form-container {
padding: 15px;
background: #fff;
min-height: 100vh;
}
.form-item-wenti {
margin-bottom: 20px;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}
.form-item {
display: flex;
margin-bottom: 30rpx;
align-items: flex-start;
border-bottom: 1px solid #eee;
}
.label {
width: 140rpx;
font-size: 28rpx;
color: #333;
line-height: 80rpx;
}
.value {
flex: 1;
font-size: 28rpx;
color: #666;
line-height: 80rpx;
text-align: right;
display: flex;
justify-content: space-between;
}
.input, .textarea {
font-size: 14px;
width: 100%;
padding: 8px 0;
box-sizing: border-box;
}
.textarea {
resize: none;
}
.upload-wrap {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 8px;
}
.upload-item {
position: relative;
width: 80px;
height: 80px;
}
.upload-item image {
width: 100%;
height: 100%;
border-radius: 4px;
}
.upload-item uni-icons {
position: absolute;
top: -5px;
right: -5px;
background: #ff3b30;
color: #fff;
border-radius: 50%;
padding: 2px;
}
.upload-btn {
width: 80px;
height: 80px;
border: 1px dashed #ccc;
border-radius: 4px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #999;
font-size: 12px;
}
.submit-btn {
width: 100%;
background: #007aff;
color: #fff;
border-radius: 6px;
height: 44px;
line-height: 44px;
font-size: 16px;
}
</style>

View File

@@ -1,263 +0,0 @@
<template>
<view class="repair-detail-container">
<!-- 维修流程模块 -->
<view class="info-section">
<uni-section title="维修流程" type="line" style="margin-bottom: 20rpx;">
<uni-row class="demo-uni-row" :width="nvueWidth">
<uni-col :span="12">
<up-steps :current="currentStep" direction="column" space="30" active-color="#007aff">
<up-steps-item title="待处理" desc="提交报修成功"></up-steps-item>
<up-steps-item title="已派单" desc="维修师傅已接单"></up-steps-item>
<up-steps-item title="已完成" desc="维修完成"></up-steps-item>
</up-steps>
</uni-col>
<uni-col :span="12">
<view class="step-time">2026-03-24 09:30:38</view>
<view class="step-time">2026-03-26 09:30:38</view>
<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>
<view class="info-value">{{repairInfo.house}}</view>
</view>
<view class="info-item">
<view class="info-label">维修项目</view>
<view class="info-value">{{repairInfo.project}}</view>
</view>
<view class="info-item">
<view class="info-label">标题</view>
<view class="info-value">{{repairInfo.title}}</view>
</view>
<view class="info-item">
<view class="info-label">问题描述</view>
<view class="info-value multi-line">{{repairInfo.description}}</view>
</view>
<view class="info-item">
<view class="info-label">手机号</view>
<view class="info-value">{{repairInfo.phone}}</view>
</view>
<view class="info-item">
<view class="info-label">报修时间</view>
<view class="info-value">{{repairInfo.time}}</view>
</view>
<view class="info-item">
<view class="info-label">问题照片</view>
<view class="photo-container">
<view class="photo-item" v-for="(item, index) in 2" :key="index">
<uni-icons type="plusempty" size="24" color="#ccc"></uni-icons>
</view>
</view>
</view>
</uni-section>
</view>
<!-- 联系物业按钮 -->
<view class="btn-container">
<button class="contact-btn" @click="contactProperty">联系物业</button>
</view>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app'
// 步骤条当前激活的步骤0=待处理1=已派单2=已完成)
const currentStep = ref(2)
const nvueWidth = ref('300')
// 报修信息数据
const repairInfo = ref({
house: '桂花园(别墅) 2号楼2层',
project: '照明-路灯',
title: '1栋楼下道路灯不亮了',
description: '1栋楼下道路路灯不亮并且有的亮度不够请物业公司及时更换防止发生意外事故。',
phone: '15688888888',
time: '2023-08-01 15:30'
})
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)
switch (status) {
case 'pending': //待处理
currentStep.value = 0
break
case 'assignde':
currentStep.value = 1
break
case 'completed':
currentStep.value = 2
break
default:
currentStep.value = 0
}
})
// 联系物业按钮点击事件
const contactProperty = () => {
uni.showToast({
title: '正在联系物业...',
icon: 'none'
})
}
</script>
<style scoped>
.repair-detail-container {
padding: 16px;
background-color: #f5f5f5;
min-height: 100vh;
}
.process-section,
.info-section {
background-color: #fff;
border-radius: 8px;
padding: 13px;
margin-bottom: 16px;
/* margin-top: 20rpx; */
}
.repair-steps-container {
padding: 20rpx 30rpx;
background-color: #fff;
}
.title {
font-size: 32rpx;
font-weight: 600;
margin-bottom: 30rpx;
color: #333;
}
.step-time {
height: 54px;
line-height: 54px;
font-size: 12px;
color: #999;
/* font-size: 22rpx;
color: #999;
text-align: right;
margin-top: 5rpx; */
}
.step-time-3{
height: 38px;
line-height: 38px;
font-size: 12px;
color: #999;
}
/* -------- */
.section-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 12px;
color: #333;
}
.slot-title {
position: absolute;
top: -10px;
font-size: 12px;
font-weight: 400;
}
.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-section.active {
color: #007AFF;
/* font-weight: 500; */
}
.info-item:last-child {
border-bottom: none;
}
.info-label {
width: 80px;
color: #666;
font-size: 14px;
}
.info-value {
flex: 1;
color: #333;
font-size: 14px;
}
.multi-line {
white-space: pre-wrap;
line-height: 1.5;
}
.photo-container {
display: flex;
gap: 12px;
margin-top: 4px;
}
.photo-item {
width: 80px;
height: 80px;
border: 1px dashed #ccc;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
}
.btn-container {
padding: 16px;
}
.contact-btn {
background-color: #007AFF;
color: #fff;
border: none;
border-radius: 8px;
height: 48px;
font-size: 16px;
width: 100%;
}
.contact-btn::after {
border: none;
}
</style>

View File

@@ -1,141 +0,0 @@
<template>
<view class="select-container">
<!-- 搜索框 -->
<view class="search-bar">
<input type="text" placeholder="请输入关键字搜索" v-model="searchKey" />
<uni-icons type="search" size="18"></uni-icons>
</view>
<!-- 分类选择 -->
<view class="category-wrap">
<!-- 左侧分类 -->
<scroll-view scroll-y class="category-left">
<view
class="category-item"
:class="{ active: activeCategory === item.id }"
@click="selectCategory(item)"
v-for="item in categoryList"
:key="item.id"
>
{{ item.name }}
</view>
</scroll-view>
<!-- 右侧项目 -->
<scroll-view scroll-y class="category-right">
<view
class="project-item"
@click="selectProject(item)"
v-for="item in currentProjectList"
:key="item.id"
>
{{ item.name }}
</view>
</scroll-view>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
// import { uniNavigateTo } from '@dcloudio/uni-app'
// 分类列表
const categoryList = ref([
{ id: 1, name: '基础设施' },
{ id: 2, name: '电子设施' }
])
// 所有项目数据
const projectList = ref([
{ id: 101, categoryId: 1, name: '路灯' },
{ id: 102, categoryId: 1, name: '楼层指示灯' },
{ id: 103, categoryId: 2, name: '应急出口灯' },
{ id: 104, categoryId: 1, name: '草坪灯' }
])
// 选中的分类
const activeCategory = ref(1)
// 搜索关键词
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))
}
return list
})
// 选择分类
const selectCategory = (item) => {
activeCategory.value = item.id
}
// 选择项目并跳转到维修项目填写页
const selectProject = (item) => {
// 获取分类名称
const categoryName = categoryList.value.find(c => c.id === item.categoryId)?.name
// 跳转并传参
uni.navigateTo({
url: `/pages/online-repair/publicRepair?category=${categoryName}&project=${item.name}`
})
}
</script>
<style scoped>
.select-container {
padding: 10px;
height: 100vh;
box-sizing: border-box;
}
.search-bar {
display: flex;
align-items: center;
padding: 8px 10px;
background: #f5f5f5;
border-radius: 6px;
margin-bottom: 10px;
}
.search-bar input {
flex: 1;
font-size: 14px;
}
.category-wrap {
display: flex;
height: calc(100% - 50px);
}
.category-left {
width: 120px;
height: 100%;
background: #f8f8f8;
border-radius: 6px 0 0 6px;
}
.category-item {
padding: 15px 10px;
text-align: center;
font-size: 14px;
border-bottom: 1px solid #eee;
}
.category-item.active {
background: #fff;
color: #007aff;
font-weight: bold;
}
.category-right {
flex: 1;
height: 100%;
background: #fff;
border-radius: 0 6px 6px 0;
padding: 10px;
}
.project-item {
padding: 12px 10px;
font-size: 14px;
border-bottom: 1px solid #eee;
}
.project-item:active {
background: #f5f5f5;
}
</style>

View File

@@ -1,114 +0,0 @@
<template>
<view class="house-container">
<!-- 房屋列表 -->
<view class="house-list">
<view
class="house-item"
:class="{ active: selectedHouse.id === item.id }"
@click="selectHouse(item)"
v-for="item in houseList"
:key="item.id"
>
<image src="/static/在线报修/房屋.png" style="width: 60rpx;height: 60rpx;"></image>
<view class="house-name">
{{ item.community }}
<view class="house-detail">{{ item.address }}</view>
</view>
<uni-icons type="checkbox-filled" size="20" color="#007aff" v-if="selectedHouse.id === item.id"></uni-icons>
</view>
</view>
<!-- 确认按钮 -->
<button class="confirm-btn" @click="confirmSelect">确认</button>
</view>
</template>
<script setup>
import { ref } from 'vue'
// import { uniShowToast, uniNavigateBack } from '@dcloudio/uni-app'
// 房屋列表数据
const houseList = ref([
{ id: 1, community: '桂花园(别墅)', address: '2号楼2幢' },
{ id: 2, community: '安泰翡翠城', address: '11号楼5单元1801' },
{ id: 3, community: '碧桂园新城·时代之光', address: '3号楼' }
])
// 选中的房屋
const selectedHouse = ref(houseList.value[0]) // 默认选中第一个
// 选择房屋
const selectHouse = (item) => {
selectedHouse.value = item
}
// 确认选择并返回
const confirmSelect = () => {
if (!selectedHouse.value) {
uni.showToast({ title: '请选择房屋', icon: 'none' })
return
}
// 把选中的房屋信息传给表单页通过uni.$emit
uni.$emit('selectHouse', {
name: `${selectedHouse.value.community} ${selectedHouse.value.address}`
})
// 返回上一页
uni.navigateBack({ delta: 1 })
}
</script>
<style scoped>
.house-container {
padding: 15px;
height: 100vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
background: #fff;
}
.house-list {
flex: 1;
}
.house-item {
display: flex;
justify-content: flex-start;
align-items: center;
padding: 15px 10px;
border: 1px solid #eee;
border-radius: 8px;
margin-bottom: 10px;
}
.house-item.active {
border-color: #007aff;
background: #f0f8ff;
}
.house-name {
font-size: 16px;
color: #333;
font-weight: 500;
display: flex;
flex-direction: column;
align-items: flex-start;
margin-left: 30rpx;
width: 80%;
}
.house-detail {
font-size: 12px;
color: #666;
margin-top: 4px;
}
.confirm-btn {
width: 100%;
height: 44px;
line-height: 44px;
background: #007aff;
color: #fff;
border-radius: 8px;
font-size: 16px;
margin-top: 20px;
}
</style>

View File

@@ -1,117 +0,0 @@
<template>
<view class="qrcode-page">
<view class="qrcode-wrap">
<view class="qrcode-box">
<!-- 调用uqrcode组件 -->
<uqrcode
:value="qrcodeValue"
:size="400"
:logo="'/static/logo.png'"
:logoSize="0.2"
canvasId="visitor-qrcode"
@success="onQrcodeSuccess"
></uqrcode>
<!-- 二维码边框装饰 -->
<view class="qrcode-border border-top"></view>
<view class="qrcode-border border-right"></view>
<view class="qrcode-border border-bottom"></view>
<view class="qrcode-border border-left"></view>
</view>
<text class="qrcode-desc">桂花园别墅东门通行二维码</text>
<text class="qrcode-tips">分享此二维码给访客可扫码开门单次有效</text>
</view>
<view class="btn-wrap">
<button class="send-btn" @click="sendToFriend">发送给好友</button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
// import { navigateBack, showToast, share } from '@dcloudio/uni-app';
// 引入本地uqrcode组件
import uqrcode from '@/components/uqrcode/uqrcode.vue';
// 二维码内容(替换为实际的通行链接)
const qrcodeValue = ref('https://your-domain.com/visitor/123456');
// 生成的二维码临时图片地址
const qrcodeImage = ref('');
// 二维码生成成功回调
const onQrcodeSuccess = (tempFilePath) => {
qrcodeImage.value = tempFilePath;
uni.showToast({ title: '二维码生成成功', icon: 'success' });
};
// 分享给好友
const sendToFriend = () => {
if (!qrcodeImage.value) {
uni.showToast({ title: '二维码生成中...', icon: 'none' });
return;
}
uni.share({
provider: 'weixin',
type: 0,
title: '访客通行二维码',
imageUrl: qrcodeImage.value,
success: () => uni.showToast({ title: '分享成功' }),
fail: () => uni.showToast({ title: '分享失败', icon: 'none' })
});
};
</script>
<style scoped>
.qrcode-page {
/* background-color: #f8f8f8; */
min-height: 100vh;
}
.qrcode-wrap {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 60rpx;
}
.qrcode-box {
position: relative;
width: 400rpx;
height: 400rpx;
}
/* 二维码边框样式 */
.qrcode-border {
position: absolute;
width: 40rpx;
height: 40rpx;
border: 4rpx solid #007aff;
}
.border-top { top: 0; left: 0; border-right: none; border-bottom: none; }
.border-right { top: 0; right: 0; border-left: none; border-bottom: none; }
.border-bottom { bottom: 0; right: 0; border-left: none; border-top: none; }
.border-left { bottom: 0; left: 0; border-right: none; border-top: none; }
/* 文字样式 */
.qrcode-desc {
font-size: 32rpx;
font-weight: 600;
margin-top: 30rpx;
color: #333;
}
.qrcode-tips {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
/* 按钮样式 */
.btn-wrap {
margin: 80rpx 20rpx 20rpx;
}
.send-btn {
background-color: #007aff;
color: #fff;
border-radius: 8rpx;
height: 88rpx;
font-size: 32rpx;
}
</style>

View File

@@ -1,494 +0,0 @@
<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
class="tab-item"
:class="{ active: activeTab === 'car' }"
@click="switchTab('car')"
>
车辆来访
</view>
<view
class="tab-item"
:class="{ active: activeTab === 'person' }"
@click="switchTab('person')"
>
人员来访
</view>
</view>
<!-- 表单区域 -->
<view class="form-wrap">
<!-- 房屋信息 -->
<view class="form-section">
<view class="section-title">房屋信息</view>
<view class="form-item">
<view class="label">
<text class="required">*</text>来访地址
</view>
<view class="value" @click="openAddressModal">
{{ form.address }}
<uni-icons type="right" size="16" color="#999"></uni-icons>
</view>
</view>
</view>
<!-- 访客信息 -->
<view class="form-section">
<view class="section-title">访客信息</view>
<view class="form-item">
<view class="label">访客姓名</view>
<input
class="input"
placeholder="请输入访客姓名"
v-model="form.name"
/>
</view>
<view class="form-item">
<view class="label">
<text class="required">*</text>访客性别
</view>
<view class="value" @click="openGenderModal">
{{ form.gender }}
<uni-icons type="right" size="16" color="#999"></uni-icons>
</view>
</view>
<view class="form-item">
<view class="label">
<text class="required">*</text>访客联系方式
</view>
<input
class="input"
placeholder="请输入联系方式"
v-model="form.phone"
type="number"
/>
</view>
<!-- 车牌号仅车辆来访显示 -->
<view class="form-item" v-if="activeTab === 'car'">
<view class="label">
<text class="required">*</text>车牌号
</view>
<view class="car-number-wrap">
<input
class="car-input"
v-for="(item, index) in 8"
:key="index"
v-model="form.carNumber[index]"
maxlength="1"
type="text"
/>
</view>
</view>
<view class="form-item">
<view class="label">
<text class="required">*</text>随行人数
</view>
<input
class="input"
placeholder="请输入随行人数"
v-model="form.peopleNum"
type="number"
/>
</view>
</view>
<!-- 有效期 -->
<view class="form-section">
<view class="section-title">有效期</view>
<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"/>
</view>
</view>
<view class="form-item">
<view class="label">
<text class="required">*</text>离开时间
</view>
<view class="value">
<uni-datetime-picker type="datetime" v-model="leaveTime" @change="changeLog" :border="false"/>
</view>
</view>
<view class="tips">预计来访时间和预计离开时间间隔不能超过24小时</view>
<view class="tips">车辆停留请勿超出有效期需及时驶出小区</view>
</view>
</view>
<!-- 底部按钮 -->
<view class="btn-wrap">
<button
class="submit-btn"
@click="submitForm"
>
{{ activeTab === 'car' ? '确认邀请' : '生成通行二维码' }}
</button>
</view>
<!-- 地址选择弹窗 -->
<uni-popup ref="addressModalRef" type="bottom" :mask-click="false">
<view class="modal-wrap">
<view class="modal-title">选择来访地址</view>
<view class="address-list">
<view
class="address-item"
:class="{ active: item === form.address }"
v-for="item in addressList"
:key="item"
@click="selectAddress(item)"
>
{{ item }}
</view>
</view>
<uni-icons
type="closeempty"
size="20"
class="modal-close"
@click="closeAddressModal"
></uni-icons>
</view>
</uni-popup>
<!-- 性别选择弹窗 -->
<uni-popup ref="genderModalRef" type="bottom" :mask-click="false">
<view class="modal-wrap">
<view class="gender-list">
<view
class="gender-item"
:class="{ active: item === form.gender }"
v-for="item in genderList"
:key="item"
@click="selectGender(item)"
>
{{ item }}
</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import { ref, reactive} from 'vue';
import{onLoad } from "@dcloudio/uni-app"
// 标签激活状态
const activeTab = ref('car');
// 表单数据
const form = reactive({
address: '桂花园别墅2号楼2层', // 来访地址默认值
name: '', // 访客姓名
gender: '男', // 性别默认值
phone: '', // 联系方式
carNumber: ['', '', '', '', '', '', ''], // 车牌号7位
peopleNum: '', // 随行人数
visitTime: '', // 来访时间默认值
leaveTime: '' // 离开时间
});
// 地址列表(模拟数据)
const addressList = ref([
'桂花园别墅1号楼1层',
'桂花园别墅2号楼2层'
]);
// 性别列表
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();
};
// 打开地址弹窗
const openAddressModal = () => {
addressModalRef.value.open();
};
// 关闭地址弹窗
const closeAddressModal = () => {
addressModalRef.value.close();
};
// 选择地址
const selectAddress = (item) => {
form.address = item;
closeAddressModal();
};
// 打开性别弹窗
const openGenderModal = () => {
genderModalRef.value.open();
};
// 选择性别
const selectGender = (item) => {
form.gender = item;
genderModalRef.value.close();
};
// 打开时间选择器
// const openTimePicker = (type) => {
// currentTimeType.value = type;
// timePickerRef.value.open();
// };
// 校验来访/离开时间间隔
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);
if (diffHours > 24) {
uni.showToast({
title: '时间间隔不能超过24小时',
icon: 'none'
});
form.leaveTime = '';
}
};
// 表单提交
const submitForm = () => {
// 必填项校验
if (!form.phone) {
uni.showToast({ title: '请输入访客联系方式', icon: 'none' });
return;
}
if (!/^1[3-9]\d{9}$/.test(form.phone)) {
uni.showToast({ title: '请输入正确的手机号', icon: 'none' });
return;
}
if (activeTab === 'car' && form.carNumber.join('').length < 8) {
uni.showToast({ title: '请填写完整车牌号', icon: 'none' });
return;
}
if (!form.peopleNum || form.peopleNum < 0) {
uni.showToast({ title: '请输入正确的随行人数', icon: 'none' });
return;
}
if (!form.leaveTime) {
uni.showToast({ title: '请选择离开时间', icon: 'none' });
return;
}
checkTimeInterval();
if (!form.leaveTime) return; // 时间校验失败则终止提交
// 提交逻辑(替换为实际接口请求)
uni.showLoading({ title: '提交中...' });
setTimeout(() => {
uni.hideLoading();
uni.showToast({
title: activeTab === 'car' ? '邀请成功' : '二维码生成成功',
icon: 'success'
});
}, 1000);
};
// 重置表单
const resetForm = () => {
form.address = '';
form.name = '';
form.gender = '';
form.phone = '';
form.carNumber = '';
form.escortNum = '';
form.visitTime = '';
form.leaveTime = '';
form.carNumber = ['', '', '', '', '', '', '']; // 重置车牌号输入
};
</script>
<style scoped>
.visitor-invite {
background-color: #f5f5f5;
min-height: 100vh;
}
/* 导航栏 */
.nav-bar {
display: flex;
align-items: center;
padding: 20rpx;
background-color: #fff;
}
.nav-title {
font-size: 36rpx;
font-weight: 500;
margin-left: 20rpx;
}
/* 标签切换 */
.tab-wrap {
display: flex;
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.tab-item {
flex: 1;
text-align: center;
padding: 20rpx 0;
font-size: 32rpx;
color: #333;
}
.tab-item.active {
color: #007aff;
border-bottom: 4rpx solid #007aff;
}
/* 表单区域 */
.form-wrap {
padding: 10rpx;
}
.form-section {
background-color: #fff;
margin-bottom: 10rpx;
padding: 20rpx 40rpx;
}
.section-title {
font-size: 32rpx;
font-weight: 500;
margin-bottom: 20rpx;
}
.form-item {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.form-item:last-child {
border-bottom: none;
}
.label {
width: 200rpx;
font-size: 30rpx;
color: #333;
}
.required {
color: red;
margin-right: 4rpx;
}
.value {
flex: 1;
font-size: 30rpx;
color: #666;
display: flex;
justify-content: flex-end;
align-items: center;
}
.input {
flex: 1;
font-size: 30rpx;
color: #666;
text-align: right;
}
/* 车牌号输入 */
.car-number-wrap {
flex: 1;
display: flex;
justify-content: space-between;
}
.car-input {
width: 52rpx;
height: 60rpx;
border: 1rpx solid #eee;
text-align: center;
font-size: 30rpx;
}
/* 提示文字 */
.tips {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
text-align: center;
}
/* 底部按钮 */
.btn-wrap {
padding: 20rpx;
}
.submit-btn {
background-color: #007aff;
color: #fff;
font-size: 32rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 8rpx;
}
/* 弹窗样式 */
.modal-wrap {
background-color: #fff;
padding: 30rpx;
border-top-left-radius: 20rpx;
border-top-right-radius: 20rpx;
position: relative;
}
.modal-title {
font-size: 32rpx;
text-align: center;
margin-bottom: 30rpx;
}
.modal-close {
position: absolute;
top: 20rpx;
right: 20rpx;
color: #999;
}
.address-list, .gender-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.address-item, .gender-item {
padding: 20rpx;
background-color: #f5f5f5;
border-radius: 8rpx;
text-align: center;
font-size: 30rpx;
}
.address-item.active, .gender-item.active {
background-color: #007aff;
color: #fff;
}
</style>

View File

@@ -1,172 +0,0 @@
<template>
<view class="visitor-detail-page">
<!-- 访客信息卡片 -->
<view class="info-card">
<view class="info-header">
<text class="title">来访者{{ visitorInfo.name }}</text>
<text class="status {{ visitorInfo.status === 'visited' ? 'visited' : 'waiting' }}">
{{ visitorInfo.status === 'visited' ? '已到访' : '等待来访' }}
</text>
</view>
<uni-divider margin="10rpx 0"></uni-divider>
<view class="info-list">
<view class="info-item">
<text class="label">来访地址</text>
<text class="value">{{ visitorInfo.address }}</text>
</view>
<view class="info-item">
<text class="label">联系方式</text>
<text class="value">{{ visitorInfo.phone }}</text>
</view>
<!-- 车牌号可选显示 -->
<view class="info-item" v-if="visitorInfo.carNumber">
<text class="label">车牌号</text>
<text class="value">{{ visitorInfo.carNumber }}</text>
</view>
<view class="info-item">
<text class="label">随行人数</text>
<text class="value">{{ visitorInfo.escortNum }}</text>
</view>
<view class="info-item">
<text class="label">有效期</text>
<text class="value">{{ visitorInfo.validTime }}</text>
</view>
<view class="info-item">
<text class="label">邀请时间</text>
<text class="value">{{ visitorInfo.inviteTime }}</text>
</view>
<!-- 已到访状态显示到访时间 -->
<view class="info-item" v-if="visitorInfo.status === 'visited'">
<text class="label">到访时间</text>
<text class="value">{{ visitorInfo.visitTime }}</text>
</view>
</view>
</view>
<!-- 提示文字 -->
<view class="tips-text">
<view>预计来访时间盒预计离开时间间隔不能超过24小时</view>
车辆停留请勿超出有效期需及时驶出小区
</view>
<!-- 分享二维码按钮仅等待来访状态显示 -->
<view class="btn-wrap" v-if="visitorInfo.status === 'waiting'">
<button class="share-btn" @click="goToQrcode">分享通行二维码</button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import {onLoad} from '@dcloudio/uni-app'
// 访客信息(实际项目中从接口获取)
// const visitorInfo = ref({})
const visitorInfo = ref({
name: '张三',
status: 'waiting', // waiting:等待来访visited:已到访
address: '桂花园别墅2号楼2层',
phone: '13866666666',
carNumber: '京A88888', // 可传空隐藏
escortNum: 0,
validTime: '2023.06.30 10:00至2023.06.30 23:00',
inviteTime: '2023年06月30日 09:35',
visitTime: '2023年06月30日 14:32' // 已到访时显示
});
onLoad((option) =>{
const infoStr = decodeURIComponent(JSON.stringify(option))
// console.log('infoStr',infoStr)
visitorInfo.value = infoStr.item
})
// 跳转到二维码页面
const goToQrcode = () => {
uni.navigateTo({
url: '/pages/visitor/qrcode-page'
});
};
</script>
<style scoped>
.visitor-detail-page {
background-color: #f8f8f8;
min-height: 100vh;
}
.info-card {
background: #fff;
margin: 20rpx;
padding: 20rpx;
border-radius: 12rpx;
}
.info-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10rpx;
}
.title {
font-size: 32rpx;
font-weight: 600;
}
.status {
font-size: 28rpx;
padding: 6rpx 16rpx;
border-radius: 20rpx;
}
.waiting {
color: #007aff;
background: #e8f4ff;
}
.visited {
color: #34c759;
background: #e6fffa;
}
.info-list {
margin-top: 20rpx;
}
.info-item {
display: flex;
margin-bottom: 16rpx;
font-size: 28rpx;
}
.label {
color: #666;
width: 160rpx;
}
.value {
color: #333;
flex: 1;
}
.tips-text {
margin: 0 20rpx;
font-size: 24rpx;
color: #999;
line-height: 1.6;
}
.btn-wrap {
margin: 40rpx 20rpx;
}
.share-btn {
background-color: #007aff;
color: #fff;
border-radius: 8rpx;
height: 88rpx;
font-size: 32rpx;
}
</style>

View File

@@ -1,335 +0,0 @@
<template>
<view class="visitor-page">
<view class="status_bar"></view>
<!-- 顶部导航栏 -->
<view class="nav-bar">
<view class="nav-left" @tap="goBack">
<image src="/static/首页/arr-left.png" style="width: 48rpx; height: 48rpx;"></image>
</view>
<view class="nav-title">访客邀请</view>
<view class="nav-right"></view>
</view>
<!-- 标签切换栏 -->
<view class="tab-bar">
<view
class="tab-item"
:class="{ active: activeTab === 'waiting' }"
@click="activeTab = 'waiting'"
>
等待来访
</view>
<view
class="tab-item"
:class="{ active: activeTab === 'visited' }"
@click="activeTab = 'visited'"
>
已到访
</view>
</view>
<!-- 访客列表 -->
<scroll-view class="list-container" scroll-y>
<!-- 列表项根据标签切换显示对应数据 -->
<view
class="visitor-item"
v-for="(item, index) in filterdList"
:key="index"
>
<!-- 访客基本信息 -->
<view class="item-header">
<text class="visitor-name" :class="item.status">来访者{{ item.name }}</text>
<text class="status-tag" :class="item.status">{{ item.statusText }}</text>
</view>
<!-- 有效期/到访时间 -->
<view class="item-row">
<text class="label" :class="item.status">{{ item.status === 'waiting' ? '有效期' : '到访时间' }}</text>
<text class="value" :class="item.status">{{ item.timeRange }}</text>
</view>
<!-- 联系方式 -->
<view class="item-row">
<text class="label" :class="item.status">联系方式</text>
<text class="value" :class="item.status">{{ item.phone }}</text>
</view>
<!-- 车牌号 -->
<view class="item-row" v-if="item.carNumber">
<text class="label" :class="item.status">车牌号</text>
<text class="value" :class="item.status">{{ item.carNumber }}</text>
</view>
<!-- 时间+查看详情 -->
<view class="item-footer">
<text class="time-text">时间{{ item.createTime }}</text>
<view class="detail-btn" @click="viewDetail(item)">
<text>查看详情</text>
<uni-icons type="arrowright" size="14"></uni-icons>
</view>
</view>
</view>
</scroll-view>
<!-- 新增邀请按钮 -->
<view class="add-btn" @click="addInvitation">
<uni-icons type="plus" size="18" color="#fff"></uni-icons>
<text class="add-text">新增邀请</text>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
// import { uniNavigateTo, uniShowToast } from '@dcloudio/uni-app'
// 激活的标签waiting=等待来访visited=已到访)
const activeTab = ref('waiting')
// 模拟访客数据(可替换为接口请求数据)
const visitorList = ref([
{
name: '张三',
status: 'waiting', // waiting/visited
statusText: '等待来访',
timeRange: '2023.06.30 10:00至2023.06.30 23:00',
phone: '13866666666',
carNumber: '京A88888',
createTime: '2023年06月30日 09:35'
},
{
name: '张三',
status: 'visited',
statusText: '已到访',
timeRange: '2023.06.30 16:32:28',
phone: '13866666666',
carNumber: '京A88888',
createTime: '2023年06月30日 09:35'
},
{
name: '张三',
status: 'waiting',
statusText: '等待来访',
timeRange: '2023.06.30 10:00至2023.06.30 23:00',
phone: '13866666666',
carNumber: '',
createTime: '2023年06月30日 09:35'
}
])
// 根据标签筛选列表
const filterdList = computed(() => {
return visitorList.value.filter(item => item.status === activeTab.value)
})
// 返回上一页
const goBack = () => {
if(uni.getStorageSync('sourcePage') === "index"){
uni.switchTab({
url:"/pages/index/index"
})
}else if(uni.getStorageSync('sourcePage') === "serviceIndex"){
uni.switchTab({
url:"/pages/serviceIndex/serviceIndex"
})
}
uni.removeStorageSync('sourcePage')
}
// 查看详情
const viewDetail = (item) => {
const itemStr = encodeURIComponent(JSON.stringify(item))
console.log('eeeee',itemStr)
uni.navigateTo({
url: `/pages/visitor/visitor-detail?item=${itemStr}` // 传参到详情页
})
}
// 新增邀请
const addInvitation = () => {
uni.navigateTo({
url: '/pages/visitor/visitor-add'
})
}
</script>
<style scoped>
/* 页面整体样式 */
.visitor-page {
background: linear-gradient(to bottom left, #E2F0FF 0%, #f9f9f9 50%);
min-height: 100vh;
padding: 0 40rpx;
}
.status_bar {
height: 32px;
width: 100%;
}
/* 顶部导航栏 */
.nav-bar {
height: 90rpx;
display: flex;
align-items: center;
/* padding: 0 30rpx; */
color: #fff;
position: sticky;
top: 44px;
z-index: 100;
margin-bottom: 20px;
}
.nav-left {
width: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 20rpx;
color: #000;
}
.nav-right {
width: 60rpx;
}
/* 标签切换栏 */
.tab-bar {
display: flex;
}
.tab-item {
flex: 1;
text-align: left;
padding: 12px 0;
font-size: 16px;
color: #666;
position: relative;
}
.tab-item.active {
color: #007aff;
font-weight: 500;
}
/* 激活标签下划线 */
.tab-item.active::after {
content: '';
position: absolute;
bottom: -2px;
left: 18%;
transform: translateX(-50%);
width: 64px;
height: 4px;
border-radius: 6rpx;
background-color: #2F77FD;
/* background-color: linear-gradient(to bottom, #2F77FD, #CEDFFF); */
}
/* 列表容器 */
.list-container {
height: calc(100vh - 150px); /* 预留导航、标签、按钮高度 */
}
/* 访客列表项 */
.visitor-item {
background-color: #fff;
margin: 10px 0px;
padding: 15px;
border-radius: 8px;
}
/* 列表项头部(姓名+状态) */
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.visitor-name {
font-size: 16px;
color: #333;
font-weight: 500;
}
.visitor-name.visited{
color: #999;
}
.status-tag {
font-size: 14px;
padding: 2px 8px;
border-radius: 12px;
}
.status-tag.waiting {
color: #2F77FD;
/* background-color: #fff8e6; */
}
.status-tag.visited {
color: #999999;
/* background-color: #e6fffa; */
}
/* 列表项行数据 */
.item-row {
display: flex;
margin-bottom: 8px;
font-size: 14px;
}
.label {
color: #222;
min-width: 80px;
}
.value {
color: #333;
flex: 1;
}
.label.visited{
color: #999;
}
.value.visited{
color: #999;
}
/* 列表项底部(时间+查看详情) */
.item-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid #f5f5f5;
}
.time-text {
font-size: 12px;
color: #666;
}
.detail-btn {
display: flex;
align-items: center;
font-size: 14px;
color: #666;
}
/* 新增邀请按钮 */
.add-btn {
position: fixed;
bottom: 20px;
left: 15px;
right: 15px;
height: 44px;
line-height: 44px;
background-color: #007aff;
color: #fff;
border-radius: 8px;
text-align: center;
font-size: 16px;
display: flex;
justify-content: center;
align-items: center;
}
.add-text {
margin-left: 5px;
}
</style>